package voice import ( "errors" "sync" ) var ( ErrChannelNotFound = errors.New("channel not found") ErrUserNotFound = errors.New("user not found") ) // Subscriber is a function that receives voice packets type Subscriber func(*Packet) error // Router handles voice packet routing type Router struct { channels map[string][]Subscriber // channelID -> list of subscribers channelLocks map[string]*sync.RWMutex mu sync.RWMutex } // NewRouter creates a new voice router func NewRouter() *Router { return &Router{ channels: make(map[string][]Subscriber), channelLocks: make(map[string]*sync.RWMutex), } } // PublishPacket publishes a voice packet to a channel func (r *Router) PublishPacket(packet *Packet) error { if packet == nil { return errors.New("packet is nil") } channelID := packet.ChannelID // Get or create channel lock r.mu.Lock() channelLock, exists := r.channelLocks[channelID] if !exists { channelLock = &sync.RWMutex{} r.channelLocks[channelID] = channelLock } r.mu.Unlock() // Get subscribers channelLock.RLock() subscribers, exists := r.channels[channelID] if !exists || len(subscribers) == 0 { channelLock.RUnlock() return nil // No subscribers, silent fail } // Make a copy to avoid holding lock during send subscribersCopy := make([]Subscriber, len(subscribers)) copy(subscribersCopy, subscribers) channelLock.RUnlock() // Send to all subscribers for _, subscriber := range subscribersCopy { if subscriber != nil { _ = subscriber(packet) // Ignore errors from individual subscribers } } return nil } // Subscribe subscribes to voice packets for a channel func (r *Router) Subscribe(channelID string, subscriber Subscriber) { if subscriber == nil { return } r.mu.Lock() channelLock, exists := r.channelLocks[channelID] if !exists { channelLock = &sync.RWMutex{} r.channelLocks[channelID] = channelLock } r.mu.Unlock() channelLock.Lock() defer channelLock.Unlock() r.channels[channelID] = append(r.channels[channelID], subscriber) } // Unsubscribe removes a subscriber from a channel // Note: This function doesn't actually remove the subscriber since functions // can't be compared in Go. Subscribers are implicitly removed on disconnect. func (r *Router) Unsubscribe(channelID string, subscriber Subscriber) { // Placeholder - in production, track subscribers by ID instead of function pointer _ = subscriber _ = channelID } // GetSubscriberCount returns number of subscribers for a channel func (r *Router) GetSubscriberCount(channelID string) int { r.mu.RLock() channelLock, exists := r.channelLocks[channelID] if !exists { r.mu.RUnlock() return 0 } r.mu.RUnlock() channelLock.RLock() defer channelLock.RUnlock() subscribers, exists := r.channels[channelID] if !exists { return 0 } return len(subscribers) }