O síťových modelech ve hrách pro začátečníky

O síťových modelech ve hrách pro začátečníky
Poslední dva týdny jsem pracoval na síťovém enginu pro svou hru. Předtím jsem o síťových technologiích ve hrách vůbec nic nevěděl, proto jsem přečetl řadu článků a provedl spoustu experimentů, abych si ujasnil všechny koncepty a mohl napsat vlastní síťový engine.

V této příručce bych se chtěl podělit o různé koncepty, které byste měli prostudovat před psaním vlastního herního enginu, a také o nejlepší zdroje a články pro jejich studium.

Celkově existují dva hlavní typy síťových architektur: peer-to-peer a klient-server. V architektuře peer-to-peer (p2p) se data přenášejí mezi libovolnými dvojicemi připojených hráčů, zatímco v architektuře klient-server se data přenášejí pouze mezi hráči a serverem.

I když se architektura peer-to-peer stále používá v některých hrách, standardem je architektura klient-server: je jednodušší na implementaci, vyžaduje menší šířku pásma a usnadňuje ochranu proti podvádění. Proto se v této příručce zaměříme na architekturu klient-server.

Konkrétně se zaměřujeme na autoritativní servery: v takových systémech má server vždy pravdu. Například, pokud si hráč myslí, že je na souřadnicích (10, 5), ale server mu říká, že je na (5, 3), musí klient nahradit svou pozici tou, kterou server sděluje, a ne naopak. Použití autoritativních serverů usnadňuje rozpoznávání podvodníků.

V herních síťových systémech existují tři hlavní komponenty:

  • Transportní protokol: jak se data přenášejí mezi klienty a serverem.
  • Aplikační protokol: co se přenáší od klientů k serveru a ze serveru k klientům a v jakém formátu.
  • Logika aplikace: jak se přenášená data používají k aktualizaci stavu klientů a serveru.

Je velmi důležité pochopit roli každé části a s nimi spojené obtíže.

Transportní protokol

Prvním krokem je výběr protokolu pro přenos dat mezi serverem a klienty. K tomu existují dva internetové protokoly: TCP a UDP. Ale můžete také vytvořit vlastní transportní protokol na základě jednoho z nich nebo použít knihovnu, ve které se používají.

Srovnání TCP a UDP

Jak TCP, tak UDP jsou založeny na IP. IP umožňuje přenos paketu od zdroje k příjemci, avšak neposkytuje záruku, že odeslaný paket se dříve nebo později dostane k příjemci, že dorazí alespoň jednou a že sekvence paketů přijde ve správném pořadí. Navíc paket může obsahovat pouze omezenou velikost dat, určenou velikostí MTU.

UDP je pouze tenká vrstva nad IP. Proto má stejné omezení. Na rozdíl od něj má TCP mnoho funkcí. Zajišťuje spolehlivé pořadí spojení mezi dvěma uzly s kontrolou chyb. Proto je TCP velmi pohodlné a používá se v mnoha dalších protokolech, například v HTTP, FTP a , včetně zpracování situací s duplicitními message-id;. Ale všechny tyto funkce mají svou cenu: zpoždění.

Abychom pochopili, proč tyto funkce mohou způsobovat zpoždění, musíme se podívat, jak TCP funguje. Když odesílající uzel posílá paket přijímajícímu uzlu, očekává, že obdrží potvrzení (ACK). Pokud jej po určité době nedostane (protože byl paket nebo potvrzení ztraceno, nebo z jiných důvodů), odešle paket znovu. Navíc TCP zaručuje, že pakety budou přijímány ve správném pořadí, takže dokud nebude ztracený paket získán, všechny ostatní pakety nemohou být zpracovány, i když byly již přijaty přijímajícím uzlem.

Jak asi chápete, zpoždění ve více hráčských hrách je velmi důležité, zejména v tak aktivních žánrech, jako je FPS. Proto mnoho her používá UDP s vlastním protokolem.

