Tungkol sa modelo ng network sa mga laro para sa mga nagsisimula

Tungkol sa modelo ng network sa mga laro para sa mga nagsisimula
Sa huling dalawang linggo, nagtatrabaho ako sa online engine para sa aking laro. Bago ito, wala akong alam tungkol sa networking sa mga laro, kaya nagbasa ako ng maraming artikulo at gumawa ng maraming mga eksperimento upang maunawaan ang lahat ng mga konsepto at makapagsulat ng sarili kong networking engine.

Sa gabay na ito, gusto kong ibahagi sa iyo ang iba't ibang konsepto na kailangan mong matutunan bago magsulat ng sarili mong engine ng laro, pati na rin ang pinakamahusay na mga mapagkukunan at artikulo upang matutunan ang mga ito.

Sa pangkalahatan, mayroong dalawang pangunahing uri ng mga arkitektura ng network: peer-to-peer at client-server. Sa arkitektura ng peer-to-peer (p2p), inililipat ang data sa pagitan ng anumang pares ng mga konektadong manlalaro, habang sa arkitektura ng client-server, ang data ay inililipat lamang sa pagitan ng mga manlalaro at ng server.

Bagama't ginagamit pa rin ang arkitektura ng peer-to-peer sa ilang mga laro, ang client-server ang pamantayan: mas madaling ipatupad, nangangailangan ng mas maliit na lapad ng channel, at ginagawang mas madaling protektahan laban sa pagdaraya. Samakatuwid, sa tutorial na ito ay tututuon natin ang arkitektura ng client-server.

Sa partikular, kami ay pinaka-interesado sa mga authoritarian server: sa mga ganitong sistema, ang server ay palaging tama. Halimbawa, kung iniisip ng isang manlalaro na siya ay nasa mga coordinate (10, 5), at sinabi sa kanya ng server na siya ay nasa (5, 3), dapat palitan ng kliyente ang posisyon nito ng iniulat ng server, at hindi ang vice. kabaligtaran. Ang paggamit ng mga makapangyarihang server ay nagpapadali sa pagtukoy ng mga manloloko.

Ang mga network gaming system ay may tatlong pangunahing bahagi:

  • Transport protocol: kung paano inililipat ang data sa pagitan ng mga kliyente at server.
  • Application protocol: kung ano ang ipinadala mula sa mga kliyente patungo sa server at mula sa server patungo sa mga kliyente at sa anong format.
  • Lohika ng aplikasyon: kung paano ginagamit ang inilipat na data upang i-update ang estado ng mga kliyente at server.

Napakahalagang maunawaan ang tungkulin ng bawat bahagi at ang mga hamon na kaakibat nito.

Protocol ng transportasyon

Ang unang hakbang ay ang pumili ng isang protocol para sa pagdadala ng data sa pagitan ng server at mga kliyente. Mayroong dalawang Internet protocol para dito: TCP ΠΈ UDP. Ngunit maaari kang lumikha ng iyong sariling transport protocol batay sa isa sa mga ito o gumamit ng library na gumagamit ng mga ito.

Paghahambing ng TCP at UDP

Parehong nakabatay ang TCP at UDP sa IP. Binibigyang-daan ng IP ang isang packet na mailipat mula sa isang pinagmulan patungo sa isang tatanggap, ngunit hindi ginagarantiyahan na ang ipinadalang packet ay maaga o huli ay makakarating sa tatanggap, na maaabot ito ng hindi bababa sa isang beses, at na ang pagkakasunud-sunod ng mga packet ay darating sa tamang utos. Bukod dito, ang isang packet ay maaari lamang maglaman ng isang limitadong halaga ng data, na ibinibigay ng halaga MTU.

Ang UDP ay isang manipis na layer lamang sa ibabaw ng IP. Samakatuwid, mayroon itong parehong mga limitasyon. Sa kaibahan, ang TCP ay may maraming mga tampok. Nagbibigay ito ng maaasahan at maayos na koneksyon sa pagitan ng dalawang node na may error checking. Samakatuwid, ang TCP ay napaka-maginhawa at ginagamit sa maraming iba pang mga protocol, hal. HTTP, FTP ΠΈ SMTP. Ngunit lahat ng mga tampok na ito ay may presyo: antala.

