Skip to content

Latest commit

 

History

22 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

McFabProxy

An external Minecraft packet fabrication proxy. Your real client connects normally; McFabProxy intercepts the connection transparently and lets you capture, inspect, craft, and inject arbitrary serverbound packets — through a web UI or a CLI console — without touching the client, the server, or any mods.

v1 scope: protocol 774 (Minecraft 1.21.11), offline-mode servers. See Scope and limitations for exactly why, and what a v2 needs.

Prefer Meteor Client? Skip --upstream entirely — see docs/METEOR.md.


Architecture

          CLI Console (nc / netcat)
                    |
                    |
                    ▼
          ┌─────────────────────┐
          │    Core Application  │
          │                      │
          │  proxy.rs  ←──────── TCP client
          │     │ │                        \
          │  pump_c2s  pump_s2c             → real server
          │     │ │
          │  capture broadcast channel
          │     │
          │  registry (Arc<RwLock>)
          │  session  (Arc<RwLock<Option>>)
          └──────────┬──────────┘
                     │
          HTTP + WebSocket API  (:25568)
                     │
                     ▼
             Web Interface
          (browser → 127.0.0.1:25568)

The web interface is one client of the core application, not the application itself. The CLI console remains fully functional with no web UI involved.


Features

Live packet capture

Every packet that passes through the proxy — in either direction — is broadcast over a WebSocket. The Monitor tab shows the live stream with direction, state, packet ID, byte count, and a hex preview. Click any row to expand its full hex dump.

Registry Wizard

Discover an unknown packet by watching it flow through the Monitor. Expand the row, click Create Registry Entry, and a wizard opens pre-filled with the packet's state and ID. Name the packet, define its fields and types, save — the registry JSON is updated on disk and reloaded live. The entry immediately appears in the Registry Explorer and Builder.

Packet Builder

The Builder tab generates a form from any registry entry. Select a packet by name, fill in the typed fields, and send. For anything not in the registry, the Raw Inject panel accepts a packet ID and a hex payload directly.

Scripting

The Script tab runs McFabScript programs against the live session. Automate packet sequences, wait for server responses, inspect field values, and react conditionally — all without touching the browser after hitting Run.

repeat 5 times {
    send(swing_arm, hand=0);
    sleep(100);
}
wait_for(entity_status, timeout=2000);
if (!timed_out) { log("entity: " + packet.entity_id); }

Full reference: docs/SCRIPTING.md.

Packet Replay

Captured packets appear in the Builder's Replay panel. One click re-injects any captured packet into the live session.

CLI console

A plain-text TCP console (nc 127.0.0.1:25567) gives the same fabrication surface without a browser — send, raw, list, state. Useful for environments where the web UI isn't convenient.

Hot-reload registry

Add entries through the wizard, or edit the registry JSON and hit Reload in the Registry tab / POST /api/registry/reload — the registry updates in memory with no proxy restart. Hot-reload requires --registry to point to a specific .json file, not a directory.


Requirements

  • Rust (stable, 2021 edition) — rustc/cargo
  • No system OpenSSL or zlib headers — flate2 uses its pure-Rust backend, so this builds cleanly on Termux without extra packages.

Installation

Termux (Android, unrooted)

pkg install rust
git clone https://github.com/codedsword/McFabProxy
cd mc-fab-proxy
cargo build --release

The binary is at target/release/mc-fab-proxy. The pre-built web UI is already in web/dist/ and served automatically — no Node.js needed.

Linux / Debian

sudo apt install cargo   # or use rustup
git clone https://github.com/codedsword/McFabProxy
cd mc-fab-proxy
cargo build --release

Rebuilding the web UI (optional)

Only needed if you modify the frontend source:

cd web
npm install
npm run build   # output goes to web/dist/

Quick start

1. Start your target Minecraft server in offline mode (online-mode=false in server.properties).

2. Start McFabProxy:

./target/release/mc-fab-proxy --upstream 127.0.0.1:25565

Default ports: proxy listens on :25566, CLI console on :25567, web/API on :25568. Override any with --listen, --ctl, --api.

3. Point your Minecraft client at 127.0.0.1:25566 instead of the real server and log in normally.