Vlastní protokol založený na UDP může být efektivnější než TCP z různých důvodů. Například může označovat některé pakety jako spolehlivé a jiné jako nespolehlivé. Proto ho nezajímá, zda se nespolehlivý paket dostal k příjemci. Nebo může zpracovávat více datových toků, aby ztracený paket v jednom toku nezpomalil ostatní toky. Například může existovat tok pro vstup hráče a další tok pro chatové zprávy. Pokud se chatová zpráva, která není naléhavými daty, ztratí, nezpomaluje to zpracování vstupu, které je naléhavé. Nebo může vlastní protokol implementovat spolehlivost jiným způsobem než v TCP, aby byl efektivnější v podmínkách videoher.

Takže, pokud je TCP tak hrozné, budeme vytvářet vlastní přenosový protokol na základě UDP?

Všechno je trochu složitější. I když je TCP téměř suboptimální pro herní síťové systémy, může fungovat docela dobře konkrétně ve vaší hře a ušetřit vám cenný čas. Například zpoždění nemusí být problémem pro tahovou hru nebo hru, která se hraje pouze v LAN sítích, kde je zpoždění a ztráta paketů mnohem menší než na internetu.

V mnoha úspěšných hrách, včetně World of Warcraft, Minecraft a Terraria, se používá TCP. Většina FPS však používá vlastní protokoly na bázi UDP, takže se o nich níže podrobněji zmíníme.

Pokud se rozhodnete použít TCP, ujistěte se, že je vypnuto Nagleův algoritmus, protože ten ukládá pakety před odesláním, což znamená, že zvyšuje zpoždění.

Chcete-li se dozvědět více o rozdílech mezi UDP a TCP v kontextu více hráčských her, můžete si přečíst článek Glenna Fiedlera UDP vs. TCP.

Vlastní protokol

Takže se chcete vytvořit vlastní přenosový protokol, ale nevíte, kde začít? Měli jste štěstí, protože Glenn Fiedler o tom napsal dvě úžasné články. Najdete v nich spoustu chytrých myšlenek.

První článek, Networking for Game Programmers z roku 2008 je jednodušší než druhý, Building A Game Network Protocol z roku 2016. Doporučuji začít s tím starším.

Mějte na paměti, že Glenn Fiedler je velkým zastáncem použití vlastního protokolu na bázi UDP. A po přečtení jeho článků si určitě osvojíte jeho názor, že TCP má ve videohrách vážné nevýhody, a budete chtít realizovat vlastní protokol.

Ale pokud jste nováček v práci se sítěmi, udělejte si laskavost a použijte TCP nebo knihovnu. Pro úspěšnou realizaci vlastního přenosového protokolu se musíte nejprve mnohému naučit.

Síťové knihovny

Pokud potřebujete něco efektivnějšího než TCP, ale nechcete se zabývat realizací vlastního protokolu a ponořit se do mnoha podrobností, můžete použít síťovou knihovnu. Existuje jich spousta:

Všechny jsem nezkoušel, ale dám přednost ENet, protože je jednoduchý na použití a spolehlivý. Navíc má přehlednou dokumentaci a tutoriál pro začátečníky.

Transportní protokol: závěr

Shrneme to takto: existují dva základní transportní protokoly: TCP a UDP. TCP má mnoho užitečných vlastností: spolehlivost, zachování pořadí paketů, detekce chyb. To vše u UDP chybí, ale TCP má přirozeně vyšší latenci, což je nepřípustné pro některé hry. To znamená, že pro zajištění nízké latence můžete vytvořit vlastní protokol založený na UDP nebo použít knihovnu, která implementuje transportní protokol na UDP a je přizpůsobena pro vícerozměrné videohry.

