Stav: návrh implementace, 30. 10. 2026. Tento dokument popisuje budoucí změny; multiplayer ani jeho nasazení zatím neprovádí.
Do hlavního menu přidat dvě možnosti: Singleplayer a Multiplayer. Singleplayer zachová současnou kampaň, pravidla, ovládání, grafiku, mobilní vyvážení a offline PWA. Multiplayer bude samostatný online režim každý proti každému, ve kterém se 2–8 lidských hráčů pohybuje po společné mapě, bojuje mezi sebou a současně potkává počítačové nepřátele.
Hráč založí místnost, ostatní se připojí přes seznam her nebo pozvánku. Každý si před zápasem vybere obličej. Přezdívky ani přihlášení nebudou potřeba. Počítačoví nepřátelé budou mít jeden společný vzhled s kuklou zakrývající tvář; jejich různé zbraně a bojové profily zůstanou zachované.
Doporučené řešení: ponechat Vite, TypeScript a současný Canvas 2D raycaster; přidat autoritativní Node.js server s Express a Socket.IO podle projektu krysi-jmena. Převzít také způsob nasazení přes SSH/SCP, Docker Compose, Nginx a verzované releasy s rollbackem.
|
Oblast |
Zjištění a dopad na návrh |
|---|---|
|
Spouštění |
src/main.ts vytváří jednu instanci Game; doplnit výběr režimu a samostatný multiplayerový klient. |
|
Herní smyčka |
src/game/Game.ts spojuje lokální simulaci s ovládáním, zvukem a UI. Celou třídu nelze jednoduše spustit na serveru. |
|
AI |
updateEnemies v src/game/enemies.ts přijímá jednoho hráče a vrací součet poškození. Pro více cílů potřebuje výběr cíle a adresné události poškození. |
|
Boj |
combat.ts, playerCombat.ts, enemyAim.ts, grenades.ts, weapons.ts a inventory.ts obsahují základ znovupoužitelných pravidel. Typy a některá pravidla dnes rozlišují přímo hráče a nepřítele. |
|
Mapa |
src/levels/campaign.ts vytváří tři mapy 54 × 54 pro postup od startu k bossovi a východu. Rozložení je určené kampani. |
|
Prostředí |
world.ts, collision.ts a levels/dynamics.ts řeší průchodnost, výhled, cesty a bedny. Tyto principy použít také v aréně. |
|
Postavy |
enemyCatalog.ts obsahuje deset vzhledů včetně bosse, šest aktivních běžných postav a tři vypnuté běžné varianty. Pro osm hráčských portrétů lze vybrat i z archivovaných variant. |
|
Grafika |
assets/manifest.ts a atlasové build skripty načítají animace postav. Vzhled je nyní v katalogu svázaný se zbraní, což multiplayer musí oddělit. |
|
Mobil |
difficulty.ts snižuje příchozí poškození od AI na dotykovém zařízení. V jednom společném zápase musí server použít jednotná pravidla pro všechny. |
|
Provoz |
Dnešní npm run deploy publikuje statický dist/ do /www/bunker07/; Nginx obsluhuje https://bunker07.rozd.cz/. Backend zatím chybí. |
Zdrojem pravdy pro přebírané parametry bude aktuální kód při implementaci. Některé starší dokumenty popisují dřívější vyvážení; nepřepisovat podle nich současné hodnoty.
Prohlédnutý projekt: C:/Users/roman/OneDrive/DEV/vibecoding/krysi-jmena. Samotná aplikace je v jeho podsložce krysi-jmena/.
|
Co převzít |
Referenční soubory relativně ke kořeni projektu krysi-jmena |
|---|---|
|
Node.js 24, Express 5, Socket.IO 4 |
krysi-jmena/package.json, krysi-jmena/server/index.js |
|
Registr místností, kapacita, hostitel, úklid |
krysi-jmena/server/rooms.js |
|
Anonymní identita a obnovení tokenem |
krysi-jmena/server/sessions.js |
|
Ověřování akcí, potvrzení, deduplikace a limity |
krysi-jmena/server/handlers.js, krysi-jmena/server/client-ip.js |
|
Připojování a návrat po výpadku |
krysi-jmena/public/js/connection.js, krysi-jmena/public/js/store.js |
|
Lobby a sdílení pozvánky |
krysi-jmena/public/js/views/lobby.js, krysi-jmena/public/js/room-invite.js |
|
Kontejner a provozní omezení |
krysi-jmena/Dockerfile, krysi-jmena/compose.yaml |
|
Windows deploy, release a ověření |
deploy/deploy.ps1, deploy/verify.mjs, krysi-jmena/deploy/release.sh |
|
Nginx, záloha a rollback |
krysi-jmena/deploy/nginx-rat.conf, krysi-jmena/deploy/configure-nginx.py, deploy/README.md |
Převzít infrastrukturu a ověřené postupy. Pravidla týmů, rolí, tahů a karet nahradit pravidly FPS. Referenční limit deseti akcí za sekundu a posílání celého stavu po každé akci se pro pohyb FPS nehodí; rozdělit řídicí zprávy a průběžné vstupy.
Následující hodnoty jsou doporučené výchozí nastavení, protože zadání neurčuje délku zápasu, skórování ani respawn. Soustředit je do samostatné multiplayerové konfigurace pro pozdější ladění.
|
Pravidlo |
Návrh |
|---|---|
|
Režim |
Deathmatch, každý proti každému; žádné týmy. |
|
Lidé |
Start s 2–8 připojenými hráči; kapacita místnosti nastavitelná od 2 do 8. AI nezabírá lidské sloty. |
|
Začátek |
Odpočet 5 sekund po načtení mapy a grafiky všemi účastníky. |
|
Výbava |
100 zdraví, bez armoru, Luger s 32 náboji a dostupné pěsti. Pistole je vybraná. Stejné při každém respawnu. |
|
Umístění |
Server náhodně přiřadí bezpečná místa v okrajových sektorech mapy. Hráči se nespawnují vedle sebe. |
|
Konec |
20 zabití lidských soupeřů nebo 10 minut hry, podle toho, co nastane dřív. |
|
Skóre |
Zabití člověka +1 frag. AI má samostatné počítadlo a do vítězného limitu se nepočítá. Sebevražda −1 frag; smrt od AI bez fragu pro jiného hráče. |
|
Shoda výsledků |
Vyhrává nejvyšší počet fragů, při shodě nižší počet úmrtí; při další shodě sdílené vítězství. Výsledky jednoho serverového kroku vyhodnotit společně. |
|
Smrt |
Krátká obrazovka smrti, automatický respawn za 3 sekundy. Zahodit právě drženou palnou zbraň se zbývající municí; pistoli ani pěsti jako kořist nevytvářet. |
|
Ochrana po respawnu |
Nejvýše 2 sekundy, viditelná a končí prvním útokem hráče; stejně vůči lidem i AI. |
|
Výzbroj |
Další zbraně z mrtvol AI a hráčů, munice a lékárničky na mapě. Sbírání, limity a obnovu zásob řídí server. |
|
AI |
Stejné běžné bojové profily jako v singleplayeru, společný vzhled s kuklou. První aréna bez kampaňového bosse a zamčeného východu. |
|
PC a mobil |
Společné serverové hodnoty poškození a rychlostí; zachovat dotykové ovládání. Mobilní snížení poškození nadále platí v singleplayeru. |
|
Pozdní připojení |
Noví hráči pouze do lobby. Během zápasu povolit jen návrat již přihlášeného účastníka. |
|
Pauza |
Menu zastaví vstupy daného hráče, společný zápas pokračuje a jeho postava zůstává zranitelná. |
Po výbuchu nebo dohoření ohně připsat zabití původnímu vlastníkovi útoku, i když už zemřel. Každou smrt vyhodnotit právě jednou. Lidé mohou zraňovat všechny ostatní lidi i AI, AI útočí na lidi; vlastní výbušniny a oheň zraňují také vlastníka. AI se v první verzi navzájem nepoškozuje. Exploze, oheň a průstřely respektují zdi i bedny.
Poškození proti lidem definovat explicitně v multiplayerové konfiguraci. Jako výchozí použít parametry hráčských palných zbraní; pro pěsti navrhnout 25 poškození proti člověku. Kampaňové škálování pěsti podle maximálního zdraví nepřítele nesmí automaticky znamenat dvě rány na lidského soupeře. Pravidla granátů, armoru a ohně musí mít samostatně otestované lidské cíle. Hodnoty kampaně tím neměnit.
Hlavní menu nabídne Singleplayer a Multiplayer. Síťový klient načítat až při vstupu do multiplayeru.
Multiplayer zobrazí dostupné místnosti: název nebo automatický kód hry, počet lidí, kapacitu a stav. Hráči nepotřebují zadávat jméno.
Vytvořit hru založí lobby. Hostitel vybere kapacitu 2–8 a obličej; základní pravidla jsou předvyplněná. Ostatní se připojí ze seznamu nebo odkazem ?room=<roomId>.
Každý má kartu s portrétem, indikátorem připojení, stavem připravenosti a případně označením hostitele. Vlastní karta má štítek TY.
V místnosti musí být každý obličej unikátní. Výběr atomicky rezervuje server; při souběžném výběru stejné tváře uspěje jen jeden hráč.
Hráč potvrdí Připraven. Změna obličeje nebo pravidel připravenost dotčených hráčů zruší. Hostitel spustí zápas, pokud jsou alespoň dva lidé a všichni mají obličej a připravenost.
Server uzavře vstupy do místnosti, pošle inicializaci zápasu a čeká na match:loaded. Po potvrzení všech spustí odpočet a společný čas zápasu. Při nenačtení do 30 sekund vrátí místnost do lobby s vysvětlením.
Po zápase zobrazit výsledky s portréty: fragy, úmrtí a zvlášť zabití AI. Návrat do lobby zachová místnost i obličeje, zruší připravenost a umožní odvetu.
Identita bude interní playerId a tajný obnovovací token jako v referenční hře. avatarId je pouze vzhled, neslouží k autorizaci. Portréty použít také v tabulce skóre a v hláškách o zabití. Pro čtečky obrazovky dát tvářím krátké rozlišovací popisky bez požadavku na osobní jméno.
Připravit nejméně osm jasně rozlišitelných obličejů. Vybrat osm z existujících běžných a archivovaných portrétů; případné nové tváře doplnit až po kontrole čitelnosti. Nepovolovat kvůli tomu vypnuté postavy v kampani. Katalog multiplayerových avatarů bude samostatný. Portrét nemění zdraví, rychlost, zbraň ani velikost zásahové oblasti.
Současnou mapu lze využít pro první technický test propojení dvou klientů. Pro výsledný režim doporučuji novou ručně navrženou arénu: kampaně mají jeden start, postupovou trasu, zásoby v odbočkách a závěr s bossem. Pro férové opakované střety 2–8 lidí vyžadují jiné rozložení.
První mapa Aréna 01 – skladový okruh, výchozí půdorys přibližně 40 × 40 polí:
Osm okrajových spawn sektorů, v každém více kandidátních bodů. Dva body mohou být na stejné straně mapy, ale musí je oddělovat kryt a dostatečná vzdálenost.
Vnější okruh a alespoň dvě vnitřní propojky, několik středních místností a zalomené výhledy. Z každého sektoru nejméně dvě únikové trasy.
Bedny a zdi jako skutečné krytí. Chodby převážně 2–3 pole široké, bez jediného průchodu ovládajícího celou mapu.
Lékárničky a základní munice rozložené rovnoměrně, silnější výzbroj na rizikovějších místech nebo získatelná z AI. Žádná nejlepší zbraň přímo u jednoho startu.
Žádný postupový východ ani povinné tajné místnosti. Mapa pro průzkum neukazuje polohy soupeřů ani AI; vlastní prozkoumání zůstává lokální.
Velikost a délky tras ověřit hrou ve dvou i osmi. Pokud se dvojice potkává příliš málo, připravit menší samostatnou variantu; nezmenšovat aktivní mapu během zápasu.
Generátor nemusí vytvářet úplně nový labyrint při každém zápase. První verze použije stabilní geometrii a serverové losování startů, AI a vybraných zásob. Pozdější varianty mohou používat seed, vždy po validaci průchodnosti.
Server vytvoří pořadí vhodných bodů a zvolí kandidáta mimo zeď, bednu, tělo a aktivní oheň, bez přímého výhledu blízkého soupeře či AI a s co největší vzdáleností po průchozí trase od hrozeb. Jako výchozí cíl zkusit alespoň 6 polí po trase, hodnotu ověřit pro osm hráčů. Úvodní starty rozdělit mezi vzdálené okrajové sektory.
Pokud není ideální bod, použít bezpečný průchozí bod s nejlepším hodnocením a krátkou ochranou; nikdy nevytvářet postavu ve zdi. Před spuštěním ověřit dosažitelnost všech sektorů, zásob i tras a znovu ji zkontrolovat po rozmístění beden.
Server pošle hotovou mřížku, umístění objektů, mapId, mapVersion, seed a hash mapy. Samotný seed nestačí: klient nesmí vlastní odlišnou verzí generátoru vytvořit jinou mapu. Náhodná rozhodnutí a výsledné rozmístění určuje server.
Oddělit bojový profil od vzhledu. Všechny multiplayerové stráže budou mít skinId: masked-guard, ale zachovají své aiProfileId, zbraň, zdraví, kadenci, dávky, reakce, míření, odvetu po zásahu a rychlosti podle aktuálního singleplayerového kódu. Zachovat také hlídky, reakci na zvuk, pronásledování, obcházení krytu a šanci na krátký výpad.
Nejde o sloučení všech nepřátel do jednoho bojového typu: AK, Uzi, Tommy Gun, FG 42, Mosin a těžký kulomet se dál chovají podle vlastních profilů. Kořist a případné nápoje určit profilem, nikoli obrázkem kukly. Stejný vzhled nesmí omylem sjednotit jejich parametry ani jim přidělit všem jednu zbraň.
Simulovat AI výhradně na serveru, jednou pro celou místnost.
Vybírat pouze živé připojené nebo dočasně odpojené zranitelné postavy. Držet současný cíl, dokud dává smysl; přepnout na bližší viditelnou hrozbu, útočníka nebo slyšený zdroj. Použít krátkou prodlevu proti přeskakování cíle každý krok.
Uchovávat targetPlayerId a poslední známé místo. Při smrti, vypršení rezervace nebo odchodu cíle vybrat nový.
Události výstřelu a poškození musí obsahovat konkrétního původce a zasažený subjekt. Nelze několikrát zavolat dnešní updateEnemies pro každého člověka: tím by AI zrychlila a několikrát posunula své časovače.
AI střela má respektovat nejbližší zasaženou lidskou postavu a překážky, ne automaticky poškodit zamýšlený cíl za jiným hráčem. Přesnost a reakce nadále vycházejí z převzatého bojového profilu.
Průchozí cesty přepočítávat při změně situace, ne pro každého nepřítele každý krok.
Výchozí počty AI k ověření: 6 pro dva lidi, 10 pro 3–4 a 12 pro 5–8. Počet zafixovat na začátku zápasu. Mrtvé stráže obnovovat po přibližně 15 sekundách v bezpečné oblasti, mimo bezprostřední výhled lidí. Omezit počet mrtvol a kořisti i jejich životnost, aby dlouhý zápas neplnil paměť.
Vytvořit nový společný model stráže s tmavou kuklou: skrytá tvář, zachované oči, čitelná silueta a jednotné oblečení. Použít stávající styl a nativní snímky 192 × 256 s totožným ukotvením nohou. Připravit animace idle, walk, attack, hurt, death a varianty držených zbraní. Kukla musí být součástí všech potřebných snímků a směrů.
Pro lidi zajistit čitelné rozlišení zepředu, z boku i zezadu a zobrazení aktuálně držené zbraně. Existující převážně čelní atlasy nepovažovat automaticky za hotové multiplayerové postavy. Využít podporu směrových manifestů a při implementaci připravit například osm směrů nebo odpovídající skládání vrstev těla, hlavy a zbraně. Zásahová oblast zůstane stejná pro všechny lidské avatary.
Nové atlasy načítat jen pro multiplayer. Stávající katalog i grafika nepřátel singleplayeru zůstanou zachované. Samotné generování a zapojení obrázků je implementační úkol, nikoli výstup tohoto plánu.
Prohlížeč: Vite + TypeScript + Canvas raycaster
├─ Singleplayer → současný Game, místní simulace, offline PWA
└─ Multiplayer → lobby, vstupy, predikce, vykreslení serverového stavu
│ HTTPS + Socket.IO / WSS
Nginx: bunker07.rozd.cz │
├─ statická hra │
└─ /socket.io/, /health, /api/*
▼
Docker Compose → Node.js 24 + Express 5 + Socket.IO 4
├─ místnosti, tokeny, hostitel, limity
└─ každá aktivní místnost: mapa, lidé, AI, boj, zásoby, skóre
Backend drží skutečnou pozici, zdraví, inventář, časovače zbraní, AI, projektily, zásahy, kořist, respawny a výsledky. Klient posílá ovládání a požadavky. Neposílá rozhodnutí „zasáhl jsem“, „mám tuto pozici“ nebo „přidej mi zdraví“.
Jeden proces obslouží více oddělených místností. Každá má vlastní svět, čas, generátor náhody a životní cyklus; síťový kanál musí ověřovat členství. První verze bude bez databáze, Redis, účtů a P2P. Restart procesu ukončí rozehrané zápasy a zneplatní obnovovací tokeny, stejně jako v referenční hře. Kapacitu serveru nastavit až podle měření FPS simulace; nepřebírat automaticky limit 100 místností z tahové hry.
Vyčlenit čisté funkce a datové typy pro pohyb, kolize, zbraně, inventář, zásahy a AI. Nesmí importovat DOM, Canvas, audio, Image, virtuální Vite moduly ani prohlížečové načítání atlasů. Na serveru běží bez grafiky. Seedovaný generátor a čas předávat jako závislosti, nespoléhat na samostatné klientské Math.random().
Kampaň ponechat na současné smyčce Game. Změny sdílených pravidel dělat po malých krocích a ověřovat stávajícími testy. Kolem AI ponechat kompatibilní vstup pro jednoho hráče, který používá společnou logiku jednoho kroku. Multiplayer dostane samostatný řadič, ne síťové podmínky rozeseté do celé kampaně.
|
Vrstva |
Výchozí návrh |
|---|---|
|
Serverová simulace |
Pevný krok 1/60 s, blízký současné simulaci. Limitovat dohánění zpoždění a měřit délku kroků. |
|
Vstupy klienta |
Přibližně 30 zpráv/s; obsahují pořadové číslo, normalizovaný pohyb, směr pohledu, stav útoku a požadovanou zbraň. |
|
Stav světa |
Přibližně 20 snímků/s, každý s matchId, serverovým krokem, časem a potvrzením zpracovaných vstupů. |
|
Vykreslování |
Nezávisle přes requestAnimationFrame, cílově 60 FPS na PC a alespoň 30 FPS na cílovém mobilu. |
|
Lobby a příkazy |
Potvrzované požadavky s requestId, chybovým kódem a deduplikací jako v krysi-jmena. |
Průběžný stav lze odesílat přes volatile a zahodit staré čekající snímky; další úplný snímek opraví stav. Začít kompaktními úplnými snímky, rozdílové zprávy přidat až při prokázané potřebě. Posílat pouze potřebná data, nikdy tokeny jiných lidí ani interní struktury AI.
Pohyb lokálního hráče predikovat stejnou funkcí jako na serveru. Server potvrdí poslední zpracovaný vstup, klient opraví pozici a přehraje zbývající vstupy. Pohyb ostatních postav interpolovat s výchozím zpožděním přibližně 100 ms. Míření a lokální zvuk výstřelu reagují ihned, zásah a změnu zdraví potvrzuje server. Události zvuků a efektů opatřit ID proti dvojímu přehrání.
Vstup není klientem určená délka simulace. Server zpracuje konečný stav ovládání jen v mezích vlastního času; záplava zpráv nezrychlí pohyb. Jednorázové interakce a hrany stisku/uvolnění mají vlastní sekvenci a potvrzení, případně se opakují do potvrzení. To je nutné zejména pro hod granátu a Molotova: ztracené uvolnění nesmí nechat přípravu viset. Staré zprávy z jiného zápasu nebo života (lifeId) odmítnout.
Pro hratelnou střelbu při běžném pingu naplánovat omezenou kompenzaci zpoždění palných zbraní: uchovávat historii pozic přibližně 200 ms, ověřovat klientský čas proti odhadu serveru a započítat interpolační zpoždění. Při ověření výstřelu přepočítat postavy k příslušnému okamžiku, ale nevracet celý svět, zdraví nebo zásoby. Nepřijmout zásah předchozího života a nekompenzovat čas mimo povolené okno. Granáty a oheň simulovat v aktuálním serverovém čase.
Socket.IO standardně neposkytuje zaručené opětovné doručení každé zprávy po výpadku. Deduplikace, potvrzení důležitých akcí a úplná synchronizace jsou součástí aplikace; její správnost nesmí záviset pouze na automatickém reconnectu. Viz doručování zpráv a obnova spojení.
|
Směr |
Zprávy a účel |
|---|---|
|
Klient → server, lobby |
rooms:subscribe, room:create, room:join, room:leave, player:avatar, player:ready, room:settings, match:start |
|
Klient → server, hra |
match:loaded, player:input, player:interact, state:sync, match:returnToLobby |
|
Klient → server, relace |
session:resume s tajným tokenem |
|
Server → klient, řízení |
rooms:snapshot, room:state, match:init, match:countdown, match:finished, room:closed, session:replaced |
|
Server → klient, hra |
world:snapshot, combat:event, player:died, player:respawned |
Protokol verzovat (protocolVersion, buildId) a před zápasem ověřit také verzi mapy a assetů. Klient se starým nekompatibilním buildem dostane srozumitelnou výzvu k aktualizaci. U každé zprávy stanovit schéma, maximální velikost, potřebnou fázi, oprávnění a způsob potvrzení.
Lobby: po výpadku držet místo i obličej 60 sekund, potom je uvolnit. Výslovný odchod je uvolní hned.
Hra: po chybějících vstupech přibližně 250 ms vynulovat pohyb a útok, zrušit rozpracovanou přípravu hodu. Postava zůstane na místě a je zranitelná. Rezervaci držet 30 sekund; při smrti odpojeného hráče neprovádět automatický respawn před návratem.
Návrat tokenem obnoví stejné playerId, portrét, zdraví nebo stav smrti, inventář a skóre. Odeslat úplný současný stav, vyprázdnit starou frontu vstupů a nepřehrávat útoky z doby odpojení. Neudělovat nové vybavení ani ochranu jen za reconnect.
Po 30 sekundách nebo při výslovném odchodu ze zápasu účast ukončit. Postavu odstranit, zanechat standardní kořist, neudělovat za samotný odchod frag; historické skóre zachovat ve výsledcích. Nový hráč uvolněné místo během zápasu nenahradí.
Otevření stejné relace v jiném panelu převezme spojení a starému zobrazí session:replaced, jako v referenční hře. Pro lokální test osmi lidí použít oddělené profily nebo izolované kontexty prohlížeče.
Hostitel spravuje lobby, není herním serverem. Při jeho odpojení předat roli nejdéle připojenému účastníkovi; zápas tím nezastavovat.
Pokud zůstane jediný připojený člověk, dát ostatním 30 sekund na návrat a pak zápas ukončit jako přerušený, bez automatického vítězství. Při návratu druhého hráče odpočet zrušit.
Pokud se odpojí všichni, herní čas pokračuje po dobu rezervace. Poté zastavit simulaci a zrušit aktivní svět; prázdnou místnost odstranit nejpozději za 60 sekund od posledního odpojení. Prázdné lobby uklízet po 10 minutách jako v referenci.
V první verzi nemusí být diváci, chat, hlas, veřejné žebříčky ani trvalé ukládání statistik. Nejsou podmínkou uvedeného režimu.
Offline musí dál fungovat celá původní kampaň. Multiplayer při nedostupném backendu zobrazí stav připojení a návrat do menu; nesmí blokovat spuštění singleplayeru. Socket.IO připojení nevytvářet při samotném hraní kampaně.
Stávající service worker ze scripts/pwa.ts už obsluhuje jen vybrané statické soubory. Zachovat tento princip a testem výslovně vyloučit /socket.io/, /api/ a /health z ukládání do cache. Pozvánku řešit query parametrem, aby fungovala i při současném statickém routování. Multiplayerové atlasy načítat podle potřeby; při změně seznamu precache nezhoršit offline dostupnost kampaně.
setupPwa dnes dostává callback pro pauzu hry. Multiplayer potřebuje vlastní reakci: aktualizaci odložit do lobby nebo po konci zápasu, neprovádět vynucený reload uprostřed boje. Escape, ztráta pointer locku, přepnutí aplikace a otočení telefonu okamžitě zruší aktivní vstupy. Menu ani skrytý panel nezastaví serverový čas.
Zachovat současné joystickové ovládání, výběr zbraní a sbírání. Přidat kompaktní skóre, zbývající čas, počet připojených lidí, stav spojení a portrét vlastního hráče. Na mobilu musí být lobby i výsledky ovladatelné na výšku i na šířku a respektovat bezpečné okraje displeje. Lokální asistence míření nesmí měnit serverové poškození ani dosah.
|
Oblast |
Návrh pro BUNKER 07 |
|---|---|
|
Runtime |
Node.js 24 v image node:24-bookworm-slim, Express 5, Socket.IO 4; konkrétní verze uzamknout lockfilem. |
|
Klient |
Současný Vite + TypeScript, přidat socket.io-client kompatibilní se serverem. |
|
Serverový build |
TypeScript; například samostatný esbuild bundle pro Node 24 s externími npm závislostmi. Oddělená kontrola typů serveru bez DOM. |
|
Server |
Stejný SSH cíl jako reference a současná hra: ubuntu@130.61.169.112; dostupné prostředky a port ověřit před nasazením. |
|
Adresa |
Zachovat https://bunker07.rozd.cz/. |
|
Backend |
Navržený volný port 127.0.0.1:3001; reference používá 3000. Dostupnost 3001 zatím nebyla ověřena. |
|
Compose |
Vlastní projekt bunker07, jeden proces, Linux host networking a naslouchání výhradně na loopbacku jako v referenci. |
|
Proxy |
Stávající Nginx servíruje statickou hru, /socket.io/ , /api/ a /health posílá do backendu. HTTPS a certifikát zachovat. |
|
Soubory |
Nové /www/bunker07/releases/<releaseId>/, /www/bunker07/current, /www/bunker07/backups/. Nginx přepnout ze současného kořene na frontend aktivního releasu. |
|
Stav |
Místnosti a relace pouze v RAM. Žádné sdílení procesu ani paměti s krysi-jmena. |
Zachovat statické servírování kampaně Nginxem, takže výpadek backendu nevyřadí offline ani běžný singleplayer. Převzatý kontejnerový vzor rozšířit o build Vite, kontrolu TypeScriptu, multiplayerové testy a vytvoření serverového bundle. Ověřit, že bundle neobsahuje browserové moduly a spustí se v čistém runtime image.
Převzít běh jako neprivilegovaný uživatel node, read-only filesystem, tmpfs, omezení oprávnění, restart po rebootu, healthcheck a rotaci logů. Počáteční rozpočet CPU/RAM určit zátěžovou zkouškou; nepřebírat bez měření 256 MiB z tahové hry. Logovat verzi, chyby a výkon, nikoli tokeny.
Připravit deploy.bat a deploy/deploy.ps1 podle reference, se samostatnými názvy a cestami BUNKER 07. Budoucí npm run deploy je může volat; současný statický postup uchovat pod jasně pojmenovaným příkazem pro návrat.
Vytvořit zdrojový archiv bez node_modules, Git metadat a lokálních konfigurací s tajnými údaji. Nahrát SCP, ověřit SHA-256, rozbalit release a sjednotit oprávnění stejně jako v referenci.
Na serveru provést Docker build nativní pro jeho architekturu. Povinná kontrolní fáze: npm ci, stávající i nové testy, kontroly typů, frontendový a serverový build. Pro provoz ponechat jen produkční závislosti a sestavené soubory.
Ještě před výměnou běžící verze zapnout režim údržby pro nové místnosti a starty. Počkat na konec aktivních zápasů, nebo hráčům oznámit plánované ukončení s odpočtem. Sestavení samotné nesmí restartovat běžící hru.
Spustit nový kontejner, ověřit lokální /health s verzí protokolu a buildu. Propojení nového frontendového releasu a backendu kontrolovat stejným buildId; během přechodu odmítnout nekompatibilní klienty s výzvou k obnově.
Při první migraci zálohovat existující konfiguraci bunker07.rozd.cz, ověřit očekávaný stav a upravit pouze tento virtual host. Přidat proxy a nový statický kořen, provést nginx -t a reload. Nedotýkat se konfigurace rat.rozd.cz ani dalších webů.
Teprve po lokálních kontrolách přepnout current na nový release a ověřit veřejné HTTPS, statické assety, worker, Socket.IO polling i upgrade na WSS. Při chybě obnovit předchozí symlink, kontejner i případně Nginx konfiguraci.
Ověřit založení lobby, vstup druhého izolovaného klienta, start, pohyb a konec testovacího zápasu; testovací místnost následně uklidit. Statické ověření samo nepotvrzuje funkčnost hry.
Uchovat předchozí release a postup návratu. Při první migraci uchovat i původní statické soubory a konfiguraci; rollback musí umět stav před zavedením Docker backendu.
/socket.io/ musí dostat HTTP/1.1, hlavičky Upgrade a Connection, vypnuté proxy buffering a timeout delší než součet Socket.IO heartbeat intervalů, například 120 sekund. Cesta musí být stejná na klientovi, serveru i proxy. Podklad: Socket.IO za Nginxem.
ALLOWED_ORIGIN=https://bunker07.rozd.cz, TRUST_PROXY=loopback; Nginx přepíše X-Real-IP. Žádný nový veřejný backendový port. Zachovat dnešní pravidla cache pro HTML, hashované assety, manifest a worker. Starší hashované soubory ponechat dostupné pro otevřené klienty a offline cache také po přepnutí releasu, například sdíleným úložištěm assetů; samotné ponechání staré složky mimo kořen Nginxu nestačí.
Každá výměna backendového procesu zruší neuložené místnosti a relace. Údržbový režim má omezit přerušení, nejde o bezvýpadkovou migraci rozehrané simulace. Tento plán neobsahuje skutečné připojení k serveru ani tvrzení o jeho aktuálním stavu.
Ověřovat členství, fázi hry, hostitelská práva, kapacitu a unikátní obličeje na serveru. Nepřijímat cizí playerId jako důkaz identity.
Validovat typy, konečnost čísel, rozsahy os a úhlů, povolenou zbraň a velikost zprávy. Zdraví, munici, kadenci, rychlost i sběr kořisti odvozovat ze serverového stavu.
Oddělit limity vstupů, interakcí a lobby akcí. Ponechat IP limit zakládání a vstupů podle reference; vysokofrekvenční vstupy mít vlastní rozpočet odpovídající přibližně 30 zprávám/s a krátkým špičkám.
Zastropovat počet místností, lidských spojení, AI, projektilů, mrtvol, předmětů, čekajících požadavků a délku historie. Pomalému klientovi neposílat nekonečnou frontu snímků.
Tajný token generovat kryptograficky, ukládat pouze u jeho vlastníka, neposílat v pozvánce ani veřejném seznamu. Obnovování ověřuje vazbu tokenu na místnost a hráče.
Nevystavovat pozice soupeřů na minimapě. Serverová autorita brání falšování stavu, ale klient znající úplný svět může vytvářet informační cheaty; případné filtrování vzdálených/skrytých entit je pozdější samostatné zlepšení.
Názvy nových souborů jsou orientační; rozhraní upravit po vyčlenění čistého jádra.
src/
main.ts výběr režimu a životní cyklus klienta
ui/multiplayerLobby.ts seznam her, obličeje, připravenost
ui/multiplayerResults.ts skóre a odveta
multiplayer/
MultiplayerGame.ts propojení vstupů, rendereru a sítě
connection.ts Socket.IO, relace a potvrzené příkazy
prediction.ts místní predikce a opravy pohybu
interpolation.ts plynulý pohyb ostatních postav
shared/
protocol.ts typy a verze zpráv
multiplayerRules.ts pravidla zápasu, společné konstanty
simulation/ čisté jádro bez browserových závislostí
levels/multiplayerArena01.ts geometrie a spawn sektory
assets/multiplayerAvatars.ts osm tváří a jednotná AI kukla
server/
index.ts Express, Socket.IO, health, ukončení
config.ts port, origin, kapacity, časové limity
rooms.ts registry, hostitel, připravenost
sessions.ts anonymní tokeny a návrat
handlers.ts validace a routování příkazů
match.ts pevný krok, svět, výsledky
ai.ts výběr více cílů a události AI
spawns.ts výběr bezpečných startů a respawnů
Dockerfile
compose.yaml
deploy.bat
deploy/ release, proxy, ověření a rollback
tests/multiplayer/ simulace, protokol, integrace a zátěž
|
Etapa |
Práce |
Podmínka dokončení |
|---|---|---|
|
1. Oddělení režimů |
Menu, samostatný multiplayerový řadič, začátek sdíleného jádra, kontrola serverového sestavení. |
Kampaň funguje jako dosud včetně offline; serverové jádro jde spustit bez DOM. |
|
2. Lobby |
Převzetí Express/Socket.IO, místnosti, tokeny, osm unikátních obličejů, připravenost, pozvánky, hostitel. |
Dva až osm izolovaných klientů vstoupí, devátý je odmítnut; souběžná rezervace tváře je korektní. |
|
3. Síťový pohyb |
Pevný krok, protokol, predikce, interpolace, kolize a serverové spawny. Dočasně lze použít existující mapu. |
Osm postav sdílí svět, neprochází zdmi a klient si nezrychlí pohyb. |
|
4. Souboj lidí |
Zásahy pistolí, pak ostatní zbraně, inventář, výbušniny, smrt, skóre, respawn a kompenzace zpoždění. |
Dva lidé se mohou potkat a porazit, všichni vidí stejné výsledky; žádné dvojí zabití nebo sběr. |
|
5. Aréna a AI |
Nová mapa, ověření startů, vícecílá AI, kukla, kořist a obnova zásob. |
AI má společný vzhled, zachované profily a běží jednou; mapa je hratelná pro dva i osm lidí. |
|
6. Celý zápas |
Čas a limit fragů, výsledky, odveta, reconnect, odchody, hostitel a úklid místností. |
Celý cyklus lobby → zápas → výsledky → lobby proběhne bez ztráty či duplikace účastníků. |
|
7. Mobil a provoz |
PWA, dotykové ovládání, výkon, Docker/SSH deploy, Nginx a rollback. |
Úspěšné kontroly na PC i fyzickém mobilu, zátěžový test a ověřený návrat verze. |
První interní hratelný milník po etapě 4: lobby, dvě unikátní tváře, společná mapa, pistole, vzájemné zásahy, smrt a respawn. Dokončené zadání vyžaduje také osm účastníků, arénu, maskovanou AI, celý průběh zápasu a nasazovací postup.
Zachovat průchod současných npm test, npm run check a npm run build; přidat kontrolu serverových typů, sestavení a start čistého runtime image.
Testovat kapacity 2 i 8, odmítnutí devátého hráče, duplicitní tvář, souběžný vstup, změnu připravenosti a zamítnutí startu bez dvou připojených účastníků.
Dva až osm skutečných Socket.IO klientů připojených k testovacímu serveru: stav zůstává oddělený mezi místnostmi, cizí token ani cizí ID neudělí přístup.
Simulace s řízeným časem a náhodou: zdi a bedny, kadence, munice, první cíl v dráze, granáty, oheň, AI, smrt právě jednou a výsledky shodných fragů.
Současné sebrání stejné zbraně nebo lékárničky smí uspět jen jednou; úspěšně zopakovaný requestId nesmí provést druhý efekt.
Životní cyklus: výpadek, ztracené uvolnění útoku, návrat živého i mrtvého, nahrazení relace, konec rezervace, odchod hostitele, jediný zbývající hráč a zánik prázdné místnosti.
Pro mapu ověřit všechny spawn sektory, průchodnost po rozmístění beden, dosažitelné zásoby a opakované losování bez kolize se zdí nebo překážkou.
Regrese AI: pro jeden cíl stejné bojové profily jako dosud; pro osm cílů časovače neposkočí osmkrát. Mobilní klient nemění serverové poškození.
PWA: singleplayer startuje offline, síťové odpovědi se necachují a starý klient nedokáže vstoupit do nekompatibilního zápasu.
Zahrát zápas ve dvou a v osmi izolovaných prohlížečích, včetně kombinace PC a skutečného telefonu; posoudit čitelnost tváří, zbraní a kukly i ze stran.
Zkoušet RTT přibližně 50/100/200 ms, kolísání, omezenou propustnost a krátké výpadky. Socket.IO používá TCP, proto při ztrátách sledovat i zpoždění celé fronty. Po návratu se nesmí přehrát staré útoky.
Zátěž: nejméně jeden 30minutový běh s osmi lidmi/simulovanými klienty, maximem navržené AI a častými výbuchy, následně několik souběžných místností. Měřit p95 délky serverového kroku vůči rozpočtu 16,7 ms, event-loop zpoždění, RAM, přenos a počet čekajících zpráv. Nastavit kapacitní limit s rezervou pro ostatní hry na stejném serveru.
Zkontrolovat HTTPS, skutečný WSS upgrade, polling fallback, odmítnutí cizího originu, hlavičky cache, dostupnost singleplayeru při vypnutém backendu a restart po rebootu.
Provést test neúspěšného releasu a rollbacku včetně kompatibility klienta se serverem. Ověřit dostupnost rat.rozd.cz a ostatních stávajících webů.
Hráč si v menu vybere multiplayer, založí nebo najde lobby, zvolí unikátní obličej a bez zadávání jména odehraje s dalšími 1–7 lidmi společný deathmatch. Všichni začínají s pistolí na různých bezpečných okrajích mapy, mohou bojovat mezi sebou, získávat výzbroj a potkávat jednotně maskované AI se známým chováním. Shodují se na zásazích, smrti, kořisti i výsledcích. Po zápase mohou hrát odvetu. Původní singleplayer včetně offline hraní zůstává funkční. Nasazení používá infrastrukturu podle krysi-jmena, vlastní izolovaný backend a ověřený postup návratu předchozí verze.