You may need to install Node.js, as well as the Yarn Package Manager.
After doing so, download or clone this repository and install the dependencies with:
$ npm installRun the server with:
$ npm run serverThis builds and runs the server.
After running the server, content will be served at localhost:PORT on your computer. The port will default to 8080, and you may override it with process.env.PORT.
Consult src/config.ts for configuration, and package.json for environ variable setup.
For support or discussion, please join our online Discord chat.
Please see CONTRIBUTING.md for information on contributing.
Please see LICENSE
This repository doubles as a Pinokio app launcher. When installed
through Pinokio, the launcher provides a one-click web control panel (panel/server.js)
instead of running the game directly. The control panel lets you:
- Edit game settings (server name, port, tick rate, countdown, spawn rates, access level, dev password, etc.) and save them to
settings.json. - Start, stop, and restart the game server, with a double-start guard and port-change-on-restart support.
- Watch the game's live log output.
- Jump straight to the game client (
http://127.0.0.1:<gamePort>/). - Browse a wiki of every dev command defined in
src/Const/Commands.tsand run them against your own connected player ("Commands" tab).
The game server stays stopped until you start it from the control panel. Launcher scripts:
install.js—npm install, applies the control panel source patches (settings override insrc/config.ts, command-result status insrc/Const/Commands.ts, and the client-tracking +POST /api/commandendpoint insrc/index.ts), then builds.start.js— launches the control panel and opens its URL.update.js— pulls the latest upstream commit (git reset --hard origin/HEAD), re-applies the source patches, reinstalls deps, and rebuilds.reset.js— removesnode_modules,lib/,settings.json, andpanel/game.pid.
src/config.ts reads an optional settings.json at the project root with precedence
settings.json > process.env > built-in default. The control panel writes this file. Keys:
| key | description | default |
|---|---|---|
serverPort |
game server port | 8080 / PORT |
serverName |
server host name sent to clients | unknown / SERVER_INFO |
mspt |
milliseconds per tick | 40 |
countdownSeconds |
countdown before the game starts | 10 |
maxPlayerLevel |
maximum player level | 45 |
connectionsPerIp |
max connections per IP (0 = unlimited) |
0 |
shinyChanceDenominator |
1/x chance for a shiny spawn | 1000000 |
factorySpawnChance |
chance to spawn out of an allied factory | 0.05 |
bossSpawnMinutes |
minutes between boss spawn attempts | 45 |
enableApi |
host the REST API at /api |
true |
enableCommands |
allow dev commands | true |
enableClient |
serve the game client | true |
doVerboseLogs |
verbose request logging | false |
defaultAccessLevel |
access level for clients without a password (0-3) |
2 |
devPasswordHash |
sha256 hex hash of the dev password | unset |
The control panel listens on 127.0.0.1:<port> (URL printed to the launcher terminal). All
endpoints return JSON.
| method | path | description |
|---|---|---|
| GET | /api/status |
{ running, pid, lastExit, gamePort, logSize } |
| GET | /api/settings |
current effective settings (defaults merged) |
| POST | /api/settings |
save settings; body may include devPassword (hashed on save) and clearDevPassword |
| POST | /api/start |
start the game server (409 if already running) |
| POST | /api/stop |
stop the game server |
| POST | /api/restart |
stop then start (applies saved settings / new port) |
| GET | /api/logs |
recent game log lines |
| GET | /api/commands |
list of dev commands from the running game (proxied) |
| POST | /api/command |
run a dev command on a target player (proxied) |
Example calls:
# check status
curl http://127.0.0.1:PORT/api/status
# save settings and start the game on port 8080
curl -X POST http://127.0.0.1:PORT/api/settings -H "Content-Type: application/json" \
-d '{"serverPort":8080,"serverName":"My Server","countdownSeconds":3}'
curl -X POST http://127.0.0.1:PORT/api/start
# list the dev commands defined in src/Const/Commands.ts
curl http://127.0.0.1:PORT/api/commands
# run a command against your own connected player (targetIp optional;
# omitted -> the panel uses the caller's IP automatically)
curl -X POST http://127.0.0.1:PORT/api/command -H "Content-Type: application/json" \
-d '{"command":"game_announce","args":["Hello World","0x00FF00","3000"]}'const res = await fetch("http://127.0.0.1:PORT/api/status");
const status = await res.json();import requests
r = requests.post("http://127.0.0.1:PORT/api/settings", json={"serverPort": 8080})
print(r.status_code)The "Commands" tab lists every command defined in src/Const/Commands.ts (id, usage,
description, required access level, and whether it counts as a cheat). Each command card
lets you fill in its arguments and run it.
How it works:
- The game tracks connected clients by their IP and exposes
POST /<apiLocation>/commandwith a JSON body{ command, args: string[], targetIp? }. - The control panel proxies this as
POST /api/command. WhentargetIpis omitted, the panel uses the caller's own connection IP (loopback clients are normalized, so::1/::ffff:127.0.0.1match127.0.0.1), and the game applies the command to the matching player(s). - Responses are
200 { ok, command, targetedClients, results: [{ target, ok, reason?, message? }] }. Commands that need a higher access level returnok: falsewith a hint — log in with the dev password in-game (or set it in Settings) to get Full access. If no player matches, the game returns404 { error }, which the panel forwards.
# run a full-access command at your player
curl -X POST http://127.0.0.1:PORT/api/command -H "Content-Type: application/json" \
-d '{"command":"game_set_level","args":["30"]}'const res = await fetch("http://127.0.0.1:PORT/api/command", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ command: "game_announce", args: ["Server restarting soon"] })
});
console.log(await res.json());import requests
r = requests.post("http://127.0.0.1:PORT/api/command",
json={"command": "game_teleport", "args": ["100", "200"]})
print(r.json())The game itself hosts a REST API at /<apiLocation> (default /api) when enableApi is true:
/api/ , /api/tanks, /api/servers, /api/commands, /api/colors. When enableCommands
is true it also accepts POST /api/command (see "Dev commands" above). The game client is
served from / when enableClient is true and the client/ folder exists.