Self-hostable game server control panel: Go controller + agent, 26 game modules, embedded web UI. One-line install via install.sh / install.ps1. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
4.1 KiB
panel-empyrion-bridge
A small .NET 8 ASP.NET Core sidecar that proxies Empyrion's native Mod API (TCP/protobuf-net) into a JSON REST surface for the panel dashboard.
Why this exists
Empyrion's only first-class admin tool is a Windows-only desktop app
(Empyrion Admin Helper / EAH) speaking protobuf-net to an in-process mod
called EPM. Re-implementing protobuf-net contract decoding in Go is a
losing battle — every Empyrion update could shift [ProtoMember] tags.
Instead, this bridge:
- References Eleon's
Mif.dlldirectly (the official mod-API SDK). - Re-uses
EPMConnector(TCP framing + protobuf-net dispatch) by compiling its source files in. - Exposes a thin REST API the panel calls.
Build
docker build -t panel-empyrion-bridge:latest modules/empyrion-bridge
Run
The bridge needs network reach to wherever EPM is listening on each
Empyrion instance. On a single-host setup with network_mode: host
empyrion containers, hostname 127.0.0.1 works:
docker run -d --name panel-bridge --network host \
-e BRIDGE_URLS=http://127.0.0.1:8090 \
panel-empyrion-bridge:latest
For multi-host setups, pass each Empyrion's LAN IP + EAH_API_PORT in the query string per request.
API (Phase 2 — read-only)
All endpoints take ?host=…&port=… query parameters identifying the
Empyrion EPM instance.
| Method | Path | Returns |
|---|---|---|
| GET | /healthz |
bridge health + version |
| GET | /api/v1/factions |
[{ origin, factionId, name, abbrev }] |
| GET | /api/v1/players |
{ playerIds: [int], count } |
| GET | /api/v1/players/{id} |
full PlayerInfo (raw protobuf-net contract) |
| GET | /api/v1/playfields |
{ playfields: [name] } |
| GET | /api/v1/playfields/{name}/entities |
{ entities: [...] } |
| POST | /api/v1/console |
fire-and-forget; body is the console line |
POST /api/v1/console is currently EXPERIMENTAL — see "Known issues" below.
Known issues / Phase 3+ work
- Console command cmd-byte truncation suspected. Empyrion's
CmdIdenum has grown past 255 entries; the legacyEPMConnector.ModProtocolwritescmdas a single byte, which means commands with id > 255 alias to other commands. Symptom seen: sendingRequest_ConsoleCommandshows up on the EPM side asEvent_ChatMessageEx. Fix: extend the protocol encoding to use a UInt16 cmd OR build a tested mapping table from Mif.dll's CmdId enum values. Tracked for Phase 3. - Authentication. EPM has none. The bridge currently has none either. Before exposing any port to the WAN, add a shared-secret bearer token matching the panel's existing service-API pattern.
- Streaming events. Right now unsolicited Event_* packages (chat,
player join, etc) are dropped. A
GET /api/v1/streamSSE endpoint will fan these out to subscribers in Phase 4. - Connection persistence. The pool reaps idle connections after 5 minutes. If Empyrion restarts, we don't yet auto-reconnect; the next request opens a fresh connection but in-flight requests fail. Phase 5.
Source layout
empyrion-bridge/
bridge/ ASP.NET Core minimal API project
Bridge.csproj
Program.cs endpoint definitions
BridgePool.cs per-(host,port) connection cache
BridgeConnection.cs request/response correlation by (seq, expectedCmd)
EPMConnector/ source files compiled in (from MichaelGoulding/sample-empyrion-mod, MIT)
lib/
Mif.dll Eleon Modding SDK reference
Dockerfile multi-stage build, alpine runtime
Verified end-to-end on 2026-04-30
curl 'http://<agent-host>:8090/api/v1/factions?host=127.0.0.1&port=7028'
→ {"factions":[{"origin":1,"factionId":1,"name":"Human","abbrev":"Hum"}]}