Skip to content

Latest commit

 

History

History
301 lines (223 loc) · 6.28 KB

File metadata and controls

301 lines (223 loc) · 6.28 KB

McFabProxy HTTP + WebSocket API

Base URL: http://127.0.0.1:25568 (override with --api).

All JSON error responses use {"error": "<message>"}.


GET /api/status

Returns proxy session state. Always 200 — check the active field.

No active session:

{ "active": false }

Active session:

{
  "active": true,
  "upstream": "127.0.0.1:25565",
  "state": "play",
  "compression": 256,
  "packets_c2s": 142,
  "packets_s2c": 389,
  "started_at": 1722000000000
}
Field Type Notes
active bool Whether a client is connected
upstream string Upstream server address
state string handshake / status / login / configuration / play
compression int | null null = disabled, 0 = enabled (no threshold), N = threshold in bytes
packets_c2s int Total client→server packets relayed this session
packets_s2c int Total server→client packets relayed this session
started_at int Unix milliseconds when the session started

GET /api/registry

Returns all packet definitions, keyed by protocol state.

{
  "play": [
    {
      "name": "swing_arm",
      "id": 27,
      "fields": [
        { "name": "hand", "type": "varint" }
      ]
    }
  ],
  "login": [...]
}

POST /api/registry/reload

Reloads the registry JSON from disk without restarting the proxy. No request body.

{ "ok": true, "packets": 12 }

Returns 500 if the file cannot be read or parsed.


POST /api/registry/add

Adds a new packet definition to the registry, or replaces an existing one with the same name in the same state. Persists to the registry JSON file and updates the in-memory registry atomically.

Request:

{
  "state": "play",
  "name": "swing_arm",
  "id": 27,
  "dir": "c2s",
  "fields": [
    { "name": "hand", "type": "varint" }
  ]
}

dir is optional. Omit for C2S (client→server) entries; set to "s2c" for server→client entries. Existing registry files with no dir field are treated as C2S.

Response:

{ "ok": true, "packets": 13 }

Errors:

  • 400 — name is empty or is the reserved name "raw"
  • 500 — could not write the registry file to disk

Valid field types: bool, i8, u8, i16, u16, i32, u32, i64, f32, f64, varint, string, uuid, hexbytes.


POST /api/inject/raw

Injects a packet by numeric ID and a raw hex payload into the active session's client→server stream.

Request:

{ "id": 27, "hex": "01" }

hex may be space-separated bytes ("01 00 ff") or a contiguous string ("0100ff"). id is the packet ID as a decimal integer (e.g. 27 for 0x1b).

Response:

{ "ok": true, "bytes": 1 }

Errors:

  • 400 — hex is malformed
  • 503 — no active session

POST /api/inject/named

Looks up a packet definition by name from the registry, encodes the supplied field values, and injects the result.

Request:

{
  "name": "swing_arm",
  "fields": { "hand": "0" },
  "state": "play"
}

state is optional. When omitted, the current live protocol state is used.

Response:

{ "ok": true, "id": 27, "bytes": 1 }

Errors:

  • 400 — a field value cannot be encoded to its declared type
  • 404 — no packet with that name in the given state
  • 503 — no active session

Script API

POST /api/script/run

Run a McFabScript program. If a script is already running, it is stopped first. Returns immediately — the script runs asynchronously; subscribe to /ws/events for output.

Request:

{ "code": "log(\"hello\"); sleep(500); log(\"world\");" }

Response:

{ "ok": true }

Errors:

  • 400 — script has a syntax or parse error (error message in body)

POST /api/script/stop

Stop the currently running script (no-op if none is running).

Response:

{ "ok": true }

GET /api/script/status

Returns whether a script is currently running.

{ "running": false }

WebSocket: GET /ws/events

Upgrade to WebSocket at ws://127.0.0.1:25568/ws/events to receive a live stream of proxy events. All messages are JSON objects with a "type" discriminator field.

Packet event (type: "packet")

Sent for every packet relayed in either direction:

{
  "type": "packet",
  "direction": "c2s",
  "state": "play",
  "id": 27,
  "data": [1],
  "ts": 1722000000123,
  "fields": { "hand": "0" }
}
Field Type Notes
type string Always "packet"
direction string c2s (client→server) or s2c (server→client)
state string Protocol state at capture time
id int Packet ID
data int[] Raw packet payload bytes (excluding framing and ID)
ts int Capture timestamp, Unix milliseconds
fields object Decoded field values keyed by name. Omitted when no matching registry entry exists or decoding fails — raw data is always present as a fallback.

Script log event (type: "script_log")

Emitted when a running script calls log() or encounters an error:

{
  "type": "script_log",
  "level": "info",
  "msg": "hello world"
}
Field Type Notes
type string Always "script_log"
level string info, warn, or error
msg string The log message

Script status event (type: "script_status")

Emitted when a script starts or finishes:

{
  "type": "script_status",
  "running": true
}
Field Type Notes
type string Always "script_status"
running bool true when a script started, false when it finished or was stopped

Gap event (type: "gap")

Sent when the broadcast channel drops events due to a slow consumer. The connection stays open — no reconnect needed:

{ "type": "gap", "count": 42 }

count is the number of events dropped. The consumer should note the discontinuity and continue.

Reconnection

The server sends no explicit close on session end. If the WebSocket closes, reconnect — the channel persists across proxy sessions.


Static files

All paths not matching /api/* or /ws/* are served from web/dist/ with an index.html fallback for client-side routing. The web UI is available at http://127.0.0.1:25568/.