Files
panel/modules/empyrion-bridge/README.md
T
dbledeez a00bd620a1 Panel — public source drop (v0.9.0)
Self-hostable game-server control panel: controller + agent + 26 game
modules. One-line install (prebuilt release, no Go required):

  curl -fsSL https://git.pdxtechs.com/dbledeez/panel/raw/branch/main/install.sh | sudo bash

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-14 21:17:39 -07:00

98 lines
4.1 KiB
Markdown

# 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.dll` directly (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 `CmdId`
enum has grown past 255 entries; the legacy `EPMConnector.ModProtocol`
writes `cmd` as a single byte, which means commands with id > 255 alias
to other commands. Symptom seen: sending `Request_ConsoleCommand` shows
up on the EPM side as `Event_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/stream` SSE 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"}]}
```