4. Open http://127.0.0.1:25568 in a browser. The Dashboard shows session state; Monitor shows live packets immediately.

5. (Optional) CLI console:

nc 127.0.0.1 25567
> state
state=play compression=256
> raw 0x1b 00
sent raw id=27 (1 bytes)

Meteor Client? See docs/METEOR.md — the SOCKS5 setup is simpler and keeps your server list unchanged.


Command-line flags

mc-fab-proxy [OPTIONS]

Options:
  --upstream <host:port>    Fixed target server. Omit for SOCKS5 mode.
  --listen   <addr:port>    Proxy listen address       (default 127.0.0.1:25566)
  --ctl      <addr:port>    CLI console listen address (default 127.0.0.1:25567)
  --api      <addr:port>    HTTP/WebSocket API address (default 127.0.0.1:25568)
  --registry <path>         Registry dir or .json file (default registry/)
  --web      <path>         Web UI dist directory      (default web/dist)
  -h, --help                Print this help

CLI console commands

Connect with nc 127.0.0.1 25567 (or any raw TCP client).

Command Effect
state Current protocol state and compression threshold
list [state] Registered packets for a state (default: current)
send <name> k=v ... Build and inject a named registry packet
raw <id> <hex> Inject any packet by numeric ID and raw hex payload

raw works for any packet — registered or not — from day one.


Web UI

Open http://127.0.0.1:25568 while the proxy is running.

Dashboard

Connection status, upstream address, protocol state, compression, packet counts, and session uptime. Polls automatically every two seconds.

Monitor

Live packet stream over WebSocket. Filter by ID, state, or direction. Pause the stream to freeze the view. Click any row to expand its hex dump and access the Registry Wizard.

Registry

All defined packets, grouped by state, with their fields and types. Reload hot-reloads the JSON file without restarting the proxy.

Builder

Three panels:

  • Packet Builder — select a registry packet, fill in its typed fields, send.
  • Raw Inject — packet ID (hex) + hex payload, send directly.
  • Replay — recent captured packets; click Replay to re-inject.

Extending the registry

The registry/ directory holds one JSON file per protocol version (e.g. 774.json for 1.21.11). The proxy reads the client's handshake and automatically loads the matching file. Add more versions by dropping additional <version>.json files in the same directory.

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

Via the web UI (recommended)

  1. Play normally with the proxy running.
  2. Open the Monitor tab and watch for the packet you want to name.
  3. Click the packet row → expand → Create Registry Entry.
  4. Fill in the name and add fields with their types.
  5. Save. The entry is live immediately.

Via the JSON file

Edit the file directly (e.g. registry/774.json), then click Reload in the Registry tab or run POST /api/registry/reload (only works when --registry points to a single file, not a directory).

Via the API

curl -X POST http://127.0.0.1:25568/api/registry/add \
  -H 'Content-Type: application/json' \
  -d '{"state":"play","name":"swing_arm","id":27,"fields":[{"name":"hand","type":"varint"}]}'

Generating a skeleton registry from a server jar

scripts/gen_registry.py turns a server's --reports output into a skeleton registry (names and IDs for every packet, no field types). Run it once per version and it cleans up after itself:

python3 scripts/gen_registry.py server.jar
# Output: registry/<detected_version>.json

# Or if version detection fails:
python3 scripts/gen_registry.py server.jar --version 774

Field types must still be added manually via the Registry Wizard or by editing the JSON. The skeleton is enough to identify every packet in the Monitor by name.

This writes generated/reports/packets.json — the authoritative ID table for that exact server jar.

Field types

Type Wire encoding
bool 1 byte, 0 or 1
i8 / u8 1 signed/unsigned byte
i16 / u16 2 bytes, big-endian
i32 / u32 4 bytes, big-endian
i64 8 bytes, big-endian
f32 / f64 4/8 bytes IEEE 754, big-endian
varint Minecraft-style variable-length integer
string VarInt length prefix + UTF-8 bytes
uuid 16 raw bytes (dashed or undashed hex input)
hexbytes Raw bytes, no length prefix

NBT, Slot, and prefixed-array types aren't supported — use raw / Raw Inject for packets that need them.


Scope and limitations

