mirror of
https://github.com/cwinfo/matterbridge.git
synced 2025-07-06 21:14:03 +00:00
Add Mumble support (#1245)
This commit is contained in:
37
vendor/layeh.com/gumble/gumble/MumbleProto/LICENSE
generated
vendored
Normal file
37
vendor/layeh.com/gumble/gumble/MumbleProto/LICENSE
generated
vendored
Normal file
@ -0,0 +1,37 @@
|
||||
Copyright (C) 2005-2013, Thorvald Natvig <thorvald@natvig.com>
|
||||
Copyright (C) 2007, Stefan Gehn <mETz AT gehn DOT net>
|
||||
Copyright (C) 2007, Sebastian Schlingmann <mit_service@users.sourceforge.net>
|
||||
Copyright (C) 2008-2013, Mikkel Krautz <mikkel@krautz.dk>
|
||||
Copyright (C) 2008, Andreas Messer <andi@bupfen.de>
|
||||
Copyright (C) 2007, Trenton Schulz
|
||||
Copyright (C) 2008-2013, Stefan Hacker <dd0t@users.sourceforge.net>
|
||||
Copyright (C) 2008-2011, Snares <snares@users.sourceforge.net>
|
||||
Copyright (C) 2009-2013, Benjamin Jemlich <pcgod@users.sourceforge.net>
|
||||
Copyright (C) 2009-2013, Kissaki <kissaki@gmx.de>
|
||||
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions
|
||||
are met:
|
||||
|
||||
- Redistributions of source code must retain the above copyright notice,
|
||||
this list of conditions and the following disclaimer.
|
||||
- Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
- Neither the name of the Mumble Developers nor the names of its
|
||||
contributors may be used to endorse or promote products derived from this
|
||||
software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
`AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR
|
||||
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
3085
vendor/layeh.com/gumble/gumble/MumbleProto/Mumble.pb.go
generated
vendored
Normal file
3085
vendor/layeh.com/gumble/gumble/MumbleProto/Mumble.pb.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
2
vendor/layeh.com/gumble/gumble/MumbleProto/generate.go
generated
vendored
Normal file
2
vendor/layeh.com/gumble/gumble/MumbleProto/generate.go
generated
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
//go:generate go run generate_main.go
|
||||
package MumbleProto
|
16
vendor/layeh.com/gumble/gumble/accesstokens.go
generated
vendored
Normal file
16
vendor/layeh.com/gumble/gumble/accesstokens.go
generated
vendored
Normal file
@ -0,0 +1,16 @@
|
||||
package gumble
|
||||
|
||||
import (
|
||||
"layeh.com/gumble/gumble/MumbleProto"
|
||||
)
|
||||
|
||||
// AccessTokens are additional passwords that can be provided to the server to
|
||||
// gain access to restricted channels.
|
||||
type AccessTokens []string
|
||||
|
||||
func (a AccessTokens) writeMessage(client *Client) error {
|
||||
packet := MumbleProto.Authenticate{
|
||||
Tokens: a,
|
||||
}
|
||||
return client.Conn.WriteProto(&packet)
|
||||
}
|
116
vendor/layeh.com/gumble/gumble/acl.go
generated
vendored
Normal file
116
vendor/layeh.com/gumble/gumble/acl.go
generated
vendored
Normal file
@ -0,0 +1,116 @@
|
||||
package gumble
|
||||
|
||||
import (
|
||||
"github.com/golang/protobuf/proto"
|
||||
"layeh.com/gumble/gumble/MumbleProto"
|
||||
)
|
||||
|
||||
// ACL contains a list of ACLGroups and ACLRules linked to a channel.
|
||||
type ACL struct {
|
||||
// The channel to which the ACL belongs.
|
||||
Channel *Channel
|
||||
// The ACL's groups.
|
||||
Groups []*ACLGroup
|
||||
// The ACL's rules.
|
||||
Rules []*ACLRule
|
||||
// Does the ACL inherits the parent channel's ACLs?
|
||||
Inherits bool
|
||||
}
|
||||
|
||||
func (a *ACL) writeMessage(client *Client) error {
|
||||
packet := MumbleProto.ACL{
|
||||
ChannelId: &a.Channel.ID,
|
||||
Groups: make([]*MumbleProto.ACL_ChanGroup, len(a.Groups)),
|
||||
Acls: make([]*MumbleProto.ACL_ChanACL, len(a.Rules)),
|
||||
InheritAcls: &a.Inherits,
|
||||
Query: proto.Bool(false),
|
||||
}
|
||||
|
||||
for i, group := range a.Groups {
|
||||
packet.Groups[i] = &MumbleProto.ACL_ChanGroup{
|
||||
Name: &group.Name,
|
||||
Inherit: &group.InheritUsers,
|
||||
Inheritable: &group.Inheritable,
|
||||
Add: make([]uint32, 0, len(group.UsersAdd)),
|
||||
Remove: make([]uint32, 0, len(group.UsersRemove)),
|
||||
}
|
||||
for _, user := range group.UsersAdd {
|
||||
packet.Groups[i].Add = append(packet.Groups[i].Add, user.UserID)
|
||||
}
|
||||
for _, user := range group.UsersRemove {
|
||||
packet.Groups[i].Remove = append(packet.Groups[i].Remove, user.UserID)
|
||||
}
|
||||
}
|
||||
|
||||
for i, rule := range a.Rules {
|
||||
packet.Acls[i] = &MumbleProto.ACL_ChanACL{
|
||||
ApplyHere: &rule.AppliesCurrent,
|
||||
ApplySubs: &rule.AppliesChildren,
|
||||
Grant: proto.Uint32(uint32(rule.Granted)),
|
||||
Deny: proto.Uint32(uint32(rule.Denied)),
|
||||
}
|
||||
if rule.User != nil {
|
||||
packet.Acls[i].UserId = &rule.User.UserID
|
||||
}
|
||||
if rule.Group != nil {
|
||||
packet.Acls[i].Group = &rule.Group.Name
|
||||
}
|
||||
}
|
||||
|
||||
return client.Conn.WriteProto(&packet)
|
||||
}
|
||||
|
||||
// ACLUser is a registered user who is part of or can be part of an ACL group
|
||||
// or rule.
|
||||
type ACLUser struct {
|
||||
// The user ID of the user.
|
||||
UserID uint32
|
||||
// The name of the user.
|
||||
Name string
|
||||
}
|
||||
|
||||
// ACLGroup is a named group of registered users which can be used in an
|
||||
// ACLRule.
|
||||
type ACLGroup struct {
|
||||
// The ACL group name.
|
||||
Name string
|
||||
// Is the group inherited from the parent channel's ACL?
|
||||
Inherited bool
|
||||
// Are group members are inherited from the parent channel's ACL?
|
||||
InheritUsers bool
|
||||
// Can the group be inherited by child channels?
|
||||
Inheritable bool
|
||||
// The users who are explicitly added to, explicitly removed from, and
|
||||
// inherited into the group.
|
||||
UsersAdd, UsersRemove, UsersInherited map[uint32]*ACLUser
|
||||
}
|
||||
|
||||
// ACL group names that are built-in.
|
||||
const (
|
||||
ACLGroupEveryone = "all"
|
||||
ACLGroupAuthenticated = "auth"
|
||||
ACLGroupInsideChannel = "in"
|
||||
ACLGroupOutsideChannel = "out"
|
||||
)
|
||||
|
||||
// ACLRule is a set of granted and denied permissions given to an ACLUser or
|
||||
// ACLGroup.
|
||||
type ACLRule struct {
|
||||
// Does the rule apply to the channel in which the rule is defined?
|
||||
AppliesCurrent bool
|
||||
// Does the rule apply to the children of the channel in which the rule is
|
||||
// defined?
|
||||
AppliesChildren bool
|
||||
// Is the rule inherited from the parent channel's ACL?
|
||||
Inherited bool
|
||||
|
||||
// The permissions granted by the rule.
|
||||
Granted Permission
|
||||
// The permissions denied by the rule.
|
||||
Denied Permission
|
||||
|
||||
// The ACL user the rule applies to. Can be nil.
|
||||
User *ACLUser
|
||||
// The ACL group the rule applies to. Can be nil.
|
||||
Group *ACLGroup
|
||||
}
|
85
vendor/layeh.com/gumble/gumble/audio.go
generated
vendored
Normal file
85
vendor/layeh.com/gumble/gumble/audio.go
generated
vendored
Normal file
@ -0,0 +1,85 @@
|
||||
package gumble
|
||||
|
||||
import (
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
// AudioSampleRate is the audio sample rate (in hertz) for incoming and
|
||||
// outgoing audio.
|
||||
AudioSampleRate = 48000
|
||||
|
||||
// AudioDefaultInterval is the default interval that audio packets are sent
|
||||
// at.
|
||||
AudioDefaultInterval = 10 * time.Millisecond
|
||||
|
||||
// AudioDefaultFrameSize is the number of audio frames that should be sent in
|
||||
// a 10ms window.
|
||||
AudioDefaultFrameSize = AudioSampleRate / 100
|
||||
|
||||
// AudioMaximumFrameSize is the maximum audio frame size from another user
|
||||
// that will be processed.
|
||||
AudioMaximumFrameSize = AudioSampleRate / 1000 * 60
|
||||
|
||||
// AudioDefaultDataBytes is the default number of bytes that an audio frame
|
||||
// can use.
|
||||
AudioDefaultDataBytes = 40
|
||||
|
||||
// AudioChannels is the number of audio channels that are contained in an
|
||||
// audio stream.
|
||||
AudioChannels = 1
|
||||
)
|
||||
|
||||
// AudioListener is the interface that must be implemented by types wishing to
|
||||
// receive incoming audio data from the server.
|
||||
//
|
||||
// OnAudioStream is called when an audio stream for a user starts. It is the
|
||||
// implementer's responsibility to continuously process AudioStreamEvent.C
|
||||
// until it is closed.
|
||||
type AudioListener interface {
|
||||
OnAudioStream(e *AudioStreamEvent)
|
||||
}
|
||||
|
||||
// AudioStreamEvent is event that is passed to AudioListener.OnAudioStream.
|
||||
type AudioStreamEvent struct {
|
||||
Client *Client
|
||||
User *User
|
||||
C <-chan *AudioPacket
|
||||
}
|
||||
|
||||
// AudioBuffer is a slice of PCM audio samples.
|
||||
type AudioBuffer []int16
|
||||
|
||||
func (a AudioBuffer) writeAudio(client *Client, seq int64, final bool) error {
|
||||
encoder := client.AudioEncoder
|
||||
if encoder == nil {
|
||||
return nil
|
||||
}
|
||||
dataBytes := client.Config.AudioDataBytes
|
||||
raw, err := encoder.Encode(a, len(a), dataBytes)
|
||||
if final {
|
||||
defer encoder.Reset()
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var targetID byte
|
||||
if target := client.VoiceTarget; target != nil {
|
||||
targetID = byte(target.ID)
|
||||
}
|
||||
// TODO: re-enable positional audio
|
||||
return client.Conn.WriteAudio(byte(4), targetID, seq, final, raw, nil, nil, nil)
|
||||
}
|
||||
|
||||
// AudioPacket contains incoming audio samples and information.
|
||||
type AudioPacket struct {
|
||||
Client *Client
|
||||
Sender *User
|
||||
Target *VoiceTarget
|
||||
|
||||
AudioBuffer
|
||||
|
||||
HasPosition bool
|
||||
X, Y, Z float32
|
||||
}
|
55
vendor/layeh.com/gumble/gumble/audiocodec.go
generated
vendored
Normal file
55
vendor/layeh.com/gumble/gumble/audiocodec.go
generated
vendored
Normal file
@ -0,0 +1,55 @@
|
||||
package gumble
|
||||
|
||||
import (
|
||||
"sync"
|
||||
)
|
||||
|
||||
const (
|
||||
audioCodecIDOpus = 4
|
||||
)
|
||||
|
||||
var (
|
||||
audioCodecsLock sync.Mutex
|
||||
audioCodecs [8]AudioCodec
|
||||
)
|
||||
|
||||
// RegisterAudioCodec registers an audio codec that can be used for encoding
|
||||
// and decoding outgoing and incoming audio data. The function panics if the
|
||||
// ID is invalid.
|
||||
func RegisterAudioCodec(id int, codec AudioCodec) {
|
||||
audioCodecsLock.Lock()
|
||||
defer audioCodecsLock.Unlock()
|
||||
|
||||
if id < 0 || id >= len(audioCodecs) {
|
||||
panic("id out of range")
|
||||
}
|
||||
audioCodecs[id] = codec
|
||||
}
|
||||
|
||||
func getAudioCodec(id int) AudioCodec {
|
||||
audioCodecsLock.Lock()
|
||||
defer audioCodecsLock.Unlock()
|
||||
return audioCodecs[id]
|
||||
}
|
||||
|
||||
// AudioCodec can create a encoder and a decoder for outgoing and incoming
|
||||
// data.
|
||||
type AudioCodec interface {
|
||||
ID() int
|
||||
NewEncoder() AudioEncoder
|
||||
NewDecoder() AudioDecoder
|
||||
}
|
||||
|
||||
// AudioEncoder encodes a chunk of PCM audio samples to a certain type.
|
||||
type AudioEncoder interface {
|
||||
ID() int
|
||||
Encode(pcm []int16, mframeSize, maxDataBytes int) ([]byte, error)
|
||||
Reset()
|
||||
}
|
||||
|
||||
// AudioDecoder decodes an encoded byte slice to a chunk of PCM audio samples.
|
||||
type AudioDecoder interface {
|
||||
ID() int
|
||||
Decode(data []byte, frameSize int) ([]int16, error)
|
||||
Reset()
|
||||
}
|
46
vendor/layeh.com/gumble/gumble/audiolisteners.go
generated
vendored
Normal file
46
vendor/layeh.com/gumble/gumble/audiolisteners.go
generated
vendored
Normal file
@ -0,0 +1,46 @@
|
||||
package gumble
|
||||
|
||||
type audioEventItem struct {
|
||||
parent *AudioListeners
|
||||
prev, next *audioEventItem
|
||||
listener AudioListener
|
||||
streams map[*User]chan *AudioPacket
|
||||
}
|
||||
|
||||
func (e *audioEventItem) Detach() {
|
||||
if e.prev == nil {
|
||||
e.parent.head = e.next
|
||||
} else {
|
||||
e.prev.next = e.next
|
||||
}
|
||||
if e.next == nil {
|
||||
e.parent.tail = e.prev
|
||||
} else {
|
||||
e.next.prev = e.prev
|
||||
}
|
||||
}
|
||||
|
||||
// AudioListeners is a list of audio listeners. Each attached listener is
|
||||
// called in sequence when a new user audio stream begins.
|
||||
type AudioListeners struct {
|
||||
head, tail *audioEventItem
|
||||
}
|
||||
|
||||
// Attach adds a new audio listener to the end of the current list of listeners.
|
||||
func (e *AudioListeners) Attach(listener AudioListener) Detacher {
|
||||
item := &audioEventItem{
|
||||
parent: e,
|
||||
prev: e.tail,
|
||||
listener: listener,
|
||||
streams: make(map[*User]chan *AudioPacket),
|
||||
}
|
||||
if e.head == nil {
|
||||
e.head = item
|
||||
}
|
||||
if e.tail == nil {
|
||||
e.tail = item
|
||||
} else {
|
||||
e.tail.next = item
|
||||
}
|
||||
return item
|
||||
}
|
101
vendor/layeh.com/gumble/gumble/bans.go
generated
vendored
Normal file
101
vendor/layeh.com/gumble/gumble/bans.go
generated
vendored
Normal file
@ -0,0 +1,101 @@
|
||||
package gumble
|
||||
|
||||
import (
|
||||
"net"
|
||||
"time"
|
||||
|
||||
"github.com/golang/protobuf/proto"
|
||||
"layeh.com/gumble/gumble/MumbleProto"
|
||||
)
|
||||
|
||||
// BanList is a list of server ban entries.
|
||||
//
|
||||
// Whenever a ban is changed, it does not come into effect until the ban list
|
||||
// is sent back to the server.
|
||||
type BanList []*Ban
|
||||
|
||||
// Add creates a new ban list entry with the given parameters.
|
||||
func (b *BanList) Add(address net.IP, mask net.IPMask, reason string, duration time.Duration) *Ban {
|
||||
ban := &Ban{
|
||||
Address: address,
|
||||
Mask: mask,
|
||||
Reason: reason,
|
||||
Duration: duration,
|
||||
}
|
||||
*b = append(*b, ban)
|
||||
return ban
|
||||
}
|
||||
|
||||
// Ban represents an entry in the server ban list.
|
||||
//
|
||||
// This type should not be initialized manually. Instead, create new ban
|
||||
// entries using BanList.Add().
|
||||
type Ban struct {
|
||||
// The banned IP address.
|
||||
Address net.IP
|
||||
// The IP mask that the ban applies to.
|
||||
Mask net.IPMask
|
||||
// The name of the banned user.
|
||||
Name string
|
||||
// The certificate hash of the banned user.
|
||||
Hash string
|
||||
// The reason for the ban.
|
||||
Reason string
|
||||
// The start time from which the ban applies.
|
||||
Start time.Time
|
||||
// How long the ban is for.
|
||||
Duration time.Duration
|
||||
|
||||
unban bool
|
||||
}
|
||||
|
||||
// SetAddress sets the banned IP address.
|
||||
func (b *Ban) SetAddress(address net.IP) {
|
||||
b.Address = address
|
||||
}
|
||||
|
||||
// SetMask sets the IP mask that the ban applies to.
|
||||
func (b *Ban) SetMask(mask net.IPMask) {
|
||||
b.Mask = mask
|
||||
}
|
||||
|
||||
// SetReason changes the reason for the ban.
|
||||
func (b *Ban) SetReason(reason string) {
|
||||
b.Reason = reason
|
||||
}
|
||||
|
||||
// SetDuration changes the duration of the ban.
|
||||
func (b *Ban) SetDuration(duration time.Duration) {
|
||||
b.Duration = duration
|
||||
}
|
||||
|
||||
// Unban will unban the user from the server.
|
||||
func (b *Ban) Unban() {
|
||||
b.unban = true
|
||||
}
|
||||
|
||||
// Ban will ban the user from the server. This is only useful if Unban() was
|
||||
// called on the ban entry.
|
||||
func (b *Ban) Ban() {
|
||||
b.unban = false
|
||||
}
|
||||
|
||||
func (b BanList) writeMessage(client *Client) error {
|
||||
packet := MumbleProto.BanList{
|
||||
Query: proto.Bool(false),
|
||||
}
|
||||
|
||||
for _, ban := range b {
|
||||
if !ban.unban {
|
||||
maskSize, _ := ban.Mask.Size()
|
||||
packet.Bans = append(packet.Bans, &MumbleProto.BanList_BanEntry{
|
||||
Address: ban.Address,
|
||||
Mask: proto.Uint32(uint32(maskSize)),
|
||||
Reason: &ban.Reason,
|
||||
Duration: proto.Uint32(uint32(ban.Duration / time.Second)),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
return client.Conn.WriteProto(&packet)
|
||||
}
|
207
vendor/layeh.com/gumble/gumble/channel.go
generated
vendored
Normal file
207
vendor/layeh.com/gumble/gumble/channel.go
generated
vendored
Normal file
@ -0,0 +1,207 @@
|
||||
package gumble
|
||||
|
||||
import (
|
||||
"github.com/golang/protobuf/proto"
|
||||
"layeh.com/gumble/gumble/MumbleProto"
|
||||
)
|
||||
|
||||
// Channel represents a channel in the server's channel tree.
|
||||
type Channel struct {
|
||||
// The channel's unique ID.
|
||||
ID uint32
|
||||
// The channel's name.
|
||||
Name string
|
||||
// The channel's parent. nil if the channel is the root channel.
|
||||
Parent *Channel
|
||||
// The channels directly underneath the channel.
|
||||
Children Channels
|
||||
// The channels that are linked to the channel.
|
||||
Links Channels
|
||||
// The users currently in the channel.
|
||||
Users Users
|
||||
// The channel's description. Contains the empty string if the channel does
|
||||
// not have a description, or if it needs to be requested.
|
||||
Description string
|
||||
// The channel's description hash. nil if Channel.Description has
|
||||
// been populated.
|
||||
DescriptionHash []byte
|
||||
// The maximum number of users allowed in the channel. If the value is zero,
|
||||
// the maximum number of users per-channel is dictated by the server's
|
||||
// "usersperchannel" setting.
|
||||
MaxUsers uint32
|
||||
// The position at which the channel should be displayed in an ordered list.
|
||||
Position int32
|
||||
// Is the channel temporary?
|
||||
Temporary bool
|
||||
|
||||
client *Client
|
||||
}
|
||||
|
||||
// IsRoot returns true if the channel is the server's root channel.
|
||||
func (c *Channel) IsRoot() bool {
|
||||
return c.ID == 0
|
||||
}
|
||||
|
||||
// Add will add a sub-channel to the given channel.
|
||||
func (c *Channel) Add(name string, temporary bool) {
|
||||
packet := MumbleProto.ChannelState{
|
||||
Parent: &c.ID,
|
||||
Name: &name,
|
||||
Temporary: &temporary,
|
||||
}
|
||||
c.client.Conn.WriteProto(&packet)
|
||||
}
|
||||
|
||||
// Remove will remove the given channel and all sub-channels from the server's
|
||||
// channel tree.
|
||||
func (c *Channel) Remove() {
|
||||
packet := MumbleProto.ChannelRemove{
|
||||
ChannelId: &c.ID,
|
||||
}
|
||||
c.client.Conn.WriteProto(&packet)
|
||||
}
|
||||
|
||||
// SetName will set the name of the channel. This will have no effect if the
|
||||
// channel is the server's root channel.
|
||||
func (c *Channel) SetName(name string) {
|
||||
packet := MumbleProto.ChannelState{
|
||||
ChannelId: &c.ID,
|
||||
Name: &name,
|
||||
}
|
||||
c.client.Conn.WriteProto(&packet)
|
||||
}
|
||||
|
||||
// SetDescription will set the description of the channel.
|
||||
func (c *Channel) SetDescription(description string) {
|
||||
packet := MumbleProto.ChannelState{
|
||||
ChannelId: &c.ID,
|
||||
Description: &description,
|
||||
}
|
||||
c.client.Conn.WriteProto(&packet)
|
||||
}
|
||||
|
||||
// SetPosition will set the position of the channel.
|
||||
func (c *Channel) SetPosition(position int32) {
|
||||
packet := MumbleProto.ChannelState{
|
||||
ChannelId: &c.ID,
|
||||
Position: &position,
|
||||
}
|
||||
c.client.Conn.WriteProto(&packet)
|
||||
}
|
||||
|
||||
// SetMaxUsers will set the maximum number of users allowed in the channel.
|
||||
func (c *Channel) SetMaxUsers(maxUsers uint32) {
|
||||
packet := MumbleProto.ChannelState{
|
||||
ChannelId: &c.ID,
|
||||
MaxUsers: &maxUsers,
|
||||
}
|
||||
c.client.Conn.WriteProto(&packet)
|
||||
}
|
||||
|
||||
// Find returns a channel whose path (by channel name) from the current channel
|
||||
// is equal to the arguments passed.
|
||||
//
|
||||
// For example, given the following server channel tree:
|
||||
// Root
|
||||
// Child 1
|
||||
// Child 2
|
||||
// Child 2.1
|
||||
// Child 2.2
|
||||
// Child 2.2.1
|
||||
// Child 3
|
||||
// To get the "Child 2.2.1" channel:
|
||||
// root.Find("Child 2", "Child 2.2", "Child 2.2.1")
|
||||
func (c *Channel) Find(names ...string) *Channel {
|
||||
if len(names) == 0 {
|
||||
return c
|
||||
}
|
||||
for _, child := range c.Children {
|
||||
if child.Name == names[0] {
|
||||
return child.Find(names[1:]...)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// RequestDescription requests that the actual channel description
|
||||
// (i.e. non-hashed) be sent to the client.
|
||||
func (c *Channel) RequestDescription() {
|
||||
packet := MumbleProto.RequestBlob{
|
||||
ChannelDescription: []uint32{c.ID},
|
||||
}
|
||||
c.client.Conn.WriteProto(&packet)
|
||||
}
|
||||
|
||||
// RequestACL requests that the channel's ACL to be sent to the client.
|
||||
func (c *Channel) RequestACL() {
|
||||
packet := MumbleProto.ACL{
|
||||
ChannelId: &c.ID,
|
||||
Query: proto.Bool(true),
|
||||
}
|
||||
c.client.Conn.WriteProto(&packet)
|
||||
}
|
||||
|
||||
// RequestPermission requests that the channel's permission information to be
|
||||
// sent to the client.
|
||||
//
|
||||
// Note: the server will not reply to the request if the client has up-to-date
|
||||
// permission information.
|
||||
func (c *Channel) RequestPermission() {
|
||||
packet := MumbleProto.PermissionQuery{
|
||||
ChannelId: &c.ID,
|
||||
}
|
||||
c.client.Conn.WriteProto(&packet)
|
||||
}
|
||||
|
||||
// Send will send a text message to the channel.
|
||||
func (c *Channel) Send(message string, recursive bool) {
|
||||
textMessage := TextMessage{
|
||||
Message: message,
|
||||
}
|
||||
if recursive {
|
||||
textMessage.Trees = []*Channel{c}
|
||||
} else {
|
||||
textMessage.Channels = []*Channel{c}
|
||||
}
|
||||
c.client.Send(&textMessage)
|
||||
}
|
||||
|
||||
// Permission returns the permissions the user has in the channel, or nil if
|
||||
// the permissions are unknown.
|
||||
func (c *Channel) Permission() *Permission {
|
||||
return c.client.permissions[c.ID]
|
||||
}
|
||||
|
||||
// Link links the given channels to the channel.
|
||||
func (c *Channel) Link(channel ...*Channel) {
|
||||
packet := MumbleProto.ChannelState{
|
||||
ChannelId: &c.ID,
|
||||
LinksAdd: make([]uint32, len(channel)),
|
||||
}
|
||||
for i, ch := range channel {
|
||||
packet.LinksAdd[i] = ch.ID
|
||||
}
|
||||
c.client.Conn.WriteProto(&packet)
|
||||
}
|
||||
|
||||
// Unlink unlinks the given channels from the channel. If no arguments are
|
||||
// passed, all linked channels are unlinked.
|
||||
func (c *Channel) Unlink(channel ...*Channel) {
|
||||
packet := MumbleProto.ChannelState{
|
||||
ChannelId: &c.ID,
|
||||
}
|
||||
if len(channel) == 0 {
|
||||
packet.LinksRemove = make([]uint32, len(c.Links))
|
||||
i := 0
|
||||
for channelID := range c.Links {
|
||||
packet.LinksRemove[i] = channelID
|
||||
i++
|
||||
}
|
||||
} else {
|
||||
packet.LinksRemove = make([]uint32, len(channel))
|
||||
for i, ch := range channel {
|
||||
packet.LinksRemove[i] = ch.ID
|
||||
}
|
||||
}
|
||||
c.client.Conn.WriteProto(&packet)
|
||||
}
|
28
vendor/layeh.com/gumble/gumble/channels.go
generated
vendored
Normal file
28
vendor/layeh.com/gumble/gumble/channels.go
generated
vendored
Normal file
@ -0,0 +1,28 @@
|
||||
package gumble
|
||||
|
||||
// Channels is a map of server channels.
|
||||
type Channels map[uint32]*Channel
|
||||
|
||||
// create adds a new channel with the given id to the collection. If a channel
|
||||
// with the given id already exists, it is overwritten.
|
||||
func (c Channels) create(id uint32) *Channel {
|
||||
channel := &Channel{
|
||||
ID: id,
|
||||
Links: Channels{},
|
||||
Children: Channels{},
|
||||
Users: Users{},
|
||||
}
|
||||
c[id] = channel
|
||||
return channel
|
||||
}
|
||||
|
||||
// Find returns a channel whose path (by channel name) from the server root
|
||||
// channel is equal to the arguments passed. nil is returned if c does not
|
||||
// containt the root channel.
|
||||
func (c Channels) Find(names ...string) *Channel {
|
||||
root := c[0]
|
||||
if names == nil || root == nil {
|
||||
return root
|
||||
}
|
||||
return root.Find(names...)
|
||||
}
|
289
vendor/layeh.com/gumble/gumble/client.go
generated
vendored
Normal file
289
vendor/layeh.com/gumble/gumble/client.go
generated
vendored
Normal file
@ -0,0 +1,289 @@
|
||||
package gumble
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"errors"
|
||||
"math"
|
||||
"net"
|
||||
"runtime"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"github.com/golang/protobuf/proto"
|
||||
"layeh.com/gumble/gumble/MumbleProto"
|
||||
)
|
||||
|
||||
// State is the current state of the client's connection to the server.
|
||||
type State int
|
||||
|
||||
const (
|
||||
// StateDisconnected means the client is no longer connected to the server.
|
||||
StateDisconnected State = iota
|
||||
|
||||
// StateConnected means the client is connected to the server and is
|
||||
// syncing initial information. This is an internal state that will
|
||||
// never be returned by Client.State().
|
||||
StateConnected
|
||||
|
||||
// StateSynced means the client is connected to a server and has been sent
|
||||
// the server state.
|
||||
StateSynced
|
||||
)
|
||||
|
||||
// ClientVersion is the protocol version that Client implements.
|
||||
const ClientVersion = 1<<16 | 3<<8 | 0
|
||||
|
||||
// Client is the type used to create a connection to a server.
|
||||
type Client struct {
|
||||
// The User associated with the client.
|
||||
Self *User
|
||||
// The client's configuration.
|
||||
Config *Config
|
||||
// The underlying Conn to the server.
|
||||
Conn *Conn
|
||||
|
||||
// The users currently connected to the server.
|
||||
Users Users
|
||||
// The connected server's channels.
|
||||
Channels Channels
|
||||
permissions map[uint32]*Permission
|
||||
tmpACL *ACL
|
||||
|
||||
// Ping stats
|
||||
tcpPacketsReceived uint32
|
||||
tcpPingTimes [12]float32
|
||||
tcpPingAvg uint32
|
||||
tcpPingVar uint32
|
||||
|
||||
// A collection containing the server's context actions.
|
||||
ContextActions ContextActions
|
||||
|
||||
// The audio encoder used when sending audio to the server.
|
||||
AudioEncoder AudioEncoder
|
||||
audioCodec AudioCodec
|
||||
// To whom transmitted audio will be sent. The VoiceTarget must have already
|
||||
// been sent to the server for targeting to work correctly. Setting to nil
|
||||
// will disable voice targeting (i.e. switch back to regular speaking).
|
||||
VoiceTarget *VoiceTarget
|
||||
|
||||
state uint32
|
||||
|
||||
// volatile is held by the client when the internal data structures are being
|
||||
// modified.
|
||||
volatile rpwMutex
|
||||
|
||||
connect chan *RejectError
|
||||
end chan struct{}
|
||||
disconnectEvent DisconnectEvent
|
||||
}
|
||||
|
||||
// Dial is an alias of DialWithDialer(new(net.Dialer), addr, config, nil).
|
||||
func Dial(addr string, config *Config) (*Client, error) {
|
||||
return DialWithDialer(new(net.Dialer), addr, config, nil)
|
||||
}
|
||||
|
||||
// DialWithDialer connects to the Mumble server at the given address.
|
||||
//
|
||||
// The function returns after the connection has been established, the initial
|
||||
// server information has been synced, and the OnConnect handlers have been
|
||||
// called.
|
||||
//
|
||||
// nil and an error is returned if server synchronization does not complete by
|
||||
// min(time.Now() + dialer.Timeout, dialer.Deadline), or if the server rejects
|
||||
// the client.
|
||||
func DialWithDialer(dialer *net.Dialer, addr string, config *Config, tlsConfig *tls.Config) (*Client, error) {
|
||||
start := time.Now()
|
||||
|
||||
conn, err := tls.DialWithDialer(dialer, "tcp", addr, tlsConfig)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
client := &Client{
|
||||
Conn: NewConn(conn),
|
||||
Config: config,
|
||||
Users: make(Users),
|
||||
Channels: make(Channels),
|
||||
|
||||
permissions: make(map[uint32]*Permission),
|
||||
|
||||
state: uint32(StateConnected),
|
||||
|
||||
connect: make(chan *RejectError),
|
||||
end: make(chan struct{}),
|
||||
}
|
||||
|
||||
go client.readRoutine()
|
||||
|
||||
// Initial packets
|
||||
versionPacket := MumbleProto.Version{
|
||||
Version: proto.Uint32(ClientVersion),
|
||||
Release: proto.String("gumble"),
|
||||
Os: proto.String(runtime.GOOS),
|
||||
OsVersion: proto.String(runtime.GOARCH),
|
||||
}
|
||||
authenticationPacket := MumbleProto.Authenticate{
|
||||
Username: &client.Config.Username,
|
||||
Password: &client.Config.Password,
|
||||
Opus: proto.Bool(getAudioCodec(audioCodecIDOpus) != nil),
|
||||
Tokens: client.Config.Tokens,
|
||||
}
|
||||
client.Conn.WriteProto(&versionPacket)
|
||||
client.Conn.WriteProto(&authenticationPacket)
|
||||
|
||||
go client.pingRoutine()
|
||||
|
||||
var timeout <-chan time.Time
|
||||
{
|
||||
var deadline time.Time
|
||||
if !dialer.Deadline.IsZero() {
|
||||
deadline = dialer.Deadline
|
||||
}
|
||||
if dialer.Timeout > 0 {
|
||||
diff := start.Add(dialer.Timeout)
|
||||
if deadline.IsZero() || diff.Before(deadline) {
|
||||
deadline = diff
|
||||
}
|
||||
}
|
||||
if !deadline.IsZero() {
|
||||
timer := time.NewTimer(deadline.Sub(start))
|
||||
defer timer.Stop()
|
||||
timeout = timer.C
|
||||
}
|
||||
}
|
||||
|
||||
select {
|
||||
case <-timeout:
|
||||
client.Conn.Close()
|
||||
return nil, errors.New("gumble: synchronization timeout")
|
||||
case err := <-client.connect:
|
||||
if err != nil {
|
||||
client.Conn.Close()
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return client, nil
|
||||
}
|
||||
}
|
||||
|
||||
// State returns the current state of the client.
|
||||
func (c *Client) State() State {
|
||||
return State(atomic.LoadUint32(&c.state))
|
||||
}
|
||||
|
||||
// AudioOutgoing creates a new channel that outgoing audio data can be written
|
||||
// to. The channel must be closed after the audio stream is completed. Only
|
||||
// a single channel should be open at any given time (i.e. close the channel
|
||||
// before opening another).
|
||||
func (c *Client) AudioOutgoing() chan<- AudioBuffer {
|
||||
ch := make(chan AudioBuffer)
|
||||
go func() {
|
||||
var seq int64
|
||||
previous := <-ch
|
||||
for p := range ch {
|
||||
previous.writeAudio(c, seq, false)
|
||||
previous = p
|
||||
seq = (seq + 1) % math.MaxInt32
|
||||
}
|
||||
if previous != nil {
|
||||
previous.writeAudio(c, seq, true)
|
||||
}
|
||||
}()
|
||||
return ch
|
||||
}
|
||||
|
||||
// pingRoutine sends ping packets to the server at regular intervals.
|
||||
func (c *Client) pingRoutine() {
|
||||
ticker := time.NewTicker(time.Second * 5)
|
||||
defer ticker.Stop()
|
||||
|
||||
var timestamp uint64
|
||||
var tcpPingAvg float32
|
||||
var tcpPingVar float32
|
||||
packet := MumbleProto.Ping{
|
||||
Timestamp: ×tamp,
|
||||
TcpPackets: &c.tcpPacketsReceived,
|
||||
TcpPingAvg: &tcpPingAvg,
|
||||
TcpPingVar: &tcpPingVar,
|
||||
}
|
||||
|
||||
t := time.Now()
|
||||
for {
|
||||
timestamp = uint64(t.UnixNano())
|
||||
tcpPingAvg = math.Float32frombits(atomic.LoadUint32(&c.tcpPingAvg))
|
||||
tcpPingVar = math.Float32frombits(atomic.LoadUint32(&c.tcpPingVar))
|
||||
c.Conn.WriteProto(&packet)
|
||||
|
||||
select {
|
||||
case <-c.end:
|
||||
return
|
||||
case t = <-ticker.C:
|
||||
// continue to top of loop
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// readRoutine reads protocol buffer messages from the server.
|
||||
func (c *Client) readRoutine() {
|
||||
c.disconnectEvent = DisconnectEvent{
|
||||
Client: c,
|
||||
Type: DisconnectError,
|
||||
}
|
||||
|
||||
for {
|
||||
pType, data, err := c.Conn.ReadPacket()
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
if int(pType) < len(handlers) {
|
||||
handlers[pType](c, data)
|
||||
}
|
||||
}
|
||||
|
||||
wasSynced := c.State() == StateSynced
|
||||
atomic.StoreUint32(&c.state, uint32(StateDisconnected))
|
||||
close(c.end)
|
||||
if wasSynced {
|
||||
c.Config.Listeners.onDisconnect(&c.disconnectEvent)
|
||||
}
|
||||
}
|
||||
|
||||
// RequestUserList requests that the server's registered user list be sent to
|
||||
// the client.
|
||||
func (c *Client) RequestUserList() {
|
||||
packet := MumbleProto.UserList{}
|
||||
c.Conn.WriteProto(&packet)
|
||||
}
|
||||
|
||||
// RequestBanList requests that the server's ban list be sent to the client.
|
||||
func (c *Client) RequestBanList() {
|
||||
packet := MumbleProto.BanList{
|
||||
Query: proto.Bool(true),
|
||||
}
|
||||
c.Conn.WriteProto(&packet)
|
||||
}
|
||||
|
||||
// Disconnect disconnects the client from the server.
|
||||
func (c *Client) Disconnect() error {
|
||||
if c.State() == StateDisconnected {
|
||||
return errors.New("gumble: client is already disconnected")
|
||||
}
|
||||
c.disconnectEvent.Type = DisconnectUser
|
||||
c.Conn.Close()
|
||||
return nil
|
||||
}
|
||||
|
||||
// Do executes f in a thread-safe manner. It ensures that Client and its
|
||||
// associated data will not be changed during the lifetime of the function
|
||||
// call.
|
||||
func (c *Client) Do(f func()) {
|
||||
c.volatile.RLock()
|
||||
defer c.volatile.RUnlock()
|
||||
|
||||
f()
|
||||
}
|
||||
|
||||
// Send will send a Message to the server.
|
||||
func (c *Client) Send(message Message) {
|
||||
message.writeMessage(c)
|
||||
}
|
53
vendor/layeh.com/gumble/gumble/config.go
generated
vendored
Normal file
53
vendor/layeh.com/gumble/gumble/config.go
generated
vendored
Normal file
@ -0,0 +1,53 @@
|
||||
package gumble
|
||||
|
||||
import (
|
||||
"time"
|
||||
)
|
||||
|
||||
// Config holds the Mumble configuration used by Client. A single Config should
|
||||
// not be shared between multiple Client instances.
|
||||
type Config struct {
|
||||
// User name used when authenticating with the server.
|
||||
Username string
|
||||
// Password used when authenticating with the server. A password is not
|
||||
// usually required to connect to a server.
|
||||
Password string
|
||||
// The initial access tokens to the send to the server. Access tokens can be
|
||||
// resent to the server using:
|
||||
// client.Send(config.Tokens)
|
||||
Tokens AccessTokens
|
||||
|
||||
// AudioInterval is the interval at which audio packets are sent. Valid
|
||||
// values are: 10ms, 20ms, 40ms, and 60ms.
|
||||
AudioInterval time.Duration
|
||||
// AudioDataBytes is the number of bytes that an audio frame can use.
|
||||
AudioDataBytes int
|
||||
|
||||
// The event listeners used when client events are triggered.
|
||||
Listeners Listeners
|
||||
AudioListeners AudioListeners
|
||||
}
|
||||
|
||||
// NewConfig returns a new Config struct with default values set.
|
||||
func NewConfig() *Config {
|
||||
return &Config{
|
||||
AudioInterval: AudioDefaultInterval,
|
||||
AudioDataBytes: AudioDefaultDataBytes,
|
||||
}
|
||||
}
|
||||
|
||||
// Attach is an alias of c.Listeners.Attach.
|
||||
func (c *Config) Attach(l EventListener) Detacher {
|
||||
return c.Listeners.Attach(l)
|
||||
}
|
||||
|
||||
// AttachAudio is an alias of c.AudioListeners.Attach.
|
||||
func (c *Config) AttachAudio(l AudioListener) Detacher {
|
||||
return c.AudioListeners.Attach(l)
|
||||
}
|
||||
|
||||
// AudioFrameSize returns the appropriate audio frame size, based off of the
|
||||
// audio interval.
|
||||
func (c *Config) AudioFrameSize() int {
|
||||
return int(c.AudioInterval/AudioDefaultInterval) * AudioDefaultFrameSize
|
||||
}
|
200
vendor/layeh.com/gumble/gumble/conn.go
generated
vendored
Normal file
200
vendor/layeh.com/gumble/gumble/conn.go
generated
vendored
Normal file
@ -0,0 +1,200 @@
|
||||
package gumble
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"io"
|
||||
"net"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/golang/protobuf/proto"
|
||||
"layeh.com/gumble/gumble/MumbleProto"
|
||||
"layeh.com/gumble/gumble/varint"
|
||||
)
|
||||
|
||||
// DefaultPort is the default port on which Mumble servers listen.
|
||||
const DefaultPort = 64738
|
||||
|
||||
// Conn represents a control protocol connection to a Mumble client/server.
|
||||
type Conn struct {
|
||||
sync.Mutex
|
||||
net.Conn
|
||||
|
||||
MaximumPacketBytes int
|
||||
Timeout time.Duration
|
||||
|
||||
buffer []byte
|
||||
}
|
||||
|
||||
// NewConn creates a new Conn with the given net.Conn.
|
||||
func NewConn(conn net.Conn) *Conn {
|
||||
return &Conn{
|
||||
Conn: conn,
|
||||
Timeout: time.Second * 20,
|
||||
MaximumPacketBytes: 1024 * 1024 * 10,
|
||||
}
|
||||
}
|
||||
|
||||
// ReadPacket reads a packet from the server. Returns the packet type, the
|
||||
// packet data, and nil on success.
|
||||
//
|
||||
// This function should only be called by a single go routine.
|
||||
func (c *Conn) ReadPacket() (uint16, []byte, error) {
|
||||
c.Conn.SetReadDeadline(time.Now().Add(c.Timeout))
|
||||
var header [6]byte
|
||||
if _, err := io.ReadFull(c.Conn, header[:]); err != nil {
|
||||
return 0, nil, err
|
||||
}
|
||||
pType := binary.BigEndian.Uint16(header[:])
|
||||
pLength := binary.BigEndian.Uint32(header[2:])
|
||||
pLengthInt := int(pLength)
|
||||
if pLengthInt > c.MaximumPacketBytes {
|
||||
return 0, nil, errors.New("gumble: packet larger than maximum allowed size")
|
||||
}
|
||||
if pLengthInt > len(c.buffer) {
|
||||
c.buffer = make([]byte, pLengthInt)
|
||||
}
|
||||
if _, err := io.ReadFull(c.Conn, c.buffer[:pLengthInt]); err != nil {
|
||||
return 0, nil, err
|
||||
}
|
||||
return pType, c.buffer[:pLengthInt], nil
|
||||
}
|
||||
|
||||
// WriteAudio writes an audio packet to the connection.
|
||||
func (c *Conn) WriteAudio(format, target byte, sequence int64, final bool, data []byte, X, Y, Z *float32) error {
|
||||
var buff [1 + varint.MaxVarintLen*2]byte
|
||||
buff[0] = (format << 5) | target
|
||||
n := varint.Encode(buff[1:], sequence)
|
||||
if n == 0 {
|
||||
return errors.New("gumble: varint out of range")
|
||||
}
|
||||
l := int64(len(data))
|
||||
if final {
|
||||
l |= 0x2000
|
||||
}
|
||||
m := varint.Encode(buff[1+n:], l)
|
||||
if m == 0 {
|
||||
return errors.New("gumble: varint out of range")
|
||||
}
|
||||
header := buff[:1+n+m]
|
||||
|
||||
var positionalLength int
|
||||
if X != nil {
|
||||
positionalLength = 3 * 4
|
||||
}
|
||||
|
||||
c.Lock()
|
||||
defer c.Unlock()
|
||||
|
||||
if err := c.writeHeader(1, uint32(len(header)+len(data)+positionalLength)); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := c.Conn.Write(header); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := c.Conn.Write(data); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if positionalLength > 0 {
|
||||
if err := binary.Write(c.Conn, binary.LittleEndian, *X); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := binary.Write(c.Conn, binary.LittleEndian, *Y); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := binary.Write(c.Conn, binary.LittleEndian, *Z); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// WritePacket writes a data packet of the given type to the connection.
|
||||
func (c *Conn) WritePacket(ptype uint16, data []byte) error {
|
||||
c.Lock()
|
||||
defer c.Unlock()
|
||||
if err := c.writeHeader(uint16(ptype), uint32(len(data))); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := c.Conn.Write(data); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Conn) writeHeader(pType uint16, pLength uint32) error {
|
||||
var header [6]byte
|
||||
binary.BigEndian.PutUint16(header[:], pType)
|
||||
binary.BigEndian.PutUint32(header[2:], pLength)
|
||||
if _, err := c.Conn.Write(header[:]); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// WriteProto writes a protocol buffer message to the connection.
|
||||
func (c *Conn) WriteProto(message proto.Message) error {
|
||||
var protoType uint16
|
||||
switch message.(type) {
|
||||
case *MumbleProto.Version:
|
||||
protoType = 0
|
||||
case *MumbleProto.Authenticate:
|
||||
protoType = 2
|
||||
case *MumbleProto.Ping:
|
||||
protoType = 3
|
||||
case *MumbleProto.Reject:
|
||||
protoType = 4
|
||||
case *MumbleProto.ServerSync:
|
||||
protoType = 5
|
||||
case *MumbleProto.ChannelRemove:
|
||||
protoType = 6
|
||||
case *MumbleProto.ChannelState:
|
||||
protoType = 7
|
||||
case *MumbleProto.UserRemove:
|
||||
protoType = 8
|
||||
case *MumbleProto.UserState:
|
||||
protoType = 9
|
||||
case *MumbleProto.BanList:
|
||||
protoType = 10
|
||||
case *MumbleProto.TextMessage:
|
||||
protoType = 11
|
||||
case *MumbleProto.PermissionDenied:
|
||||
protoType = 12
|
||||
case *MumbleProto.ACL:
|
||||
protoType = 13
|
||||
case *MumbleProto.QueryUsers:
|
||||
protoType = 14
|
||||
case *MumbleProto.CryptSetup:
|
||||
protoType = 15
|
||||
case *MumbleProto.ContextActionModify:
|
||||
protoType = 16
|
||||
case *MumbleProto.ContextAction:
|
||||
protoType = 17
|
||||
case *MumbleProto.UserList:
|
||||
protoType = 18
|
||||
case *MumbleProto.VoiceTarget:
|
||||
protoType = 19
|
||||
case *MumbleProto.PermissionQuery:
|
||||
protoType = 20
|
||||
case *MumbleProto.CodecVersion:
|
||||
protoType = 21
|
||||
case *MumbleProto.UserStats:
|
||||
protoType = 22
|
||||
case *MumbleProto.RequestBlob:
|
||||
protoType = 23
|
||||
case *MumbleProto.ServerConfig:
|
||||
protoType = 24
|
||||
case *MumbleProto.SuggestConfig:
|
||||
protoType = 25
|
||||
default:
|
||||
return errors.New("gumble: unknown message type")
|
||||
}
|
||||
data, err := proto.Marshal(message)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return c.WritePacket(protoType, data)
|
||||
}
|
57
vendor/layeh.com/gumble/gumble/contextaction.go
generated
vendored
Normal file
57
vendor/layeh.com/gumble/gumble/contextaction.go
generated
vendored
Normal file
@ -0,0 +1,57 @@
|
||||
package gumble
|
||||
|
||||
import (
|
||||
"layeh.com/gumble/gumble/MumbleProto"
|
||||
)
|
||||
|
||||
// ContextActionType is a bitmask of contexts where a ContextAction can be
|
||||
// triggered.
|
||||
type ContextActionType int
|
||||
|
||||
// Supported ContextAction contexts.
|
||||
const (
|
||||
ContextActionServer ContextActionType = ContextActionType(MumbleProto.ContextActionModify_Server)
|
||||
ContextActionChannel ContextActionType = ContextActionType(MumbleProto.ContextActionModify_Channel)
|
||||
ContextActionUser ContextActionType = ContextActionType(MumbleProto.ContextActionModify_User)
|
||||
)
|
||||
|
||||
// ContextAction is an triggerable item that has been added by a server-side
|
||||
// plugin.
|
||||
type ContextAction struct {
|
||||
// The context action type.
|
||||
Type ContextActionType
|
||||
// The name of the context action.
|
||||
Name string
|
||||
// The user-friendly description of the context action.
|
||||
Label string
|
||||
|
||||
client *Client
|
||||
}
|
||||
|
||||
// Trigger will trigger the context action in the context of the server.
|
||||
func (c *ContextAction) Trigger() {
|
||||
packet := MumbleProto.ContextAction{
|
||||
Action: &c.Name,
|
||||
}
|
||||
c.client.Conn.WriteProto(&packet)
|
||||
}
|
||||
|
||||
// TriggerUser will trigger the context action in the context of the given
|
||||
// user.
|
||||
func (c *ContextAction) TriggerUser(user *User) {
|
||||
packet := MumbleProto.ContextAction{
|
||||
Session: &user.Session,
|
||||
Action: &c.Name,
|
||||
}
|
||||
c.client.Conn.WriteProto(&packet)
|
||||
}
|
||||
|
||||
// TriggerChannel will trigger the context action in the context of the given
|
||||
// channel.
|
||||
func (c *ContextAction) TriggerChannel(channel *Channel) {
|
||||
packet := MumbleProto.ContextAction{
|
||||
ChannelId: &channel.ID,
|
||||
Action: &c.Name,
|
||||
}
|
||||
c.client.Conn.WriteProto(&packet)
|
||||
}
|
12
vendor/layeh.com/gumble/gumble/contextactions.go
generated
vendored
Normal file
12
vendor/layeh.com/gumble/gumble/contextactions.go
generated
vendored
Normal file
@ -0,0 +1,12 @@
|
||||
package gumble
|
||||
|
||||
// ContextActions is a map of ContextActions.
|
||||
type ContextActions map[string]*ContextAction
|
||||
|
||||
func (c ContextActions) create(action string) *ContextAction {
|
||||
contextAction := &ContextAction{
|
||||
Name: action,
|
||||
}
|
||||
c[action] = contextAction
|
||||
return contextAction
|
||||
}
|
7
vendor/layeh.com/gumble/gumble/detacher.go
generated
vendored
Normal file
7
vendor/layeh.com/gumble/gumble/detacher.go
generated
vendored
Normal file
@ -0,0 +1,7 @@
|
||||
package gumble
|
||||
|
||||
// Detacher is an interface that event listeners implement. After the Detach
|
||||
// method is called, the listener will no longer receive events.
|
||||
type Detacher interface {
|
||||
Detach()
|
||||
}
|
45
vendor/layeh.com/gumble/gumble/doc.go
generated
vendored
Normal file
45
vendor/layeh.com/gumble/gumble/doc.go
generated
vendored
Normal file
@ -0,0 +1,45 @@
|
||||
// Package gumble is a client for the Mumble voice chat software.
|
||||
//
|
||||
// Getting started
|
||||
//
|
||||
// 1. Create a new Config to hold your connection settings:
|
||||
//
|
||||
// config := gumble.NewConfig()
|
||||
// config.Username = "gumble-test"
|
||||
//
|
||||
// 2. Attach event listeners to the configuration:
|
||||
//
|
||||
// config.Attach(gumbleutil.Listener{
|
||||
// TextMessage: func(e *gumble.TextMessageEvent) {
|
||||
// fmt.Printf("Received text message: %s\n", e.Message)
|
||||
// },
|
||||
// })
|
||||
//
|
||||
// 3. Connect to the server:
|
||||
//
|
||||
// client, err := gumble.Dial("example.com:64738", config)
|
||||
// if err != nil {
|
||||
// panic(err)
|
||||
// }
|
||||
//
|
||||
// Audio codecs
|
||||
//
|
||||
// Currently, only the Opus codec (https://www.opus-codec.org/) is supported
|
||||
// for transmitting and receiving audio. It can be enabled by importing the
|
||||
// following package for its side effect:
|
||||
// import (
|
||||
// _ "layeh.com/gumble/opus"
|
||||
// )
|
||||
//
|
||||
// To ensure that gumble clients can always transmit and receive audio to and
|
||||
// from your server, add the following line to your murmur configuration file:
|
||||
//
|
||||
// opusthreshold=0
|
||||
//
|
||||
// Thread safety
|
||||
//
|
||||
// As a general rule, a Client everything that is associated with it
|
||||
// (Users, Channels, Config, etc.), is thread-unsafe. Accessing or modifying
|
||||
// those structures should only be done from inside of an event listener or via
|
||||
// Client.Do.
|
||||
package gumble
|
223
vendor/layeh.com/gumble/gumble/event.go
generated
vendored
Normal file
223
vendor/layeh.com/gumble/gumble/event.go
generated
vendored
Normal file
@ -0,0 +1,223 @@
|
||||
package gumble
|
||||
|
||||
import (
|
||||
"layeh.com/gumble/gumble/MumbleProto"
|
||||
)
|
||||
|
||||
// EventListener is the interface that must be implemented by a type if it
|
||||
// wishes to be notified of Client events.
|
||||
//
|
||||
// Listener methods are executed synchronously as event happen. They also block
|
||||
// network reads from happening until all handlers for an event are called.
|
||||
// Therefore, it is not recommended to do any long processing from inside of
|
||||
// these methods.
|
||||
type EventListener interface {
|
||||
OnConnect(e *ConnectEvent)
|
||||
OnDisconnect(e *DisconnectEvent)
|
||||
OnTextMessage(e *TextMessageEvent)
|
||||
OnUserChange(e *UserChangeEvent)
|
||||
OnChannelChange(e *ChannelChangeEvent)
|
||||
OnPermissionDenied(e *PermissionDeniedEvent)
|
||||
OnUserList(e *UserListEvent)
|
||||
OnACL(e *ACLEvent)
|
||||
OnBanList(e *BanListEvent)
|
||||
OnContextActionChange(e *ContextActionChangeEvent)
|
||||
OnServerConfig(e *ServerConfigEvent)
|
||||
}
|
||||
|
||||
// ConnectEvent is the event that is passed to EventListener.OnConnect.
|
||||
type ConnectEvent struct {
|
||||
Client *Client
|
||||
WelcomeMessage *string
|
||||
MaximumBitrate *int
|
||||
}
|
||||
|
||||
// DisconnectType specifies why a Client disconnected from a server.
|
||||
type DisconnectType int
|
||||
|
||||
// Client disconnect reasons.
|
||||
const (
|
||||
DisconnectError DisconnectType = iota + 1
|
||||
DisconnectKicked
|
||||
DisconnectBanned
|
||||
DisconnectUser
|
||||
)
|
||||
|
||||
// Has returns true if the DisconnectType has changeType part of its bitmask.
|
||||
func (d DisconnectType) Has(changeType DisconnectType) bool {
|
||||
return d&changeType == changeType
|
||||
}
|
||||
|
||||
// DisconnectEvent is the event that is passed to EventListener.OnDisconnect.
|
||||
type DisconnectEvent struct {
|
||||
Client *Client
|
||||
Type DisconnectType
|
||||
|
||||
String string
|
||||
}
|
||||
|
||||
// TextMessageEvent is the event that is passed to EventListener.OnTextMessage.
|
||||
type TextMessageEvent struct {
|
||||
Client *Client
|
||||
TextMessage
|
||||
}
|
||||
|
||||
// UserChangeType is a bitmask of items that changed for a user.
|
||||
type UserChangeType int
|
||||
|
||||
// User change items.
|
||||
const (
|
||||
UserChangeConnected UserChangeType = 1 << iota
|
||||
UserChangeDisconnected
|
||||
UserChangeKicked
|
||||
UserChangeBanned
|
||||
UserChangeRegistered
|
||||
UserChangeUnregistered
|
||||
UserChangeName
|
||||
UserChangeChannel
|
||||
UserChangeComment
|
||||
UserChangeAudio
|
||||
UserChangeTexture
|
||||
UserChangePrioritySpeaker
|
||||
UserChangeRecording
|
||||
UserChangeStats
|
||||
)
|
||||
|
||||
// Has returns true if the UserChangeType has changeType part of its bitmask.
|
||||
func (u UserChangeType) Has(changeType UserChangeType) bool {
|
||||
return u&changeType == changeType
|
||||
}
|
||||
|
||||
// UserChangeEvent is the event that is passed to EventListener.OnUserChange.
|
||||
type UserChangeEvent struct {
|
||||
Client *Client
|
||||
Type UserChangeType
|
||||
User *User
|
||||
Actor *User
|
||||
|
||||
String string
|
||||
}
|
||||
|
||||
// ChannelChangeType is a bitmask of items that changed for a channel.
|
||||
type ChannelChangeType int
|
||||
|
||||
// Channel change items.
|
||||
const (
|
||||
ChannelChangeCreated ChannelChangeType = 1 << iota
|
||||
ChannelChangeRemoved
|
||||
ChannelChangeMoved
|
||||
ChannelChangeName
|
||||
ChannelChangeLinks
|
||||
ChannelChangeDescription
|
||||
ChannelChangePosition
|
||||
ChannelChangePermission
|
||||
ChannelChangeMaxUsers
|
||||
)
|
||||
|
||||
// Has returns true if the ChannelChangeType has changeType part of its
|
||||
// bitmask.
|
||||
func (c ChannelChangeType) Has(changeType ChannelChangeType) bool {
|
||||
return c&changeType == changeType
|
||||
}
|
||||
|
||||
// ChannelChangeEvent is the event that is passed to
|
||||
// EventListener.OnChannelChange.
|
||||
type ChannelChangeEvent struct {
|
||||
Client *Client
|
||||
Type ChannelChangeType
|
||||
Channel *Channel
|
||||
}
|
||||
|
||||
// PermissionDeniedType specifies why a Client was denied permission to perform
|
||||
// a particular action.
|
||||
type PermissionDeniedType int
|
||||
|
||||
// Permission denied types.
|
||||
const (
|
||||
PermissionDeniedOther PermissionDeniedType = PermissionDeniedType(MumbleProto.PermissionDenied_Text)
|
||||
PermissionDeniedPermission PermissionDeniedType = PermissionDeniedType(MumbleProto.PermissionDenied_Permission)
|
||||
PermissionDeniedSuperUser PermissionDeniedType = PermissionDeniedType(MumbleProto.PermissionDenied_SuperUser)
|
||||
PermissionDeniedInvalidChannelName PermissionDeniedType = PermissionDeniedType(MumbleProto.PermissionDenied_ChannelName)
|
||||
PermissionDeniedTextTooLong PermissionDeniedType = PermissionDeniedType(MumbleProto.PermissionDenied_TextTooLong)
|
||||
PermissionDeniedTemporaryChannel PermissionDeniedType = PermissionDeniedType(MumbleProto.PermissionDenied_TemporaryChannel)
|
||||
PermissionDeniedMissingCertificate PermissionDeniedType = PermissionDeniedType(MumbleProto.PermissionDenied_MissingCertificate)
|
||||
PermissionDeniedInvalidUserName PermissionDeniedType = PermissionDeniedType(MumbleProto.PermissionDenied_UserName)
|
||||
PermissionDeniedChannelFull PermissionDeniedType = PermissionDeniedType(MumbleProto.PermissionDenied_ChannelFull)
|
||||
PermissionDeniedNestingLimit PermissionDeniedType = PermissionDeniedType(MumbleProto.PermissionDenied_NestingLimit)
|
||||
PermissionDeniedChannelCountLimit PermissionDeniedType = PermissionDeniedType(MumbleProto.PermissionDenied_ChannelCountLimit)
|
||||
)
|
||||
|
||||
// Has returns true if the PermissionDeniedType has changeType part of its
|
||||
// bitmask.
|
||||
func (p PermissionDeniedType) Has(changeType PermissionDeniedType) bool {
|
||||
return p&changeType == changeType
|
||||
}
|
||||
|
||||
// PermissionDeniedEvent is the event that is passed to
|
||||
// EventListener.OnPermissionDenied.
|
||||
type PermissionDeniedEvent struct {
|
||||
Client *Client
|
||||
Type PermissionDeniedType
|
||||
Channel *Channel
|
||||
User *User
|
||||
|
||||
Permission Permission
|
||||
String string
|
||||
}
|
||||
|
||||
// UserListEvent is the event that is passed to EventListener.OnUserList.
|
||||
type UserListEvent struct {
|
||||
Client *Client
|
||||
UserList RegisteredUsers
|
||||
}
|
||||
|
||||
// ACLEvent is the event that is passed to EventListener.OnACL.
|
||||
type ACLEvent struct {
|
||||
Client *Client
|
||||
ACL *ACL
|
||||
}
|
||||
|
||||
// BanListEvent is the event that is passed to EventListener.OnBanList.
|
||||
type BanListEvent struct {
|
||||
Client *Client
|
||||
BanList BanList
|
||||
}
|
||||
|
||||
// ContextActionChangeType specifies how a ContextAction changed.
|
||||
type ContextActionChangeType int
|
||||
|
||||
// ContextAction change types.
|
||||
const (
|
||||
ContextActionAdd ContextActionChangeType = ContextActionChangeType(MumbleProto.ContextActionModify_Add)
|
||||
ContextActionRemove ContextActionChangeType = ContextActionChangeType(MumbleProto.ContextActionModify_Remove)
|
||||
)
|
||||
|
||||
// ContextActionChangeEvent is the event that is passed to
|
||||
// EventListener.OnContextActionChange.
|
||||
type ContextActionChangeEvent struct {
|
||||
Client *Client
|
||||
Type ContextActionChangeType
|
||||
ContextAction *ContextAction
|
||||
}
|
||||
|
||||
// ServerConfigEvent is the event that is passed to
|
||||
// EventListener.OnServerConfig.
|
||||
type ServerConfigEvent struct {
|
||||
Client *Client
|
||||
|
||||
MaximumBitrate *int
|
||||
WelcomeMessage *string
|
||||
AllowHTML *bool
|
||||
MaximumMessageLength *int
|
||||
MaximumImageMessageLength *int
|
||||
MaximumUsers *int
|
||||
|
||||
CodecAlpha *int32
|
||||
CodecBeta *int32
|
||||
CodecPreferAlpha *bool
|
||||
CodecOpus *bool
|
||||
|
||||
SuggestVersion *Version
|
||||
SuggestPositional *bool
|
||||
SuggestPushToTalk *bool
|
||||
}
|
1261
vendor/layeh.com/gumble/gumble/handlers.go
generated
vendored
Normal file
1261
vendor/layeh.com/gumble/gumble/handlers.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
153
vendor/layeh.com/gumble/gumble/listeners.go
generated
vendored
Normal file
153
vendor/layeh.com/gumble/gumble/listeners.go
generated
vendored
Normal file
@ -0,0 +1,153 @@
|
||||
package gumble
|
||||
|
||||
type eventItem struct {
|
||||
parent *Listeners
|
||||
prev, next *eventItem
|
||||
listener EventListener
|
||||
}
|
||||
|
||||
func (e *eventItem) Detach() {
|
||||
if e.prev == nil {
|
||||
e.parent.head = e.next
|
||||
} else {
|
||||
e.prev.next = e.next
|
||||
}
|
||||
if e.next == nil {
|
||||
e.parent.tail = e.prev
|
||||
} else {
|
||||
e.next.prev = e.prev
|
||||
}
|
||||
}
|
||||
|
||||
// Listeners is a list of event listeners. Each attached listener is called in
|
||||
// sequence when a Client event is triggered.
|
||||
type Listeners struct {
|
||||
head, tail *eventItem
|
||||
}
|
||||
|
||||
// Attach adds a new event listener to the end of the current list of listeners.
|
||||
func (e *Listeners) Attach(listener EventListener) Detacher {
|
||||
item := &eventItem{
|
||||
parent: e,
|
||||
prev: e.tail,
|
||||
listener: listener,
|
||||
}
|
||||
if e.head == nil {
|
||||
e.head = item
|
||||
}
|
||||
if e.tail != nil {
|
||||
e.tail.next = item
|
||||
}
|
||||
e.tail = item
|
||||
return item
|
||||
}
|
||||
|
||||
func (e *Listeners) onConnect(event *ConnectEvent) {
|
||||
event.Client.volatile.Lock()
|
||||
for item := e.head; item != nil; item = item.next {
|
||||
event.Client.volatile.Unlock()
|
||||
item.listener.OnConnect(event)
|
||||
event.Client.volatile.Lock()
|
||||
}
|
||||
event.Client.volatile.Unlock()
|
||||
}
|
||||
|
||||
func (e *Listeners) onDisconnect(event *DisconnectEvent) {
|
||||
event.Client.volatile.Lock()
|
||||
for item := e.head; item != nil; item = item.next {
|
||||
event.Client.volatile.Unlock()
|
||||
item.listener.OnDisconnect(event)
|
||||
event.Client.volatile.Lock()
|
||||
}
|
||||
event.Client.volatile.Unlock()
|
||||
}
|
||||
|
||||
func (e *Listeners) onTextMessage(event *TextMessageEvent) {
|
||||
event.Client.volatile.Lock()
|
||||
for item := e.head; item != nil; item = item.next {
|
||||
event.Client.volatile.Unlock()
|
||||
item.listener.OnTextMessage(event)
|
||||
event.Client.volatile.Lock()
|
||||
}
|
||||
event.Client.volatile.Unlock()
|
||||
}
|
||||
|
||||
func (e *Listeners) onUserChange(event *UserChangeEvent) {
|
||||
event.Client.volatile.Lock()
|
||||
for item := e.head; item != nil; item = item.next {
|
||||
event.Client.volatile.Unlock()
|
||||
item.listener.OnUserChange(event)
|
||||
event.Client.volatile.Lock()
|
||||
}
|
||||
event.Client.volatile.Unlock()
|
||||
}
|
||||
|
||||
func (e *Listeners) onChannelChange(event *ChannelChangeEvent) {
|
||||
event.Client.volatile.Lock()
|
||||
for item := e.head; item != nil; item = item.next {
|
||||
event.Client.volatile.Unlock()
|
||||
item.listener.OnChannelChange(event)
|
||||
event.Client.volatile.Lock()
|
||||
}
|
||||
event.Client.volatile.Unlock()
|
||||
}
|
||||
|
||||
func (e *Listeners) onPermissionDenied(event *PermissionDeniedEvent) {
|
||||
event.Client.volatile.Lock()
|
||||
for item := e.head; item != nil; item = item.next {
|
||||
event.Client.volatile.Unlock()
|
||||
item.listener.OnPermissionDenied(event)
|
||||
event.Client.volatile.Lock()
|
||||
}
|
||||
event.Client.volatile.Unlock()
|
||||
}
|
||||
|
||||
func (e *Listeners) onUserList(event *UserListEvent) {
|
||||
event.Client.volatile.Lock()
|
||||
for item := e.head; item != nil; item = item.next {
|
||||
event.Client.volatile.Unlock()
|
||||
item.listener.OnUserList(event)
|
||||
event.Client.volatile.Lock()
|
||||
}
|
||||
event.Client.volatile.Unlock()
|
||||
}
|
||||
|
||||
func (e *Listeners) onACL(event *ACLEvent) {
|
||||
event.Client.volatile.Lock()
|
||||
for item := e.head; item != nil; item = item.next {
|
||||
event.Client.volatile.Unlock()
|
||||
item.listener.OnACL(event)
|
||||
event.Client.volatile.Lock()
|
||||
}
|
||||
event.Client.volatile.Unlock()
|
||||
}
|
||||
|
||||
func (e *Listeners) onBanList(event *BanListEvent) {
|
||||
event.Client.volatile.Lock()
|
||||
for item := e.head; item != nil; item = item.next {
|
||||
event.Client.volatile.Unlock()
|
||||
item.listener.OnBanList(event)
|
||||
event.Client.volatile.Lock()
|
||||
}
|
||||
event.Client.volatile.Unlock()
|
||||
}
|
||||
|
||||
func (e *Listeners) onContextActionChange(event *ContextActionChangeEvent) {
|
||||
event.Client.volatile.Lock()
|
||||
for item := e.head; item != nil; item = item.next {
|
||||
event.Client.volatile.Unlock()
|
||||
item.listener.OnContextActionChange(event)
|
||||
event.Client.volatile.Lock()
|
||||
}
|
||||
event.Client.volatile.Unlock()
|
||||
}
|
||||
|
||||
func (e *Listeners) onServerConfig(event *ServerConfigEvent) {
|
||||
event.Client.volatile.Lock()
|
||||
for item := e.head; item != nil; item = item.next {
|
||||
event.Client.volatile.Unlock()
|
||||
item.listener.OnServerConfig(event)
|
||||
event.Client.volatile.Lock()
|
||||
}
|
||||
event.Client.volatile.Unlock()
|
||||
}
|
13
vendor/layeh.com/gumble/gumble/message.go
generated
vendored
Normal file
13
vendor/layeh.com/gumble/gumble/message.go
generated
vendored
Normal file
@ -0,0 +1,13 @@
|
||||
package gumble
|
||||
|
||||
// Message is data that be encoded and sent to the server. The following
|
||||
// types implement this interface:
|
||||
// AccessTokens
|
||||
// ACL
|
||||
// BanList
|
||||
// RegisteredUsers
|
||||
// TextMessage
|
||||
// VoiceTarget
|
||||
type Message interface {
|
||||
writeMessage(client *Client) error
|
||||
}
|
33
vendor/layeh.com/gumble/gumble/permission.go
generated
vendored
Normal file
33
vendor/layeh.com/gumble/gumble/permission.go
generated
vendored
Normal file
@ -0,0 +1,33 @@
|
||||
package gumble
|
||||
|
||||
// Permission is a bitmask of permissions given to a certain user.
|
||||
type Permission int
|
||||
|
||||
// Permissions that can be applied in any channel.
|
||||
const (
|
||||
PermissionWrite Permission = 1 << iota
|
||||
PermissionTraverse
|
||||
PermissionEnter
|
||||
PermissionSpeak
|
||||
PermissionMuteDeafen
|
||||
PermissionMove
|
||||
PermissionMakeChannel
|
||||
PermissionLinkChannel
|
||||
PermissionWhisper
|
||||
PermissionTextMessage
|
||||
PermissionMakeTemporaryChannel
|
||||
)
|
||||
|
||||
// Permissions that can only be applied in the root channel.
|
||||
const (
|
||||
PermissionKick Permission = 0x10000 << iota
|
||||
PermissionBan
|
||||
PermissionRegister
|
||||
PermissionRegisterSelf
|
||||
)
|
||||
|
||||
// Has returns true if the Permission p contains Permission o has part of its
|
||||
// bitmask.
|
||||
func (p Permission) Has(o Permission) bool {
|
||||
return p&o == o
|
||||
}
|
108
vendor/layeh.com/gumble/gumble/ping.go
generated
vendored
Normal file
108
vendor/layeh.com/gumble/gumble/ping.go
generated
vendored
Normal file
@ -0,0 +1,108 @@
|
||||
package gumble
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"io"
|
||||
"net"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
// PingResponse contains information about a server that responded to a UDP
|
||||
// ping packet.
|
||||
type PingResponse struct {
|
||||
// The address of the pinged server.
|
||||
Address *net.UDPAddr
|
||||
// The round-trip time from the client to the server.
|
||||
Ping time.Duration
|
||||
// The server's version. Only the Version field and SemanticVersion method of
|
||||
// the value will be valid.
|
||||
Version Version
|
||||
// The number users currently connected to the server.
|
||||
ConnectedUsers int
|
||||
// The maximum number of users that can connect to the server.
|
||||
MaximumUsers int
|
||||
// The maximum audio bitrate per user for the server.
|
||||
MaximumBitrate int
|
||||
}
|
||||
|
||||
// Ping sends a UDP ping packet to the given server. If interval is positive,
|
||||
// the packet is retransmitted at every interval.
|
||||
//
|
||||
// Returns a PingResponse and nil on success. The function will return nil and
|
||||
// an error if a valid response is not received after the given timeout.
|
||||
func Ping(address string, interval, timeout time.Duration) (*PingResponse, error) {
|
||||
if timeout < 0 {
|
||||
return nil, errors.New("gumble: timeout must be positive")
|
||||
}
|
||||
deadline := time.Now().Add(timeout)
|
||||
conn, err := net.DialTimeout("udp", address, timeout)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer conn.Close()
|
||||
conn.SetReadDeadline(deadline)
|
||||
|
||||
var (
|
||||
idsLock sync.Mutex
|
||||
ids = make(map[string]time.Time)
|
||||
)
|
||||
|
||||
buildSendPacket := func() {
|
||||
var packet [12]byte
|
||||
if _, err := rand.Read(packet[4:]); err != nil {
|
||||
return
|
||||
}
|
||||
id := string(packet[4:])
|
||||
idsLock.Lock()
|
||||
ids[id] = time.Now()
|
||||
idsLock.Unlock()
|
||||
conn.Write(packet[:])
|
||||
}
|
||||
|
||||
if interval > 0 {
|
||||
end := make(chan struct{})
|
||||
defer close(end)
|
||||
go func() {
|
||||
ticker := time.NewTicker(interval)
|
||||
defer ticker.Stop()
|
||||
for {
|
||||
select {
|
||||
case <-ticker.C:
|
||||
buildSendPacket()
|
||||
case <-end:
|
||||
return
|
||||
}
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
buildSendPacket()
|
||||
|
||||
for {
|
||||
var incoming [24]byte
|
||||
if _, err := io.ReadFull(conn, incoming[:]); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
id := string(incoming[4:12])
|
||||
idsLock.Lock()
|
||||
sendTime, ok := ids[id]
|
||||
idsLock.Unlock()
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
return &PingResponse{
|
||||
Address: conn.RemoteAddr().(*net.UDPAddr),
|
||||
Ping: time.Since(sendTime),
|
||||
Version: Version{
|
||||
Version: binary.BigEndian.Uint32(incoming[0:]),
|
||||
},
|
||||
ConnectedUsers: int(binary.BigEndian.Uint32(incoming[12:])),
|
||||
MaximumUsers: int(binary.BigEndian.Uint32(incoming[16:])),
|
||||
MaximumBitrate: int(binary.BigEndian.Uint32(incoming[20:])),
|
||||
}, nil
|
||||
}
|
||||
}
|
61
vendor/layeh.com/gumble/gumble/reject.go
generated
vendored
Normal file
61
vendor/layeh.com/gumble/gumble/reject.go
generated
vendored
Normal file
@ -0,0 +1,61 @@
|
||||
package gumble
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
|
||||
"layeh.com/gumble/gumble/MumbleProto"
|
||||
)
|
||||
|
||||
// RejectType describes why a client connection was rejected by the server.
|
||||
type RejectType int
|
||||
|
||||
// The possible reason why a client connection was rejected by the server.
|
||||
const (
|
||||
RejectNone RejectType = RejectType(MumbleProto.Reject_None)
|
||||
RejectVersion RejectType = RejectType(MumbleProto.Reject_WrongVersion)
|
||||
RejectUserName RejectType = RejectType(MumbleProto.Reject_InvalidUsername)
|
||||
RejectUserCredentials RejectType = RejectType(MumbleProto.Reject_WrongUserPW)
|
||||
RejectServerPassword RejectType = RejectType(MumbleProto.Reject_WrongServerPW)
|
||||
RejectUsernameInUse RejectType = RejectType(MumbleProto.Reject_UsernameInUse)
|
||||
RejectServerFull RejectType = RejectType(MumbleProto.Reject_ServerFull)
|
||||
RejectNoCertificate RejectType = RejectType(MumbleProto.Reject_NoCertificate)
|
||||
RejectAuthenticatorFail RejectType = RejectType(MumbleProto.Reject_AuthenticatorFail)
|
||||
)
|
||||
|
||||
// RejectError is returned by DialWithDialer when the server rejects the client
|
||||
// connection.
|
||||
type RejectError struct {
|
||||
Type RejectType
|
||||
Reason string
|
||||
}
|
||||
|
||||
// Error implements error.
|
||||
func (e RejectError) Error() string {
|
||||
var msg string
|
||||
switch e.Type {
|
||||
case RejectNone:
|
||||
msg = "none"
|
||||
case RejectVersion:
|
||||
msg = "wrong client version"
|
||||
case RejectUserName:
|
||||
msg = "invalid username"
|
||||
case RejectUserCredentials:
|
||||
msg = "incorrect user credentials"
|
||||
case RejectServerPassword:
|
||||
msg = "incorrect server password"
|
||||
case RejectUsernameInUse:
|
||||
msg = "username in use"
|
||||
case RejectServerFull:
|
||||
msg = "server full"
|
||||
case RejectNoCertificate:
|
||||
msg = "no certificate"
|
||||
case RejectAuthenticatorFail:
|
||||
msg = "authenticator fail"
|
||||
default:
|
||||
msg = "unknown type " + strconv.Itoa(int(e.Type))
|
||||
}
|
||||
if e.Reason != "" {
|
||||
msg += ": " + e.Reason
|
||||
}
|
||||
return msg
|
||||
}
|
36
vendor/layeh.com/gumble/gumble/rpwmutex.go
generated
vendored
Normal file
36
vendor/layeh.com/gumble/gumble/rpwmutex.go
generated
vendored
Normal file
@ -0,0 +1,36 @@
|
||||
package gumble
|
||||
|
||||
import "sync"
|
||||
|
||||
// rpwMutex is a reader-preferred RWMutex.
|
||||
type rpwMutex struct {
|
||||
w sync.Mutex
|
||||
r sync.Mutex
|
||||
n int
|
||||
}
|
||||
|
||||
func (m *rpwMutex) Lock() {
|
||||
m.w.Lock()
|
||||
}
|
||||
|
||||
func (m *rpwMutex) Unlock() {
|
||||
m.w.Unlock()
|
||||
}
|
||||
|
||||
func (m *rpwMutex) RLock() {
|
||||
m.r.Lock()
|
||||
m.n++
|
||||
if m.n == 1 {
|
||||
m.w.Lock()
|
||||
}
|
||||
m.r.Unlock()
|
||||
}
|
||||
|
||||
func (m *rpwMutex) RUnlock() {
|
||||
m.r.Lock()
|
||||
m.n--
|
||||
if m.n == 0 {
|
||||
m.w.Unlock()
|
||||
}
|
||||
m.r.Unlock()
|
||||
}
|
45
vendor/layeh.com/gumble/gumble/textmessage.go
generated
vendored
Normal file
45
vendor/layeh.com/gumble/gumble/textmessage.go
generated
vendored
Normal file
@ -0,0 +1,45 @@
|
||||
package gumble
|
||||
|
||||
import (
|
||||
"layeh.com/gumble/gumble/MumbleProto"
|
||||
)
|
||||
|
||||
// TextMessage is a chat message that can be received from and sent to the
|
||||
// server.
|
||||
type TextMessage struct {
|
||||
// User who sent the message (can be nil).
|
||||
Sender *User
|
||||
// Users that receive the message.
|
||||
Users []*User
|
||||
// Channels that receive the message.
|
||||
Channels []*Channel
|
||||
// Channels that receive the message and send it recursively to sub-channels.
|
||||
Trees []*Channel
|
||||
// Chat message.
|
||||
Message string
|
||||
}
|
||||
|
||||
func (t *TextMessage) writeMessage(client *Client) error {
|
||||
packet := MumbleProto.TextMessage{
|
||||
Message: &t.Message,
|
||||
}
|
||||
if t.Users != nil {
|
||||
packet.Session = make([]uint32, len(t.Users))
|
||||
for i, user := range t.Users {
|
||||
packet.Session[i] = user.Session
|
||||
}
|
||||
}
|
||||
if t.Channels != nil {
|
||||
packet.ChannelId = make([]uint32, len(t.Channels))
|
||||
for i, channel := range t.Channels {
|
||||
packet.ChannelId[i] = channel.ID
|
||||
}
|
||||
}
|
||||
if t.Trees != nil {
|
||||
packet.TreeId = make([]uint32, len(t.Trees))
|
||||
for i, channel := range t.Trees {
|
||||
packet.TreeId[i] = channel.ID
|
||||
}
|
||||
}
|
||||
return client.Conn.WriteProto(&packet)
|
||||
}
|
233
vendor/layeh.com/gumble/gumble/user.go
generated
vendored
Normal file
233
vendor/layeh.com/gumble/gumble/user.go
generated
vendored
Normal file
@ -0,0 +1,233 @@
|
||||
package gumble
|
||||
|
||||
import (
|
||||
"github.com/golang/protobuf/proto"
|
||||
"layeh.com/gumble/gumble/MumbleProto"
|
||||
)
|
||||
|
||||
// User represents a user that is currently connected to the server.
|
||||
type User struct {
|
||||
// The user's unique session ID.
|
||||
Session uint32
|
||||
// The user's ID. Contains an invalid value if the user is not registered.
|
||||
UserID uint32
|
||||
// The user's name.
|
||||
Name string
|
||||
// The channel that the user is currently in.
|
||||
Channel *Channel
|
||||
|
||||
// Has the user has been muted?
|
||||
Muted bool
|
||||
// Has the user been deafened?
|
||||
Deafened bool
|
||||
// Has the user been suppressed?
|
||||
Suppressed bool
|
||||
// Has the user been muted by him/herself?
|
||||
SelfMuted bool
|
||||
// Has the user been deafened by him/herself?
|
||||
SelfDeafened bool
|
||||
// Is the user a priority speaker in the channel?
|
||||
PrioritySpeaker bool
|
||||
// Is the user recording audio?
|
||||
Recording bool
|
||||
|
||||
// The user's comment. Contains the empty string if the user does not have a
|
||||
// comment, or if the comment needs to be requested.
|
||||
Comment string
|
||||
// The user's comment hash. nil if User.Comment has been populated.
|
||||
CommentHash []byte
|
||||
// The hash of the user's certificate (can be empty).
|
||||
Hash string
|
||||
// The user's texture (avatar). nil if the user does not have a
|
||||
// texture, or if the texture needs to be requested.
|
||||
Texture []byte
|
||||
// The user's texture hash. nil if User.Texture has been populated.
|
||||
TextureHash []byte
|
||||
|
||||
// The user's stats. Contains nil if the stats have not yet been requested.
|
||||
Stats *UserStats
|
||||
|
||||
client *Client
|
||||
decoder AudioDecoder
|
||||
}
|
||||
|
||||
// SetTexture sets the user's texture.
|
||||
func (u *User) SetTexture(texture []byte) {
|
||||
packet := MumbleProto.UserState{
|
||||
Session: &u.Session,
|
||||
Texture: texture,
|
||||
}
|
||||
u.client.Conn.WriteProto(&packet)
|
||||
}
|
||||
|
||||
// SetPrioritySpeaker sets if the user is a priority speaker in the channel.
|
||||
func (u *User) SetPrioritySpeaker(prioritySpeaker bool) {
|
||||
packet := MumbleProto.UserState{
|
||||
Session: &u.Session,
|
||||
PrioritySpeaker: &prioritySpeaker,
|
||||
}
|
||||
u.client.Conn.WriteProto(&packet)
|
||||
}
|
||||
|
||||
// SetRecording sets if the user is recording audio.
|
||||
func (u *User) SetRecording(recording bool) {
|
||||
packet := MumbleProto.UserState{
|
||||
Session: &u.Session,
|
||||
Recording: &recording,
|
||||
}
|
||||
u.client.Conn.WriteProto(&packet)
|
||||
}
|
||||
|
||||
// IsRegistered returns true if the user's certificate has been registered with
|
||||
// the server. A registered user will have a valid user ID.
|
||||
func (u *User) IsRegistered() bool {
|
||||
return u.UserID > 0
|
||||
}
|
||||
|
||||
// Register will register the user with the server. If the client has
|
||||
// permission to do so, the user will shortly be given a UserID.
|
||||
func (u *User) Register() {
|
||||
packet := MumbleProto.UserState{
|
||||
Session: &u.Session,
|
||||
UserId: proto.Uint32(0),
|
||||
}
|
||||
u.client.Conn.WriteProto(&packet)
|
||||
}
|
||||
|
||||
// SetComment will set the user's comment to the given string. The user's
|
||||
// comment will be erased if the comment is set to the empty string.
|
||||
func (u *User) SetComment(comment string) {
|
||||
packet := MumbleProto.UserState{
|
||||
Session: &u.Session,
|
||||
Comment: &comment,
|
||||
}
|
||||
u.client.Conn.WriteProto(&packet)
|
||||
}
|
||||
|
||||
// Move will move the user to the given channel.
|
||||
func (u *User) Move(channel *Channel) {
|
||||
packet := MumbleProto.UserState{
|
||||
Session: &u.Session,
|
||||
ChannelId: &channel.ID,
|
||||
}
|
||||
u.client.Conn.WriteProto(&packet)
|
||||
}
|
||||
|
||||
// Kick will kick the user from the server.
|
||||
func (u *User) Kick(reason string) {
|
||||
packet := MumbleProto.UserRemove{
|
||||
Session: &u.Session,
|
||||
Reason: &reason,
|
||||
}
|
||||
u.client.Conn.WriteProto(&packet)
|
||||
}
|
||||
|
||||
// Ban will ban the user from the server.
|
||||
func (u *User) Ban(reason string) {
|
||||
packet := MumbleProto.UserRemove{
|
||||
Session: &u.Session,
|
||||
Reason: &reason,
|
||||
Ban: proto.Bool(true),
|
||||
}
|
||||
u.client.Conn.WriteProto(&packet)
|
||||
}
|
||||
|
||||
// SetMuted sets whether the user can transmit audio or not.
|
||||
func (u *User) SetMuted(muted bool) {
|
||||
packet := MumbleProto.UserState{
|
||||
Session: &u.Session,
|
||||
Mute: &muted,
|
||||
}
|
||||
u.client.Conn.WriteProto(&packet)
|
||||
}
|
||||
|
||||
// SetSuppressed sets whether the user is suppressed by the server or not.
|
||||
func (u *User) SetSuppressed(supressed bool) {
|
||||
packet := MumbleProto.UserState{
|
||||
Session: &u.Session,
|
||||
Suppress: &supressed,
|
||||
}
|
||||
u.client.Conn.WriteProto(&packet)
|
||||
}
|
||||
|
||||
// SetDeafened sets whether the user can receive audio or not.
|
||||
func (u *User) SetDeafened(muted bool) {
|
||||
packet := MumbleProto.UserState{
|
||||
Session: &u.Session,
|
||||
Deaf: &muted,
|
||||
}
|
||||
u.client.Conn.WriteProto(&packet)
|
||||
}
|
||||
|
||||
// SetSelfMuted sets whether the user can transmit audio or not.
|
||||
//
|
||||
// This method should only be called on Client.Self().
|
||||
func (u *User) SetSelfMuted(muted bool) {
|
||||
packet := MumbleProto.UserState{
|
||||
Session: &u.Session,
|
||||
SelfMute: &muted,
|
||||
}
|
||||
u.client.Conn.WriteProto(&packet)
|
||||
}
|
||||
|
||||
// SetSelfDeafened sets whether the user can receive audio or not.
|
||||
//
|
||||
// This method should only be called on Client.Self().
|
||||
func (u *User) SetSelfDeafened(muted bool) {
|
||||
packet := MumbleProto.UserState{
|
||||
Session: &u.Session,
|
||||
SelfDeaf: &muted,
|
||||
}
|
||||
u.client.Conn.WriteProto(&packet)
|
||||
}
|
||||
|
||||
// RequestStats requests that the user's stats be sent to the client.
|
||||
func (u *User) RequestStats() {
|
||||
packet := MumbleProto.UserStats{
|
||||
Session: &u.Session,
|
||||
}
|
||||
u.client.Conn.WriteProto(&packet)
|
||||
}
|
||||
|
||||
// RequestTexture requests that the user's actual texture (i.e. non-hashed) be
|
||||
// sent to the client.
|
||||
func (u *User) RequestTexture() {
|
||||
packet := MumbleProto.RequestBlob{
|
||||
SessionTexture: []uint32{u.Session},
|
||||
}
|
||||
u.client.Conn.WriteProto(&packet)
|
||||
}
|
||||
|
||||
// RequestComment requests that the user's actual comment (i.e. non-hashed) be
|
||||
// sent to the client.
|
||||
func (u *User) RequestComment() {
|
||||
packet := MumbleProto.RequestBlob{
|
||||
SessionComment: []uint32{u.Session},
|
||||
}
|
||||
u.client.Conn.WriteProto(&packet)
|
||||
}
|
||||
|
||||
// Send will send a text message to the user.
|
||||
func (u *User) Send(message string) {
|
||||
textMessage := TextMessage{
|
||||
Users: []*User{u},
|
||||
Message: message,
|
||||
}
|
||||
u.client.Send(&textMessage)
|
||||
}
|
||||
|
||||
// SetPlugin sets the user's plugin data.
|
||||
//
|
||||
// Plugins are currently only used for positional audio. Clients will receive
|
||||
// positional audio information from other users if their plugin context is the
|
||||
// same. The official Mumble client sets the context to:
|
||||
//
|
||||
// PluginShortName + "\x00" + AdditionalContextInformation
|
||||
func (u *User) SetPlugin(context []byte, identity string) {
|
||||
packet := MumbleProto.UserState{
|
||||
Session: &u.Session,
|
||||
PluginContext: context,
|
||||
PluginIdentity: &identity,
|
||||
}
|
||||
u.client.Conn.WriteProto(&packet)
|
||||
}
|
74
vendor/layeh.com/gumble/gumble/userlist.go
generated
vendored
Normal file
74
vendor/layeh.com/gumble/gumble/userlist.go
generated
vendored
Normal file
@ -0,0 +1,74 @@
|
||||
package gumble
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"layeh.com/gumble/gumble/MumbleProto"
|
||||
)
|
||||
|
||||
// RegisteredUser represents a registered user on the server.
|
||||
type RegisteredUser struct {
|
||||
// The registered user's ID.
|
||||
UserID uint32
|
||||
// The registered user's name.
|
||||
Name string
|
||||
// The last time the user was seen by the server.
|
||||
LastSeen time.Time
|
||||
// The last channel the user was seen in.
|
||||
LastChannel *Channel
|
||||
|
||||
changed bool
|
||||
deregister bool
|
||||
}
|
||||
|
||||
// SetName sets the new name for the user.
|
||||
func (r *RegisteredUser) SetName(name string) {
|
||||
r.Name = name
|
||||
r.changed = true
|
||||
}
|
||||
|
||||
// Deregister will remove the registered user from the server.
|
||||
func (r *RegisteredUser) Deregister() {
|
||||
r.deregister = true
|
||||
}
|
||||
|
||||
// Register will keep the user registered on the server. This is only useful if
|
||||
// Deregister() was called on the registered user.
|
||||
func (r *RegisteredUser) Register() {
|
||||
r.deregister = false
|
||||
}
|
||||
|
||||
// ACLUser returns an ACLUser for the given registered user.
|
||||
func (r *RegisteredUser) ACLUser() *ACLUser {
|
||||
return &ACLUser{
|
||||
UserID: r.UserID,
|
||||
Name: r.Name,
|
||||
}
|
||||
}
|
||||
|
||||
// RegisteredUsers is a list of users who are registered on the server.
|
||||
//
|
||||
// Whenever a registered user is changed, it does not come into effect until
|
||||
// the registered user list is sent back to the server.
|
||||
type RegisteredUsers []*RegisteredUser
|
||||
|
||||
func (r RegisteredUsers) writeMessage(client *Client) error {
|
||||
packet := MumbleProto.UserList{}
|
||||
|
||||
for _, user := range r {
|
||||
if user.deregister || user.changed {
|
||||
userListUser := &MumbleProto.UserList_User{
|
||||
UserId: &user.UserID,
|
||||
}
|
||||
if !user.deregister {
|
||||
userListUser.Name = &user.Name
|
||||
}
|
||||
packet.Users = append(packet.Users, userListUser)
|
||||
}
|
||||
}
|
||||
|
||||
if len(packet.Users) <= 0 {
|
||||
return nil
|
||||
}
|
||||
return client.Conn.WriteProto(&packet)
|
||||
}
|
30
vendor/layeh.com/gumble/gumble/users.go
generated
vendored
Normal file
30
vendor/layeh.com/gumble/gumble/users.go
generated
vendored
Normal file
@ -0,0 +1,30 @@
|
||||
package gumble
|
||||
|
||||
// Users is a map of server users.
|
||||
//
|
||||
// When accessed through client.Users, it contains all users currently on the
|
||||
// server. When accessed through a specific channel
|
||||
// (e.g. client.Channels[0].Users), it contains only the users in the
|
||||
// channel.
|
||||
type Users map[uint32]*User
|
||||
|
||||
// create adds a new user with the given session to the collection. If a user
|
||||
// with the given session already exists, it is overwritten.
|
||||
func (u Users) create(session uint32) *User {
|
||||
user := &User{
|
||||
Session: session,
|
||||
}
|
||||
u[session] = user
|
||||
return user
|
||||
}
|
||||
|
||||
// Find returns the user with the given name. nil is returned if no user exists
|
||||
// with the given name.
|
||||
func (u Users) Find(name string) *User {
|
||||
for _, user := range u {
|
||||
if user.Name == name {
|
||||
return user
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
62
vendor/layeh.com/gumble/gumble/userstats.go
generated
vendored
Normal file
62
vendor/layeh.com/gumble/gumble/userstats.go
generated
vendored
Normal file
@ -0,0 +1,62 @@
|
||||
package gumble
|
||||
|
||||
import (
|
||||
"crypto/x509"
|
||||
"net"
|
||||
"time"
|
||||
)
|
||||
|
||||
// UserStats contains additional information about a user.
|
||||
type UserStats struct {
|
||||
// The owner of the stats.
|
||||
User *User
|
||||
|
||||
// Stats about UDP packets sent from the client.
|
||||
FromClient UserStatsUDP
|
||||
// Stats about UDP packets sent by the server.
|
||||
FromServer UserStatsUDP
|
||||
|
||||
// Number of UDP packets sent by the user.
|
||||
UDPPackets uint32
|
||||
// Average UDP ping.
|
||||
UDPPingAverage float32
|
||||
// UDP ping variance.
|
||||
UDPPingVariance float32
|
||||
|
||||
// Number of TCP packets sent by the user.
|
||||
TCPPackets uint32
|
||||
// Average TCP ping.
|
||||
TCPPingAverage float32
|
||||
// TCP ping variance.
|
||||
TCPPingVariance float32
|
||||
|
||||
// The user's version.
|
||||
Version Version
|
||||
// When the user connected to the server.
|
||||
Connected time.Time
|
||||
// How long the user has been idle.
|
||||
Idle time.Duration
|
||||
// How much bandwidth the user is current using.
|
||||
Bandwidth int
|
||||
// The user's certificate chain.
|
||||
Certificates []*x509.Certificate
|
||||
// Does the user have a strong certificate? A strong certificate is one that
|
||||
// is not self signed, nor expired, etc.
|
||||
StrongCertificate bool
|
||||
// A list of CELT versions supported by the user's client.
|
||||
CELTVersions []int32
|
||||
// Does the user's client supports the Opus audio codec?
|
||||
Opus bool
|
||||
|
||||
// The user's IP address.
|
||||
IP net.IP
|
||||
}
|
||||
|
||||
// UserStatsUDP contains stats about UDP packets that have been sent to or from
|
||||
// the server.
|
||||
type UserStatsUDP struct {
|
||||
Good uint32
|
||||
Late uint32
|
||||
Lost uint32
|
||||
Resync uint32
|
||||
}
|
52
vendor/layeh.com/gumble/gumble/varint/read.go
generated
vendored
Normal file
52
vendor/layeh.com/gumble/gumble/varint/read.go
generated
vendored
Normal file
@ -0,0 +1,52 @@
|
||||
package varint
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
)
|
||||
|
||||
// Decode reads the first varint encoded number from the given buffer.
|
||||
//
|
||||
// On success, the function returns the varint as an int64, and the number of
|
||||
// bytes read (0 if there was an error).
|
||||
func Decode(b []byte) (int64, int) {
|
||||
if len(b) == 0 {
|
||||
return 0, 0
|
||||
}
|
||||
// 0xxxxxxx 7-bit positive number
|
||||
if (b[0] & 0x80) == 0 {
|
||||
return int64(b[0]), 1
|
||||
}
|
||||
// 10xxxxxx + 1 byte 14-bit positive number
|
||||
if (b[0]&0xC0) == 0x80 && len(b) >= 2 {
|
||||
return int64(b[0]&0x3F)<<8 | int64(b[1]), 2
|
||||
}
|
||||
// 110xxxxx + 2 bytes 21-bit positive number
|
||||
if (b[0]&0xE0) == 0xC0 && len(b) >= 3 {
|
||||
return int64(b[0]&0x1F)<<16 | int64(b[1])<<8 | int64(b[2]), 3
|
||||
}
|
||||
// 1110xxxx + 3 bytes 28-bit positive number
|
||||
if (b[0]&0xF0) == 0xE0 && len(b) >= 4 {
|
||||
return int64(b[0]&0xF)<<24 | int64(b[1])<<16 | int64(b[2])<<8 | int64(b[3]), 4
|
||||
}
|
||||
// 111100__ + int (32-bit) 32-bit positive number
|
||||
if (b[0]&0xFC) == 0xF0 && len(b) >= 5 {
|
||||
return int64(binary.BigEndian.Uint32(b[1:])), 5
|
||||
}
|
||||
// 111101__ + long (64-bit) 64-bit number
|
||||
if (b[0]&0xFC) == 0xF4 && len(b) >= 9 {
|
||||
return int64(binary.BigEndian.Uint64(b[1:])), 9
|
||||
}
|
||||
// 111110__ + varint Negative recursive varint
|
||||
if b[0]&0xFC == 0xF8 {
|
||||
if v, n := Decode(b[1:]); n > 0 {
|
||||
return -v, n + 1
|
||||
}
|
||||
return 0, 0
|
||||
}
|
||||
// 111111xx Byte-inverted negative two bit number (~xx)
|
||||
if b[0]&0xFC == 0xFC {
|
||||
return ^int64(b[0] & 0x03), 1
|
||||
}
|
||||
|
||||
return 0, 0
|
||||
}
|
64
vendor/layeh.com/gumble/gumble/varint/write.go
generated
vendored
Normal file
64
vendor/layeh.com/gumble/gumble/varint/write.go
generated
vendored
Normal file
@ -0,0 +1,64 @@
|
||||
package varint
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"math"
|
||||
)
|
||||
|
||||
// MaxVarintLen is the maximum number of bytes required to encode a varint
|
||||
// number.
|
||||
const MaxVarintLen = 10
|
||||
|
||||
// Encode encodes the given value to varint format.
|
||||
func Encode(b []byte, value int64) int {
|
||||
// 111111xx Byte-inverted negative two bit number (~xx)
|
||||
if value <= -1 && value >= -4 {
|
||||
b[0] = 0xFC | byte(^value&0xFF)
|
||||
return 1
|
||||
}
|
||||
// 111110__ + varint Negative recursive varint
|
||||
if value < 0 {
|
||||
b[0] = 0xF8
|
||||
return 1 + Encode(b[1:], -value)
|
||||
}
|
||||
// 0xxxxxxx 7-bit positive number
|
||||
if value <= 0x7F {
|
||||
b[0] = byte(value)
|
||||
return 1
|
||||
}
|
||||
// 10xxxxxx + 1 byte 14-bit positive number
|
||||
if value <= 0x3FFF {
|
||||
b[0] = byte(((value >> 8) & 0x3F) | 0x80)
|
||||
b[1] = byte(value & 0xFF)
|
||||
return 2
|
||||
}
|
||||
// 110xxxxx + 2 bytes 21-bit positive number
|
||||
if value <= 0x1FFFFF {
|
||||
b[0] = byte((value>>16)&0x1F | 0xC0)
|
||||
b[1] = byte((value >> 8) & 0xFF)
|
||||
b[2] = byte(value & 0xFF)
|
||||
return 3
|
||||
}
|
||||
// 1110xxxx + 3 bytes 28-bit positive number
|
||||
if value <= 0xFFFFFFF {
|
||||
b[0] = byte((value>>24)&0xF | 0xE0)
|
||||
b[1] = byte((value >> 16) & 0xFF)
|
||||
b[2] = byte((value >> 8) & 0xFF)
|
||||
b[3] = byte(value & 0xFF)
|
||||
return 4
|
||||
}
|
||||
// 111100__ + int (32-bit) 32-bit positive number
|
||||
if value <= math.MaxInt32 {
|
||||
b[0] = 0xF0
|
||||
binary.BigEndian.PutUint32(b[1:], uint32(value))
|
||||
return 5
|
||||
}
|
||||
// 111101__ + long (64-bit) 64-bit number
|
||||
if value <= math.MaxInt64 {
|
||||
b[0] = 0xF4
|
||||
binary.BigEndian.PutUint64(b[1:], uint64(value))
|
||||
return 9
|
||||
}
|
||||
|
||||
return 0
|
||||
}
|
24
vendor/layeh.com/gumble/gumble/version.go
generated
vendored
Normal file
24
vendor/layeh.com/gumble/gumble/version.go
generated
vendored
Normal file
@ -0,0 +1,24 @@
|
||||
package gumble
|
||||
|
||||
// Version represents a Mumble client or server version.
|
||||
type Version struct {
|
||||
// The semantic version information as a single unsigned integer.
|
||||
//
|
||||
// Bits 0-15 are the major version, bits 16-23 are the minor version, and
|
||||
// bits 24-31 are the patch version.
|
||||
Version uint32
|
||||
// The name of the client.
|
||||
Release string
|
||||
// The operating system name.
|
||||
OS string
|
||||
// The operating system version.
|
||||
OSVersion string
|
||||
}
|
||||
|
||||
// SemanticVersion returns the version's semantic version components.
|
||||
func (v *Version) SemanticVersion() (major uint16, minor, patch uint8) {
|
||||
major = uint16(v.Version>>16) & 0xFFFF
|
||||
minor = uint8(v.Version>>8) & 0xFF
|
||||
patch = uint8(v.Version) & 0xFF
|
||||
return
|
||||
}
|
76
vendor/layeh.com/gumble/gumble/voicetarget.go
generated
vendored
Normal file
76
vendor/layeh.com/gumble/gumble/voicetarget.go
generated
vendored
Normal file
@ -0,0 +1,76 @@
|
||||
package gumble
|
||||
|
||||
import (
|
||||
"layeh.com/gumble/gumble/MumbleProto"
|
||||
)
|
||||
|
||||
// VoiceTargetLoopback is a special voice target which causes any audio sent to
|
||||
// the server to be returned to the client.
|
||||
//
|
||||
// Its ID should not be modified, and it does not have to to be sent to the
|
||||
// server before use.
|
||||
var VoiceTargetLoopback *VoiceTarget = &VoiceTarget{
|
||||
ID: 31,
|
||||
}
|
||||
|
||||
type voiceTargetChannel struct {
|
||||
channel *Channel
|
||||
links, recursive bool
|
||||
group string
|
||||
}
|
||||
|
||||
// VoiceTarget represents a set of users and/or channels that the client can
|
||||
// whisper to.
|
||||
type VoiceTarget struct {
|
||||
// The voice target ID. This value must be in the range [1, 30].
|
||||
ID uint32
|
||||
users []*User
|
||||
channels []*voiceTargetChannel
|
||||
}
|
||||
|
||||
// Clear removes all users and channels from the voice target.
|
||||
func (v *VoiceTarget) Clear() {
|
||||
v.users = nil
|
||||
v.channels = nil
|
||||
}
|
||||
|
||||
// AddUser adds a user to the voice target.
|
||||
func (v *VoiceTarget) AddUser(user *User) {
|
||||
v.users = append(v.users, user)
|
||||
}
|
||||
|
||||
// AddChannel adds a user to the voice target. If group is non-empty, only
|
||||
// users belonging to that ACL group will be targeted.
|
||||
func (v *VoiceTarget) AddChannel(channel *Channel, recursive, links bool, group string) {
|
||||
v.channels = append(v.channels, &voiceTargetChannel{
|
||||
channel: channel,
|
||||
links: links,
|
||||
recursive: recursive,
|
||||
group: group,
|
||||
})
|
||||
}
|
||||
|
||||
func (v *VoiceTarget) writeMessage(client *Client) error {
|
||||
packet := MumbleProto.VoiceTarget{
|
||||
Id: &v.ID,
|
||||
Targets: make([]*MumbleProto.VoiceTarget_Target, 0, len(v.users)+len(v.channels)),
|
||||
}
|
||||
for _, user := range v.users {
|
||||
packet.Targets = append(packet.Targets, &MumbleProto.VoiceTarget_Target{
|
||||
Session: []uint32{user.Session},
|
||||
})
|
||||
}
|
||||
for _, vtChannel := range v.channels {
|
||||
target := &MumbleProto.VoiceTarget_Target{
|
||||
ChannelId: &vtChannel.channel.ID,
|
||||
Links: &vtChannel.links,
|
||||
Children: &vtChannel.recursive,
|
||||
}
|
||||
if vtChannel.group != "" {
|
||||
target.Group = &vtChannel.group
|
||||
}
|
||||
packet.Targets = append(packet.Targets, target)
|
||||
}
|
||||
|
||||
return client.Conn.WriteProto(&packet)
|
||||
}
|
Reference in New Issue
Block a user