Pri la reto-modelo en ludoj por komencantoj

Pri la reto-modelo en ludoj por komencantoj
Dum la lastaj du semajnoj mi laboris pri la reta motoro por mia ludo. Antaŭ tio, mi tute nenion sciis pri retoj en ludoj, do mi legis multajn artikolojn kaj faris multajn eksperimentojn por kompreni ĉiujn konceptojn kaj povi skribi mian propran retan motoron.

En ĉi tiu gvidilo, mi ŝatus dividi kun vi la diversajn konceptojn, kiujn vi bezonas lerni antaŭ ol verki vian propran ludmaŝinon, kaj ankaŭ la plej bonajn rimedojn kaj artikolojn por lerni ilin.

Ĝenerale, ekzistas du ĉefaj specoj de retaj arkitekturoj: kunulo-al-kunulo kaj kliento-servilo. En kunul-al-kunula (p2p) arkitekturo, datenoj estas transdonitaj inter iuj paroj de ligitaj ludantoj, dum en klient-servila arkitekturo, datenoj estas transdonitaj nur inter ludantoj kaj la servilo.

Kvankam kunul-al-kunula arkitekturo daŭre estas uzita en kelkaj ludoj, kliento-servilo estas la normo: ĝi estas pli facile efektivigi, postulas pli malgrandan kanallarĝon, kaj faciligas protekti kontraŭ trompado. Tial, en ĉi tiu lernilo ni koncentriĝos pri la kliento-servila arkitekturo.

Precipe ni plej interesiĝas pri aŭtoritataj serviloj: en tiaj sistemoj, la servilo ĉiam pravas. Ekzemple, se ludanto opinias, ke li estas ĉe koordinatoj (10, 5), kaj la servilo diras al li, ke li estas ĉe (5, 3), tiam la kliento devas anstataŭigi ĝian pozicion kun tiu raportita de la servilo, kaj ne malvirto. inverse. Uzado de aŭtoritataj serviloj faciligas identigi trompulojn.

Retaj videoludadsistemoj havas tri ĉefajn komponentojn:

  • Transportprotokolo: kiel datumoj estas transdonitaj inter klientoj kaj servilo.
  • Aplika protokolo: kio estas transdonita de klientoj al la servilo kaj de la servilo al klientoj kaj en kiu formato.
  • Aplika logiko: kiel la transdonitaj datumoj estas uzataj por ĝisdatigi la staton de la klientoj kaj servilo.

Estas tre grave kompreni la rolon de ĉiu parto kaj la defiojn asociitajn kun ili.

Transportprotokolo

La unua paŝo estas elekti protokolon por transporti datumojn inter la servilo kaj klientoj. Estas du Interretaj protokoloj por ĉi tio: TCP и UDP. Sed vi povas krei vian propran transportprotokolon surbaze de unu el ili aŭ uzi bibliotekon kiu uzas ilin.

Komparo de TCP kaj UDP

Kaj TCP kaj UDP estas bazitaj sur IP. IP permesas pakaĵeton esti transdonita de fonto al ricevanto, sed ne garantias, ke la sendita pakaĵeto pli aŭ malpli frue atingos la ricevanton, ke ĝi atingos ĝin almenaŭ unufoje, kaj ke la sekvenco de pakaĵetoj alvenos en la ĝusta. ordo. Plie, pako povas enhavi nur limigitan kvanton da datumoj, donitaj de la valoro MTU.

UDP estas nur maldika tavolo supre de IP. Tial ĝi havas la samajn limojn. Kontraste, TCP havas multajn funkciojn. Ĝi disponigas fidindan, bonordan ligon inter du nodoj kun erarkontrolado. Tial, TCP estas tre oportuna kaj estas uzata en multaj aliaj protokoloj, ekz. HTTP, FTP и SMTP. Sed ĉiuj ĉi tiuj funkcioj havas prezon: prokrasti.

Por kompreni kial ĉi tiuj funkcioj povas kaŭzi latentecon, ni devas kompreni kiel funkcias TCP. Kiam senda nodo elsendas pakaĵeton al riceva nodo, ĝi atendas ricevi agnoskon (ACK). Se post certa tempo ĝi ne ricevas ĝin (ĉar la pako aŭ agnosko estis perdita, aŭ pro alia kialo), tiam ĝi resendas la pakaĵeton. Plie, TCP garantias, ke pakaĵoj estas ricevitaj en la ĝusta ordo, do ĝis la perdita pako estas ricevita, ĉiuj aliaj pakoj ne povas esti procesitaj, eĉ se ili jam estis ricevitaj de la ricevanta gastiganto.