Offline-mode only. Encryption in the Minecraft protocol is tied to Mojang session verification. Supporting online-mode servers requires the proxy to hold its own Microsoft OAuth token and perform its own session-join call — a real, separable chunk of work involving RSA, AES-CFB8, and OAuth. v1 skips this; target offline-mode servers (your own dev/test server is the right environment for packet research anyway).

It's not close to being the best. Tools like ui-utils, ui-utils cevapi rewrite and HelixCraft's packet logger are all miles ahead primarily because they are in the form of mods that can directly read game data and not just an intercepting proxy

Single session at a time. The proxy handles one client connection at a time by design — it is meant to be you, your client, your test server.

Skeleton registries. Packet IDs shift between even adjacent Minecraft versions. The included registry/774.json has verified handshake, login, and configuration entries; play entries are left empty. Use scripts/gen_registry.py against any server jar to generate a full skeleton (names + IDs for every state and direction) for any version.


Project structure

mc-fab-proxy/
├── Cargo.toml
├── scripts/
│   └── gen_registry.py   # generate skeleton registry from a server jar --reports
├── registry/
│   └── 774.json          # packet definitions for protocol 774 (1.21.11)
├── docs/
│   ├── METEOR.md         # Meteor Client SOCKS5 setup
│   ├── API.md            # HTTP + WebSocket API reference
│   ├── ARCHITECTURE.md   # internals, data flow, module map
│   ├── CONTRIBUTING.md   # development guide
│   └── SCRIPTING.md      # McFabScript language reference
├── web/
│   ├── src/              # Svelte 5 + TypeScript source
│   └── dist/             # pre-built, served by the binary at runtime
└── src/
    ├── main.rs           # startup: args, registry load, API server, accept loop
    ├── args.rs           # hand-rolled CLI flag parser
    ├── errors.rs         # shared Result/error alias
    ├── varint.rs         # Minecraft VarInt codec
    ├── frame.rs          # length-prefix + zlib framing, FrameCodec
    ├── state.rs          # Shared struct (protocol state, compression, counters)
    ├── registry.rs       # JSON packet defs, field encoder, hot-reload
    ├── capture.rs        # WsEvent enum, broadcast channel, CaptureEvent
    ├── session.rs        # SessionHandle, ActiveSession (Arc<RwLock<Option<…>>>)
    ├── proxy.rs          # bidirectional relay, pump_c2s / pump_s2c
    ├── control.rs        # CLI fabrication console
    ├── socks5.rs         # SOCKS5 CONNECT handshake (Meteor mode)
    ├── script/
    │   ├── mod.rs        # parse() entry point
    │   ├── lexer.rs      # tokenizer
    │   ├── parser.rs     # AST + recursive descent parser
    │   ├── decode.rs     # packet field decoder (inverse of encode_field)
    │   └── interpreter.rs # async tree-walking evaluator
    └── api/
        ├── mod.rs        # AppState, axum router, ServeDir for web/dist
        ├── routes.rs     # HTTP handlers (incl. script run/stop/status)
        └── ws.rs         # WebSocket /ws/events stream

API

Full reference: docs/API.md.

Quick reference:

Method Path Purpose
GET /api/status Session status (always 200)
GET /api/registry All packet definitions
POST /api/registry/reload Hot-reload registry from disk
POST /api/registry/add Add/replace a packet definition
POST /api/inject/raw Inject by ID + hex
POST /api/inject/named Inject by registry name
POST /api/script/run Run a McFabScript program
POST /api/script/stop Stop the running script
GET /api/script/status Script running state
GET (WS) /ws/events Live event stream (packets + script output)

Roadmap

  • Online-mode / encryption — proxy holds its own MS OAuth token, performs its own session-join upstream. Cleanly separable from v1.
  • NBT / Slot / array field types — broadens the set of packets reachable by name through the Builder.
  • Script enhancementswhile loops, return values, file-based scripts, a clock/timestamp built-in, and a macro save/load system.

Credits

Protocol details verified against the Minecraft Wiki (CC BY-SA 3.0), which explicitly permits building servers, clients, and bots from its contents.

About

simple Minecraft packet fabricator in rust v1 released

Resources

Contributing

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages