pnyx/channel.go

177 lines
4.8 KiB
Go

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
}