8a94ffd58f
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
98 lines
4.1 KiB
Markdown
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"}]}
|
|
```
|