Minimal server wiring
Minimal server wiring
Section titled “Minimal server wiring”After golem-bake, a typical main constructs an golem.Server through the generated Go integration, registers gameplay callbacks, and calls Run. The generated runtime attaches the removal serializer, command router, event broadcaster, and snapshot fingerprint helpers so your entrypoint only has to describe game-specific behavior. Imports below use placeholder module paths—replace them with your module and generated package paths.
package main
import ( "context" "log"
"golem-engine/golem"
"example.com/mygame/internal/synced" // generated go-server integration)
func main() { ctx, cancel := context.WithCancel(context.Background()) defer cancel()
srv, rt := synced.NewServer(golem.ServerConfig{ TickRate: 20, Addr: ":8080", StaticDir: "./public", // optional: serve static files (maps, sprites) over HTTP })
srv.OnTick(func(dt float64, s *golem.Server) { // Your simulation: read/write entities via s (CreateEntity, Get, DeleteEntity, …). _ = dt })
srv.OnConnect(func(sess *golem.Session) { // Player joined — sess.ID is the optional owner argument to CreateEntity(..., sess.ID) })
rt.Commands.BindAllHandlers(func(sess *golem.Session, err error) { log.Printf("session %d command: %v", sess.ID, err) })
srv.OnDisconnect(func(sess *golem.Session) { // Clean up: remove the player's entity (srv.DeleteEntity(...)). })
if err := srv.Run(ctx); err != nil && err != context.Canceled { log.Fatal(err) }}Notes:
Addrenables integrated networking. When set,Runstarts the HTTP server plus the selected transport endpoint alongside the tick loop and auto-broadcasts entity updates to all connected clients after each tick.Transportselects the integrated transport. The default isgolem.TransportWebTransport; usegolem.TransportWebSocketwithStateUpdateLaneStreamwhen you want WebSocket.Pathoverrides the transport endpoint path. The defaults are/wsfor WebSocket and/wtfor WebTransport.TLSCertFile/TLSKeyFileorDevSelfSignedCertare required when you use WebTransport, because browsers require HTTP/3 + TLS for WebTransport sessions.- With
DevSelfSignedCert,golemalso allows loopback cross-port WebTransport origins for local development, so a page likehttp://localhost:8080can connect tohttps://localhost:4433. Outside that mode, WebTransport stays same-origin by default unless you override it withSetWebTransportCheckOrigin. StaticDiris optional. When set, non-transport HTTP requests are served from that directory—useful for sprites, audio, or any static assets your client needs.MapDiris optional. When set, files from that directory are served at/maps/—designed for Tiled (.tmj) and LDtk (.ldtk) map files. See Map file serving and World schemas for theurl_prefixworkflow that pairs with this option.CellSizeenables interest management. When set to a positive value, each client receives only entities near its avatar instead of a full broadcast. See the interest management page forAssignFOI/RemoveFOIwiring.- The generated
NewServerhelper callsSetRemovalSerializerfor you. If you constructgolem.NewServerdirectly, removals still require the generated serializer. - With generated JS clients, the generated command router should normally bind
DispatchPacketto the reliable stream. The JS runtime batches one or more encoded commands into aClientPacket, so the raw reliable payload is no longer a singleClientMessage. That logical packet shape stays the same regardless of whether the transport carried it inside a WebSocket message or a WebTransport stream write. - If you opt into WebTransport datagrams, register
OnDatagramfor the lossy lane separately; generated commands still travel throughOnMessageon the reliable lane. - Cancel
ctx(e.g. on SIGINT) for clean shutdown—Runthen returnsctx.Err(). - With commands, use
rt.Commands.BindAllHandlersfrom generated code—see Client commands.
With go-server commands, reliable-stream and reliable-datagram handlers can share one error callback:
srv, rt := synced.NewServer(golem.ServerConfig{Addr: ":8080"})// rt.Commands.OnMove(...); rt.Commands.OnPing(...); etc.
rt.Commands.BindAllHandlers(func(sess *golem.Session, err error) { log.Printf("session %d command: %v", sess.ID, err)})Use Dispatch() only when your transport still hands you a single encoded ClientMessage at a time, such as tests or a custom non-batched client path.
Advanced: custom transport
Section titled “Advanced: custom transport”If you omit Addr, Run only ticks—no HTTP server is started. Mount the server’s transport handler on your own server (the same Server instance holds sessions, snapshots, and broadcast hooks you already registered with OnConnect / OnMessage / OnDisconnect):
import "net/http"
srv := golem.NewServer(golem.ServerConfig{TickRate: 20, Path: "/ws"})mux := http.NewServeMux()mux.Handle("/ws", srv.Handler())
go func() { _ = http.ListenAndServe(":8080", mux) }()if err := srv.Run(ctx); err != nil && err != context.Canceled { log.Fatal(err)}If you configure golem.TransportWebTransport, mount srv.Handler() on an HTTP/3 server instead of http.ListenAndServe. The same origin defaults apply there too: same-origin in normal mode, loopback cross-port when DevSelfSignedCert is enabled, or your own policy via srv.SetWebTransportCheckOrigin(...).
See also Game loop, Typical workflow, and Channels and transports.