Upang maunawaan kung bakit maaaring magdulot ng latency ang mga function na ito, kailangan nating maunawaan kung paano gumagana ang TCP. Kapag ang isang sending node ay nagpapadala ng isang packet sa isang receiving node, inaasahan nitong makatanggap ng isang acknowledgement (ACK). Kung pagkatapos ng isang tiyak na oras ay hindi ito natanggap (dahil ang packet o pagkilala ay nawala, o para sa ilang iba pang dahilan), pagkatapos ay muling ipinapadala nito ang packet. Bukod dito, ginagarantiyahan ng TCP na ang mga packet ay natanggap sa tamang pagkakasunud-sunod, kaya hanggang sa ang nawawalang packet ay natanggap, ang lahat ng iba pang mga packet ay hindi mapoproseso, kahit na sila ay natanggap na ng tatanggap na host.

Ngunit tulad ng maaari mong isipin, ang latency sa mga multiplayer na laro ay napakahalaga, lalo na sa mga genre na puno ng aksyon tulad ng FPS. Ito ang dahilan kung bakit maraming laro ang gumagamit ng UDP na may sariling protocol.

Ang isang katutubong UDP-based na protocol ay maaaring maging mas mahusay kaysa sa TCP para sa iba't ibang dahilan. Halimbawa, maaari nitong markahan ang ilang packet bilang pinagkakatiwalaan at ang iba ay hindi pinagkakatiwalaan. Samakatuwid, walang pakialam kung maabot ng hindi pinagkakatiwalaang packet ang tatanggap. O maaari itong magproseso ng maramihang mga stream ng data upang ang isang nawawalang packet sa isang stream ay hindi makapagpabagal sa natitirang mga stream. Halimbawa, maaaring mayroong thread para sa input ng player at isa pang thread para sa mga mensahe sa chat. Kung nawala ang isang mensahe sa chat na hindi apurahan, hindi nito babagal ang pag-input na apurahan. O ang isang proprietary protocol ay maaaring magpatupad ng pagiging maaasahan sa ibang paraan kaysa sa TCP upang maging mas mahusay sa isang kapaligiran ng video game.

Kaya, kung napakasakit ng TCP, gagawa tayo ng sarili nating transport protocol batay sa UDP?

Ito ay medyo mas kumplikado. Kahit na ang TCP ay halos suboptimal para sa mga gaming network system, maaari itong gumana nang maayos para sa iyong partikular na laro at makatipid sa iyo ng mahalagang oras. Halimbawa, ang latency ay maaaring hindi isang isyu para sa isang turn-based na laro o isang laro na maaari lamang laruin sa mga LAN network, kung saan ang latency at packet loss ay mas mababa kaysa sa Internet.

Maraming matagumpay na laro, kabilang ang World of Warcraft, Minecraft at Terraria, ang gumagamit ng TCP. Gayunpaman, karamihan sa mga FPS ay gumagamit ng sarili nilang mga protocol na nakabatay sa UDP, kaya mas marami tayong pag-uusapan sa ibaba.

Kung magpasya kang gumamit ng TCP, tiyaking naka-disable ito Ang algorithm ni Nagle, dahil nagbu-buffer ito ng mga packet bago ipadala, na nangangahulugang pinapataas nito ang latency.

Upang matuto nang higit pa tungkol sa mga pagkakaiba sa pagitan ng UDP at TCP sa konteksto ng mga larong multiplayer, maaari mong basahin ang artikulo ni Glenn Fiedler UDP vs. TCP.

Sariling protocol

Kaya gusto mong lumikha ng iyong sariling transport protocol, ngunit hindi mo alam kung saan magsisimula? Maswerte ka dahil nagsulat si Glenn Fiedler ng dalawang kamangha-manghang artikulo tungkol dito. Makakakita ka ng maraming matalinong pag-iisip sa kanila.

Ang unang artikulo Networking para sa mga Game Programmer 2008, mas madali kaysa sa pangalawa, Pagbuo ng Game Network Protocol 2016. Inirerekomenda ko na magsimula ka sa mas matanda.