Volba mezi TCP, UDP a knihovnou závisí na několika faktorech. Za prvé, na potřebách hry: potřebuje nízké latency? Za druhé, na požadavcích protokolu aplikace: potřebuje spolehlivý protokol? Jak uvidíme v následující části, lze vytvořit aplikační protokol, pro který je naprosto vhodný nespolehlivý protokol. Nakonec je také třeba vzít v úvahu zkušenosti vývojáře síťového enginu.

Mám dva tipy:

  • Maximálně abstrakci transportního protokolu od ostatních částí aplikace, aby ho bylo možné snadno vyměnit, aniž byste museli přepisovat celý kód.
  • Nevěnujte se předčasné optimalizaci. Pokud nejste odborník na sítě a nejste si jisti, zda potřebujete vlastní transportní protokol založený na UDP, můžete začít s TCP nebo knihovnou, která zajišťuje spolehlivost, a poté otestovat a změřit výkon. Pokud nastanou problémy a jste si jisti, že problém spočívá v transportním protokolu, může být čas vytvořit vlastní transportní protokol.

Na závěr této části doporučuji přečíst si Introduction to Multiplayer Game Programming Bryana Hooka, v němž jsou probrána mnohá témata diskutovaná zde.

Aplikační protokol

Teď, když můžeme vyměňovat data mezi klienty a serverem, musíme rozhodnout, jaká data přesně posílat a v jakém formátu.

Klasické schéma spočívá v tom, že klienti posílají serveru vstupy nebo akce a server klientům posílá aktuální herní stav.

Server odesílá neúplný, ale filtrovaný stav s entitami, které se nacházejí blízko hráče. Dělá to ze tří důvodů. Za prvé, úplný stav může být příliš velký pro přenos s vysokou frekvencí. Za druhé, klienty většinou zajímají vizuální a audio data, protože většina herní logiky se simuluje na herním serveru. Za třetí, v některých hrách by hráč neměl znát určité údaje, například polohu protivníka na druhém konci mapy, protože by mohl odposlouchávat pakety a přesně vědět, kam se má pohybovat, aby ho zabil.

Serializace

Prvním krokem bude převod dat, která chceme odeslat (vstup nebo herní stav), do vhodného formátu pro přenos. Tento proces se nazývá serializace.

Ihned mě napadá, že bychom mohli použít lidsky čitelný formát, jako JSON nebo XML. To by však bylo zcela neefektivní a zbytečně by zabralo velkou část kanálu.

Místo toho se doporučuje použít binární formát, který je mnohem kompaktnější. To znamená, že pakety budou obsahovat pouze několik bytů. Je třeba vzít v úvahu problém pořadí bytů, které se může lišit na různých počítačích.

Pro serializaci dat lze použít knihovnu, například:

Jen se ujistěte, že knihovna vytváří přenosné archivy a postará se o pořadí bytů.

Alternativním řešením může být vlastní implementace, která není příliš složitá, zejména pokud v kódu používáte datově orientovaný přístup. Kromě toho vám umožní provádět optimalizace, které nejsou vždy možné při používání knihovny.

Glenn Fiddler napsal o serializaci dva články: Čtení a psaní paketů a Strategie serializace.

Kompresce

Množství dat přenášených mezi klienty a serverem je omezeno šířkou pásma kanálu. Komprese dat umožní přenášet v každém snímku více dat, zvýšit frekvenci aktualizací nebo jednoduše snížit nároky na kanál.

Bitové balení

První technika je bitové balení. Spočívá v použití přesně takového množství bitů, které je potřeba k popisu požadované veličiny. Například, pokud máte výčet, který může mít 16 různých hodnot, můžete místo celého bajtu (8 bitů) použít pouze 4 bity.

Glenn Fidler vysvětluje, jak to realizovat, ve druhé části článku. Čtení a psaní paketů.

Bitové balení funguje obzvlášť dobře s diskrétními daty, která budou tématem následující části.

Diskrétní data

