11 KiB
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.
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.
1. At a glance
| 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 |
2. The two SDKs — the root of the confusion
The word "SDK" refers to two different TeamSpeak products.
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 tableTS3Functions. - We cannot route voice at the server level. We can only alter incoming voice locally (quieter, pan, filter).
2b. Plan: TeamSpeak 3 SDK (ClientLib + ServerLib)
- A separate download, different libraries, different function families
(
ts3client_*andts3server_*). - 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).
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)
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
- 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.
Plan (Option B)
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)"]
- 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. API comparison — concrete functions
4a. Callbacks (events we react to)
| 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 |
Evidence: src/ts/entry/ts3_exports.h, src/ts/entry/ts3_entry.c.
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_setClientWhisperListexists only in the ServerLib.
5. Data flow in detail
5a. Position exchange
| Live V8 | Plan | |
|---|---|---|
| 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 |
5b. "Who hears whom"
- 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
VoiceRouterbuilds a target list per speaker and callsts3server_setClientWhisperList(A, ..., {B,C}). Anyone not on the list never receives A's voice at all.
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.
6. Operations, deployment, license
| 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 |
7. What stays the same / is reusable
- 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 asIGameDataProvider. - HUD / voice modes / config / hub-parser ideas → carry over as logic.
8. What is fundamentally different
- Two new processes (a custom client and a custom server) instead of one DLL.
- Routing moves from client to server (whisper lists instead of local volume).
- Position path reverses: no longer peer-to-peer, but client to server.
- Retail TeamSpeak disappears — users need our app.
- License and EOL become new concerns.
9. 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.
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.