.NET port / test (macos-latest) (push) Canceled after 0s
.NET port / test (ubuntu-24.04) (push) Canceled after 0s
.NET port / test (windows-latest) (push) Canceled after 0s
.NET port / apple-client (push) Canceled after 0s
.NET port / cpp-conformance (push) Canceled after 0s
183 lines
9.2 KiB
Markdown
183 lines
9.2 KiB
Markdown
# VoiceCat .NET rewrite
|
||
|
||
The .NET 10 implementation includes protocol, TLS/TOFU and media crypto, the control and
|
||
UDP server, client state, real-time audio, a console client and the Windows application.
|
||
The existing C++ implementation remains the conformance oracle while Apple clients move.
|
||
|
||
Codec/DSP wrappers now cover Opus, DRED recovery, RNNoise, and energy VAD. Build
|
||
the desktop native library before running their tests (CMake and a C compiler required):
|
||
|
||
```powershell
|
||
./dotnet/build-native.ps1
|
||
```
|
||
|
||
The script downloads upstream Opus 1.5.2 with a pinned SHA-256, builds DRED-enabled
|
||
Opus and the existing vendored RNNoise model, and stages `voicecat_media` plus license
|
||
notices under `dotnet/artifacts/native/`. It builds independently of the C++ core and
|
||
vcpkg. On Windows, Visual Studio's C++ workload works with the default generator;
|
||
for this repository's MinGW toolchain use:
|
||
|
||
```powershell
|
||
./dotnet/build-native.ps1 -Generator Ninja -CCompiler C:/tools/msys64/ucrt64/bin/cc.exe
|
||
```
|
||
|
||
Linux/macOS can run the same script with PowerShell, or use CMake directly:
|
||
|
||
```sh
|
||
cmake -S dotnet/native -B dotnet/artifacts/native-build -DCMAKE_BUILD_TYPE=Release
|
||
cmake --build dotnet/artifacts/native-build --target voicecat_media --parallel 2
|
||
cmake --install dotnet/artifacts/native-build --component DotnetMedia --prefix dotnet/artifacts/native
|
||
```
|
||
|
||
MSBuild copies the staged library into managed build/publish output for the selected
|
||
RID. Override `VoiceCatNativeRid` or `VoiceCatNativeDirectory` for explicit staging;
|
||
`RuntimeIdentifier` takes priority over the SDK's host RID. Cross-compilation is not
|
||
automatic. iOS static linking and audio-device shims belong to later client phases.
|
||
Native codec/DSP tests require this library; they do not silently skip.
|
||
|
||
From the repository root:
|
||
|
||
```powershell
|
||
dotnet restore dotnet/VoiceCat.slnx --locked-mode
|
||
dotnet build dotnet/VoiceCat.slnx -c Release --no-restore
|
||
dotnet test dotnet/VoiceCat.slnx -c Release --no-build
|
||
```
|
||
|
||
Run the managed console client interactively:
|
||
|
||
```powershell
|
||
dotnet run --project dotnet/src/VoiceCat.Cli -- --host 127.0.0.1 --port 8384 --nickname Alice --trust-first
|
||
```
|
||
|
||
Plain input sends channel text; `/join ID` changes channel and `/quit` exits. Headless
|
||
conformance options include `--voice`, `--send-text`, `--expect-text`, `--expect-voice`,
|
||
`--start-delay-ms` and `--timeout-seconds`; `--help` lists the complete syntax.
|
||
|
||
Dependencies are pinned in project files and lock files. Generated protobuf is build
|
||
output; the schema remains `core/proto/voicecat.proto`. Production dependencies are
|
||
Google.Protobuf (BSD-3-Clause), BouncyCastle.Cryptography (MIT), and the build-only
|
||
Grpc.Tools (Apache-2.0). No GPL/LGPL dependencies are permitted.
|
||
|
||
## C# conventions
|
||
|
||
Use file-scoped namespaces, standard .NET naming, immutable values where useful, and
|
||
spans for binary data. Invalid arguments throw; invalid network packets use parsing
|
||
results or protocol exceptions. Async APIs accept cancellation tokens.
|
||
|
||
Comments explain constraints that cannot be made clear in code. Avoid banners,
|
||
implementation history, and narration. Keep durable design explanations in `docs/`.
|
||
|
||
## Regenerating C++ fixtures
|
||
|
||
The optional oracle target calls the existing C++ protobuf, header serializer, and
|
||
libsodium media implementation. From the root, with the development dependencies:
|
||
|
||
```powershell
|
||
cmake --preset dev -DVOICECAT_BUILD_DOTNET_ORACLE=ON
|
||
cmake --build --preset dev --target voicecat-dotnet-oracle
|
||
New-Item -ItemType Directory -Force dotnet/tests/VoiceCat.Tests/Fixtures
|
||
./build/dev/bin/voicecat-dotnet-oracle.exe dotnet/tests/VoiceCat.Tests/Fixtures/cpp-wire.json
|
||
git diff -- dotnet/tests/VoiceCat.Tests/Fixtures/cpp-wire.json
|
||
```
|
||
|
||
On Linux/macOS, omit `.exe` and create the directory with `mkdir -p`.
|
||
The oracle writes deterministic JSON directly, avoiding shell output encoding.
|
||
Fixtures contain a framed ClientHello and media packets at counters 0, 1, 65535,
|
||
and 65536. Keys contain bytes 0–31; payload bytes count upward from zero. The
|
||
20-byte header has type 1, marker flag, codec 0, SSRC `0xcafebabe`, timestamp 960.
|
||
Both managed crypto backends must match these bytes.
|
||
|
||
The DSP oracle calls the existing C++ `ApmProcessor` with 200 deterministic noise
|
||
frames and records the final 960 samples. Regenerate its fixture with:
|
||
|
||
```powershell
|
||
cmake --build --preset dev --target voicecat-dotnet-dsp-oracle
|
||
./build/dev/bin/voicecat-dotnet-dsp-oracle.exe dotnet/tests/VoiceCat.Tests/Fixtures/cpp-noise.json
|
||
```
|
||
|
||
The managed test allows a one-unit PCM difference for floating-point rounding.
|
||
|
||
## TLS interoperability
|
||
|
||
The optional TLS oracle uses the existing mbedTLS context and libsodium media crypto.
|
||
The test authenticates an encrypted challenge in both directions, proving exporter
|
||
compatibility without sending raw keys. It also loads the C++ server's credential files.
|
||
|
||
```powershell
|
||
cmake --build --preset dev --target voicecat-dotnet-tls-oracle
|
||
$env:VOICECAT_TLS_ORACLE = (Resolve-Path build/dev/bin/voicecat-dotnet-tls-oracle.exe).Path
|
||
dotnet test dotnet/VoiceCat.slnx -c Release --no-restore
|
||
```
|
||
|
||
On Linux/macOS, set `VOICECAT_TLS_ORACLE` to the absolute executable path without
|
||
`.exe`. Without that variable, only this native interoperability test is skipped;
|
||
managed TLS loopback, rejection, persistence, and wire tests still run. CI's C++
|
||
conformance job requires the native test. See `docs/api-dotnet.md` for ownership
|
||
and certificate acceptance requirements.
|
||
|
||
## Managed server checkpoint
|
||
|
||
Run the TLS control server on loopback (optional arguments: data directory, TCP port):
|
||
|
||
```powershell
|
||
dotnet run --project dotnet/src/VoiceCat.Server -c Release -- ./voicecat-data 7443
|
||
./build/dev/bin/vccli.exe --host 127.0.0.1 --port 7443 --nick Guest --text "hello"
|
||
```
|
||
|
||
It creates or imports `server_identity.key`, `server.crt`, `server.key`, and
|
||
`voicecat.db`. An empty channel table gets Lobby and Music Room; existing channels
|
||
are preserved. Guests are enabled by the CLI; hosting `VoiceServer` directly can
|
||
disable them. Existing accounts authenticate without resetting passwords. Account
|
||
creation is currently available through `AccountStore`; bootstrap/admin CLI and
|
||
wire administration are pending.
|
||
|
||
Tests cover real TLS sockets, authentication retries, snapshots, channel moves,
|
||
text routing, sender attribution, ping, and disconnect events. Enable native checks:
|
||
|
||
```powershell
|
||
cmake --build --preset dev --target voicecat-dotnet-password-oracle voicecat-dotnet-database-oracle vccli
|
||
$env:VOICECAT_DATABASE_ORACLE = (Resolve-Path build/dev/bin/voicecat-dotnet-database-oracle.exe).Path
|
||
$env:VOICECAT_VCCLI = (Resolve-Path build/dev/bin/vccli.exe).Path
|
||
dotnet test dotnet/VoiceCat.slnx -c Release --no-restore
|
||
```
|
||
|
||
The database oracle creates an account/channel using the shipped C++ database code;
|
||
managed code imports and authenticates it, then C++ authenticates a managed-created
|
||
account. CI also regenerates the libsodium password fixture. Native checks require
|
||
the optional `VOICECAT_BUILD_DOTNET_ORACLE=ON` configure flag and a real-deps build.
|
||
|
||
The server also advertises UDP on the TCP port number, supports voice subscription
|
||
and stream signaling, and reseals encoded audio for subscribers in the same channel.
|
||
UDP binding fixes the first endpoint for the session; reconnect after endpoint changes.
|
||
Protected joins, administration, moderation and production configuration remain
|
||
before Phase 4 completion. The server's media-aware reaper defaults to 45 seconds
|
||
of inactivity with a 15-second sweep. Parsed control envelopes, valid encrypted
|
||
voice and keepalives from bound endpoints refresh activity; invalid media does not.
|
||
`VoiceServerOptions` configures timeouts and capacity; zero idle timeout disables
|
||
reaping. The constructor overload accepts `TimeProvider` for deterministic expiry tests.
|
||
|
||
Enable deterministic native voice interoperability (no audio hardware required):
|
||
|
||
```powershell
|
||
cmake --build --preset dev --target voicecat-dotnet-voice-oracle
|
||
$env:VOICECAT_VOICE_ORACLE = (Resolve-Path build/dev/bin/voicecat-dotnet-voice-oracle.exe).Path
|
||
dotnet test dotnet/VoiceCat.slnx -c Release --no-restore
|
||
```
|
||
|
||
Two existing C++ clients authenticate, join Lobby or Music Room, publish three
|
||
concurrent streams, feed PCM, and verify decoded energy and metadata in both directions.
|
||
The native clients use external capture/playback to avoid device dependencies in CI.
|
||
`MediaFanoutTests` separately verifies 50-subscriber routing/resealing without managed
|
||
allocations after warm-up and reports throughput; socket scheduling is excluded.
|
||
The transport load test delivers all 2,500 recipient packets from a paced 50 pps sender.
|
||
Native `vccli --test-tone-ms 4000` runs finite external capture/playback, feeds a tone,
|
||
and fails without decoded remote audio. Tests start two CLI processes in mono/stereo
|
||
channels and also verify channel text. Normal `--voice` now explicitly subscribes before
|
||
announcing its microphone stream. No C ABI or wire changes were needed.
|
||
|
||
The managed CLI also accepts `--test-tone-seconds N` (maximum 3600) for a finite, accurately
|
||
paced peer used during device checks. Use a channel with Opus DTX disabled, such as the seeded
|
||
Music Room: a steady sine is intentionally classified as non-speech by DTX and becomes comfort
|
||
noise after its hangover period in Lobby. The finite peer prints received voice energy and exits
|
||
normally when its requested duration completes.
|