"Kule Konuşuyor: Node'da Sıfırdan Otoriter Multiplayer Sunucusu" makalesinin çalışan kodu. İstemci nerede olduğunu değil, ne yapmak istediğini gönderir; konumu sunucu hesaplar. Odalar, sabit tick döngüsü (20 Hz), snapshot yayını, girdi doğrulama, token bucket hız limiti ve kopma/yeniden katılma — hepsi TypeScript ile.
Kritik tasarım kararı: oyun mantığı ağı tanımaz. Room, RoomRegistry,
TickAccumulator, TokenBucket ve parseClientMessage saf ve senkrondur;
WebSocket yalnızca src/server.ts içindeki ince kabukta geçer. Bu yüzden bütün
testler tek bir port dinlemeden, tek bir soket açmadan koşar.
npm installİki ayrı terminal gerekir:
npm run server # 1. terminal — ws sunucusu: ws://localhost:8080 · 20 Hz
npm run dev # 2. terminal — Vite: tarayıcı istemcisi (http://localhost:5173)Sunucu ayrı bir Node sürecidir; Vite yalnızca tarayıcı istemcisini servis eder. Sunucu kapalıyken sayfa açılır ama "bağlantı: kopuk" yazar ve istemci üstel geri çekilmeyle (0.5s → 1s → 2s … 8s) yeniden bağlanmayı dener.
file://ile açarsanız boş ekran gelir; mutlakanpm run devile açın.
- İki sekme:
http://localhost:5173/adresini iki sekmede açın. İki daire görürsünüz (kendi karakteriniz mavi, diğerleri gri). Birinde ok tuşlarına / WASD'ye basın, öteki sekmede aynı daire hareket eder (gecikme ~1 tick = 50 ms). - Ayrı oda:
http://localhost:5173/?room=ikinci— bu sekme diğerlerini görmez, tek daire. Odalar birbirini duymaz. - Yeniden katılma: bir sekmeyi kapatıp 10 sn içinde aynı sekmede (aynı
sessionStorage) tekrar açın: karakter bıraktığı yerde. 30 sn beklerseniz ortadan ({x:320, y:200}) doğar —RESUME_GRACE_MS = 15_000. - Hile denemesi: konsolda soket üzerinden
{"type":"input","input":{"seq":1,"x":9999}}gönderin. Hiçbir şey olmaz;parseInputmesajı hareket bilgisi taşımadığı için sessizce düşürür, sunucu log basmaz, bağlantı kopmaz.
Not: bu istemcide prediction yok (bilinçli). Karakteriniz sunucu snapshot'ını
bekler; input lag görürsünüz. Tahmin + uzlaştırma katmanı için
client-prediction-server-reconciliation projesindeki GameClient buraya
doğrudan takılabilir — protokol (Input.seq, lastProcessedSeq) aynıdır.
npm test16 deterministik test, 4 dosya, ~400 ms. Hiçbiri port dinlemez, soket açmaz,
ws import etmez:
test/room.test.ts(5) — katılma/ayrılma, girdinin tick'te uygulanması,lastProcessedSeqyalnızca simüle edileni gösterir, tekrar/geri giden seq reddi, kuyruk tavanı + bilinmeyen oyuncu.test/registry.test.ts(4) — boş oda temizliği, oda izolasyonu, grace içinde resume, süresi dolmuş/bilinmeyen token.test/tick.test.ts(3) — 100 ms = 2 tick, drift telafisi (61 ms → sonraki bekleme 39 ms), ölüm sarmalı koruması.test/guards.test.ts(4) — token bucket, geçerli girdi, çöp reddi, konum iddiasının yok sayılması.
npm run benchTek oda, 2000 tick, oyuncu başına tick'te 3 girdi; Math.random yok. Tick
maliyetini ve snapshot boyutunu oyuncu sayısına karşı ölçer. Örnek çıktı
(Node 22):
tick bütçesi: 50 ms (20 Hz) · tick başına oyuncu başına 3 girdi (tavan 5)
oyuncu | ms/tick | bütçe % | snapshot (byte) | 20 Hz yayın (KB/s)
-------|---------|---------|-----------------|-------------------
1 | 0.0016 | 0.003 | 130 | 2.5
2 | 0.0016 | 0.003 | 196 | 7.7
8 | 0.0039 | 0.008 | 573 | 89.5
32 | 0.0122 | 0.024 | 2101 | 1313.0
128 | 0.0489 | 0.098 | 8250 | 20624.1
512 | 0.1876 | 0.375 | 33117 | 331169.0
Simülasyon bedava (512 oyuncuda bile tick bütçesinin binde dördü); patlayan taraf yayın trafiği, çünkü tam snapshot maliyeti oyuncu sayısının karesiyle büyür. Delta compression ve area of interest tam olarak bunun için var.
npm run typecheck # tsc --noEmit
npm run build # tsc && vite build
npm run preview # üretim derlemesini servis et
npm run scratch # makaledeki minimal 3 olaylı ws örneği (8080)src/
sim.ts # PlayerState, Input, SIM_DT, SPEED, WORLD, saf step() + clamp
room.ts # Room: girdi kuyruğu, tick, Snapshot, lastProcessedSeq
registry.ts # RoomRegistry: join/leave/resume/sweep/tickAll, boş oda temizliği
tick.ts # TickAccumulator (drift telafisi) + startTickLoop (enjekte saat)
validate.ts # parseInput / parseClientMessage — gelen her byte düşman
ratelimit.ts # TokenBucket
server.ts # ws katmanı: connection/message/close + snapshot yayını
bench-cli.ts # tick maliyeti ve snapshot boyutu ölçümü
client/net.ts # üstel geri çekilmeli yeniden bağlanma + Connection
client/main.ts # tarayıcı istemcisi: girdi üretimi (60 Hz), çizim, durum
scratch/minimal-ws.ts# makaledeki en küçük ws örneği
test/
room.test.ts registry.test.ts tick.test.ts guards.test.ts
index.html # Vite giriş noktası (canvas 640x400)
MIT