Reliable datagram commands
WebTransport command datagrams let player actions avoid waiting behind unrelated reliable-stream traffic. Choose the default send for independent actions, or ordered sending when one action must be processed after another. The same two client calls remain valid on WebSocket, where both use the reliable stream.
router := synced.NewCommandRouter(server)
router.OnMove(func(senderID int64, entity *synced.SyncedPlayer, cmd *synced.MoveCommand) { x, y := entity.Position() entity.SetPosition(x+cmd.Dx, y+cmd.Dy)})
router.BindAllHandlers(nil)import { createClient, sendMove, sendMoveOrdered,} from "./synced/client.js";
const client = createClient();
client.connect({ transport: "webtransport", url: "https://example.com/wt", serverCertificateHashes,});
sendMove(client, playerId, dx, dy);sendMoveOrdered(client, playerId, dx, dy);client.Send(CommandBuilders.BuildMoveCommand(playerId, dx, dy));client.SendOrdered(CommandBuilders.BuildMoveCommand(playerId, dx, dy));Transport lanes
Section titled “Transport lanes”send/Send uses reliable unordered datagrams on WebTransport. Delivery is retried, but commands may be dispatched in a different order from the one in which they were sent.
sendOrdered/SendOrdered uses reliable ordered datagrams on WebTransport. Commands are dispatched in send order.
On WebSocket, both choices use the same ordered reliable stream. Snapshots, world bootstrap, server events, and stream-mode entity replication also use that stream.
Pick the right lane
Section titled “Pick the right lane”Use the default send for movement inputs, independent ability activations, acknowledgements, and other commands whose relative order does not affect the result. On WebTransport this avoids stream head-of-line blocking through the reliable unordered lane.
Use ordered sending for inventory operations, staged interactions, or any sequence where processing a later command first would be incorrect. The ordered lane avoids blocking behind the main stream while preserving command order.
If clients may use either WebTransport or WebSocket, bind all command handlers on the server. The client handles transport selection; gameplay code does not need separate WebSocket calls.
Server wiring
Section titled “Server wiring”The generated CommandRouter provides three binding helpers:
BindStreamHandler(): installs stream command routing onServer.OnMessage(...).BindReliableUnorderedHandler(): installs datagram command routing onServer.OnReliableUnordered(...).BindReliableOrderedHandler(): installs datagram command routing onServer.OnReliableOrdered(...).
BindAllHandlers() installs all three at once, which is the right default when clients may use any lane:
router := synced.NewCommandRouter(server)
router.OnAction(func(senderID int64, entity *synced.SyncedPlayer, cmd *synced.ActionCommand) { // handle action})
router.BindAllHandlers(nil)Use the narrower helpers when you want to restrict which lanes are active:
router := synced.NewCommandRouter(server)router.BindStreamHandler(nil)// or:router.BindReliableOrderedHandler(nil)Each binding helper takes an optional error callback and replaces any previously registered callback on the same hook. If you already set Server.OnMessage, Server.OnReliableUnordered, or Server.OnReliableOrdered manually, decide whether the generated router should own that hook or whether you want to keep custom dispatch code.
Client wiring
Section titled “Client wiring”golem-bake generates two helpers for each command. In JavaScript, a Move command can be sent either way:
sendMove(client, playerId, dx, dy);sendMoveOrdered(client, playerId, dx, dy);In generated Go:
if err := clientpkg.SendMove(client.GameClient, playerID, dx, dy); err != nil { log.Println("move:", err)}if err := clientpkg.SendMoveOrdered(client.GameClient, playerID, dx, dy); err != nil { log.Println("ordered move:", err)}In generated C#:
ClientFactory.SendMove(client, playerId, dx, dy);ClientFactory.SendMoveOrdered(client, playerId, dx, dy);Behavior notes
Section titled “Behavior notes”WebTransport command sends encode one ClientMessage without a ClientPacket wrapper. The protocol leaves 1176 bytes for reliable unordered command payloads and 1174 bytes for reliable ordered payloads. Oversized commands fail at the client without falling back to the stream.
WebSocket command sends use ClientPacket on the reliable stream instead. Both methods deliver in call order on this transport and share its 32000-byte message cap.
Protocol ACK packets are handled internally and never reach gameplay command handlers.