Tandaan na si Glenn Fiedler ay isang malaking proponent ng paggamit ng custom na protocol batay sa UDP. At pagkatapos basahin ang kanyang mga artikulo, malamang na tatanggapin mo ang kanyang opinyon na ang TCP ay may malubhang pagkukulang sa mga video game, at gugustuhin mong ipatupad ang iyong sariling protocol.

Ngunit kung bago ka sa networking, gawin ang iyong sarili ng isang pabor at gumamit ng TCP o isang library. Upang matagumpay na maipatupad ang iyong sariling transport protocol, kailangan mong matuto ng maraming bago.

Mga aklatan sa network

Kung kailangan mo ng isang bagay na mas mahusay kaysa sa TCP, ngunit ayaw mong dumaan sa abala sa pagpapatupad ng iyong sariling protocol at pagpunta sa maraming detalye, maaari kang gumamit ng networking library. Marami sila:

Hindi ko pa nasubukan lahat, pero mas gusto ko ang ENet dahil madali itong gamitin at maaasahan. Bilang karagdagan, mayroon itong malinaw na dokumentasyon at isang tutorial para sa mga nagsisimula.

Transport Protocol: Konklusyon

Upang ibuod: mayroong dalawang pangunahing protocol ng transportasyon: TCP at UDP. Ang TCP ay may maraming kapaki-pakinabang na tampok: pagiging maaasahan, pagpapanatili ng order ng packet, pagtuklas ng error. Wala sa UDP ang lahat ng ito, ngunit ang TCP sa likas na katangian nito ay nagpapataas ng latency, na hindi katanggap-tanggap para sa ilang mga laro. Ibig sabihin, para matiyak ang mababang latency, maaari kang gumawa ng sarili mong protocol batay sa UDP o gumamit ng library na nagpapatupad ng transport protocol sa UDP at inangkop para sa mga multiplayer na video game.

Ang pagpili sa pagitan ng TCP, UDP at library ay nakasalalay sa ilang mga kadahilanan. Una, mula sa mga pangangailangan ng laro: kailangan ba nito ng mababang latency? Pangalawa, mula sa mga kinakailangan sa protocol ng aplikasyon: kailangan ba ng maaasahang protocol? Tulad ng makikita natin sa susunod na bahagi, posible na lumikha ng isang application protocol kung saan ang isang hindi pinagkakatiwalaang protocol ay lubos na angkop. Sa wakas, kailangan mo ring isaalang-alang ang karanasan ng developer ng network engine.

Mayroon akong dalawang piraso ng payo:

  • I-abstract ang transport protocol mula sa natitirang bahagi ng application hangga't maaari upang madali itong mapalitan nang hindi muling isinulat ang lahat ng code.
  • Huwag mag-over-optimize. Kung hindi ka eksperto sa networking at hindi sigurado kung kailangan mo ng custom na UDP-based na transport protocol, maaari kang magsimula sa TCP o isang library na nagbibigay ng pagiging maaasahan, at pagkatapos ay subukan at sukatin ang pagganap. Kung lumitaw ang mga problema at kumpiyansa kang ang dahilan ay ang transport protocol, maaaring oras na para gumawa ng sarili mong transport protocol.

Sa dulo ng bahaging ito, inirerekumenda kong basahin mo Panimula sa Multiplayer Game Programming ni Brian Hook, na sumasaklaw sa marami sa mga paksang tinalakay dito.

Protocol ng aplikasyon

Ngayon na maaari na tayong makipagpalitan ng data sa pagitan ng mga kliyente at server, kailangan nating magpasya kung anong data ang ililipat at sa anong format.

Ang klasikong pamamaraan ay ang mga kliyente ay nagpapadala ng input o mga aksyon sa server, at ang server ay nagpapadala ng kasalukuyang estado ng laro sa mga kliyente.

Ang server ay hindi nagpapadala ng buong estado, ngunit isang na-filter na estado na may mga entity na matatagpuan malapit sa player. Ginagawa niya ito sa tatlong dahilan. Una, ang kumpletong estado ay maaaring masyadong malaki upang maipadala sa mataas na dalas. Pangalawa, ang mga kliyente ay pangunahing interesado sa visual at audio na data, dahil karamihan sa logic ng laro ay ginagaya sa server ng laro. Pangatlo, sa ilang mga laro ang manlalaro ay hindi kailangang malaman ang ilang partikular na data, halimbawa, ang posisyon ng kaaway sa kabilang panig ng mapa, kung hindi, maaari niyang singhutin ang mga packet at alam kung saan eksaktong lilipat upang patayin siya.