Diskrétní data je technika ztrátového komprese, která spočívá v použití pouze podmnožiny možných hodnot k kódování veličiny. Nejsnazší je realizovat diskrétní data zaokroulením čísel s plovoucí řádovou čárkou.

Glenn Fidler (opět!) ukazuje, jak aplikovat diskrétní data v praxi ve svém článku. Snapshot Compression.

Algoritmy komprese

Další technikou budou algoritmy bezztrátové komprese.

Tady jsou podle mého názoru tři nejzajímavější algoritmy, které je třeba znát:

  • Huffmanovo kódování s předem vypočítaným kódem, které je extrémně rychlé a může dávat dobré výsledky. Bylo použito pro kompresi paketů v síťovém enginu Quake3.
  • zlib je algoritmus obecné komprese, který nikdy nezvětšuje objem dat. Jak je vidět, zdebyl použit v mnoha oblastech použití. Pro aktualizace stavů může být nadbytečný. Ale může být také užitečný, pokud potřebujete posílat klientům z serveru zdroje, dlouhé texty nebo terén.
  • Kopírování dlouhých sekvencí je pravděpodobně nejjednodušší algoritmus komprese, ale je velmi efektivní pro určité typy dat a může být používán jako předzpracování před zlib. Je obzvlášť vhodný pro kompresi terénu složeného z dlaždic nebo voxelů, kde se mnoho sousedních prvků opakuje.

Delta-komprese

Poslední metodika komprese je delta-komprese. Spočívá v tom, že se předávají pouze rozdíly mezi aktuálním herním stavem a posledním stavem, který klient obdržel.

Poprvé byla aplikována v síťovém enginu Quake3. Tady jsou dva články, které vysvětlují způsob jejího použití:

Glen Fiddler ji také použil ve druhé části svého článku Snapshot Compression.

Šifrování

Kromě toho možná budete chtít šifrovat přenos informací mezi klienty a serverem. Je k tomu několik důvodů:

  • ochrana soukromí: zprávy mohou být přečteny pouze příjemcem a žádná jiná osoba provádějící sniffing sítě je nebude schopna přečíst.
  • autenticita: osoba, která chce vykonávat roli hráče, musí znát jeho klíč.
  • prevence podvodů: zlý hráči budou mít mnohem větší obtíže při vytváření vlastních paketů pro podvádění, budou muset reprodukovat schéma šifrování a najít klíč (který se mění při každém spojení).

Doporučuji pro tento účel použít knihovnu. Navrhuji používat libsodium, protože je obzvláště jednoduchá a má skvělé tutoriály. Zvlášť zajímavý je tutoriál o výměně klíčů, umožňující generovat nové klíče při každém novém spojení.

Aplikační protokol: shrnutí

Tímto skončíme s aplikačním protokolem. Domnívám se, že komprese není zcela nezbytná a rozhodnutí o jejím použití závisí pouze na hře a požadované propustnosti kanálu. Šifrování je podle mého názoru nezbytné, ale v první prototypu se bez něj lze obejít.

Logika aplikace

Nyní jsme schopni aktualizovat stav na klientovi, ale můžeme se setkat s problémy se zpožděním. Hráč, který provede vstup, musí čekat na aktualizaci stavu hry od serveru, aby viděl, jaký dopad měl na svět.

Navíc mezi dvěma aktualizacemi stavu je svět zcela statický. Pokud je frekvence aktualizací stavu nízká, bude pohyb velmi trhaný.

Existuje několik technik, které mohou pomoci snížit vliv tohoto problému, a v následující části o nich budu hovořit.

Techniky vyhlazování zpoždění

Všechny techniky popsané v této části jsou podrobně rozebrány v sérii Fast-Paced Multiplayer Gabriele Gambetty. Důrazně doporučuji přečíst si tuto vynikající sérii článků. Obsahuje také interaktivní demo, které ukazuje, jak tyto techniky fungují v praxi.