Sed kiel vi verŝajne povas imagi, latenco en plurludantaj ludoj estas tre grava, precipe en agoplenaj ĝenroj kiel FPS. Tial multaj ludoj uzas UDP kun sia propra protokolo.

Indiĝena UDP-bazita protokolo povas esti pli efika ol TCP pro diversaj kialoj. Ekzemple, ĝi povas marki kelkajn pakaĵojn kiel fidindajn kaj aliajn kiel nefidindajn. Tial ne gravas ĉu la nefidinda pako atingas la ricevanton. Aŭ ĝi povas prilabori plurajn datumfluojn por ke perdita pako en unu rivereto ne malrapidigas la ceterajn fluojn. Ekzemple, povus esti fadeno por ludanto-enigo kaj alia fadeno por babilmesaĝoj. Se babilmesaĝo kiu ne urĝas estas perdita, ĝi ne malrapidigos enigon kiu estas urĝa. Aŭ proprieta protokolo povus efektivigi fidindecon alimaniere ol TCP por esti pli efika en videoluda medio.

Do, se TCP tiom suĉas, tiam ni kreos nian propran transportprotokolon bazitan sur UDP?

Ĝi estas iom pli komplika. Kvankam TCP estas preskaŭ suboptimuma por videoludaj retsistemoj, ĝi povas funkcii sufiĉe bone por via specifa ludo kaj ŝpari al vi valoran tempon. Ekzemple, latencia eble ne estas problemo por turno-bazita ludo aŭ ludo kiu povas esti ludita nur sur LAN-retoj, kie latencia kaj paka perdo estas multe pli malaltaj ol en la Interreto.

Multaj sukcesaj ludoj, inkluzive de World of Warcraft, Minecraft kaj Terraria, uzas TCP. Tamen, plej multaj FPS uzas siajn proprajn UDP-bazitajn protokolojn, do ni parolos pli pri ili sube.

Se vi decidas uzi TCP, certigu, ke ĝi estas malŝaltita La algoritmo de Nagle, ĉar ĝi bufras pakaĵojn antaŭ sendado, kio signifas, ke ĝi pliigas latencian.

Por lerni pli pri la diferencoj inter UDP kaj TCP en la kunteksto de plurludantaj ludoj, vi povas legi la artikolon de Glenn Fiedler. UDP vs. TCP.

Propra protokolo

Do vi volas krei vian propran transportprotokolon, sed ne scias kie komenci? Vi bonŝancas ĉar Glenn Fiedler skribis du mirindajn artikolojn pri tio. Vi trovos multajn inteligentajn pensojn en ili.

La unua artikolo Retoj por Ludaj Programistoj 2008, pli facila ol la dua, Konstruante Ludan Retan Protokolon 2016. Mi rekomendas, ke vi komencu per la pli malnova.

Notu, ke Glenn Fiedler estas granda propagandanto de uzado de kutima protokolo bazita sur UDP. Kaj leginte liajn artikolojn, vi verŝajne adoptos lian opinion, ke TCP havas gravajn mankojn en videoludoj, kaj vi volos efektivigi vian propran protokolon.

Sed se vi estas nova pri retoj, faru al vi favoron kaj uzu TCP aŭ bibliotekon. Por sukcese efektivigi vian propran transportprotokolon, vi devas lerni multon antaŭe.

Retaj bibliotekoj

Se vi bezonas ion pli efikan ol TCP, sed ne volas trapasi la ĝenon efektivigi vian propran protokolon kaj eniri multajn detalojn, vi povas uzi interkonektan bibliotekon. Estas multaj el ili:

Mi ne provis ilin ĉiujn, sed mi preferas ENet ĉar ĝi estas facile uzebla kaj fidinda. Krome, ĝi havas klaran dokumentadon kaj lernilon por komencantoj.

Transportprotokolo: Konkludo

Por resumi: ekzistas du ĉefaj transportprotokoloj: TCP kaj UDP. TCP havas multajn utilajn funkciojn: fidindeco, konservado de paka ordo, erardetekto. UDP ne havas ĉion ĉi, sed TCP laŭ sia naturo pliigis latentecon, kio estas neakceptebla por iuj ludoj. Tio estas, por certigi malaltan latentecon, vi povas krei vian propran protokolon bazitan sur UDP aŭ uzi bibliotekon kiu efektivigas transportprotokolon sur UDP kaj estas adaptita por multludantaj videoludoj.