Serialization

Ang unang hakbang ay ang pag-convert ng data na gusto naming ipadala (input o estado ng laro) sa isang format na angkop para sa paghahatid. Ang prosesong ito ay tinatawag na serialization.

Ang kaisipang agad na naiisip ay ang gumamit ng format na nababasa ng tao, gaya ng JSON o XML. Ngunit ito ay magiging ganap na hindi epektibo at mag-aaksaya ng karamihan sa channel.

Inirerekomenda na gamitin ang binary format sa halip, na mas compact. Iyon ay, ang mga packet ay maglalaman lamang ng ilang byte. May problemang dapat isaalang-alang dito byte order, na maaaring mag-iba sa iba't ibang mga computer.

Upang i-serialize ang data maaari kang gumamit ng library, halimbawa:

Siguraduhin lamang na ang library ay gumagawa ng mga portable na archive at nagmamalasakit sa endianness.

Ang isang alternatibong solusyon ay ang ipatupad ito sa iyong sarili; ito ay hindi partikular na mahirap, lalo na kung gumagamit ka ng isang data-centric na diskarte sa iyong code. Bilang karagdagan, ito ay magbibigay-daan sa iyong magsagawa ng mga pag-optimize na hindi laging posible kapag ginagamit ang library.

Si Glenn Fiedler ay sumulat ng dalawang artikulo tungkol sa serialization: Pagbasa at Pagsulat ng mga Pakete ΠΈ Mga Istratehiya sa Serialization.

Compression

Ang dami ng data na inilipat sa pagitan ng mga kliyente at server ay limitado ng bandwidth ng channel. Ang data compression ay magbibigay-daan sa iyo na maglipat ng higit pang data sa bawat snapshot, pataasin ang dalas ng pag-update, o bawasan lang ang mga kinakailangan sa channel.

Bit packaging

Ang unang pamamaraan ay bit packing. Binubuo ito ng paggamit ng eksaktong bilang ng mga bit na kinakailangan upang ilarawan ang nais na halaga. Halimbawa, kung mayroon kang isang enum na maaaring magkaroon ng 16 na magkakaibang mga halaga, sa halip na isang buong byte (8 bits), maaari mong gamitin ang 4 na bits lamang.

Ipinapaliwanag ni Glenn Fiedler kung paano ito ipatupad sa ikalawang bahagi ng artikulo Pagbasa at Pagsulat ng mga Pakete.

Gumagana ang bit packing lalo na sa sampling, na magiging paksa ng susunod na seksyon.

Sampling

Sampling ay isang lossy compression technique na gumagamit lamang ng subset ng mga posibleng value para mag-encode ng value. Ang pinakamadaling paraan upang ipatupad ang discretization ay sa pamamagitan ng pag-round sa mga floating point na numero.

Si Glenn Fiedler (muli!) ay nagpapakita kung paano isasagawa ang sampling sa kanyang artikulo Snapshot Compression.

Mga algorithm ng compression

Ang susunod na pamamaraan ay ang lossless compression algorithm.

Narito, sa aking opinyon, ang tatlong pinaka-kagiliw-giliw na mga algorithm na kailangan mong malaman:

  • Huffman coding na may pre-computed code, na napakabilis at makakapagdulot ng magagandang resulta. Ito ay ginamit upang i-compress ang mga packet sa Quake3 networking engine.
  • zlib ay isang pangkalahatang layunin na compression algorithm na hindi kailanman nagpapataas ng dami ng data. Paano mo makikita dito, ito ay ginamit sa iba't ibang mga aplikasyon. Maaaring ito ay kalabisan para sa pag-update ng mga estado. Ngunit maaari itong maging kapaki-pakinabang kung kailangan mong magpadala ng mga asset, mahabang text o terrain sa mga kliyente mula sa server.
  • Kinokopya ang haba ng pagtakbo - Ito ay marahil ang pinakasimpleng compression algorithm, ngunit ito ay napaka-epektibo para sa ilang mga uri ng data, at maaaring gamitin bilang isang paunang pagproseso na hakbang bago ang zlib. Ito ay partikular na angkop para sa pag-compress ng lupain na binubuo ng mga tile o voxel kung saan maraming katabing elemento ang inuulit.

