diff --git a/plans.md b/plans.md new file mode 100644 index 0000000..5b35f57 --- /dev/null +++ b/plans.md @@ -0,0 +1,113 @@ +--- +name: Plugin Server Data Plan +overview: "Concrete work plan for the current V8 plugin (Plugin API 26): improve what we send via retail TeamSpeak (plugin commands + hub policy). No ServerLib app. No hybrid authority in v1. Inventory already in vergleichs.md." +todos: + - id: inventory-wire + content: "DONE — vergleichs.md inventories CEPOS, hub keys, used/unused API 26" + status: completed + - id: protocol-design + content: "Write doku design: versioned CE* plugin-command map + hub schema version (doc only, freeze wire rules)" + status: pending + - id: hub-schema-vnext + content: "Specify additive hub keys (feature flags, schema version) — clients read only" + status: pending + - id: implement-cemode + content: "First code WP: CEMODE (or equivalent) edge send on voice-mode change — one function, keep CEPOS" + status: pending + - id: wire-filter-intensity + content: "Optional follow-up WP: actually wire hubAudioFilterIntensity / FilterIntensity into PCM if still unwired" + status: pending + - id: whisper-experiment + content: "Later experiment only: requestClientSetWhisperList for admin overlay — not proximity" + status: pending + - id: no-serverlib + content: "Never revive ClientLib/ServerLib custom app in this workstream" + status: pending +isProject: false +--- + +# Current-plugin plan: better use of retail TeamSpeak (API 26) + +**Locked product:** existing V8 DLL on retail TeamSpeak (`PLUGIN_API_VERSION 26`). +**Locked approach:** **A-only** — send/store more via TeamSpeak relay + hub description. +**Deferred:** hybrid voice authority, radio product, ServerLib custom app. +**Baseline inventory:** [`vergleichs.md`](../../vergleichs.md). + +```text +Keep: Pos.txt -> CEPOS -> client proximity -> PCM/3D +Improve: cleaner peer messages + richer hub policy on TS server +Never: expect ts3server to compute who hears whom +``` + +--- + +## 0. Constraints (from live plugin) + +| Can | Cannot | +|---|---| +| `sendPluginCommand` relay (CEPOS today) | Proximity math inside retail `ts3server` | +| Hub policy in root channel description | True packet drop for inaudible speakers | +| Client PCM + 3D as hearing | Trust client position as anti-cheat truth | +| Optional later: `requestClientSetWhisperList` overlays | Replace PCM with ServerLib routing | + +--- + +## 1. Work packages (ordered) + +### WP0 — Inventory (done) + +- CEPOS 56-byte layout, throttle/keepalive +- Hub `[GLOBAL]` / zones / races / defaults keys +- Used vs unused `TS3Functions` +- Documented in `vergleichs.md` + +### WP1 — Protocol + hub design (doc only) + +**Plugin commands (keep CEPOS wire-compatible):** + +| Prefix | Purpose | When to send | +|---|---|---| +| `CEPOS:` | Position + voiceDistance (unchanged 56 B) | Throttled move + 1 Hz keepalive | +| `CEDRAIN:` | Wakeup (unchanged) | Internal | +| `CEMODE:` **(first new)** | Voice mode id + distance | On mode change only (edge) | +| `CEPING:` (later) | Seq / stale hint | Low rate | +| `CEAUTH:` (later) | Soft identity hint | On connect — still spoofable | + +Rules: `sendPluginCommand` only; bounds-check; throttle; no secrets; unknown prefixes ignored. + +**Hub schema (additive):** `SchemaVersion` + flags; missing keys = old defaults; clients read only. + +### WP2 — First code change (Golden Rule: one function) + +**Implement `CEMODE` only:** + +1. On voice-mode change, send small plugin command (mode + distance). +2. Peers update without waiting for CEPOS keepalive. +3. Do not break 56-byte CEPOS. + +Likely files: `ts3_cepos.c` / new cmd dispatch, `voice_modes.c`, `ts3_entry.c`, `doku/aenderungen/…`. + +**Off-limits in WP2:** hub keys, whisper lists, filter wiring, radio, hybrid backend. + +### WP3 — Optional: wire FilterIntensity if still unwired (ask first) + +### WP4 — Optional: whisper-list admin overlay experiment (not proximity) + +--- + +## 2. Out of scope + +- ServerLib custom app / `ts3-Testing` product +- Proximity inside retail `ts3server` +- Hybrid authority (unless reopened) +- Breaking CEPOS without compatibility plan + +--- + +## 3. Success / test + +- Two clients: mode hotkey → peer sees update via `CEMODE`; CEPOS distance still works; old clients ignore `CEMODE` and keep CEPOS. + +## 4. Default on “go” + +Implement **CEMODE** first. diff --git a/vergleichs.md b/vergleichs.md deleted file mode 100644 index 9e70109..0000000 --- a/vergleichs.md +++ /dev/null @@ -1,470 +0,0 @@ -# Live Plugin (V8) vs Planned SDK/ServerLib Architecture - -A **development-ready** side-by-side comparison of: - -| 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.* - ---- - -## 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) | -|---|---|---| -| 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) | - ---- - -## 3. The two SDKs explained - -### 3.1 Live V8 — Client Plugin SDK - -Evidence in this repo: - -```1:3:sdk/README.md -# TeamSpeak 3 Client Plugin SDK - -Welcome to the official helper repository for creating native plugins for the TeamSpeak 3 Client. -``` - -- 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`. - -There are **zero** `ts3client_*` / `ts3server_*` symbols in this repository. - -### 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 -``` - ---- - -## 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 -``` - ---- - -## 5. Lifecycle: boot order (Live V8) - -Retail TeamSpeak owns the process. Our plugin only hooks in. - -```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 -``` - -Key files: - -| 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` | - ---- - -## 6. Lifecycle: boot order (Plan) - -### 6.1 Server process (`conan-voice-server`) - -```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 | -|---|---|---| -| 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 | - -Names marked **VERIFY** in the plan must be confirmed against `serverlib.h` / `clientlib.h` in Phase 0. - ---- - -## 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] -> 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. - ---- - -## 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 → `%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** | - ---- - -## 15. What is reusable vs rewritten - -### Reuse (rewrite into new adapters — do not blind-copy) - -- 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 - -### 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 - ---- - -## 16. Conclusion - -| | 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] -> 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)