La elekto inter TCP, UDP kaj la biblioteko dependas de pluraj faktoroj. Unue, de la bezonoj de la ludo: ĉu ĝi bezonas malaltan latentecon? Due, de la aplikaj protokolaj postuloj: ĉu ĝi bezonas fidindan protokolon? Kiel ni vidos en la sekva parto, eblas krei aplikan protokolon por kiu nefidinda protokolo sufiĉe taŭgas. Fine, vi ankaŭ devas konsideri la sperton de la programisto de retaj motoroj.

Mi havas du konsilojn:

  • Abstraktu la transportprotokolon de la resto de la aplikaĵo kiel eble plej multe por ke ĝi povas esti facile anstataŭigita sen reverki la tutan kodon.
  • Ne trooptimumigu. Se vi ne estas fakulo pri retoj kaj ne certas ĉu vi bezonas kutiman UDP-bazitan transportprotokolon, vi povas komenci per TCP aŭ biblioteko, kiu provizas fidindecon, kaj poste testi kaj mezuri rendimenton. Se aperas problemoj kaj vi certas, ke la kaŭzo estas la transportprotokolo, tiam eble estas tempo krei vian propran transportprotokolon.

Fine de ĉi tiu parto, mi rekomendas vin legi Enkonduko al Plurludanta Ludo-Programado de Brian Hook, kiu kovras multajn el la temoj ĉi tie diskutitaj.

Aplika protokolo

Nun kiam ni povas interŝanĝi datumojn inter klientoj kaj la servilo, ni devas decidi kiajn datumojn transloki kaj en kiu formato.

La klasika skemo estas, ke klientoj sendas enigaĵon aŭ agojn al la servilo, kaj la servilo sendas la nunan ludstaton al la klientoj.

La servilo sendas ne la plenan staton, sed filtritan ŝtaton kun estaĵoj kiuj situas proksime de la ludanto. Li faras tion pro tri kialoj. Unue, la kompleta stato povas esti tro granda por esti elsendita ĉe altfrekvenco. Due, klientoj ĉefe interesiĝas pri vidaj kaj aŭdaj datumoj, ĉar plejparto de la ludlogiko estas simulita sur la ludservilo. Trie, en iuj ludoj la ludanto ne bezonas scii certajn datumojn, ekzemple la pozicion de la malamiko sur la alia flanko de la mapo, alie li povas flari pakaĵetojn kaj scii precize kien moviĝi por mortigi lin.

Seriigo

La unua paŝo estas konverti la datumojn, kiujn ni volas sendi (enigo aŭ ludstato) al formato taŭga por transdono. Ĉi tiu procezo nomiĝas seriigo.

La penso, kiu tuj venas al la menso, estas uzi homlegeblan formaton, kiel JSON aŭ XML. Sed ĉi tio estos tute neefika kaj malŝparos la plej grandan parton de la kanalo.

Oni rekomendas uzi la binaran formaton anstataŭe, kiu estas multe pli kompakta. Tio estas, la pakaĵoj enhavos nur kelkajn bajtojn. Estas problemo por konsideri ĉi tie bajta ordo, kiu povas malsami en malsamaj komputiloj.

Por seriigi datumojn, vi povas uzi bibliotekon, ekzemple:

Nur certigu, ke la biblioteko kreas porteblajn arkivojn kaj zorgas pri endianeco.

Alternativa solvo estas efektivigi ĝin mem; ĝi ne estas precipe malfacila, precipe se vi uzas datumcentran aliron al via kodo. Krome, ĝi permesos al vi fari optimumojn, kiuj ne ĉiam eblas kiam vi uzas la bibliotekon.

Glenn Fiedler skribis du artikolojn pri seriigo: Legado kaj Skribo Pakoj и Serigaj Strategioj.

Kunpremo

La kvanto de datumoj transdonitaj inter klientoj kaj servilo estas limigita de la bendolarĝo de la kanalo. Datuma kunpremado permesos al vi translokigi pli da datumoj en ĉiu momentfoto, pliigi la ĝisdatigan frekvencon aŭ simple redukti la kanalajn postulojn.

Bit-pakaĵo

La unua tekniko estas iom pakado. Ĝi konsistas en uzi ĝuste la nombron da bitoj kiuj estas necesaj por priskribi la deziratan valoron. Ekzemple, se vi havas enumon, kiu povas havi 16 malsamajn valorojn, tiam anstataŭ tuta bajto (8 bitoj), vi povas uzi nur 4 bitojn.

