尚未偵測到更新。GitHub 發布版本後會顯示在這裡。
個人 GitHub 帳號下公開儲存庫的擁有者可以登入並驗證所有權,自行管理介紹,無需管理員核准。
登入並產生驗證檔案,在 30 分鐘內將其提交至此儲存庫的預設分支。其他分支或分叉儲存庫中的檔案無效。然後在下方檢查檔案,即可無需管理員核准直接管理專案。請保留檔案以維持管理權限。
如需登入,將前往登入頁面。登入後請回到此處,再次點擊所選的驗證按鈕。
在專案資料夾中執行 /nomorevibe verify,即可驗證所有權並自行管理介紹。
一分鐘完成驗證製作者的介紹僅有上面的一句話。 更多詳情請閱讀儲存庫 README。 儲存庫 README ↗
正在載入完整 README。
檢視完整 README ↗A self-hosted, game-agnostic multiplayer backend. One Docker stack on your VPS gives you:
POST /api/setup/recommend with a profile (players, update rate, latency target, data model) and apply the result with PUT /api/setup: room types, limits, servers, leaderboards, storage rules and the server script. The game's Setup tab then shows the expected numbers next to the measured ones (average and peak players, players per room, update rate, tick, ping, traffic).quickJoin), host election and migration, kick/ban, locking, passwords, reconnects that keep the player's slot.predict(), connect and join in one round trip, per-player ping in the dashboard, and kernel tuning (tune-network.sh).| Game detail: key, Claude prompt, live rooms | Room inspector: players, data, live state |
|---|---|
Demo game (/demo): five players in one room. The host spawns coins and validates every pickup.
server/ Node 24 server (ws + node:sqlite), Dockerfile, smoke test
client/ multiplayer.js: browser/Node SDK (served at /sdk.js)
docs/ Dashboard (GitHub Pages root, also served at /)
examples/ demo.html: Coin Arena demo (served at /demo)
MULTIPLAYER.md Integration guide to hand to any Claude chat
ssh root@YOUR_VPS_IP
git clone https://github.com/ZDStudios/Multiplayer-Game-System.git mp && cd mp # or scp this folder up
sh deploy.sh
deploy.sh installs Docker if it's missing and creates .env with a random ADMIN_TOKEN. It sets DOMAIN=<ip-with-dashes>.sslip.io, a free hostname that resolves to your IP, so HTTPS works without buying a domain. It also records the VPS's public IP as PUBLIC_IP (WebRTC advertises it to browsers). It then opens ports 80, 443, 8081/udp and 8082/udp in ufw and runs docker compose up -d --build. To use your own domain, point an A record at the VPS, set DOMAIN= in .env, and run docker compose up -d.
Make sure ports 80 and 443 (TCP) and 8081 and 8082 (UDP) are open in the Hostinger panel firewall too. UDP goes straight to the container, not through Caddy/Traefik. If those UDP ports are blocked, everything still works over WebSocket.
| URL | What it is |
|---|---|
https://DOMAIN/ | Dashboard |
https://DOMAIN/demo | Demo game. Open it from a game's page in the dashboard so the key is filled in. |
https://DOMAIN/sdk.js | Client SDK |
https://DOMAIN/MULTIPLAYER.md | Integration guide |
wss://DOMAIN/ws | WebSocket endpoint (signalling + fallback) |
udp://DOMAIN:8081 | Raw UDP link for native/Node clients |
udp://DOMAIN:8082 | WebRTC data channels (all peers multiplexed on one port) |
https://DOMAIN/health | Health check |
Operations:
docker compose logs -f mp # logs
docker compose up -d --build # update after git pull
docker compose exec mp cat /data/mp.db > backup.db # or back up the mp-data volume
Latency is mostly distance plus routing, so host where your players are. Measured from Perth: Sydney ~51 ms, Melbourne ~59 ms, Singapore ~179 ms, Kuala Lumpur ~182 ms. For players all over Australia, Sydney is the best single location. Any Linux VPS with a public IPv4 works (x64 or arm64, 1 vCPU / 1 GB is plenty to start), with Ubuntu 22.04 or 24.04.
ssh-keygen -t ed25519 && ssh-copy-id root@OLD_IPgit clone <your-repo-url> mp && cd mp
sh migrate.sh root@OLD_IP
This installs Docker, then copies the database and the admin token. Game keys, player identities, accounts, storage and leaderboards all carry over. The old server is left stopped; the script prints a rollback command.DOMAIN= in .env, then run docker compose up -d. Nothing changes for players.SERVER_URL in your games and in the dashboard login.sudo sh tune-network.sh.sudo sh tune-network.sh # BBR + fq, low TCP send buffering, no slow-start after idle (persists across reboots)
The biggest factor is the network path between players and the VPS. Check the Ping / link column in the dashboard; it also shows which link each player ended up on. If players are far from the server or their ISP routes badly, put the domain behind Cloudflare (proxied DNS record; WebSockets work on the free plan). Cloudflare doesn't proxy raw UDP, so set UDP_HOST to a DNS-only record (or the IP) when you do this. Cloudflare's backbone often beats ISP routing. Measured from Perth to the Kuala Lumpur VPS: 314 ms direct vs 239 ms through Cloudflare. Hosting the VPS in the same region as your players matters most of all.
Push the repo, then go to Settings → Pages → Deploy from branch → main / /docs. Open the Pages URL and enter your server URL and admin token. They're stored only in that browser's localStorage. The same dashboard is also served from the VPS at /.
MULTIPLAYER.md. Claude can also fetch the guide from https://DOMAIN/MULTIPLAYER.md.cd server && npm install && npm start # http://localhost:8080, admin token printed on first run
npm test # end-to-end smoke test (106 checks: rooms, setup API, public servers, open-world interest management, room types, storage, leaderboards, social, inbox, matchmaking, chat, UDP/WebRTC, packet loss, failover)
.env)| Var | Default | |
|---|---|---|
DOMAIN | – | Hostname for Caddy TLS |
ADMIN_TOKEN | auto-generated, printed in logs | Dashboard and admin API bearer token (≥16 chars) |
OPEN_GAMES | false | When true, any unknown key ≥8 chars auto-creates a game. Dev convenience only. |
RATE_LIMIT / RATE_BURST | 60 / 200 | Messages per second per connection |
MAX_MSG_BYTES | 65536 | Maximum size per message and per stored value |
RECONNECT_GRACE_MS | 15000 | How long a dropped player keeps their room slot |
MAX_CONNS_PER_IP | 50 | |
PUBLIC_IP | – | Public IPv4 advertised in WebRTC candidates. Required in Docker for browsers to reach WebRTC. |
UDP_PORT | 8081 | Raw UDP transport, 0 = off |
RTC_PORT | 0 (random ports) | WebRTC on one multiplexed UDP port (compose sets 8082) |
WEBRTC | true | false turns off WebRTC |
UDP_HOST | host the client connected to | Host clients send raw UDP to |
ICE_SERVERS | – | Comma-separated STUN/TURN URLs for the server's WebRTC peer |
STORAGE_MAX_KEYS | 1000 | Keys per player per scope (the game scope gets 10×) |
MAX_ROOM_PLAYERS / HARD_MAX_ROOM_PLAYERS | 1000 / 5000 | Default room-size cap / the most any game may raise it to |
MAX_ROOMS_PER_GAME | 10000 | Open rooms per game (games can lower it) |
Authorization: Bearer SETUP_KEY (dashboard → game → Setup). The key only reaches that game's configuration, never other games, players or the admin API. GET /api/setup · POST /api/setup/recommend ({profile}) · PUT /api/setup (?dryRun=1) · GET|POST /api/setup/messages (the setup chat with the dashboard; ?wait=60 long-polls for your reply) · POST /api/setup/done ({summary}: closes the setup API, after which settings change only in the dashboard). See MULTIPLAYER.md §0. Rotate the key in the dashboard to revoke it; Re-open setup issues a new key after done.
Admin equivalents: GET|PUT /api/admin/games/:id/setup, GET|POST /api/admin/games/:id/setup/messages (your replies), POST /api/admin/games/:id/setup/reopen.
All routes take Authorization: Bearer ADMIN_TOKEN.
GET /api/admin/overview (includes online players) · POST /api/admin/games/:id/players/:pid/kick ({ban, reason}) · POST /api/admin/games/:id/players/:pid/unban · GET /api/admin/games/:id/leaderboards · GET|DELETE /api/admin/games/:id/leaderboards/:board (?period=day&previous=1; DELETE resets every period, ?config=delete also drops the config) · GET|PUT /api/admin/games/:id/leaderboards/:board/config ({mode, sort, periods, clientWrite, min, max}) · POST /api/admin/games/:id/leaderboards/:board/submit ({player, score, meta}) · DELETE /api/admin/games/:id/leaderboards/:board/players/:pid · GET|PUT /api/admin/games/:id/setup (?dryRun=1) · POST /api/admin/games/:id/setup/recommend · GET|POST /api/admin/games/:id/servers · GET|PATCH|DELETE /api/admin/games/:id/servers/:sid ({name, description, region, tags, maxPlayers, password, visibility, whitelist, queue, motd, type, options, enabled}) · POST /api/admin/games/:id/inbox ({to: id | [ids], type, data}) · GET|POST /api/admin/games · GET|PATCH|DELETE /api/admin/games/:id (PATCH accepts {name, enabled, rotateKey, rotateSetupKey, settings:{allowedOrigins, gameDataWrite, playerDataWrite, lockLeaderboards, inboxClientSend, config, limits, roomTypes, roomDefaults, requireRoomType}}; changing config pushes it to every connected client) · POST /api/admin/games/:id/rooms/:room/close|kick|state · POST /api/admin/games/:id/broadcast · GET|PUT|DELETE /api/admin/games/:id/kv · GET|DELETE /api/admin/games/:id/leaderboards/:board