Delta compression

Ang huling compression technique ay delta compression. Binubuo ito sa katotohanan na ang mga pagkakaiba lamang sa pagitan ng kasalukuyang estado ng laro at ang huling estado na natanggap ng kliyente ay ipinadala.

Ito ay unang ginamit sa Quake3 network engine. Narito ang dalawang artikulong nagpapaliwanag kung paano ito gamitin:

Ginamit din ito ni Glenn Fiedler sa ikalawang bahagi ng kanyang artikulo Snapshot Compression.

Pag-encrypt

Bilang karagdagan, maaaring kailanganin mong i-encrypt ang paglilipat ng impormasyon sa pagitan ng mga kliyente at ng server. Mayroong ilang mga dahilan para dito:

  • privacy/confidentiality: ang mga mensahe ay mababasa lamang ng tatanggap, at walang ibang taong sumisinghot sa network ang makakabasa ng mga ito.
  • pagpapatunay: ang isang taong gustong gumanap sa papel ng isang manlalaro ay dapat alam ang kanyang susi.
  • Pag-iwas sa cheat: Mas magiging mahirap para sa mga nakakahamak na manlalaro na lumikha ng kanilang sariling mga pakete ng cheat, kakailanganin nilang kopyahin ang scheme ng pag-encrypt at hanapin ang susi (na nagbabago sa bawat koneksyon).

Lubos kong inirerekomenda ang paggamit ng library para dito. Iminumungkahi kong gamitin libsodium, dahil ito ay lalong simple at may mahusay na mga tutorial. Partikular na kawili-wili ay ang tutorial sa pagpapalitan ng susi, na nagbibigay-daan sa iyong bumuo ng mga bagong key sa bawat bagong koneksyon.

Application Protocol: Konklusyon

Ito ay nagtatapos sa aming application protocol. Naniniwala ako na ang compression ay ganap na opsyonal at ang desisyon na gamitin ito ay nakasalalay lamang sa laro at sa kinakailangang bandwidth. Ang pag-encrypt, sa palagay ko, ay sapilitan, ngunit sa unang prototype magagawa mo nang wala ito.

Lohika ng aplikasyon

Nagagawa na naming i-update ang estado sa client, ngunit maaaring magkaroon ng mga isyu sa latency. Ang manlalaro, pagkatapos makumpleto ang input, ay kailangang maghintay para sa estado ng laro na mag-update mula sa server upang makita kung ano ang epekto nito sa mundo.

Bukod dito, sa pagitan ng dalawang pag-update ng estado, ang mundo ay ganap na static. Kung mababa ang rate ng pag-update ng estado, ang mga paggalaw ay magiging napakaalog.

Mayroong ilang mga diskarte upang mabawasan ang epekto ng problemang ito, at tatalakayin ko ang mga ito sa susunod na seksyon.

Latency Smoothing Techniques

Ang lahat ng mga pamamaraan na inilarawan sa seksyong ito ay tinalakay nang detalyado sa serye Mabilis na Multiplayer Gabriel Gambetta. Lubos kong inirerekumenda ang pagbabasa nitong mahusay na serye ng mga artikulo. Kasama rin dito ang isang interactive na demo na nagbibigay-daan sa iyong makita kung paano gumagana ang mga diskarteng ito sa pagsasanay.

Ang unang pamamaraan ay direktang ilapat ang resulta ng input nang hindi naghihintay ng tugon mula sa server. Ito ay tinatawag na pagtataya sa panig ng kliyente. Gayunpaman, kapag nakatanggap ang kliyente ng update mula sa server, dapat nitong i-verify na tama ang hula nito. Kung hindi ito ang kaso, kailangan lang niyang baguhin ang kanyang estado ayon sa natanggap niya mula sa server, dahil ang server ay authoritarian. Ang pamamaraan na ito ay unang ginamit sa Quake. Maaari mong basahin ang higit pa tungkol dito sa artikulo Pagsusuri ng code ng Quake Engine Fabien Sanglars [pagsasalin sa HabrΓ©].