Glenn Fiedler klarigas kiel efektivigi tion en la dua parto de la artikolo Legado kaj Skribo Pakoj.

Bita pakado funkcias precipe bone kun specimenigo, kiu estos la temo de la sekva sekcio.

Specimenado

Specimenado estas perda kunprema tekniko, kiu uzas nur subaron de eblaj valoroj por kodi valoron. La plej facila maniero efektivigi diskretigon estas per rondigo de glitkomaj nombroj.

Glenn Fiedler (denove!) montras kiel praktiki specimenon en sia artikolo Momentfoto-kunpremado.

Kunpremaj algoritmoj

La sekva tekniko estos senperdaj kunpremaj algoritmoj.

Jen, laŭ mi, la tri plej interesaj algoritmoj, kiujn vi bezonas scii:

  • Huffman-kodigo kun antaŭkomputita kodo, kiu estas ekstreme rapida kaj povas produkti bonajn rezultojn. Ĝi kutimis kunpremi pakaĵetojn en la interkonekta motoro Quake3.
  • zlib estas ĝeneraluzebla kunpremadalgoritmo kiu neniam pliigas la kvanton de datumoj. Kiel vi povas vidi tie, ĝi estis uzata en diversaj aplikoj. Ĝi povas esti redunda por ĝisdatigi ŝtatojn. Sed ĝi povas esti utila se vi bezonas sendi valoraĵojn, longajn tekstojn aŭ terenon al klientoj de la servilo.
  • Kopiante kurlongojn - Ĉi tio verŝajne estas la plej simpla kunprema algoritmo, sed ĝi estas tre efika por certaj specoj de datumoj, kaj povas esti uzata kiel antaŭprilabora paŝo antaŭ zlib. Ĝi estas precipe taŭga por kunpremi terenon kunmetitan de kaheloj aŭ vokseloj en kiuj multaj apudaj elementoj estas ripetitaj.

Delta kunpremo

La lasta kunprema tekniko estas delta kunpremo. Ĝi konsistas en tio, ke nur la diferencoj inter la nuna ludstato kaj la lasta stato ricevita de la kliento estas transdonitaj.

Ĝi unue estis uzita en la Quake3 retmotoro. Jen du artikoloj klarigante kiel uzi ĝin:

Glenn Fiedler ankaŭ uzis ĝin en la dua parto de sia artikolo Momentfoto-kunpremado.

Ĉifrado

Krome, vi eble bezonos ĉifri la translokigon de informoj inter klientoj kaj la servilo. Estas pluraj kialoj por ĉi tio:

  • privateco/konfidenco: mesaĝoj povas esti legitaj nur de la ricevanto, kaj neniu alia persono flaranta la reton povos legi ilin.
  • aŭtentigo: homo, kiu volas ludi la rolon de ludanto, devas scii sian ŝlosilon.
  • Preventado de trompoj: estos multe pli malfacile por malicaj ludantoj krei siajn proprajn tromppakaĵojn, ili devos reprodukti la ĉifradan skemon kaj trovi la ŝlosilon (kiu ŝanĝiĝas kun ĉiu konekto).

Mi forte rekomendas uzi bibliotekon por tio. Mi sugestas uzi libsodio, ĉar ĝi estas precipe simpla kaj havas bonegajn lernilojn. Aparte interesa estas la lernilo pri ŝlosilŝanĝo, kiu ebligas al vi generi novajn ŝlosilojn kun ĉiu nova konekto.

Aplika Protokolo: Konkludo

Ĉi tio finas nian aplikan protokolon. Mi kredas, ke kunpremado estas tute nedeviga kaj la decido uzi ĝin dependas nur de la ludo kaj la bezonata larĝa bando. Ĉifrado, laŭ mi, estas deviga, sed en la unua prototipo oni povas malhavi ĝin.

Aplika logiko

Ni nun povas ĝisdatigi staton en la kliento, sed eble renkontos problemojn pri latenteco. La ludanto, post kompletigado de la enigo, devas atendi ke la ludstato ĝisdatiĝos de la servilo por vidi kian efikon ĝi havis sur la mondo.

Cetere, inter du ŝtataj ĝisdatigoj, la mondo estas tute senmova. Se la ŝtata ĝisdatiga indico estas malalta, tiam la movadoj estos tre saciaj.

Estas pluraj teknikoj por redukti la efikon de ĉi tiu problemo, kaj mi kovros ilin en la sekva sekcio.

Latencaj Glatigaj Teknikoj

