
Gjatë dy javëve të fundit kam punuar mbi motorin e rrjetit për lojën time. Para kësaj nuk dija pothuajse asgjë për teknologjitë e rrjetit në lojëra, ndaj lexova shumë artikuj dhe bëra shumë eksperimente që të kuptoja qartë të gjitha konceptet dhe të isha në gjendje të shkruaja motorin tim të rrjetit.
Në këtë udhëzues dua të ndaj me ju koncepte të ndryshme që duhet t’i mësoni para se të shkruani motorin tuaj të lojës, si edhe burimet dhe artikujt më të mirë për t’i studiuar ato.
Në përgjithësi ekzistojnë dy lloje kryesore të arkitekturave të rrjetit: peer-to-peer dhe klient-server. Në arkitekturën peer-to-peer (p2p), të dhënat transmetohen midis çdo çifti lojtarësh të lidhur, ndërsa në arkitekturën klient-server të dhënat transmetohen vetëm midis lojtarëve dhe serverit.
Edhe pse arkitektura peer-to-peer ende përdoret në disa lojëra, standardi është arkitektura klient-server: ajo është më e thjeshtë për t’u zbatuar, kërkon kanal me gjerësi më të vogël bande dhe e bën më të lehtë mbrojtjen nga mashtrimi. Prandaj, në këtë udhëzues do të përqendrohemi te arkitektura klient-server.
Në veçanti, më së shumti na interesojnë serverët autoritativë: në sisteme të tilla, serveri ka gjithmonë të drejtë. Për shembull, nëse lojtari mendon se ndodhet në koordinatat (10, 5), por serveri i thotë se është në (5, 3), atëherë klienti duhet ta zëvendësojë pozicionin e vet me atë që dërgon serveri, dhe jo anasjelltas. Përdorimi i serverëve autoritativë e bën më të lehtë zbulimin e mashtruesve.
Në sistemet e rrjetit për lojëra ka tre komponentë kryesorë:
- Protokolli i transportit: si transmetohen të dhënat midis klientëve dhe serverit.
- Protokolli i aplikacionit: çfarë transmetohet nga klientët te serveri dhe nga serveri te klientët, si edhe në çfarë formati.
- Logjika e aplikacionit: si përdoren të dhënat e transmetuara për të përditësuar gjendjen e klientëve dhe të serverit.
Është shumë e rëndësishme të kuptohet roli i secilës pjesë dhe vështirësitë që lidhen me to.
Protokolli i transportit
Hapi i parë është zgjedhja e një protokolli për transportimin e të dhënave midis serverit dhe klientëve. Për këtë ekzistojnë dy protokolle të Internetit: dhe . Por ju mund të krijoni edhe protokollin tuaj të transportit mbi bazën e njërës prej tyre ose të përdorni një bibliotekë që i shfrytëzon ato.
Krahasimi i TCP dhe UDP
Si TCP, ashtu edhe UDP bazohen në . IP lejon transmetimin e një pakete nga burimi te marrësi, por nuk garanton që paketa e dërguar do të arrijë te marrësi herët a vonë, që do të mbërrijë të paktën një herë dhe që sekuenca e paketave do të vijë në rendin e duhur. Për më tepër, paketa mund të përmbajë vetëm një sasi të kufizuar të dhënash, e përcaktuar nga madhësia e .
UDP është thjesht një shtresë e hollë mbi IP. Si rrjedhojë, ai ka të njëjtat kufizime. Ndryshe prej tij, TCP ka shumë veçori. Ai siguron një lidhje të besueshme dhe të renditur mes dy nyjeve, me kontroll gabimesh. Prandaj, TCP është shumë praktik dhe përdoret në shumë protokolle të tjera, për shembull në , dhe . Por të gjitha këto funksione kanë një çmim: .
Për të kuptuar pse këto funksione mund të shkaktojnë vonesë, duhet të kuptojmë si funksionon TCP. Kur nyja dërguese i transmeton një paketë nyjës marrëse, ajo pret të marrë një konfirmim (ACK). Nëse pas një kohe të caktuar nuk e merr atë (sepse paketa ose konfirmimi humbi, ose për ndonjë arsye tjetër), atëherë e dërgon paketën sërish. Për më tepër, TCP garanton marrjen e paketave në rendin e duhur, prandaj derisa paketa e humbur të merret, të gjitha paketat e tjera nuk mund të përpunohen, edhe nëse tashmë janë marrë nga nyja marrëse.
Por, siç e kuptoni me siguri, vonesa në lojërat shumëlojtarëshe është shumë e rëndësishme, sidomos në zhanre kaq dinamike si FPS. Pikërisht për këtë arsye shumë lojëra përdorin UDP me protokollin e tyre.
Një protokoll i vetin i bazuar në UDP mund të jetë më efikas se TCP për arsye të ndryshme. Për shembull, ai mund të shënojë disa paketa si të besueshme, ndërsa të tjerat si jo të besueshme. Prandaj, nuk ka rëndësi nëse një paketë jo e besueshme ka arritur te marrësi. Ose mund të përpunojë disa rrjedha të dhënash, në mënyrë që një paketë e humbur në një rrjedhë të mos ngadalësojë rrjedhat e tjera. Për shembull, mund të ketë një rrjedhë për hyrjen e lojtarit dhe një tjetër për mesazhet e bisedës. Nëse humbet një mesazh bisede, i cili nuk është e dhënë urgjente, ai nuk do të ngadalësojë reagimin e hyrjes, e cila është urgjente. Ose një protokoll i tillë mund ta zbatojë besueshmërinë ndryshe nga TCP, për të qenë më efikas në kushtet e videolojërave.
Pra, nëse TCP është kaq i dobët, a do të krijojmë protokollin tonë të transportit mbi bazën e UDP?
Gjithçka është pak më e ndërlikuar. Edhe pse TCP është pothuajse suboptimal për sistemet e rrjetit në lojëra, ai mund të funksionojë mjaft mirë pikërisht për lojën tuaj dhe t’ju kursejë kohë të çmuar. Për shembull, vonesa mund të mos jetë problem për një lojë me ture ose për një lojë që luhet vetëm në rrjete LAN, ku vonesat dhe humbja e paketave janë shumë më të ulëta se në internet.
Shumë lojëra të suksesshme, përfshirë World of Warcraft, Minecraft dhe Terraria, përdorin TCP. Megjithatë, në shumicën e FPS-ve përdoren protokolle të personalizuara të bazuara në UDP, ndaj më poshtë do t’i shqyrtojmë ato më në detaje.
Nëse vendosni të përdorni TCP, sigurohuni që të jetë çaktivizuar , sepse ai i ruan paketat në buffer para dërgimit dhe, si rrjedhojë, rrit vonesën.
Për të mësuar më shumë rreth dallimeve midis UDP dhe TCP në kontekstin e lojërave multiplayer, mund të lexoni artikullin e Glenn Fiedler .
Protokoll i personalizuar
Pra, doni të krijoni protokollin tuaj të transportit, por nuk dini nga t’ia nisni? Jeni me fat, sepse Glenn Fiedler ka shkruar dy artikuj të shkëlqyer për këtë temë. Në to do të gjeni shumë ide të zgjuara.
Artikulli i parë, i vitit 2008, është më i thjeshtë se i dyti, i vitit 2016. Ju rekomandoj të filloni me më të vjetrin.
Mbani parasysh se Glenn Fiedler është një mbështetës i madh i përdorimit të një protokolli të personalizuar mbi bazën e UDP. Dhe pasi të lexoni artikujt e tij, ka shumë gjasa të ndani mendimin e tij se TCP ka mangësi serioze në videolojëra dhe të dëshironi të zbatoni protokollin tuaj.
Por nëse jeni fillestar në rrjete, bëjini vetes një nder dhe përdorni TCP ose një bibliotekë. Për të zbatuar me sukses një protokoll transporti të personalizuar, duhet të mësoni shumë gjëra paraprakisht.
Biblioteka rrjeti
Nëse ju duhet diçka më efikase se TCP, por nuk doni të merreni me zbatimin e protokollit tuaj dhe të hyni në shumë hollësi, mund të përdorni një bibliotekë rrjeti. Ka shumë të tilla:
- e Glenn Fiedler
- , e cila nuk mbështetet më, por forku i saj duket se është ende aktiv.
- është një bibliotekë e krijuar për FPS multiplayer
- e kompanisë Valve
Nuk i kam provuar të gjitha, por preferoj ENet, sepse është e thjeshtë për t’u përdorur dhe e besueshme. Përveç kësaj, ajo ka dokumentacion të qartë dhe një tutorial për fillestarët.
Protokolli i transportit: përfundim
Si përfundim, ekzistojnë dy protokolle kryesore të transportit: TCP dhe UDP. TCP ka shumë veçori të dobishme: besueshmëri, ruajtje të rendit të paketave dhe zbulim të gabimeve. UDP nuk i ka këto, por nga natyra TCP ka vonesë më të lartë, e cila është e papranueshme për disa lojëra. Kjo do të thotë se, për të siguruar vonesë të ulët, mund të krijoni protokollin tuaj mbi bazën e UDP ose të përdorni një bibliotekë që zbaton një protokoll transporti mbi UDP dhe është përshtatur për videolojëra multiplayer.
Zgjedhja midis TCP, UDP dhe një biblioteke varet nga disa faktorë. Së pari, nga nevojat e lojës: a i duhen vonesa të ulëta? Së dyti, nga kërkesat e protokollit të aplikacionit: a ka nevojë ai për një protokoll të besueshëm? Siç do ta shohim në pjesën tjetër, mund të krijohet një protokoll aplikacioni për të cilin një protokoll jo i besueshëm është plotësisht i mjaftueshëm. Së fundi, duhet marrë parasysh edhe përvoja e zhvilluesit të motorit të rrjetit.
Kam dy këshilla:
- Abstrahojeni sa më shumë që të jetë e mundur protokollin e transportit nga pjesa tjetër e aplikacionit, në mënyrë që të mund të zëvendësohet lehtësisht pa rishkruar të gjithë kodin.
- Mos u merrni me optimizim të parakohshëm. Nëse nuk jeni specialist i rrjeteve dhe nuk jeni të sigurt nëse ju duhet një protokoll transporti i personalizuar mbi bazën e UDP, mund të filloni me TCP ose me një bibliotekë që siguron besueshmëri, e më pas të testoni dhe të matni performancën. Nëse shfaqen probleme dhe jeni të sigurt se shkaku qëndron te protokolli i transportit, atëherë ndoshta ka ardhur koha të krijoni protokollin tuaj të transportit.
Për ta mbyllur këtë pjesë, ju rekomandoj të lexoni nga Brian Hook, ku trajtohen shumë nga temat e diskutuara këtu.
Protokolli i aplikacionit
Tani që mund të shkëmbejmë të dhëna midis klientëve dhe serverit, duhet të vendosim saktësisht se cilat të dhëna do të transmetohen dhe në çfarë formati.
Skema klasike është që klientët t’i dërgojnë serverit hyrjet ose veprimet e tyre, ndërsa serveri u dërgon klientëve gjendjen aktuale të lojës.
Serveri nuk dërgon gjendjen e plotë, por një gjendje të filtruar me entitete që ndodhen pranë lojtarit. Kjo bëhet për tre arsye. Së pari, gjendja e plotë mund të jetë tepër e madhe për t’u transmetuar me frekuencë të lartë. Së dyti, klientët në pjesën më të madhe interesohen për të dhënat vizuale dhe audio, sepse pjesa më e madhe e logjikës së lojës simulohet në serverin e lojës. Së treti, në disa lojëra lojtari nuk duhet të dijë disa të dhëna të caktuara, për shembull pozicionin e kundërshtarit në skajin tjetër të hartës, sepse përndryshe ai mund të analizojë paketat dhe të dijë saktësisht ku të lëvizë për ta vrarë.
Serializimi
Hapi i parë është shndërrimi i të dhënave që duam të dërgojmë (inputi ose gjendja e lojës) në një format të përshtatshëm për transmetim. Ky proces quhet .
Mendimi i parë që të vjen në mendje është përdorimi i një formati të lexueshëm për njeriun, si JSON ose XML. Por kjo do të ishte krejtësisht joefikase dhe do të zinte kot pjesën më të madhe të kanalit.
Në vend të kësaj, rekomandohet përdorimi i një formati binar, i cili është shumë më kompakt. Kjo do të thotë se paketat do të përmbajnë vetëm disa byte. Këtu duhet marrë parasysh problemi i , i cili mund të ndryshojë në kompjuterë të ndryshëm.
Për serializimin e të dhënave mund të përdorni një bibliotekë, për shembull:
- nga Google
- nga Sandstorm
- nga Shane Grant dhe Randolph Voorhies
Vetëm sigurohuni që biblioteka të krijojë arkiva portabël dhe të kujdeset për renditjen e byte-ve.
Një zgjidhje alternative mund të jetë implementimi vetjak; ai nuk është veçanërisht i ndërlikuar, sidomos nëse në kod përdorni një qasje të orientuar te të dhënat. Për më tepër, kjo do t’ju lejojë të bëni optimizime që jo gjithmonë janë të mundshme kur përdorni një bibliotekë.
Glenn Fiedler ka shkruar dy artikuj për serializimin: dhe .
Kompresimi
Sasia e të dhënave që transmetohen midis klientëve dhe serverit kufizohet nga gjerësia e brezit të kanalit. Kompresimi i të dhënave do t’ju lejojë të transmetoni më shumë të dhëna në çdo snapshot, të rrisni frekuencën e përditësimit ose thjesht të ulni kërkesat ndaj kanalit.
Paketimi i biteve
Teknika e parë është paketimi në nivel bitësh. Ajo konsiston në përdorimin e saktësisht atij numri bitësh që nevojitet për të përshkruar vlerën përkatëse. Për shembull, nëse keni një enumerim që mund të marrë 16 vlera të ndryshme, në vend të një bajti të plotë (8 bit), mund të përdorni vetëm 4 bitë.
Glenn Fiedler shpjegon si ta zbatoni këtë në pjesën e dytë të artikullit .
Paketimi në nivel bitësh funksionon veçanërisht mirë së bashku me diskretizimin, që do të jetë tema e seksionit vijues.
Diskretizimi
— është një teknikë kompresimi me humbje, e cila konsiston në përdorimin e vetëm një nënndarjeje të vlerave të mundshme për kodimin e një madhësie. Mënyra më e thjeshtë për ta zbatuar diskretizimin është rrumbullakosja e numrave me presje lëvizëse.
Glenn Fiedler (sërish!) tregon si të përdoret diskretizimi në praktikë, në artikullin e tij .
Algoritmet e kompresimit
Teknika e radhës janë algoritmet e kompresimit pa humbje.
Sipas mendimit tim, këto janë tre algoritmet më interesante që duhet të njihni:
- me kod të parapërllogaritur, i cili është jashtëzakonisht i shpejtë dhe mund të japë rezultate të mira. Është përdorur për kompresimin e paketave në motorin e rrjetit të Quake3.
- — një algoritëm kompresimi për përdorim të përgjithshëm, që nuk e rrit kurrë vëllimin e të dhënave. Siç mund të shihet , ai është përdorur në shumë fusha. Për përditësimet e gjendjes mund të jetë i tepërt. Por mund të jetë i dobishëm nëse keni nevojë t’u dërgoni klientëve nga serveri asete, tekste të gjata ose terren.
- — është ndoshta algoritmi më i thjeshtë i kompresimit, por është shumë efektiv për lloje të caktuara të të dhënave dhe mund të përdoret si hap parapërpunimi përpara zlib. Është veçanërisht i përshtatshëm për kompresimin e terrenit të përbërë nga tile ose voxels, ku shumë elemente fqinje përsëriten.
Kompresimi delta
Metoda e fundit e kompresimit është kompresimi delta. Ajo konsiston në transmetimin vetëm të dallimeve midis gjendjes aktuale të lojës dhe gjendjes së fundit të marrë nga klienti.
Për herë të parë u përdor në motorin e rrjetit të Quake3. Ja dy artikuj që shpjegojnë mënyrën e përdorimit të saj:
- nga Brian Hook
- nga Fabien Sanglard [ artikulli në Habr, shihni seksionin «Modeli i rrjetit»]
Glenn Fiedler e përdori gjithashtu në pjesën e dytë të artikullit të tij .
Kriptimi
Përveç kësaj, mund t’ju duhet të enkriptoni transmetimin e informacionit midis klientëve dhe serverit. Ka disa arsye për këtë:
- privatësi/konfidencialitet: mesazhet mund të lexohen vetëm nga marrësi dhe askush tjetër që përgjon rrjetin nuk do të mund t’i lexojë.
- autentikim: personi që dëshiron të luajë rolin e lojtarit duhet të dijë çelësin e tij.
- parandalimi i mashtrimit: për lojtarët keqdashës do të jetë shumë më e vështirë të krijojnë paketat e tyre për mashtrim; atyre do t’u duhet të riprodhojnë skemën e enkriptimit dhe të gjejnë çelësin (i cili ndryshon në çdo lidhje).
Ju rekomandoj me këmbëngulje të përdorni një bibliotekë për këtë. Ju sugjeroj të përdorni , sepse është veçanërisht e thjeshtë dhe ka udhëzues të shkëlqyer. Veçanërisht interesant është udhëzuesi për , i cili ju lejon të gjeneroni çelësa të rinj në çdo lidhje të re.
Protokolli i aplikacionit: përfundim
Këtu do ta përmbyllim temën e protokollit të aplikacionit. Mendoj se kompresimi nuk është aspak i domosdoshëm dhe vendimi për ta përdorur varet vetëm nga loja dhe gjerësia e brezit të kërkuar. Sipas mendimit tim, enkriptimi është i domosdoshëm, por në prototipin e parë mund të bëhet edhe pa të.
Logjika e aplikacionit
Tani jemi në gjendje të përditësojmë gjendjen në klient, por mund të përballemi me probleme vonese. Pasi lojtari jep një input, ai duhet të presë përditësimin e gjendjes së lojës nga serveri për të parë çfarë ndikimi pati në botë.
Për më tepër, midis dy përditësimeve të gjendjes, bota mbetet krejtësisht statike. Nëse frekuenca e përditësimit të gjendjes është e ulët, lëvizjet do të jenë shumë të ndërprera.
Ekzistojnë disa teknika që ndihmojnë në uljen e ndikimit të këtij problemi, dhe në seksionin vijues do t’i shqyrtoj ato.
Teknika për zbutjen e vonesës
Të gjitha teknikat e përshkruara në këtë seksion trajtohen hollësisht në serinë të Gabriel Gambetta-s. Ju rekomandoj me këmbëngulje ta lexoni këtë seri të shkëlqyer artikujsh. Ajo përfshin gjithashtu një demonstrim interaktiv që ju lejon të shihni se si funksionojnë këto teknika në praktikë.
Teknika e parë konsiston në zbatimin e rezultatit të inputit drejtpërdrejt, pa pritur përgjigjen nga serveri. Kjo quhet parashikim nga ana e klientit. Megjithatë, kur klienti merr një përditësim nga serveri, ai duhet të verifikojë nëse parashikimi i tij ishte i saktë. Nëse jo, ai thjesht duhet ta korrigjojë gjendjen e vet sipas asaj që mori nga serveri, sepse serveri është autoritar. Kjo teknikë u përdor për herë të parë në Quake. Më shumë rreth saj mund të lexoni në artikullin nga Fabien Sanglard [ në Habr].
Grupi i dytë i teknikave përdoret për të zbutur lëvizjen e entiteteve të tjera midis dy përditësimeve të gjendjes. Ka dy mënyra për ta zgjidhur këtë detyrë: interpolimi dhe ekstrapolimi. Në rastin e interpolimit, merren dy gjendjet e fundit dhe shfaqet kalimi nga njëra te tjetra. Mangësia e tij është se shton një vonesë të vogël, sepse klienti gjithmonë sheh atë që ka ndodhur në të kaluarën. Ekstrapolimi konsiston në parashikimin se ku duhet të ndodhen tani entitetet bazuar në gjendjen e fundit që ka marrë klienti. Mangësia e tij është se, nëse entiteti ndryshon plotësisht drejtimin e lëvizjes, do të krijohet një devijim i madh midis parashikimit dhe pozicionit real.
Teknika e fundit dhe më e avancuar, e dobishme vetëm në FPS, është kompensimi i vonesës. Kur përdoret kompensimi i vonesës, serveri merr parasysh vonesat e klientit në momentin kur ai qëllon drejt objektivit. Për shembull, nëse lojtari bën një headshot në ekranin e tij, por në realitet objektivi i tij për shkak të vonesës ndodhej në një vend tjetër, atëherë do të ishte e padrejtë t'i mohohej lojtarit eliminimi për shkak të vonesës. Prandaj serveri e kthen kohën pas deri në momentin kur lojtari qëlloi, në mënyrë që të simulojë atë që lojtari pa në ekranin e tij dhe të kontrollojë përplasjen midis të shtënës së tij dhe objektivit.
Glenn Fiedler (si gjithmonë!) shkroi në vitin 2004 artikullin , në të cilin vendosi bazat e sinkronizimit të simulimit të fizikës midis serverit dhe klientit. Në vitin 2014 ai shkroi një seri të re artikujsh , ku përshkroi teknika të tjera për sinkronizimin e simulimit të fizikës.
Gjithashtu, në wiki-n e kompanisë Valve ka dy artikuj, dhe ku trajtohet kompensimi i vonesës.
Parandalimi i mashtrimit
Ekzistojnë dy teknika kryesore për parandalimin e mashtrimit.
Së pari: ta vështirësoni dërgimin e paketave keqdashëse nga mashtruesit. Siç u përmend më sipër, një mënyrë e mirë për ta zbatuar këtë është enkriptimi.
Së dyti: serveri autoritativ duhet të marrë vetëm komanda/hyrje/veprime. Klienti nuk duhet të ketë mundësi të ndryshojë gjendjen në server, përveçse duke dërguar hyrje. Prandaj, sa herë që serveri merr hyrje, para se ta zbatojë, duhet të kontrollojë nëse ajo është e lejuar.
Logjika e aplikacionit: përfundim
Ju rekomandoj të zbatoni një mënyrë për të simuluar vonesa të mëdha dhe frekuenca të ulëta përditësimi, që të mund të testoni sjelljen e lojës suaj në kushte të pafavorshme, edhe kur klienti dhe serveri ekzekutohen në të njëjtin kompjuter. Kjo do ta thjeshtojë shumë zbatimin e metodave për zbutjen e vonesave.
Burime të tjera të dobishme
Nëse dëshironi të studioni burime të tjera kushtuar modeleve të rrjetit, mund t’i gjeni këtu:
- — ia vlen të lexoni të gjithë blogun e tij; aty ka shumë artikuj të shkëlqyer. këtu janë mbledhur të gjithë artikujt mbi teknologjitë e rrjetit.
- nga autori M. Fatih MAR — është një listë e detajuar artikujsh dhe videosh mbi engine-et e rrjetit për videolojëra.
- Në aty ka gjithashtu shumë lidhje të dobishme.
Burimi: habr.com
