1
0
Fork 0
forked from External/grumble

Add VoiceTarget support (whispers and shouts)

This commit is contained in:
Mikkel Krautz 2011-11-12 20:29:18 +01:00
parent b799eb2bda
commit a42e4d07b4
7 changed files with 276 additions and 18 deletions

View file

@ -54,6 +54,7 @@ GOFILES = \
acl.go \
group.go \
user.go \
voicetarget.go \
freeze.go \
gencert.go \
register.go \

View file

@ -84,3 +84,41 @@ func (channel *Channel) DescriptionBlobHashBytes() (buf []byte) {
}
return buf
}
// Returns a slice of all channels in this channel's
// link chain.
func (channel *Channel) AllLinks() (seen map[int]*Channel) {
seen = make(map[int]*Channel)
walk := []*Channel{channel}
for len(walk) > 0 {
current := walk[len(walk)-1]
walk = walk[0 : len(walk)-1]
for _, linked := range current.Links {
if _, alreadySeen := seen[linked.Id]; !alreadySeen {
seen[linked.Id] = linked
walk = append(walk, linked)
}
}
}
return
}
// Returns a slice of all of this channel's subchannels.
func (channel *Channel) AllSubChannels() (seen map[int]*Channel) {
seen = make(map[int]*Channel)
walk := []*Channel{}
if len(channel.children) > 0 {
walk = append(walk, channel)
for len(walk) > 0 {
current := walk[len(walk)-1]
walk = walk[0 : len(walk)-1]
for _, child := range current.children {
if _, alreadySeen := seen[child.Id]; !alreadySeen {
seen[child.Id] = child
walk = append(walk, child)
}
}
}
}
return
}

View file

