|
| 1 | +# bun-ws-gameserver |
| 2 | + |
| 3 | +Production-grade Bun-native WebSocket game server with room-based architecture, binary protocol (msgpack), server-authoritative tick loop, and per-client rate limiting. 5-8x faster than Node.js `ws` — same protocol, same clients. |
| 4 | + |
| 5 | +[](https://app.alternatefutures.ai/deploy/bun-ws-gameserver) |
| 6 | + |
| 7 | +[](https://railway.com/template/YOUR_TEMPLATE_ID?referralCode=YOUR_CODE) |
| 8 | + |
| 9 | +## Features |
| 10 | + |
| 11 | +- **Bun-native WebSocket** — Uses `Bun.serve()` built-in WebSocket (5-8x faster than Node.js `ws`) |
| 12 | +- **Room-based architecture** — `/ws/:roomId` with auto-created rooms and configurable player caps |
| 13 | +- **Binary protocol (msgpack)** — ~40% smaller payloads than JSON |
| 14 | +- **Server-authoritative tick loop** — Configurable Hz for snapshot broadcasting |
| 15 | +- **Player state sync** — Position, rotation, action, and timestamp per player |
| 16 | +- **Bun pub/sub** — Built-in topic-based broadcasting for efficient room messages |
| 17 | +- **Zero-allocation per-connection state** — `ws.data` pattern for per-client metadata |
| 18 | +- **Per-client rate limiting** — Sliding window algorithm |
| 19 | +- **KeepAlive** — Bun's built-in ping/pong with configurable idle timeout |
| 20 | +- **Origin allowlist** — Configurable CORS protection |
| 21 | +- **Health + Metrics endpoints** — `/health` and `/metrics` for monitoring and autoscaling |
| 22 | +- **Production Dockerfile** — Multi-stage (oven/bun:1-alpine), non-root user, HEALTHCHECK |
| 23 | + |
| 24 | +## Quick Start |
| 25 | + |
| 26 | +```bash |
| 27 | +# Install dependencies |
| 28 | +bun install |
| 29 | + |
| 30 | +# Development (with hot reload) |
| 31 | +bun run dev |
| 32 | + |
| 33 | +# Production |
| 34 | +bun src/index.ts |
| 35 | +``` |
| 36 | + |
| 37 | +## Docker |
| 38 | + |
| 39 | +```bash |
| 40 | +# Build and run |
| 41 | +docker compose up --build |
| 42 | + |
| 43 | +# Or manually |
| 44 | +docker build -t bun-ws-gameserver . |
| 45 | +docker run -p 8080:8080 bun-ws-gameserver |
| 46 | +``` |
| 47 | + |
| 48 | +## Environment Variables |
| 49 | + |
| 50 | +| Variable | Default | Description | |
| 51 | +|----------|---------|-------------| |
| 52 | +| `PORT` | `8080` | Server listen port | |
| 53 | +| `ALLOWED_ORIGINS` | `*` | Comma-separated allowed origins | |
| 54 | +| `SNAPSHOT_HZ` | `20` | Tick rate (snapshots/sec) | |
| 55 | +| `KEEPALIVE_MS` | `30000` | Idle timeout (ms) | |
| 56 | +| `MAX_MESSAGES_PER_SECOND` | `60` | Per-client rate limit | |
| 57 | +| `MAX_PLAYERS_PER_ROOM` | `50` | Room capacity | |
| 58 | + |
| 59 | +## Protocol |
| 60 | + |
| 61 | +Both [`node-ws-gameserver`](https://github.com/alternatefutures/node-ws-gameserver) and `bun-ws-gameserver` use the same **msgpack binary protocol**, so clients are backend-agnostic. |
| 62 | + |
| 63 | +### Client → Server |
| 64 | + |
| 65 | +```typescript |
| 66 | +{ type: "join", payload: { displayName: string } } |
| 67 | +{ type: "state", payload: { position: {x,y,z}, rotation: {x,y,z,w}, action: string } } |
| 68 | +{ type: "chat", payload: { message: string } } |
| 69 | +``` |
| 70 | + |
| 71 | +### Server → Client |
| 72 | + |
| 73 | +```typescript |
| 74 | +{ type: "snapshot", payload: { players: Record<id, PlayerState>, timestamp: number } } |
| 75 | +{ type: "player_joined", payload: { id: string, displayName: string } } |
| 76 | +{ type: "player_left", payload: { id: string } } |
| 77 | +{ type: "chat", payload: { id: string, message: string } } |
| 78 | +{ type: "error", payload: { code: string, message: string } } |
| 79 | +``` |
| 80 | + |
| 81 | +### Example Client (browser) |
| 82 | + |
| 83 | +```typescript |
| 84 | +import { encode, decode } from '@msgpack/msgpack'; |
| 85 | + |
| 86 | +const ws = new WebSocket('ws://localhost:8080/ws/lobby'); |
| 87 | +ws.binaryType = 'arraybuffer'; |
| 88 | + |
| 89 | +ws.onopen = () => { |
| 90 | + ws.send(encode({ type: 'join', payload: { displayName: 'Player1' } })); |
| 91 | +}; |
| 92 | + |
| 93 | +ws.onmessage = (event) => { |
| 94 | + const msg = decode(new Uint8Array(event.data)); |
| 95 | + if (msg.type === 'snapshot') { |
| 96 | + // Update game state with msg.payload.players |
| 97 | + } |
| 98 | +}; |
| 99 | + |
| 100 | +// Send player state at 30fps |
| 101 | +setInterval(() => { |
| 102 | + ws.send(encode({ |
| 103 | + type: 'state', |
| 104 | + payload: { |
| 105 | + position: { x: 0, y: 0, z: 0 }, |
| 106 | + rotation: { x: 0, y: 0, z: 0, w: 1 }, |
| 107 | + action: 'idle', |
| 108 | + }, |
| 109 | + })); |
| 110 | +}, 33); |
| 111 | +``` |
| 112 | + |
| 113 | +## Why Bun? |
| 114 | + |
| 115 | +| | Node.js (`ws`) | Bun (native) | |
| 116 | +|---|---|---| |
| 117 | +| WebSocket throughput | ~50k msg/s | ~400k msg/s | |
| 118 | +| HTTP + WS server | Separate setup | Single `Bun.serve()` | |
| 119 | +| Per-connection state | WeakMap lookup | `ws.data` (zero-alloc) | |
| 120 | +| Broadcasting | Manual loop | Built-in pub/sub topics | |
| 121 | +| Startup time | ~200ms | ~20ms | |
| 122 | + |
| 123 | +Same protocol, same clients, same API — just faster. |
| 124 | + |
| 125 | +## Endpoints |
| 126 | + |
| 127 | +| Path | Method | Description | |
| 128 | +|------|--------|-------------| |
| 129 | +| `/ws/:roomId` | WS | WebSocket game connection (default room: "lobby") | |
| 130 | +| `/health` | GET | Health check — status, rooms, connections, uptime | |
| 131 | +| `/metrics` | GET | Detailed metrics — memory, messages/sec per room | |
| 132 | + |
| 133 | +## Deploy |
| 134 | + |
| 135 | +### Alternate Futures |
| 136 | + |
| 137 | +Click the deploy button at the top, or go to [app.alternatefutures.ai](https://app.alternatefutures.ai) — select this template and deploy to decentralized cloud in one click. |
| 138 | + |
| 139 | +### Railway |
| 140 | + |
| 141 | +1. Fork this repo |
| 142 | +2. Connect to Railway |
| 143 | +3. Deploy — Railway reads `railway.toml` automatically |
| 144 | + |
| 145 | +### Docker (any host) |
| 146 | + |
| 147 | +```bash |
| 148 | +docker build --platform linux/amd64 -t bun-ws-gameserver . |
| 149 | +docker run -p 8080:8080 -e PORT=8080 bun-ws-gameserver |
| 150 | +``` |
| 151 | + |
| 152 | +## License |
| 153 | + |
| 154 | +MIT |
0 commit comments