增加了发布通道
This commit is contained in:
+52
-5
@@ -222,6 +222,7 @@ type TextMsg struct {
|
||||
InvokerName string
|
||||
InvokerUID string
|
||||
Message string
|
||||
InvokerID int
|
||||
TargetMode int
|
||||
TargetID string
|
||||
}
|
||||
@@ -261,6 +262,7 @@ type TSClient struct {
|
||||
callback EventCallback
|
||||
connected bool
|
||||
host string // 服务器地址(用于文件传输 TCP 连接)
|
||||
selfUID string // 当前连接的 TeamSpeak UID,用于识别同 identity 的残留 session
|
||||
|
||||
// 事件队列:保证所有 JNI 回调在同一个协程中顺序执行
|
||||
evtQueueMu sync.Mutex // protects eventQueue replacement
|
||||
@@ -303,6 +305,11 @@ func (c *TSClient) Connect(host, nickname, password, defaultChannel, defaultChan
|
||||
return fmt.Sprintf("生成身份失败: %v", err)
|
||||
}
|
||||
|
||||
// 计算 TeamSpeak UID 并存储,用于僵尸会话过滤(isStaleSelfSession)。
|
||||
c.mu.Lock()
|
||||
c.selfUID = crypto.GetUidFromPublicKey(identity.PublicKeyBase64())
|
||||
c.mu.Unlock()
|
||||
|
||||
opts := []ts.ClientOption{}
|
||||
if password != "" {
|
||||
opts = append(opts, ts.WithServerPassword(password))
|
||||
@@ -1423,6 +1430,25 @@ func (c *TSClient) isCurrentClient(client *ts.Client) bool {
|
||||
return c.client == client
|
||||
}
|
||||
|
||||
// isStaleSelfSession checks if a remote client event belongs to a stale session
|
||||
// using the same identity as the current connection. This happens when the previous
|
||||
// process crashed without sending clientdisconnect, leaving a "zombie" session on
|
||||
// the server that the new connection must isolate from.
|
||||
func (c *TSClient) isStaleSelfSession(uid string, clid int) bool {
|
||||
c.mu.Lock()
|
||||
selfUID := c.selfUID
|
||||
selfClid := 0
|
||||
if c.client != nil {
|
||||
selfClid = int(c.client.ClientID())
|
||||
}
|
||||
c.mu.Unlock()
|
||||
|
||||
if selfUID == "" || uid == "" || uid != selfUID {
|
||||
return false
|
||||
}
|
||||
return clid != selfClid
|
||||
}
|
||||
|
||||
func (c *TSClient) queueClientEvent(client *ts.Client, evt queuedEvent) {
|
||||
if c.isCurrentClient(client) {
|
||||
c.queueEvent(evt)
|
||||
@@ -1444,6 +1470,10 @@ func (c *TSClient) registerEvents(client *ts.Client) {
|
||||
client.OnDisconnected(func(err error) {
|
||||
c.StopReceiveAudio()
|
||||
c.mu.Lock()
|
||||
if c.client != client {
|
||||
c.mu.Unlock()
|
||||
return
|
||||
}
|
||||
c.connected = false
|
||||
c.client = nil
|
||||
c.mu.Unlock()
|
||||
@@ -1455,12 +1485,13 @@ func (c *TSClient) registerEvents(client *ts.Client) {
|
||||
})
|
||||
|
||||
client.OnTextMessage(func(msg ts.TextMessage) {
|
||||
c.queueEvent(queuedEvent{
|
||||
c.queueClientEvent(client, queuedEvent{
|
||||
evtType: "textmessage",
|
||||
data: &TextMsg{
|
||||
InvokerName: msg.InvokerName,
|
||||
InvokerUID: msg.InvokerUID,
|
||||
Message: msg.Message,
|
||||
InvokerID: int(msg.InvokerID),
|
||||
TargetMode: msg.TargetMode,
|
||||
TargetID: fmt.Sprintf("%d", msg.TargetID),
|
||||
},
|
||||
@@ -1468,7 +1499,11 @@ func (c *TSClient) registerEvents(client *ts.Client) {
|
||||
})
|
||||
|
||||
client.OnClientEnter(func(info ts.ClientInfo) {
|
||||
c.queueEvent(queuedEvent{
|
||||
if c.isStaleSelfSession(info.UID, int(info.ID)) {
|
||||
log.Printf("[TSBridge] ignoring stale self session enter clid=%d uid=%s", info.ID, info.UID)
|
||||
return
|
||||
}
|
||||
c.queueClientEvent(client, queuedEvent{
|
||||
evtType: "cliententer",
|
||||
data: &Client{
|
||||
ID: int(info.ID),
|
||||
@@ -1481,8 +1516,13 @@ func (c *TSClient) registerEvents(client *ts.Client) {
|
||||
})
|
||||
|
||||
client.OnClientLeave(func(data ts.ClientLeftViewEvent) {
|
||||
// 不过滤僵尸的 leave 事件:GetClientsJSON 初始同步可能已包含僵尸,
|
||||
// 需要 leave 事件来更新 UI 将其移除。同时确保音频解码器被清理。
|
||||
if c.isStaleSelfSession(data.UID, int(data.ID)) {
|
||||
log.Printf("[TSBridge] stale self session leave clid=%d uid=%s (allowing for UI cleanup)", data.ID, data.UID)
|
||||
}
|
||||
c.RemoveRemoteAudioClient(int(data.ID))
|
||||
c.queueEvent(queuedEvent{
|
||||
c.queueClientEvent(client, queuedEvent{
|
||||
evtType: "clientleave",
|
||||
data: struct {
|
||||
ID int
|
||||
@@ -1495,7 +1535,7 @@ func (c *TSClient) registerEvents(client *ts.Client) {
|
||||
})
|
||||
|
||||
client.OnClientMoved(func(data ts.ClientMovedEvent) {
|
||||
c.queueEvent(queuedEvent{
|
||||
c.queueClientEvent(client, queuedEvent{
|
||||
evtType: "clientmoved",
|
||||
data: struct {
|
||||
ID int
|
||||
@@ -1510,6 +1550,10 @@ func (c *TSClient) registerEvents(client *ts.Client) {
|
||||
client.OnKicked(func(reason string) {
|
||||
c.StopReceiveAudio()
|
||||
c.mu.Lock()
|
||||
if c.client != client {
|
||||
c.mu.Unlock()
|
||||
return
|
||||
}
|
||||
c.connected = false
|
||||
c.client = nil
|
||||
c.mu.Unlock()
|
||||
@@ -1526,6 +1570,9 @@ func (c *TSClient) registerEvents(client *ts.Client) {
|
||||
}
|
||||
// Keep the existing raw bridge active until an Android libopus decoder
|
||||
// factory has been supplied and the Go receiver explicitly started.
|
||||
if !c.isCurrentClient(client) {
|
||||
return
|
||||
}
|
||||
c.queueEvent(queuedEvent{
|
||||
evtType: "voicedata",
|
||||
data: struct {
|
||||
@@ -1545,7 +1592,7 @@ func (c *TSClient) registerEvents(client *ts.Client) {
|
||||
})
|
||||
|
||||
client.OnPoked(func(evt ts.PokeEvent) {
|
||||
c.queueEvent(queuedEvent{
|
||||
c.queueClientEvent(client, queuedEvent{
|
||||
evtType: "poked",
|
||||
data: &PokeEvent{
|
||||
InvokerID: int(evt.InvokerID),
|
||||
|
||||
@@ -70,6 +70,13 @@ func (c *TSClient) ConnectWithIdentity(identityStr, host, nickname, password, de
|
||||
return fmt.Sprintf("identity 解析失败: %v", err)
|
||||
}
|
||||
|
||||
// 计算 TeamSpeak UID(base64(SHA1(publicKey))),用于僵尸会话过滤。
|
||||
// 不能直接存储 identity 字符串,因为 UID 是公钥的哈希,格式完全不同。
|
||||
uid := crypto.GetUidFromPublicKey(identity.PublicKeyBase64())
|
||||
c.mu.Lock()
|
||||
c.selfUID = uid
|
||||
c.mu.Unlock()
|
||||
|
||||
opts := []ts.ClientOption{}
|
||||
if password != "" {
|
||||
opts = append(opts, ts.WithServerPassword(password))
|
||||
@@ -102,7 +109,15 @@ func (c *TSClient) ConnectWithIdentity(identityStr, host, nickname, password, de
|
||||
return fmt.Sprintf("连接失败: %v", err)
|
||||
}
|
||||
|
||||
c.cleanupDuplicateIdentitySessions(client, identity)
|
||||
// 延迟执行僵尸清理:连接刚建立时客户端在默认频道,ListClients 可能尚未
|
||||
// 收到服务器发来的完整客户端列表。延迟 3 秒确保所有 notifycliententerview
|
||||
// 事件已到达,此时 ListClients 能发现不同频道中的僵尸会话。
|
||||
go func() {
|
||||
time.Sleep(3 * time.Second)
|
||||
if c.isCurrentClient(client) {
|
||||
c.cleanupDuplicateIdentitySessions(client, identity)
|
||||
}
|
||||
}()
|
||||
return ""
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user