A Rust rewrite of the classic Warcraft III hosting bot GHost++.
English | 繁體中文
ghostpp-rs targets Warcraft III 1.26–1.28 on PVPGN servers (private realms / gaming platforms). The original C++ single-threaded 50 ms select loop is replaced with a tokio async actor architecture, while the protocol layer is ported byte-for-byte against the C++ original.
- PVPGN login — CD key decoding, XSHA1 password hashing and checkRevision (exe hash) implemented in pure Rust (embedded bncsutil port), no external C libraries
- Hosting & lobby — STARTADVEX3 advertising with 3-second refresh, player joins / slot management, free team/colour/race/handicap switching, HCL mode-string encoding
- Map downloads — MAPCHECK / STARTDOWNLOAD / MAPPART sliding-window transfer with live progress in the lobby
- Full in-game loop — load synchronization, action batching with adjustable latency (5–500 ms), keepalive desync detection, lag screen (tolerance auto-scaled from latency)
- Autohost — continuous auto-hosting and auto-start when the lobby fills
- GProxy++ reliable reconnects — send buffering with ACK trimming, players kept alive through disconnects, buffered resend on reconnect, safe removal on timeout
- Spoofcheck — identity verification via the
scwhisper (sent automatically by GProxy); in-game admin commands always require verification and check permissions against the verified realm - Database — both SQLite (default, zero-config) and PostgreSQL built in, switched by a single
db_url; admins / bans / game & player records - Replay saving — every game is saved as a
.w3g(zlib segmented packed container, playable directly in the W3 client) - Status endpoint — optional read-only
GET /statusJSON (games, phase, players) for external managers, isolated from the game loop - i18n — all user-visible messages go through a language catalog (Traditional Chinese and English built in, switched by one
bot_languageline) - Commands — 31 battle.net whisper commands + 30+ in-game commands, see COMMANDS.md
main ─┬─ BotCore (event loop: command dispatch, permissions, autohost, db)
├─ BnetActor (one per PVPGN connection: login state machine, anti-flood queue, refresh)
├─ GameActor (one per game: lobby / downloads / in-game loop / GProxy buffers / replay)
│ └─ PlayerConn (one per player: read/write task pair, framed codec)
├─ listener (host_port) and reconnect listener (GProxy)
└─ console (stdin commands)
Actors communicate exclusively via mpsc messages (events up, commands down) with no shared mutable state. The protocol codecs (src/core/) are ported function-by-function against the C++ source, with the critical traps (string null terminators, integer widths, packet ordering) annotated with their corresponding C++ locations.
Prebuilt binaries for every tagged release are published on the Releases page:
| Platform | Artifact | Notes |
|---|---|---|
| Windows x64 | ghostpp-rs-windows-x64.exe |
|
| macOS (Apple Silicon) | ghostpp-rs-macos-arm64 |
|
| Linux x64 | ghostpp-rs-linux-x64 |
glibc distros (Debian, Ubuntu, Fedora, …) |
| Linux ARM64 | ghostpp-rs-linux-arm64-musl |
fully static — runs on any ARM64 distro, including Alpine |
All four platforms above are built and verified by CI.
Requirements: Rust (stable) and CMake ≥ 4.1 (stormlib-sys uses it to build the bundled StormLib C++ source), plus a platform C/C++ toolchain:
-
Windows — MSVC build tools. If building with the VS2026 toolchain, also run
cargo update -p cmake(older versions of the cmake crate aren't compatible with VS2026). -
Linux — C/C++ toolchain and the zlib / bzip2 dev packages (linked by StormLib).
Debian/Ubuntu:
sudo apt install build-essential cmake zlib1g-dev libbz2-devFedora/RHEL:
sudo dnf install gcc-c++ cmake zlib-devel bzip2-devel -
macOS — Xcode Command Line Tools and CMake (
brew install cmake); zlib and bzip2 ship with the SDK. Verified on Apple Silicon by CI.
cargo build --release
For copyright reasons the repository contains no Blizzard game files. The one exception is maps/Empty.w3x, a blank 16KB map committed as a test fixture so the MPQ parsing test runs on a fresh clone. To run, supply locally:
lib/— War3 installation files (war3.exe/warcraft.exe,Storm.dll,game.dll) used by checkRevision to compute the exe hashmaps/— the map files to host (.w3x/.w3m)config/ghost.toml— main settings (ports, latency, replays, database, language file, …)bnet.toml— PVPGN server, account credentials, CD keys, root admins (seebnet.toml.example; this file holds secrets and is gitignored)map.toml— current map settings
cargo run --release
On startup the bot logs in to PVPGN and (if autohost is enabled) starts hosting automatically; or host manually by whispering !pub <game name>. See COMMANDS.md for command usage.
| Key | Description |
|---|---|
db_url |
sqlite://ghost.db (default) or postgres://user:pass@host/db |
bot_language |
language file; config/language.toml (zh-TW) / config/language_en.toml |
bot_latency |
action interval in ms (5–500; adjustable at runtime with !latency) |
bot_reconnect / bot_reconnectport |
GProxy++ reconnect toggle and port |
bot_savereplays / bot_replaypath |
automatic replay saving |
autohost_gamename / autohost_maxgames / autohost_startplayers |
autohosting |
status_bind / status_allow |
read-only GET /status JSON endpoint (games, phase, players) and the IPs/CIDRs allowed to read it; blank bind = disabled |
Set status_bind (e.g. 0.0.0.0:6200) to serve a read-only JSON snapshot of all games at GET /status. Only peers listed in status_allow (IPs / CIDR blocks; blank = loopback only) can connect; others are closed without a reply. There is no TLS or token, so also restrict the port in your firewall.
{
"bot": "MyHostBot", "version": "0.1.12", "started_at": 1759480000,
"lobby_players": 3, "ingame_players": 10,
"games": [{
"host_counter": 10, "name": "dota #10", "map": "Maps\\Download\\dota.w3x",
"phase": "playing", "created_at": 1759481000, "started_at": 1759481300, "open_slots": 0,
"players": [{ "name": "Alice", "slot": 0, "team": 0, "colour": 0, "observer": false, "reconnecting": false }]
}]
}phase:lobby(countdown included) /loading/playing;playerslists human players onlybotis the instance's bnet account name. With several instances, poll each one separately and key games by(bot, started_at, host_counter); instances on one host need distinctstatus_bindportsstarted_at(top level) changes when the bot restarts;host_counterrestarts from 1 with it- If
status_bindcannot be bound, the error is logged and the bot keeps running without the endpoint - Updates land within about a second of the change
- Polling clients should reuse the connection (keep-alive), send the last
ETagback asIf-None-Match(an unchanged board answers304with no body), and sendAccept-Encoding: gzip - Treat unknown fields as optional; fields may be added but are not renamed or removed
This project is a rewrite of GHost++ (originally by Trevor Hogan); all protocol knowledge and behavioral semantics derive from the original project. Licensed under the Apache License 2.0.