Ĉiuj teknikoj priskribitaj en ĉi tiu sekcio estas diskutitaj detale en la serio Rapida Plurludanto Gabriel Gambetta. Mi tre rekomendas legi ĉi tiun bonegan serion de artikoloj. Ĝi ankaŭ inkluzivas interagan demonstraĵon, kiu ebligas al vi vidi kiel ĉi tiuj teknikoj funkcias praktike.

La unua tekniko estas apliki la enigrezulton rekte sen atendi respondon de la servilo. Ĝi nomiĝas klientflanka prognozo. Tamen, kiam la kliento ricevas ĝisdatigon de la servilo, ĝi devas kontroli, ke ĝia antaŭdiro estis ĝusta. Se ĉi tio ne estas la kazo, tiam li nur bezonas ŝanĝi sian staton laŭ tio, kion li ricevis de la servilo, ĉar la servilo estas aŭtoritata. Tiu tekniko unue estis uzita en Quake. Vi povas legi pli pri ĝi en la artikolo Revizio de la kodo de Quake Engine Fabien Sanglars [traduko sur Habré].

La dua aro de teknikoj estas uzata por glatigi la movadon de aliaj estaĵoj inter du ŝtataj ĝisdatigoj. Estas du manieroj solvi ĉi tiun problemon: interpolado kaj eksterpolado. En la kazo de interpolado, la lastaj du statoj estas prenitaj kaj la transiro de unu al la alia estas montrita. Ĝia malavantaĝo estas, ke ĝi kaŭzas malgrandan prokraston ĉar la kliento ĉiam vidas tion, kio okazis en la pasinteco. Eksterpolado temas pri antaŭdiro kie entoj devus esti nun bazitaj sur la lasta stato ricevita de la kliento. Ĝia malavantaĝo estas, ke se la ento tute ŝanĝas la direkton de movado, tiam estos granda eraro inter la prognozo kaj la reala pozicio.

La plej nova, plej altnivela tekniko utila nur en FPS estas malfrua kompenso. Kiam vi uzas malfruan kompenson, la servilo konsideras la malfruojn de la kliento kiam ĝi pafas al la celo. Ekzemple, se ludanto elfaris kappafon sur ilia ekrano, sed en realeco ilia celo estis en malsama loko pro prokrasto, tiam estus maljuste nei al la ludanto la rajton mortigi pro prokrasto. Tial, la servilo rebobenas tempon reen al la momento kiam la ludanto pafis por simuli kion la ludanto vidis sur ilia ekrano kaj kontroli por kolizio inter ilia pafo kaj la celo.

Glenn Fiedler (kiel ĉiam!) skribis artikolon en 2004 Reta fiziko (2004), en kiu li amorigis la fundamenton por sinkronigado de fiziksimuladoj inter servilo kaj kliento. En 2014 li verkis novan serion de artikoloj Reta Fiziko, kiu priskribis aliajn teknikojn por sinkronigado de fiziksimuladoj.

Ekzistas ankaŭ du artikoloj en la Valve-vikio, Fonto Plurludanta Reto и Latentecaj Kompensaj Metodoj en Kliento/Servilo En-luda Protokolo-Dezajno kaj Optimumigo kiuj konsideras kompenson por prokrastoj.

Malhelpi trompadon

Estas du ĉefaj teknikoj por malhelpi trompadon.

Unue: plimalfaciligante por trompantoj sendi malicajn pakaĵojn. Kiel menciite supre, bona maniero efektivigi ĉi tion estas ĉifrado.

Due: aŭtoritata servilo devus ricevi nur komandojn/enigaĵon/agojn. La kliento ne devus povi ŝanĝi staton sur la servilo krom sendante enigaĵon. Tiam, ĉiufoje kiam la servilo ricevas enigon, ĝi devas kontroli ĉu ĝi validas antaŭ ol uzi ĝin.

Aplika logiko: konkludo

Mi rekomendas, ke vi efektivigu manieron simuli altajn latentecojn kaj malaltajn refreŝigajn indicojn por ke vi povu testi la konduton de via ludo en malbonaj kondiĉoj, eĉ kiam la kliento kaj servilo funkcias per la sama komputilo. Ĉi tio multe simpligos la efektivigon de prokrastigaj teknikoj.

Aliaj Helpemaj Rimedoj

Se vi ŝatus esplori aliajn rimedojn pri retaj modeloj, vi povas trovi ilin ĉi tie:

fonto: www.habr.com

Aldoni komenton