Server Game Loop
Implement deterministic game loops with fixed timestep for consistent gameplay.
Fixed Timestep Loop
class GameLoop {
constructor(tickRate = 60) {
this.tickRate = tickRate;
this.tickMs = 1000 / tickRate;
this.tick = 0;
this.running = false;
}
start() {
this.running = true;
this.lastTime = process.hrtime.bigint();
this.accumulator = 0n;
this.loop();
}
loop() {
if (!this.running) return;
const now = process.hrtime.bigint();
const deltaNs = now - this.lastTime;
this.lastTime = now;
this.accumulator += deltaNs;
const tickNs = BigInt(Math.round(this.tickMs * 1_000_000));
let ticksProcessed = 0;
while (this.accumulator >= tickNs && ticksProcessed < 5) {
this.update(this.tickMs);
this.tick++;
this.accumulator -= tickNs;
ticksProcessed++;
}
// Prevent spiral of death
if (ticksProcessed === 5) {
this.accumulator = 0n;
console.warn('Tick catchup limit reached');
}
const sleepMs = Math.max(1, this.tickMs - Number(this.accumulator) / 1_000_000);
setTimeout(() => this.loop(), sleepMs);
}
update(dt) {
this.processInputs();
this.updatePhysics(dt);
this.updateEntities(dt);
this.checkGameState();
this.broadcastState();
}
}
Tick Rate Guide
| Game Type | Rate | Budget | Rationale | |-----------|------|--------|-----------| | FPS | 60-128 Hz | 8-16ms | Hit precision | | MOBA | 30-60 Hz | 16-33ms | Balance | | BR | 20-30 Hz | 33-50ms | Scale | | MMO | 10-20 Hz | 50-100ms | Massive |
Performance Monitoring
class TickMetrics {
constructor(size = 100) {
this.samples = [];
this.size = size;
}
record(ms) {
this.samples.push(ms);
if (this.samples.length > this.size) this.samples.shift();
}
stats() {
const sorted = [...this.samples].sort((a, b) => a - b);
return {
avg: this.samples.reduce((a, b) => a + b, 0) / this.samples.length,
p50: sorted[Math.floor(sorted.length * 0.5)],
p99: sorted[Math.floor(sorted.length * 0.99)]
};
}
}
Troubleshooting
Common Failure Modes
| Error | Root Cause | Solution | |-------|------------|----------| | Tick spikes | GC pauses | Object pooling | | Drift | Float precision | Fixed-point math | | Explosion | Large dt | Cap dt value | | Stuttering | Variable tick | Fixed timestep |
Debug Checklist
const stats = metrics.stats();
console.log(`Avg: ${stats.avg.toFixed(2)}ms`);
console.log(`P99: ${stats.p99.toFixed(2)}ms`);
console.log(`Budget: ${(stats.avg / tickBudget * 100).toFixed(1)}%`);
Unit Test Template
describe('GameLoop', () => {
test('maintains tick rate', async () => {
const loop = new GameLoop(60);
const ticks = [];
loop.update = () => ticks.push(Date.now());
loop.start();
await new Promise(r => setTimeout(r, 100));
loop.running = false;
expect(ticks.length).toBeGreaterThan(4);
});
});
Resources
assets/- Loop templatesreferences/- Optimization guides