První technika spočívá v tom, že se přímo aplikuje výsledek vstupu, aniž by se čekalo na odpověď ze serveru. To se nazývá klientské predikceKdyž klient obdrží aktualizaci od serveru, musí se ujistit, že jeho předpověď byla správná. Pokud tomu tak není, musí jednoduše změnit svůj stav podle toho, co obdržel od serveru, protože server je autoritativní. Tato technika byla poprvé použita ve hře Quake. O této technice si můžete přečíst více v článku Recenze kódu Quake Engine Fabiena Sanglara [překlad na Habr].

Druhý soubor technik se používá k hladkému pohybu dalších entit mezi dvěma aktualizacemi stavu. Existují dva způsoby, jak tento problém vyřešit: interpolace a extrapolace. Při interpolaci se berou dva poslední stavy a ukazuje se přechod z jednoho do druhého. Její nevýhodou je, že způsobuje mírné zpoždění, protože klient vždy vidí to, co se dělo v minulosti. Extrapolace spočívá v předpokladu, kde by nyní měly být entity na základě posledního stavu, který klient obdržel. Její nevýhodou je, že pokud se entita zcela změní v pohybu, vznikne velká chyba mezi předpovědí a skutečnou polohou.

Poslední, nejpokročilejší technika, která je užitečná pouze v FPS — to je kompenzace zpoždění. Při použití kompenzace zpoždění server zohledňuje zpoždění klienta, když cílí. Například, pokud hráč provedl headshot na své obrazovce, ale ve skutečnosti byl jeho cíl kvůli zpoždění na jiném místě, bylo by nespravedlivé odepřít hráči právo na zabití kvůli zpoždění. Proto server vrátí čas zpět na okamžik, kdy hráč vystřelil, aby simuloval, co hráč viděl na své obrazovce, a zkontroloval kolizi mezi jeho výstřelem a cílem.

Glen Fiedler (jak jinak!) napsal v roce 2004 článek Síťová fyzika (2004), ve kterém položil základy synchronizace simulace fyziky mezi serverem a klientem. V roce 2014 napsal novou sérii článků Síťová fyzika, ve které popsal další techniky pro synchronizaci simulace fyziky.

Na wiki společnosti Valve jsou také dva články, Síťování multiplayeru Source a Metody kompenzace latence v návrhu a optimalizaci in-game protokolů klient/server , které se zabývají kompenzací zpoždění.

Prevence podvádění

Existují dvě hlavní techniky prevence podvádění.

První: ztěžování zasílání škodlivých paketů podvodníky. Jak bylo uvedeno výše, dobrým způsobem její realizace je šifrování.

Druhá: autoritativní server by měl přijímat pouze příkazy/vstupy/akce. Klient by neměl mít možnost měnit stav na serveru, kromě zasílání vstupu. Server by měl vždy při přijímání vstupu zkontrolovat jeho platnost před jeho použitím.

Logika aplikace: závěr

Doporučuji vám implementovat způsob simulace velkých zpoždění a nízkých frekvencí aktualizací, abyste mohli otestovat chování své hry za špatných podmínek, i když je klient a server spuštěn na jednom počítači. To velmi usnadní implementaci metod vyhlazování zpoždění.

Další užitečné zdroje

Pokud chcete prozkoumat další zdroje věnované síťovým modelům, můžete je najít zde:

  • Blog Glenna Fiedlera — stojí za to přečíst celý jeho blog, obsahuje mnoho skvělých článků. Zde shromážděny všechny články o síťových technologiích.
  • Awesome Game Networking autor M. Fatih MAR — je to podrobný seznam článků a videí o síťových enginech videoher.
  • V wiki subredditu r/gamedev také obsahuje mnoho užitečných odkazů.

Zdroj: habr.com

Koupit spolehlivý hosting pro webové stránky s ochranou proti DDoS, VPS VDS servery 🔥 Koupit spolehlivý hosting pro webové stránky s ochranou proti DDoS, VPS VDS servery | ProHoster