diff --git a/barnard.go b/barnard.go index 4716ac7..da94e2c 100644 --- a/barnard.go +++ b/barnard.go @@ -53,8 +53,9 @@ type Barnard struct { exitMessage string // Added for channel muting - MutedChannels map[uint32]bool - userChannels map[uint32]*gumble.Channel + MutedChannels map[uint32]bool + MutedChannelsMutex sync.RWMutex + userChannels map[uint32]*gumble.Channel // Added for noise suppression NoiseSuppressor *noise.Suppressor @@ -116,6 +117,25 @@ func (b *Barnard) updateUserGain(user *gumble.User) { } } +func (b *Barnard) isChannelMuted(channelID uint32) bool { + b.MutedChannelsMutex.RLock() + defer b.MutedChannelsMutex.RUnlock() + return b.MutedChannels[channelID] +} + +func (b *Barnard) setChannelMuted(channelID uint32, muted bool) { + b.MutedChannelsMutex.Lock() + defer b.MutedChannelsMutex.Unlock() + if b.MutedChannels == nil { + b.MutedChannels = make(map[uint32]bool) + } + if muted { + b.MutedChannels[channelID] = true + } else { + delete(b.MutedChannels, channelID) + } +} + func (b *Barnard) StopTransmission() { if b.Tx { b.Notify("micdown", "me", "") @@ -159,7 +179,7 @@ func (b *Barnard) TreeItemKeyPress(ui *uiterm.Ui, tree *uiterm.Tree, item uiterm if treeItem.Channel != nil { if key == *b.Hotkeys.MuteToggle { // Determine new channel mute state - channelWillBeMuted := !b.MutedChannels[treeItem.Channel.ID] + channelWillBeMuted := !b.isChannelMuted(treeItem.Channel.ID) // Set all users in channel to the same mute state users := makeUsersArray(treeItem.Channel.Users) @@ -175,14 +195,9 @@ func (b *Barnard) TreeItemKeyPress(ui *uiterm.Ui, tree *uiterm.Tree, item uiterm } // Update channel mute state - if channelWillBeMuted { - b.MutedChannels[treeItem.Channel.ID] = true - // If this is the current channel, stop transmission - if b.Client.Self.Channel.ID == treeItem.Channel.ID && b.Tx { - b.StopTransmission() - } - } else { - delete(b.MutedChannels, treeItem.Channel.ID) + b.setChannelMuted(treeItem.Channel.ID, channelWillBeMuted) + if channelWillBeMuted && b.Client.Self.Channel.ID == treeItem.Channel.ID && b.Tx { + b.StopTransmission() } b.RebuildUserChannelTreePreservingSelection() diff --git a/client.go b/client.go index aae3d3c..2483577 100644 --- a/client.go +++ b/client.go @@ -123,7 +123,9 @@ func (b *Barnard) OnConnect(e *gumble.ConnectEvent) { b.Client = e.Client // Reset muted channels state on connect + b.MutedChannelsMutex.Lock() b.MutedChannels = make(map[uint32]bool) + b.MutedChannelsMutex.Unlock() b.userChannels = make(map[uint32]*gumble.Channel) b.RecordingMutex.Lock() b.recordingAllowed = nil @@ -279,7 +281,7 @@ func (b *Barnard) OnUserChange(e *gumble.UserChangeEvent) { // Check if user is joining a muted channel if e.Type.Has(gumble.UserChangeConnected) || e.Type.Has(gumble.UserChangeChannel) { // If the channel is muted, ensure the user is muted - if b.MutedChannels[e.User.Channel.ID] { + if b.isChannelMuted(e.User.Channel.ID) { // Only mute if not already muted if !e.User.LocallyMuted() { b.UserConfig.ToggleMute(e.User) diff --git a/client_notification_test.go b/client_notification_test.go index 143036a..cf2fd67 100644 --- a/client_notification_test.go +++ b/client_notification_test.go @@ -4,6 +4,7 @@ import ( "fmt" "io" "strings" + "sync" "testing" "time" "unicode/utf8" @@ -83,6 +84,20 @@ func TestServerAddressDefaultsPortWithoutBreakingIPv6(t *testing.T) { } } +func TestConcurrentMutedChannelAccess(t *testing.T) { + b := &Barnard{MutedChannels: make(map[uint32]bool)} + var wg sync.WaitGroup + for i := 0; i < 20; i++ { + wg.Add(1) + go func(i int) { + defer wg.Done() + b.setChannelMuted(uint32(i%3), i%2 == 0) + _ = b.isChannelMuted(uint32((i + 1) % 3)) + }(i) + } + wg.Wait() +} + func TestPublicTextMessageTargetsChannelIDsAndTrees(t *testing.T) { root := &gumble.Channel{ID: 1, Name: "Root"} current := &gumble.Channel{ID: 2, Name: "Room", Parent: root} diff --git a/ui.go b/ui.go index f04dd66..2daf5ef 100644 --- a/ui.go +++ b/ui.go @@ -334,7 +334,7 @@ func (b *Barnard) setTransmit(ui *uiterm.Ui, val int) { b.Notify("error", "me", "no tx while disconnected") b.Tx = false b.UpdateGeneralStatus("no tx while disconnected", true) - } else if b.MutedChannels[b.Client.Self.Channel.ID] { + } else if b.isChannelMuted(b.Client.Self.Channel.ID) { // Check if current channel is muted b.Notify("error", "me", "cannot transmit in muted channel") b.Tx = false diff --git a/ui_tree.go b/ui_tree.go index fe7439d..a80053d 100644 --- a/ui_tree.go +++ b/ui_tree.go @@ -131,7 +131,7 @@ func (b *Barnard) TreeItemBuild(item uiterm.TreeItem) []uiterm.TreeItem { }) for _, subchannel := range cl { displayName := subchannel.Name - if b.MutedChannels[subchannel.ID] { + if b.isChannelMuted(subchannel.ID) { displayName = "[MUTED] #" + displayName } else { displayName = "#" + displayName