package pnyx import ( "slices" "fmt" ) type ChannelID byte const ( MODE_CHANNEL ModeID = iota MODE_RAW MODE_AUDIO CHANNEL_JOIN byte = iota CHANNEL_LEAVE CHANNEL_MEMBERS AUDIO_SET_SAMPLE_RATE = iota AUDIO_GET_SAMPLE_RATE RAW_DATA = iota ) type ModeID uint8 type CommandID uint8 type Permission string type Channel struct { id ChannelID name string modes map[ModeID]Mode sessions []SessionID } func(channel *Channel) Data(session *Session, mode ModeID, data []byte) []SendPacket { m, has_mode := channel.modes[mode] if has_mode == false { return nil } else { return m.Data(session, channel, data) } } func(channel *Channel) Command(session *Session, packet ChannelCommandPacket) ([]SendPacket, error) { if packet.Mode == MODE_CHANNEL { switch packet.Command { case CHANNEL_JOIN: if slices.Contains(channel.sessions, session.ID) { return nil, fmt.Errorf("Session %s already in channel %d, can't join", session.ID, channel.id) } else { channel.sessions = append(channel.sessions, session.ID) return nil, nil } case CHANNEL_LEAVE: idx := slices.Index(channel.sessions, session.ID) if idx == -1 { return nil, fmt.Errorf("Session %s not in channel %d, can't leave", session.ID, channel.id) } else { channel.sessions = slices.Delete(channel.sessions, idx, idx+1) return nil, nil } default: return nil, fmt.Errorf("Unknown MODE_CHANNEL command: 0x%02x", packet.Command) } } else { mode, has_mode := channel.modes[packet.Mode] if has_mode == false { return nil, fmt.Errorf("Channel has no mode 0x%02x", mode) } else { return mode.Command(session, channel, packet) } } } type SendPacket struct { Packet *Packet Session SessionID } type Mode interface { // Process takes incoming packets from a session and returns a list of packets to send Command(session *Session, channel *Channel, packet ChannelCommandPacket) ([]SendPacket, error) Data(session *Session, channel *Channel, data []byte) []SendPacket } func multiplex_without_sender(origin SessionID, packet *Packet, sessions []SessionID) []SendPacket { send_packets := make([]SendPacket, len(sessions) - 1) i := 0 for _, session_id := range(sessions) { if session_id == origin { continue } send_packets[i] = SendPacket{ Packet: packet, Session: session_id, } i += 1 } return send_packets } func multiplex(packet *Packet, sessions []SessionID) []SendPacket { send_packets := make([]SendPacket, len(sessions)) for i, session_id := range(sessions) { send_packets[i] = SendPacket{ Packet: packet, Session: session_id, } } return send_packets } type RawMode struct { } func(mode *RawMode) Command(session *Session, channel *Channel, packet ChannelCommandPacket) ([]SendPacket, error) { return nil, fmt.Errorf("unknown raw mode command 0x%02x", packet.Command) } func(mode *RawMode) Data(session *Session, channel *Channel, data []byte) []SendPacket { if slices.Contains(channel.sessions, session.ID) { new_packet := NewChannelPeerPacket(session.Peer, channel.id, MODE_RAW, data) return multiplex_without_sender(session.ID, new_packet, channel.sessions) } return nil } type SampleRate byte const ( SAMPLE_RATE_UNSET SampleRate = 0xFF SAMPLE_RATE_24KHZ = 0x01 SAMPLE_RATE_48KHZ = 0x02 ) type AudioMode struct { SampleRate SampleRate } func(mode *AudioMode) Command(session *Session, channel *Channel, packet ChannelCommandPacket) ([]SendPacket, error) { switch packet.Command { case AUDIO_SET_SAMPLE_RATE: if len(packet.Data) == 1 { switch SampleRate(packet.Data[0]) { case SAMPLE_RATE_24KHZ: fallthrough case SAMPLE_RATE_48KHZ: mode.SampleRate = SampleRate(packet.Data[0]) update_packet := NewChannelCommandPacket(packet.ReqID, channel.id, MODE_AUDIO, AUDIO_SET_SAMPLE_RATE, packet.Data) return multiplex(update_packet, channel.sessions), nil default: return nil, fmt.Errorf("Invalid sample rate: %x", packet.Data[0]) } } else { return nil, fmt.Errorf("Invalid AUDIO_SET_SAMPLE_RATE payload: %x", packet.Data) } case AUDIO_GET_SAMPLE_RATE: return []SendPacket{{ Packet: NewChannelCommandPacket(packet.ReqID, channel.id, MODE_AUDIO, AUDIO_SET_SAMPLE_RATE, []byte{byte(mode.SampleRate)}), Session: session.ID, }}, nil default: return nil, fmt.Errorf("unknown audio mode command 0x%02x", packet.Command) } } func(mode *AudioMode) Data(session *Session, channel *Channel, data []byte) []SendPacket { if slices.Contains(channel.sessions, session.ID) { new_packet := NewChannelPeerPacket(session.Peer, channel.id, MODE_AUDIO, data) return multiplex_without_sender(session.ID, new_packet, channel.sessions) } return nil }