Ang pangalawang hanay ng mga diskarte ay ginagamit upang pakinisin ang paggalaw ng iba pang mga entity sa pagitan ng dalawang update ng estado. Mayroong dalawang paraan upang malutas ang problemang ito: interpolation at extrapolation. Sa kaso ng interpolation, ang huling dalawang estado ay kinuha at ang paglipat mula sa isa patungo sa isa ay ipinapakita. Ang kawalan nito ay nagdudulot ito ng kaunting pagkaantala dahil laging nakikita ng kliyente ang nangyari sa nakaraan. Ang extrapolation ay tungkol sa paghula kung saan dapat nakabatay ang mga entity sa huling estado na natanggap ng kliyente. Ang kawalan nito ay kung ang entity ay ganap na nagbabago sa direksyon ng paggalaw, magkakaroon ng malaking error sa pagitan ng forecast at ang aktwal na posisyon.

Ang pinakabago, pinaka-advanced na pamamaraan na kapaki-pakinabang lamang sa FPS ay lag compensation. Kapag gumagamit ng lag compensation, isinasaalang-alang ng server ang mga pagkaantala ng kliyente kapag nag-shoot ito sa target. Halimbawa, kung ang isang player ay nagsagawa ng isang headshot sa kanilang screen, ngunit sa katotohanan ang kanilang target ay nasa ibang lokasyon dahil sa pagkaantala, kung gayon magiging hindi patas na tanggihan ang manlalaro ng karapatang pumatay dahil sa pagkaantala. Samakatuwid, i-rewind ng server ang oras pabalik sa sandaling nagpaputok ang player upang gayahin ang nakita ng player sa kanilang screen at tingnan kung may banggaan sa pagitan ng kanilang shot at ng target.

Si Glenn Fiedler (gaya ng dati!) ay nagsulat ng isang artikulo noong 2004 Network Physics (2004), kung saan inilatag niya ang pundasyon para sa pag-synchronize ng physics simulation sa pagitan ng server at client. Noong 2014 nagsulat siya ng bagong serye ng mga artikulo Networking Physics, na naglalarawan ng iba pang mga diskarte para sa pag-synchronize ng physics simulation.

Mayroon ding dalawang artikulo sa Valve wiki, Pinagmulan Multiplayer Networking ΠΈ Latency Compensating Methods sa Client/Server In-game Protocol Design and Optimization na isinasaalang-alang ang kabayaran para sa mga pagkaantala.

Pag-iwas sa pagdaraya

Mayroong dalawang pangunahing pamamaraan para maiwasan ang pagdaraya.

Una: ginagawang mas mahirap para sa mga manloloko na magpadala ng mga malisyosong packet. Tulad ng nabanggit sa itaas, ang isang mahusay na paraan upang ipatupad ito ay ang pag-encrypt.

Pangalawa: ang isang authoritarian server ay dapat lamang makatanggap ng mga utos/input/aksyon. Hindi dapat baguhin ng kliyente ang estado sa server maliban sa pamamagitan ng pagpapadala ng input. Pagkatapos, sa tuwing makakatanggap ang server ng input, dapat itong suriin kung ito ay wasto bago ito gamitin.

Lohika ng aplikasyon: konklusyon

Inirerekomenda kong magpatupad ka ng paraan para gayahin ang matataas na latency at mababang rate ng pag-refresh para masubukan mo ang gawi ng iyong laro sa hindi magandang kundisyon, kahit na tumatakbo ang client at server sa parehong computer. Ito ay lubos na magpapasimple sa pagpapatupad ng mga delay smoothing techniques.

Iba pang Nakatutulong na Mapagkukunan

Kung gusto mong tuklasin ang iba pang mapagkukunan sa mga modelo ng network, mahahanap mo ang mga ito dito:

  • Blog ni Glenn Fiedler – sulit na basahin ang kanyang buong blog, maraming magagandang artikulo doon. Dito Ang lahat ng mga artikulo sa mga teknolohiya ng network ay kinokolekta.
  • Galing Game Networking ni M. Fatih MAR ay isang komprehensibong listahan ng mga artikulo at video sa mga online video game engine.
  • Π’ wiki ng r/gamedev subreddit Mayroon ding maraming mga kapaki-pakinabang na link.

Pinagmulan: www.habr.com

Magdagdag ng komento