From dt-brigid
Custom binary protocol networking for Godot 4.6 C# — WebSocket transport, Channel-based threading, action-based movement protocol
npx claudepluginhub dreamteam-hq/brigid --plugin dt-brigidThis skill uses the workspace's default tool permissions.
This is NOT Godot's built-in MultiplayerAPI. No MCP servers.
Searches, retrieves, and installs Agent Skills from prompts.chat registry using MCP tools like search_skills and get_skill. Activates for finding skills, browsing catalogs, or extending Claude.
Searches prompts.chat for AI prompt templates by keyword or category, retrieves by ID with variable handling, and improves prompts via AI. Use for discovering or enhancing prompts.
Checks Next.js compilation errors using a running Turbopack dev server after code edits. Fixes actionable issues before reporting complete. Replaces `next build`.
This is NOT Godot's built-in MultiplayerAPI. No MCP servers.
WebSocket transport with custom binary serialization. The wire format is fully controlled — no Godot MultiplayerAPI, no RPC, no MultiplayerSpawner/Synchronizer.
Why custom:
Messages are length-prefixed binary frames over WebSocket binary messages. Each frame starts with a message type ID (ushort) followed by the serialized payload.
Network thread (WebSocket recv) → Channel<MemoryStream> → Main thread (_Process drains)
Channel.CreateBounded<MemoryStream>(capacity) with SingleReader = true, SingleWriter = true.SingleWriter = false because multiple BackgroundServices may enqueue messages for the same user._Process(double delta) loop calls TryRead in a while-loop to drain the channel each frame.Movement is NOT position-synced. Clients send actions, server relays to other clients, remote clients replay through the same physics pipeline.
enum CharacterAction : byte { Jump, MoveBegin, MoveEnd, Stop }
MoveBegin carries FaceDirection (Vector2 as two floats) and IsRunning (bool).MoveBegin to server → server broadcasts to other clients in the same map.CharacterMovementController, producing deterministic movement through Godot's physics.Stop includes a final authoritative position snap to correct drift.Model classes are partial. A Roslyn source generator (ModelSerializationGenerator) emits Serialize(BinaryWriter) and static Deserialize(BinaryReader) methods.
base.Serialize(writer) first, then writes own fields.MemoryStream and BinaryWriter instances are rented from a pool on the hot path — zero allocation in steady state.List<T> (count-prefixed), nested models, Vector2/Vector3 (written as raw floats).Networking surface is defined by IClientHub interfaces. Separate hubs per domain:
Server/IMapHub — server-side handlers for map-related messages
Game/IMapHub — client-side handlers (what the Godot client receives)
ServerClient / GameClient (one method per interface method, serializes args and writes to channel).IClientHub interface. Re-run generators. Implement the handler. Done.| Concern | Solution |
|---|---|
| User registry (server) | ConcurrentDictionary<Guid, ConnectedUser> |
| Cross-thread message passing | Channel<MemoryStream> (bounded) |
| Broadcasting to many users | Iterate ConnectedUsers.Values, write to each user's outbound channel |
| Shared mutable game state | Avoid. If necessary, use Channel<T> to funnel mutations to a single consumer |
| Locks | Not needed if you follow the Channel pattern consistently |
BackgroundService instances can safely read ConnectedUsers.Values and write to individual user channels without locking because ConcurrentDictionary iteration is snapshot-safe and channel writes are thread-safe.
Client Server
| -- JoinRequest --> |
| | creates CharacterData
| | registers in map
| <-- JoinMapResponse -- | (includes ExistingCharacters[])
| |
| | -- CharacterSpawned --> (broadcast to others)
JoinMapResponse contains the player's own CharacterData plus a list of all other characters already on the map.CharacterSpawned and instantiate the new remote character.CharacterDespawned with the leaving character's ID.