@ -41,6 +41,7 @@ type Client struct {
crypt *cryptstate.CryptState
codecs []int32
udp bool
voiceTargets map[uint32]*VoiceTarget
// Ping stats
UdpPingAvg float32
@ -170,6 +171,13 @@ func (client *Client) ForceDisconnect() {
client.disconnect(true)
}
// Clear the client's caches
func (client *Client) ClearCaches() {
for _, vt := range client.voiceTargets {
vt.ClearCache()
}
}
// Reject an authentication attempt
func (client *Client) RejectAuth(rejectType mumbleproto.Reject_RejectType, reason string) {
var reasonString *string = nil

View file

@ -287,7 +287,7 @@ func (server *Server) handleChannelStateMessage(client *Client, msg *Message) {
channel.ACL = append(channel.ACL, acl)
server.ClearACLCache()
server.ClearCaches()
}
chanstate.ChannelId = proto.Uint32(uint32(channel.Id))
@ -862,7 +862,7 @@ func (server *Server) handleUserStateMessage(client *Client, msg *Message) {
}
if userRegistrationChanged {
server.ClearACLCache()
server.ClearCaches()
}
err := server.broadcastProtoMessageWithPredicate(userstate, func(client *Client) bool {
@ -1207,8 +1207,8 @@ func (server *Server) handleAclMessage(client *Client, msg *Message) {
channel.ACL = append(channel.ACL, chanacl)
}
// Clear the server's ACL cache
server.ClearACLCache()
// Clear the Server's caches
server.ClearCaches()
// Regular user?
if !server.HasPermission(client, channel, WritePermission) && client.IsRegistered() || client.HasCertificate() {
@ -1226,7 +1226,7 @@ func (server *Server) handleAclMessage(client *Client, msg *Message) {
channel.ACL = append(channel.ACL, chanacl)
server.ClearACLCache()
server.ClearCaches()
}
// Update freezer
@ -1374,6 +1374,57 @@ func (server *Server) handleUserStatsMessage(client *Client, msg *Message) {
}
}
// Voice target message
func (server *Server) handleVoiceTarget(client *Client, msg *Message) {
vt := &mumbleproto.VoiceTarget{}
err := proto.Unmarshal(msg.buf, vt)
if err != nil {
client.Panic(err.Error())
return
}
if vt.Id == nil {
return
}
id := *vt.Id
if id < 1 || id >= 0x1f {
return
}
if len(vt.Targets) == 0 {
delete(client.voiceTargets, id)
}
for _, target := range vt.Targets {
newTarget := &VoiceTarget{}
for _, session := range target.Session {
newTarget.AddSession(session)
}
if target.ChannelId != nil {
chanid := *target.ChannelId
group := ""
links := false
subchannels := false
if target.Group != nil {
group = *target.Group
}
if target.Links != nil {
links = *target.Links
}
if target.Children != nil {
subchannels = *target.Children
}
newTarget.AddChannel(chanid, subchannels, links, group)
}
if newTarget.IsEmpty() {
delete(client.voiceTargets, id)
} else {
client.voiceTargets[id] = newTarget
}
}
}
// Permission query
func (server *Server) handlePermissionQuery(client *Client, msg *Message) {
query := &mumbleproto.PermissionQuery{}

View file

@ -249,6 +249,7 @@ func (server *Server) NewClient(conn net.Conn) (err error) {
client.state = StateClientConnected
client.udprecv = make(chan []byte)
client.voiceTargets = make(map[uint32]*VoiceTarget)
client.user = nil
@ -345,8 +346,7 @@ func (server *Server) handlerLoop() {
server.handleIncomingMessage(client, msg)
// Voice broadcast
case vb := <-server.voicebroadcast:
server.Printf("VoiceBroadcast!")
if vb.target == 0 {
if vb.target == 0 { // Current channel
channel := vb.client.Channel
for _, client := range channel.clients {
if client != vb.client {
@ -356,6 +356,13 @@ func (server *Server) handlerLoop() {
}
}
}
} else {
target, ok := vb.client.voiceTargets[uint32(vb.target)]
if !ok {
continue
}
target.SendVoiceBroadcast(vb)
}
// Finish client authentication. Send post-authentication
// server info.
@ -413,7 +420,7 @@ func (server *Server) handleAuthenticate(client *Client, msg *Message) {
// by sending an Authenticate message with he contents of their new
// access token list.
client.Tokens = auth.Tokens
server.ClearACLCache()
server.ClearCaches()
if client.state >= StateClientAuthenticated {
return
@ -847,7 +854,7 @@ func (server *Server) handleIncomingMessage(client *Client, msg *Message) {
case mumbleproto.MessageUserList:
server.handleUserList(msg.client, msg)
case mumbleproto.MessageVoiceTarget:
server.Printf("MessageVoiceTarget from client")
server.handleVoiceTarget(msg.client, msg)
case mumbleproto.MessagePermissionQuery:
server.handlePermissionQuery(msg.client, msg)
case mumbleproto.MessageUserStats:
@ -958,9 +965,12 @@ func (server *Server) handleUdpPacket(udpaddr *net.UDPAddr, buf []byte, nread in
match.udprecv <- plain
}
// Clear the ACL cache
func (s *Server) ClearACLCache() {
s.aclcache = NewACLCache()
// Clear the Server's caches
func (server *Server) ClearCaches() {
server.aclcache = NewACLCache()
for _, client := range server.clients {
client.ClearCaches()
}
}
// Helper method for users entering new channels
@ -975,7 +985,7 @@ func (server *Server) userEnterChannel(client *Client, channel *Channel, usersta
}
channel.AddClient(client)
server.ClearACLCache()
server.ClearCaches()
// fixme(mkrautz): Set LastChannel for user in datastore
// fixme(mkrautz): Remove channel if temporary

150
voicetarget.go Normal file
View file

@ -0,0 +1,150 @@
// Copyright (c) 2011 The Grumble Authors
// The use of this source code is goverened by a BSD-style
// license that can be found in the LICENSE-file.
package main
// A VoiceTarget holds information about a single
// VoiceTarget entry of a Client.
type VoiceTarget struct {
sessions []uint32
channels []voiceTargetChannel
directCache map[uint32]*Client
fromChannelsCache map[uint32]*Client
}
type voiceTargetChannel struct {
id uint32
subChannels bool
links bool
onlyGroup string
}
// Add's a client's session to the VoiceTarget
func (vt *VoiceTarget) AddSession(session uint32) {
vt.sessions = append(vt.sessions, session)
}
// Add a channel to the VoiceTarget.
// If subchannels is true, any sent voice packets will also be sent to all subchannels.
// If links is true, any sent voice packets will also be sent to all linked channels.
// If group is a non-empty string, any sent voice packets will only be broadcast to members
// of that group who reside in the channel (or its children or linked channels).
func (vt *VoiceTarget) AddChannel(id uint32, subchannels bool, links bool, group string) {
vt.channels = append(vt.channels, voiceTargetChannel{
id: id,
subChannels: subchannels,
links: links,
onlyGroup: group,
})
}
// Checks whether the VoiceTarget is empty (has no targets)
func (vt *VoiceTarget) IsEmpty() bool {
return len(vt.sessions) == 0 && len(vt.channels) == 0
}
// Clear the VoiceTarget's cache.
func (vt *VoiceTarget) ClearCache() {
vt.directCache = nil
vt.fromChannelsCache = nil
}
// Send the contents of the VoiceBroadcast to all targets specified in the
// VoiceTarget.
func (vt *VoiceTarget) SendVoiceBroadcast(vb *VoiceBroadcast) {
buf := vb.buf
client := vb.client
server := client.server
direct := vt.directCache
fromChannels := vt.fromChannelsCache
if direct == nil || fromChannels == nil {
direct = make(map[uint32]*Client)
fromChannels = make(map[uint32]*Client)
for _, vtc := range vt.channels {
channel := server.Channels[int(vtc.id)]
if channel == nil {
continue
}
if !vtc.subChannels && !vtc.links && vtc.onlyGroup == "" {
if server.HasPermission(client, channel, WhisperPermission) {
for _, target := range channel.clients {
fromChannels[target.Session] = target
}
}
} else {
server.Printf("%v", vtc)
newchans := make(map[int]*Channel)
if vtc.links {
newchans = channel.AllLinks()
} else {
newchans[channel.Id] = channel
}
if vtc.subChannels {
subchans := channel.AllSubChannels()
for k, v := range subchans {
newchans[k] = v
}
}
for _, newchan := range newchans {
if server.HasPermission(client, newchan, WhisperPermission) {
for _, target := range newchan.clients {
if vtc.onlyGroup == "" || GroupMemberCheck(newchan, newchan, vtc.onlyGroup, target) {
fromChannels[target.Session] = target
}
}
}
}
}
}
for _, session := range vt.sessions {
target := server.clients[session]
if target != nil {
if _, alreadyInFromChannels := fromChannels[target.Session]; !alreadyInFromChannels {
direct[target.Session] = target
}
}
}
// Make sure we don't send to ourselves.
delete(direct, client.Session)
delete(fromChannels, client.Session)
if vt.directCache == nil {
vt.directCache = direct
}
if vt.fromChannelsCache == nil {
vt.fromChannelsCache = fromChannels
}
}
kind := buf[0] & 0xe0
if len(fromChannels) > 0 {
for _, target := range fromChannels {
buf[0] = kind | 2
err := target.SendUDP(buf)
if err != nil {
target.Panicf("Unable to send UDP packet: %v", err.Error())
}
}
}
if len(direct) > 0 {
for _, target := range direct {
buf[0] = kind | 2
target.SendUDP(buf)
err := target.SendUDP(buf)
if err != nil {
target.Panicf("Unable to send UDP packet: %v", err.Error())
}
}
}
}