vergleichs.md aktualisiert
This commit is contained in:
+414
-162
@@ -1,218 +1,470 @@
|
||||
# Live Plugin (V8) vs. Planned SDK/ServerLib Architecture
|
||||
# Live Plugin (V8) vs Planned SDK/ServerLib Architecture
|
||||
|
||||
A detailed, side-by-side comparison of **what the shipping plugin does today** (V8, branch `tsmain`)
|
||||
versus **what the new plan proposes** (branch `ts3-Testing`, "Option B"): which API each uses,
|
||||
what stays the same, and what fundamentally changes.
|
||||
A **development-ready** side-by-side comparison of:
|
||||
|
||||
> [!IMPORTANT]
|
||||
> Both use "a TeamSpeak SDK" — but **two completely different ones**.
|
||||
> **Today:** the *Client Plugin SDK* (a guest inside the retail TeamSpeak client).
|
||||
> **Plan:** the *TeamSpeak 3 SDK* (ClientLib + ServerLib — your own client and your own server).
|
||||
> Same word, opposite architecture.
|
||||
| Document side | What it is |
|
||||
|---|---|
|
||||
| **Live V8** | Shipping Conan Exiles proximity plugin (`tsmain`): a DLL loaded by the **retail** TeamSpeak 3 client |
|
||||
| **Plan (Option B)** | New product on branch `ts3-Testing`: custom **ClientLib** app + custom **ServerLib** voice server |
|
||||
|
||||
This file answers: *which API does each use, in what order, who sends data to whom, and what actually changes.*
|
||||
|
||||
---
|
||||
|
||||
## 1. At a glance
|
||||
## Table of contents
|
||||
|
||||
1. [One-sentence difference](#1-one-sentence-difference)
|
||||
2. [At-a-glance table](#2-at-a-glance-table)
|
||||
3. [The two SDKs explained](#3-the-two-sdks-explained)
|
||||
4. [Architecture diagrams](#4-architecture-diagrams)
|
||||
5. [Lifecycle: boot order (Live V8)](#5-lifecycle-boot-order-live-v8)
|
||||
6. [Lifecycle: boot order (Plan)](#6-lifecycle-boot-order-plan)
|
||||
7. [API map: every callback](#7-api-map-every-callback)
|
||||
8. [API map: every active call we make](#8-api-map-every-active-call-we-make)
|
||||
9. [Data flow: position](#9-data-flow-position)
|
||||
10. [Data flow: who hears whom](#10-data-flow-who-hears-whom)
|
||||
11. [Data flow: 3D and audio effects](#11-data-flow-3d-and-audio-effects)
|
||||
12. [Threading models](#12-threading-models)
|
||||
13. [Module map](#13-module-map)
|
||||
14. [Operations, license, scale](#14-operations-license-scale)
|
||||
15. [What is reusable vs rewritten](#15-what-is-reusable-vs-rewritten)
|
||||
16. [Conclusion](#16-conclusion)
|
||||
|
||||
---
|
||||
|
||||
## 1. One-sentence difference
|
||||
|
||||
> **Live V8** is a guest DLL inside retail TeamSpeak that edits voice **locally**.
|
||||
> **The plan** is our own TeamSpeak-compatible voice stack where a **server** decides who receives whose packets.
|
||||
|
||||
Same English word "SDK". Two different TeamSpeak products. Not an upgrade — an architecture switch.
|
||||
|
||||
---
|
||||
|
||||
## 2. At-a-glance table
|
||||
|
||||
| Topic | Live V8 (today) | Plan (Option B) |
|
||||
|---|---|---|
|
||||
| SDK used | **Client Plugin SDK** | **TeamSpeak 3 SDK** (ClientLib + ServerLib) |
|
||||
| Header(s) | `sdk/include/ts3_functions.h` (`TS3Functions`) | `clientlib.h` + `serverlib.h` |
|
||||
| Function family | `ts3plugin_*` (callbacks), `ts3Functions->*` | `ts3client_*` and `ts3server_*` |
|
||||
| What "the server" is | Retail `ts3server` (foreign) | **Our own** `conan-voice-server` (ServerLib) |
|
||||
| What "the client" is | Retail TeamSpeak loads our DLL | **Our own** app embedding ClientLib |
|
||||
| Who decides "who hears whom" | The client, locally (volume/mute) | The **server** (`ts3server_setClientWhisperList`) |
|
||||
| Position exchange | Peer-to-peer via `sendPluginCommand` (CEPOS) | Client to server via `CLIENT_META_DATA` |
|
||||
| Audio control | `onEditPlaybackVoiceDataEvent` (edits samples) | Server-side whisper routing + client-side 3D/PCM |
|
||||
| 3D sound | `systemset3D*` / `channelset3DAttributes` (in plugin) | Same `ts3client_*3D*` functions (in our app) |
|
||||
| Needs retail TeamSpeak | **Yes** (users already have it) | **No** — a custom client app is required |
|
||||
| License | Free | **Paid** (free evaluation for dev only) |
|
||||
| Lifecycle | Tied to TS3 | TS3 SDK **EOL 2027-12-31** |
|
||||
| "Who hears whom" reach | Local volume only; everyone still gets packets | Real packet routing (B/C never receive A) |
|
||||
| Effort | Exists, ships today | Build **both** a client and a server |
|
||||
| Product shape | One DLL (`conan_exiles.dll`) | Client app + server process |
|
||||
| SDK product | **Client Plugin SDK** (free) | **TeamSpeak 3 SDK** ClientLib + ServerLib (paid) |
|
||||
| Headers | `sdk/include/ts3_functions.h` | `clientlib.h`, `serverlib.h` |
|
||||
| Function prefixes | `ts3plugin_*`, `TS3Functions.*` | `ts3client_*`, `ts3server_*` |
|
||||
| Voice host | Foreign retail `ts3server` | Our `conan-voice-server` |
|
||||
| User runs | TeamSpeak 3 Client + our plugin | Our Conan Voice Client app |
|
||||
| Who hears whom | Client PCM mute/gain | Server `ts3server_setClientWhisperList` |
|
||||
| Position path | Peer-to-peer `sendPluginCommand("CEPOS:…")` | Client → server `CLIENT_META_DATA` |
|
||||
| Packet bandwidth | All channel members always receive voice | Only audible targets receive voice |
|
||||
| License | Free | Paid (eval free for development) |
|
||||
| EOL risk | Tied to TS3 client | TS3 SDK EOL **2027-12-31** |
|
||||
| Branch | `tsmain` | `ts3-Testing` (isolated) |
|
||||
|
||||
---
|
||||
|
||||
## 2. The two SDKs — the root of the confusion
|
||||
## 3. The two SDKs explained
|
||||
|
||||
The word "SDK" refers to two different TeamSpeak products.
|
||||
### 3.1 Live V8 — Client Plugin SDK
|
||||
|
||||
### 2a. Today: Client Plugin SDK
|
||||
- Proof in the repo: `sdk/README.md`, line 1 = "TeamSpeak 3 Client Plugin SDK".
|
||||
- We ship a **DLL** that the **retail TeamSpeak client loads**.
|
||||
- We are a guest: we react to events (`ts3plugin_*`) and trigger a few client actions
|
||||
through the function table `TS3Functions`.
|
||||
- We **cannot** route voice at the server level. We can only alter incoming voice
|
||||
**locally** (quieter, pan, filter).
|
||||
Evidence in this repo:
|
||||
|
||||
### 2b. Plan: TeamSpeak 3 SDK (ClientLib + ServerLib)
|
||||
- A separate download, different libraries, different function families
|
||||
(`ts3client_*` and `ts3server_*`).
|
||||
- We **host our own voice server** (ServerLib) and **build our own client** (ClientLib).
|
||||
- The retail TeamSpeak client is no longer involved — and in fact **cannot** connect to
|
||||
an SDK server (error `0x0705`, per TeamSpeak docs).
|
||||
```1:3:sdk/README.md
|
||||
# TeamSpeak 3 Client Plugin SDK
|
||||
|
||||
> [!NOTE]
|
||||
> This is not an "update" — it is an architecture switch: from "a plugin guest inside
|
||||
> TeamSpeak" to "a standalone client + server product".
|
||||
|
||||
---
|
||||
|
||||
## 3. Architecture, side by side
|
||||
|
||||
### Live V8 (today)
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
mod["Conan Exiles mod"] -->|"Pos.txt"| plugin["Our plugin (DLL inside retail TeamSpeak)"]
|
||||
plugin -->|"sendPluginCommand CEPOS"| peers["Other clients"]
|
||||
peers -->|"CEPOS packets"| plugin
|
||||
plugin -->|"onEditPlaybackVoiceDataEvent: gain/pan/filter"| out["Local audio output"]
|
||||
ts["Retail ts3server"] -.->|"transport + channels only, no game logic"| plugin
|
||||
Welcome to the official helper repository for creating native plugins for the TeamSpeak 3 Client.
|
||||
```
|
||||
|
||||
- Each client computes **for itself** how loud it hears the others.
|
||||
- Positions travel **peer-to-peer** via plugin commands (the retail server just relays them).
|
||||
- "Who hears whom" is really just a **volume**: voice packets still reach everyone in the
|
||||
channel; they are only mixed quiet/muted locally.
|
||||
- TeamSpeak loads our DLL and calls exported `ts3plugin_*` functions.
|
||||
- We call back into TeamSpeak through `struct TS3Functions` filled by `ts3plugin_setFunctionPointers`.
|
||||
- We **cannot** create a virtual server, set server-side whisper routing, or replace TeamSpeak.
|
||||
- Proximity = edit PCM samples in `ts3plugin_onEditPlaybackVoiceDataEvent`.
|
||||
|
||||
### Plan (Option B)
|
||||
There are **zero** `ts3client_*` / `ts3server_*` symbols in this repository.
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
game["Conan Exiles"] -->|"local IPC"| client["Conan Voice Client (custom app, ClientLib)"]
|
||||
client -->|"CLIENT_META_DATA = position"| server["Conan Voice Server (custom process, ServerLib)"]
|
||||
server -->|"PlayerManager + ProximityEngine + VoiceRouter"| router["setClientWhisperList A to B,C"]
|
||||
router --> transport["TeamSpeak voice transport (SDK)"]
|
||||
transport -->|"A's voice only to B and C"| listeners["Clients B, C (D gets nothing)"]
|
||||
### 3.2 Plan — TeamSpeak 3 SDK (ClientLib + ServerLib)
|
||||
|
||||
- Separate commercial download (not the Plugin SDK folder in this repo).
|
||||
- We link **ServerLib** into `conan-voice-server` and **ClientLib** into our custom client.
|
||||
- Retail TeamSpeak **cannot** connect to an SDK ServerLib server (community-confirmed error `0x0705`).
|
||||
- Users install **our** client app (similar to how Mumble works), not TeamSpeak + a plugin.
|
||||
|
||||
```text
|
||||
Plugin SDK (today) TeamSpeak 3 SDK (plan)
|
||||
───────────────── ──────────────────────
|
||||
ts3plugin_* ts3client_* (client app)
|
||||
TS3Functions.* ts3server_* (our server)
|
||||
retail TS loads DLL we host ServerLib ourselves
|
||||
local PCM mix server whisper-list routing
|
||||
```
|
||||
|
||||
- The **server** decides centrally who hears whom, and enforces it via whisper lists (real routing).
|
||||
- Positions go **client to server** (no longer peer-to-peer).
|
||||
---
|
||||
|
||||
## 4. Architecture diagrams
|
||||
|
||||
### 4.1 Live V8
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant Game as Conan Mod
|
||||
participant Plugin as Plugin DLL
|
||||
participant Peer as Other Plugin
|
||||
participant PCM as Playback PCM
|
||||
participant Retail as Retail ts3server
|
||||
|
||||
Game->>Plugin: Pos.txt (x,y,z,yaw)
|
||||
Plugin->>Retail: sendPluginCommand("CEPOS:...")
|
||||
Retail->>Peer: relay plugin command
|
||||
Peer->>Peer: player_table_put + proximity_math
|
||||
Retail->>Plugin: voice packets from Peer
|
||||
Plugin->>PCM: onEditPlaybackVoiceDataEvent gain/pan/filter
|
||||
```
|
||||
|
||||
### 4.2 Plan (Option B)
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant Game as Conan
|
||||
participant Client as Conan Voice Client
|
||||
participant Server as Conan Voice Server
|
||||
participant Router as VoiceRouter
|
||||
|
||||
Game->>Client: IGameDataProvider position
|
||||
Client->>Server: CLIENT_META_DATA + flushClientSelfUpdates
|
||||
Server->>Server: PlayerManager.updatePos
|
||||
Router->>Router: spatial hash -> audible set
|
||||
Router->>Server: ts3server_setClientWhisperList(A,{B,C})
|
||||
Note over Client,Server: A speaks - only B and C receive packets
|
||||
Client->>Client: 3D attributes + optional local effects
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. API comparison — concrete functions
|
||||
## 5. Lifecycle: boot order (Live V8)
|
||||
|
||||
### 4a. Callbacks (events we react to)
|
||||
Retail TeamSpeak owns the process. Our plugin only hooks in.
|
||||
|
||||
| Purpose | Live V8 (`ts3plugin_*`) | Plan (`ts3client_*` / `ts3server_*`) |
|
||||
|---|---|---|
|
||||
| Connection status | `onConnectStatusChangeEvent` | Client: `onConnectStatusChangeEvent` |
|
||||
| Our own data transfer | `onPluginCommandEvent` (CEPOS/CEDRAIN) | Server: read `CLIENT_META_DATA` change |
|
||||
| Alter audio | `onEditPlaybackVoiceDataEvent` | Client: `onEditPlaybackVoiceDataEvent` (local effects only) |
|
||||
| 3D rolloff | `onCustom3dRolloffCalculationClientEvent` | Client: `onCustom3dRolloffCalculationClientEvent` |
|
||||
| Client moves | `onClientMoveEvent` / `onClientMoveMovedEvent` | Server: `onClientMoved`; Client: `onClientMoveEvent` |
|
||||
| Channel description (hub) | `onChannelDescriptionUpdateEvent`, `onUpdateChannelEvent`, `onUpdateChannelEditedEvent` | Handled differently (root-channel config) |
|
||||
| Who is talking | `onTalkStatusChangeEvent` | Server: `onClientStartTalkingEvent` / `onClientStopTalkingEvent` |
|
||||
| Connect/disconnect (server) | not available to us | Server: `onClientConnected` / `onClientDisconnected` |
|
||||
```text
|
||||
1. TeamSpeak loads conan_exiles.dll
|
||||
2. ts3plugin_name / version / apiVersion / author / description
|
||||
3. ts3plugin_setFunctionPointers(TS3Functions) -> ts3_adapter_set_functions
|
||||
4. ts3plugin_registerPluginID(id) -> needed for sendPluginCommand
|
||||
5. ts3plugin_init()
|
||||
6. onConnectStatusChangeEvent(STATUS_CONNECTION_ESTABLISHED)
|
||||
7. Runtime loop (callback thread + CEDRAIN + PCM):
|
||||
- pos watcher reads Pos.txt
|
||||
- CEPOS send/receive via onPluginCommandEvent
|
||||
- proximity recompute on callback thread
|
||||
- onEditPlaybackVoiceDataEvent applies mix
|
||||
- systemset3DListenerAttributes / channelset3DAttributes
|
||||
8. ts3plugin_shutdown() -> ts3_adapter_shutdown
|
||||
```
|
||||
|
||||
*Evidence: `src/ts/entry/ts3_exports.h`, `src/ts/entry/ts3_entry.c`.*
|
||||
Key files:
|
||||
|
||||
### 4b. Functions we actively call (what we trigger)
|
||||
|
||||
| Purpose | Live V8 (`TS3Functions`, `g_ts3.*`) | Plan (SDK) |
|
||||
|---|---|---|
|
||||
| Send our own data | `sendPluginCommand("CEPOS:...")` | Client: `setClientSelfVariableAsString(CLIENT_META_DATA)` + `flushClientSelfUpdates` |
|
||||
| Wakeup / tick | `sendPluginCommand("CEDRAIN:1")` | Own event loop in the server |
|
||||
| Unmute clients | `requestUnmuteClientsTemporary` | Not needed (routing is server-side) |
|
||||
| Per-client volume | `setClientVolumeModifier` | Mainly via whisper routing; fine gain optionally local |
|
||||
| Move into channel (hub) | `requestClientMove` | Server: `ts3server_*` channel ops |
|
||||
| 3D global | `systemset3DSettings` | Client: `ts3client_systemset3DSettings` |
|
||||
| 3D listener (self) | `systemset3DListenerAttributes` | Client: `ts3client_systemset3DListenerAttributes` |
|
||||
| 3D per speaker | `channelset3DAttributes` | Client: `ts3client_channelset3DAttributes` |
|
||||
| **Who hears whom (routing)** | **does not exist** (local volume only) | **Server: `ts3server_setClientWhisperList`** |
|
||||
| Start server | not applicable | `ts3server_initServerLib` then `ts3server_createVirtualServer` |
|
||||
| Stop server | not applicable | `ts3server_stopVirtualServer` then `ts3server_destroyServerLib` |
|
||||
| Start client | (retail client does this) | `ts3client_initClientLib` then `spawnNewServerConnectionHandler` then `startConnection` |
|
||||
|
||||
*Evidence: `src/ts/adapter/ts3_adapter.c` (around lines 427, 653-690, 737-768).*
|
||||
|
||||
> [!TIP]
|
||||
> The 3D functions (`systemset3DListenerAttributes`, `channelset3DAttributes`,
|
||||
> `systemset3DSettings`) are named almost identically in **both** SDKs, so that part is
|
||||
> largely portable. The **big** difference is routing: `ts3server_setClientWhisperList`
|
||||
> exists only in the ServerLib.
|
||||
| Step | File |
|
||||
|---|---|
|
||||
| Exports | `src/ts/entry/ts3_exports.h`, `ts3_entry.c` |
|
||||
| TS API wrapper | `src/ts/adapter/ts3_adapter.c` |
|
||||
| CEPOS | `src/ts/proximity/ts3_cepos.c` |
|
||||
| PCM mix | `src/ts/proximity/ts3_proximity_audio.c` |
|
||||
| 3D | `src/ts/proximity/ts3_3d.c` |
|
||||
| Position file | `src/core/mod_file/pos_file.c` |
|
||||
|
||||
---
|
||||
|
||||
## 5. Data flow in detail
|
||||
## 6. Lifecycle: boot order (Plan)
|
||||
|
||||
### 5a. Position exchange
|
||||
### 6.1 Server process (`conan-voice-server`)
|
||||
|
||||
| | Live V8 | Plan |
|
||||
```text
|
||||
1. Load config + start logger
|
||||
2. memset(&ServerLibFunctions, 0, sizeof(...))
|
||||
3. Assign callbacks (onClientConnected, onClientDisconnected, ...)
|
||||
4. ts3server_initServerLib(&funcs, LogType_FILE|LogType_CONSOLE, logDir)
|
||||
5. Create/load server keypair [VERIFY exact name]
|
||||
6. ts3server_createVirtualServer(port, ip, name, keyPair, maxClients, &serverID)
|
||||
7. Init PlayerManager, ProximityEngine, VoiceRouter
|
||||
8. Event loop + routing tick (5-10 Hz)
|
||||
9. Shutdown:
|
||||
ts3server_stopVirtualServer(serverID)
|
||||
ts3server_destroyServerLib()
|
||||
```
|
||||
|
||||
Confirmed server APIs:
|
||||
|
||||
| Function | Role |
|
||||
|---|---|
|
||||
| `ts3server_initServerLib` | Start ServerLib with callback table |
|
||||
| `ts3server_createVirtualServer` | Create the virtual server |
|
||||
| `ts3server_stopVirtualServer` | Stop one virtual server |
|
||||
| `ts3server_destroyServerLib` | Tear down ServerLib |
|
||||
| `ts3server_setClientWhisperList` | **Core routing API** |
|
||||
| `ts3server_getClientList` | Enumerate clients |
|
||||
| `ts3server_getChannelList` | Enumerate channels |
|
||||
| `ts3server_freeMemory` | Free SDK-allocated arrays |
|
||||
|
||||
### 6.2 Client app (`conan-voice-client`)
|
||||
|
||||
```text
|
||||
1. Fill ClientUIFunctions callbacks
|
||||
2. ts3client_initClientLib(...)
|
||||
3. ts3client_spawnNewServerConnectionHandler(0, &scHandlerID)
|
||||
4. ts3client_createIdentity (or load stored identity)
|
||||
5. ts3client_openPlaybackDevice / openCaptureDevice
|
||||
6. ts3client_startConnection(scHandlerID, identity, host, port, nick, ...)
|
||||
7. Wait onConnectStatusChangeEvent == STATUS_CONNECTION_ESTABLISHED
|
||||
8. Loop:
|
||||
- read game position
|
||||
- setClientSelfVariableAsString(CLIENT_META_DATA, packed)
|
||||
- flushClientSelfUpdates
|
||||
- apply 3D listener/speaker attributes
|
||||
9. Shutdown:
|
||||
ts3client_stopConnection
|
||||
ts3client_destroyServerConnectionHandler
|
||||
ts3client_destroyClientLib
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 7. API map: every callback
|
||||
|
||||
### Live V8 — `ts3plugin_*` (exported)
|
||||
|
||||
From `src/ts/entry/ts3_exports.h`:
|
||||
|
||||
| Callback | What V8 does with it |
|
||||
|---|---|
|
||||
| `ts3plugin_init` / `shutdown` | Start/stop plugin subsystems |
|
||||
| `ts3plugin_setFunctionPointers` | Store `TS3Functions` into adapter |
|
||||
| `ts3plugin_registerPluginID` | Required for `sendPluginCommand` |
|
||||
| `onConnectStatusChangeEvent` | Adopt active connection / reset caches |
|
||||
| `onPluginCommandEvent` | Receive CEPOS / CEDRAIN / plugin traffic |
|
||||
| `onEditPlaybackVoiceDataEvent` | **Primary proximity mix** (gain/pan/filter) |
|
||||
| `onCustom3dRolloffCalculationClientEvent` | Custom distance curve for 3D |
|
||||
| `onClientMoveEvent` / `onClientMoveMovedEvent` | Hub ↔ ingame channel tracking |
|
||||
| `onChannelDescriptionUpdateEvent` | Hub settings from root channel description |
|
||||
| `onUpdateChannelEvent` / `onUpdateChannelEditedEvent` | Channel metadata refresh |
|
||||
| `onTalkStatusChangeEvent` | Talk indicator / HUD hints |
|
||||
| `onHotkeyEvent` | Voice-mode hotkeys |
|
||||
|
||||
### Plan — ClientLib + ServerLib callbacks
|
||||
|
||||
| Side | Callback | Role |
|
||||
|---|---|---|
|
||||
| Source | `Pos.txt` (game mod) via `pos_file.c` | `IGameDataProvider` (ported from Mumble/V8) |
|
||||
| Packing | CEPOS, 56 bytes, base64 | compact binary/base64 in `CLIENT_META_DATA` (max 4096 B) |
|
||||
| Path | Client to **all** clients (peer-to-peer, `sendPluginCommand`) | Client to **server** (`CLIENT_META_DATA`) |
|
||||
| Who computes distance | **each client itself** | **the server** (authoritative) |
|
||||
| Trust | Client reports its position (spoofable) | Client reports position (spoofable) — server is still the authority for routing/permissions |
|
||||
| Client | `onConnectStatusChangeEvent` | Connection lifecycle |
|
||||
| Client | `onEditPlaybackVoiceDataEvent` | Local effects only (reverb/lowpass) |
|
||||
| Client | `onCustom3dRolloffCalculationClientEvent` | Distance curve |
|
||||
| Client | `onClientMoveEvent` | Channel visibility |
|
||||
| Client | `onTalkStatusChangeEvent` | HUD |
|
||||
| Server | `onClientConnected` | `PlayerManager.add` |
|
||||
| Server | `onClientDisconnected` | remove + clear whisper list |
|
||||
| Server | `onClientMoved` | channel tracking |
|
||||
| Server | `onClientStartTalkingEvent` / `Stop` | diagnostics / HUD hints |
|
||||
| Server | `onAccountingErrorEvent` | license/slot errors |
|
||||
|
||||
### 5b. "Who hears whom"
|
||||
Names marked **VERIFY** in the plan must be confirmed against `serverlib.h` / `clientlib.h` in Phase 0.
|
||||
|
||||
- **Live V8:** purely client-side. All voice packets arrive; the plugin only makes remote
|
||||
speakers **quieter or muted** (`onEditPlaybackVoiceDataEvent` + `setClientVolumeModifier`).
|
||||
Bandwidth: every packet always flows.
|
||||
- **Plan:** server-side. The `VoiceRouter` builds a target list per speaker and calls
|
||||
`ts3server_setClientWhisperList(A, ..., {B,C})`. Anyone not on the list never receives A's
|
||||
voice at all.
|
||||
---
|
||||
|
||||
## 8. API map: every active call we make
|
||||
|
||||
### Live V8 — through `TS3Functions` (`g_ts3.*` in `ts3_adapter.c`)
|
||||
|
||||
| Call | Wrapper | Why |
|
||||
|---|---|---|
|
||||
| `sendPluginCommand` | `ts3_send_plugin_command_server` | CEPOS positions + CEDRAIN wakeup |
|
||||
| `requestUnmuteClientsTemporary` | `ts3_unmute_clients_for_pcm` | PCM hook only receives unmuted clients |
|
||||
| `setClientVolumeModifier` | (inside unmute path) | Reset volume modifiers |
|
||||
| `requestClientMove` | `ts3_request_client_move` | Hub ↔ ingame |
|
||||
| `requestChannelDescription` | `ts3_request_channel_description` | Hub config |
|
||||
| `getChannelDescription` | `ts3_get_channel_description` | Parse hub settings |
|
||||
| `systemset3DSettings` | `ts3_set_3d_settings` | 3D global scale |
|
||||
| `systemset3DListenerAttributes` | `ts3_set_3d_listener` | Listener pose |
|
||||
| `channelset3DAttributes` | `ts3_set_3d_client` | Per-speaker 3D position |
|
||||
| get channel/client lists, nicknames, printMessage, logMessage | various | channel manage / UI / logging |
|
||||
|
||||
**Missing in Live V8:** any server API that stops packets to non-audible clients.
|
||||
|
||||
### Plan — SDK calls
|
||||
|
||||
| Call | Side | Why |
|
||||
|---|---|---|
|
||||
| `ts3server_initServerLib` / `createVirtualServer` / `stop` / `destroy` | Server | Host voice |
|
||||
| `ts3server_setClientWhisperList` | Server | **Who hears whom** |
|
||||
| `ts3server_getClientList` / `getChannelList` / `freeMemory` | Server | Enumerate + free |
|
||||
| `ts3client_initClientLib` / `spawnNewServerConnectionHandler` | Client | Host client |
|
||||
| `ts3client_createIdentity` / `startConnection` / `stopConnection` | Client | Connect |
|
||||
| `ts3client_setClientSelfVariableAsString(CLIENT_META_DATA)` | Client | Send position/state |
|
||||
| `ts3client_flushClientSelfUpdates` | Client | Push metadata to server |
|
||||
| `ts3client_systemset3DListenerAttributes` | Client | Listener 3D |
|
||||
| `ts3client_channelset3DAttributes` | Client | Speaker 3D |
|
||||
| `ts3client_openPlaybackDevice` / `openCaptureDevice` | Client | Audio devices |
|
||||
|
||||
---
|
||||
|
||||
## 9. Data flow: position
|
||||
|
||||
| Step | Live V8 | Plan |
|
||||
|---|---|---|
|
||||
| 1. Game source | Mod writes `Pos.txt` | Same idea via `IGameDataProvider` |
|
||||
| 2. Read | `pos_file.c` watcher (~30 ms) | Game provider thread (~20–30 Hz) |
|
||||
| 3. Pack | CEPOS 56-byte base64 | Compact binary/base64 in `CLIENT_META_DATA` (≤4096 B) |
|
||||
| 4. Send | `sendPluginCommand("CEPOS:…")` to **all clients** | `setClientSelfVariableAsString` + `flushClientSelfUpdates` to **server** |
|
||||
| 5. Receive | `onPluginCommandEvent` → `player_table_put` | Server reads metadata → `PlayerManager.updatePos` |
|
||||
| 6. Distance math | **Each client** (`proximity_math`) | **Server** (`ProximityEngine`) |
|
||||
| 7. Trust | Client-reported (spoofable) | Client-reported (spoofable); server still owns routing/permissions |
|
||||
|
||||
```text
|
||||
Live V8: A ──CEPOS──▶ B,C,D (everyone learns A's position)
|
||||
Plan: A ──META───▶ Server ──whisper lists──▶ only who should hear A
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 10. Data flow: who hears whom
|
||||
|
||||
### Live V8 (local presentation only)
|
||||
|
||||
```text
|
||||
Retail server delivers everyone's voice in the channel
|
||||
│
|
||||
▼
|
||||
onEditPlaybackVoiceDataEvent(clientID, samples[])
|
||||
│
|
||||
├─ distance far → multiply samples by ~0 (mute)
|
||||
├─ distance near → gain + pan + lowpass/reverb
|
||||
└─ must unmute via requestUnmuteClientsTemporary
|
||||
otherwise PCM hook gets nothing
|
||||
```
|
||||
|
||||
Result: **bandwidth still paid for every speaker**. Hearing is a local volume illusion.
|
||||
|
||||
### Plan (real network routing)
|
||||
|
||||
```text
|
||||
VoiceRouter tick (5-10 Hz)
|
||||
for each speaker S:
|
||||
audible = proximity ∪ radio ∪ whisper ∪ global ∩ permissions
|
||||
if audible changed:
|
||||
ts3server_setClientWhisperList(serverID, S, NULL, audibleClientIds)
|
||||
# or clear with (NULL, NULL)
|
||||
```
|
||||
|
||||
Result: non-audible clients **never receive** S's voice packets.
|
||||
|
||||
> [!WARNING]
|
||||
> While a whisper list is active, that client's normal channel talk path is disabled.
|
||||
> Therefore **everything** must be routed through whisper lists for routed players.
|
||||
|
||||
### 5c. Audio effects (cave reverb, lowpass)
|
||||
|
||||
- **Live V8:** applied directly on the samples in `onEditPlaybackVoiceDataEvent`
|
||||
(`ts3_proximity_audio.c`, lock-free snapshot pattern).
|
||||
- **Plan:** the same logic stays **client-side** (the custom app also has
|
||||
`onEditPlaybackVoiceDataEvent`), driven by a server-provided environment state
|
||||
(NORMAL/CAVE/...). So the effect code is **reusable**.
|
||||
> With an active whisper list, normal channel talk for that client is disabled.
|
||||
> Therefore **all** proximity/radio/whisper/global must be expressed as whisper lists.
|
||||
|
||||
---
|
||||
|
||||
## 6. Operations, deployment, license
|
||||
## 11. Data flow: 3D and audio effects
|
||||
|
||||
| Concern | Live V8 | Plan |
|
||||
|---|---|---|
|
||||
| Listener pose | `systemset3DListenerAttributes` | `ts3client_systemset3DListenerAttributes` |
|
||||
| Speaker pose | `channelset3DAttributes` | `ts3client_channelset3DAttributes` |
|
||||
| Global 3D | `systemset3DSettings` | `ts3client_systemset3DSettings` |
|
||||
| Custom curve | `onCustom3dRolloffCalculationClientEvent` | Same idea on ClientLib |
|
||||
| Cave reverb / lowpass | PCM path in `ts3_proximity_audio.c` | Still client PCM; server may send env state (CAVE/NORMAL) |
|
||||
|
||||
**Portable:** 3D call pattern and effect DSP.
|
||||
**Not portable:** using PCM mute as the *primary* "who hears whom" mechanism.
|
||||
|
||||
---
|
||||
|
||||
## 12. Threading models
|
||||
|
||||
### Live V8 (strict)
|
||||
|
||||
| Thread | May do | Must not |
|
||||
|---|---|---|
|
||||
| TS callback | All `TS3Functions` / CEDRAIN drain | Block |
|
||||
| Wakeup | Only `sendPluginCommand("CEDRAIN:1")` | Other TS API |
|
||||
| PCM audio | Read snapshots; apply gain/pan | TS API, locks |
|
||||
| Pos watcher / UI | Set flags + wakeup | TS API |
|
||||
|
||||
### Plan
|
||||
|
||||
| Thread | May do | Must not |
|
||||
|---|---|---|
|
||||
| SDK server callbacks | Short handoff into our queues | Heavy proximity math inline |
|
||||
| Routing tick (5–10 Hz) | Compute audible sets; call `setClientWhisperList` | Game file I/O |
|
||||
| SDK client callbacks | Connection/PCM/3D | Block |
|
||||
| Game provider (~20 Hz) | Read Conan; publish snapshot | Direct SDK calls without adapter |
|
||||
|
||||
Same discipline as V8: **one adapter owns all SDK calls**.
|
||||
|
||||
---
|
||||
|
||||
## 13. Module map
|
||||
|
||||
| Concern | Live V8 files | Plan modules |
|
||||
|---|---|---|
|
||||
| Entry / lifecycle | `ts3_entry.c` | `client/main.c`, `server/main.c` |
|
||||
| TS API wrapper | `ts3_adapter.c` | `TeamSpeakClientAdapter`, `TeamSpeakServerAdapter` |
|
||||
| Positions | `pos_file.c`, `ts3_cepos.c` | `IGameDataProvider`, `CLIENT_META_DATA` |
|
||||
| Player table | `player_table.c` | `PlayerManager` (server) |
|
||||
| Proximity math | `proximity_math.c`, `zone_resolve.c` | `ProximityEngine` (server) |
|
||||
| Hearability | PCM + volume modifier | `VoiceRouter` + whisper lists |
|
||||
| 3D | `ts3_3d.c` | ClientLib 3D calls |
|
||||
| Hub config | `hub_parser.c`, `ts3_server_profile.c` | root-channel / server config |
|
||||
| Voice modes | `voice_modes.c` | client state + server ranges |
|
||||
| UI / HUD | `ui_main.c`, overlay | client HUD |
|
||||
|
||||
---
|
||||
|
||||
## 14. Operations, license, scale
|
||||
|
||||
| Topic | Live V8 | Plan |
|
||||
|---|---|---|
|
||||
| User install | DLL into `%AppData%\TS3Client\plugins\` | Install/run a custom client app |
|
||||
| Server operation | none (retail server is enough) | Own process (Linux/systemd), monitoring, crash recovery |
|
||||
| Build | MSVC + MinGW cross, gcc unit tests | CMake for client + server + shared |
|
||||
| License | free (Plugin SDK) | paid, case by case; evaluation free for dev only |
|
||||
| Lifecycle | as long as TS3 lives | TS3 SDK **EOL 2027-12-31** — keep an `IVoiceTransport` seam as exit |
|
||||
| Scale | callback thread + PCM, ~200 players target | server-side routing, but slot cap per license |
|
||||
| User install | DLL → `%AppData%\TS3Client\plugins\` | Install custom client |
|
||||
| Server ops | None (use any retail TS server) | Own Linux process, systemd, monitoring |
|
||||
| Build | MSVC + MinGW cross + gcc tests | CMake: client + server + shared |
|
||||
| License | Free Plugin SDK | Paid SDK; evaluation for PoC |
|
||||
| Scale target | ~200 players (512 table cap) | License slot cap + routing cost |
|
||||
| EOL | TS3 client lifetime | **SDK EOL 2027-12-31** |
|
||||
|
||||
---
|
||||
|
||||
## 7. What stays the same / is reusable
|
||||
## 15. What is reusable vs rewritten
|
||||
|
||||
- **Proximity math** (`proximity_math`, `zone_resolve`) → port into the ServerLib engine (rewrite, don't copy).
|
||||
- **Audio effects** (reverb/lowpass in `ts3_proximity_audio.c`) → usable almost as-is, client-side.
|
||||
- **3D calls** (`systemset3D*`, `channelset3DAttributes`) → nearly identical names.
|
||||
- **Position source** (`pos_file.c`, path detection) → wrap as `IGameDataProvider`.
|
||||
- **HUD / voice modes / config / hub-parser ideas** → carry over as logic.
|
||||
### Reuse (rewrite into new adapters — do not blind-copy)
|
||||
|
||||
## 8. What is fundamentally different
|
||||
- Proximity math / zones (`proximity_math`, `zone_resolve`)
|
||||
- PCM effects (reverb/lowpass patterns from `ts3_proximity_audio.c`)
|
||||
- 3D call sequencing ideas from `ts3_3d.c`
|
||||
- Position acquisition ideas from `pos_file.c` / Mumble reference
|
||||
- HUD / voice-mode UX concepts
|
||||
|
||||
1. **Two new processes** (a custom client **and** a custom server) instead of one DLL.
|
||||
2. **Routing moves from client to server** (whisper lists instead of local volume).
|
||||
3. **Position path reverses**: no longer peer-to-peer, but client to server.
|
||||
4. **Retail TeamSpeak disappears** — users need our app.
|
||||
5. **License and EOL** become new concerns.
|
||||
### Must be new
|
||||
|
||||
- Entire ServerLib host process
|
||||
- Entire ClientLib application shell
|
||||
- Whisper-list router as the hearability authority
|
||||
- `CLIENT_META_DATA` (or equivalent) transport replacing CEPOS P2P
|
||||
- License/accounting handling
|
||||
- Deployment (systemd, crash recovery)
|
||||
|
||||
### Explicitly abandoned for Option B
|
||||
|
||||
- Depending on retail TeamSpeak client
|
||||
- CEPOS peer-to-peer as the primary position bus
|
||||
- Local PCM mute as the primary routing mechanism
|
||||
|
||||
---
|
||||
|
||||
## 9. Conclusion
|
||||
## 16. Conclusion
|
||||
|
||||
- Live V8 is a **plugin inside someone else's TeamSpeak**: easy to distribute, free, but
|
||||
"who hears whom" is only a **local volume** (all packets always flow).
|
||||
- The plan is a **standalone voice product** on the TeamSpeak **server** library: **real**
|
||||
server-side routing, at the cost of building a client and server, a paid license, and a
|
||||
2027 EOL date.
|
||||
- Reusable above all: **proximity math, audio effects, and 3D calls**. New and risky:
|
||||
**ServerLib hosting, whisper routing, data transport (`CLIENT_META_DATA`), and licensing**.
|
||||
| | Live V8 | Plan |
|
||||
|---|---|---|
|
||||
| Strength | Ships today; free; users already have TeamSpeak | True server routing; central authority; bandwidth win |
|
||||
| Weakness | Everyone still receives packets; client can spoof position; no server game logic | New client+server product; paid license; SDK EOL 2027; retail TS gone |
|
||||
| Right next step | Continue maintaining on `tsmain` | On `ts3-Testing`: prove ServerLib boot + whisper routing **before** Conan code |
|
||||
|
||||
> [!IMPORTANT]
|
||||
> The plan only makes sense once the PoC (phases 0-3) proves that the ServerLib starts
|
||||
> cleanly and whisper routing "A hears B, not C" actually works. Before that, no Conan code
|
||||
> is worth writing.
|
||||
> Do not write Conan integration for Option B until Phase 0–3 succeed:
|
||||
> SDK examples build → server starts/stops → two clients talk →
|
||||
> `ts3server_setClientWhisperList` proves **A hears B, not C**.
|
||||
|
||||
---
|
||||
|
||||
*Related living plan:* [`.cursor/plans/voice_architecture_plan_ed3d8830.plan.md`](.cursor/plans/voice_architecture_plan_ed3d8830.plan.md)
|
||||
|
||||
Reference in New Issue
Block a user