{"id":38370,"date":"2019-10-31T22:23:18","date_gmt":"2019-10-31T19:23:18","guid":{"rendered":"https:\/\/prohoster.info\/blog\/portiruem-mnogopolzovatelskuyu-igru-s-s-na-veb-c-cheerp-webrtc-i-firebase\/"},"modified":"2019-10-31T22:23:18","modified_gmt":"2019-10-31T19:23:18","slug":"portiruem-mnogopolzovatelskuyu-igru-s-s-na-veb-c-cheerp-webrtc-i-firebase","status":"publish","type":"post","link":"https:\/\/prohoster.info\/sk\/blog\/administrirovanie\/portiruem-mnogopolzovatelskuyu-igru-s-s-na-veb-c-cheerp-webrtc-i-firebase","title":{"rendered":"Portujeme multiplayerov\u00fa hru z C++ na web s Cheerp, WebRTC a Firebase","gt_translate_keys":[{"key":"rendered","format":"text"}]},"content":{"rendered":"<h2>\u00davod<\/h2>\n<p>\nNa\u0161a spolo\u010dnos\u0165 <noindex><a rel=\"nofollow\" href=\"https:\/\/leaningtech.com\">Leaning Technologies<\/a><\/noindex> poskytuje rie\u0161enia na portovanie tradi\u010dn\u00fdch desktopov\u00fdch aplik\u00e1ci\u00ed na web. N\u00e1\u0161 kompil\u00e1tor C++ <noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/leaningtech\/cheerp-meta\">Cheerp<\/a><\/noindex> generuje kombin\u00e1ciu WebAssembly a JavaScript, \u010do zabezpe\u010duje <noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/leaningtech\/cheerp-meta\/wiki\/Cheerp-Tutorial%3A-Mixed-mode-C++-to-WebAssembly-and-JavaScript\">jednoduch\u00e9 prepojenie s prehliada\u010dom<\/a><\/noindex>, a vysok\u00fd v\u00fdkon.<\/p>\n<p>Ako pr\u00edklad jeho pou\u017eitia sme sa rozhodli portova\u0165 na web multiplayerov\u00fa hru a vybrali sme pre tento projekt <noindex><a rel=\"nofollow\" href=\"https:\/\/www.teeworlds.com\/\"><strong>Teeworlds<\/strong><\/a><\/noindex>. Teeworlds je multiplayerov\u00e1 2D retro hra s malou, ale akt\u00edvnou komunitou hr\u00e1\u010dov (medzi nimi aj ja!). Je mal\u00e1 ako z h\u013eadiska s\u0165ahovan\u00fdch zdrojov, tak aj po\u017eiadaviek na CPU a GPU \u2014 ide\u00e1lny kandid\u00e1t.<\/p>\n<p><img decoding=\"async\" alt=\"Portujeme multiplayerov\u00fa hru z C++ na web s Cheerp, WebRTC a Firebase\" src=\"\/wp-content\/uploads\/2019\/09\/30bff36ee1158ddea2e76d8b11e48d2b.jpg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<i>Funguj\u00faca v prehliada\u010di Teeworlds<\/i><br \/>\n<noindex><a rel=\"nofollow\" name=\"habracut\"><\/a><\/noindex><br \/>\nSme sa rozhodli vyu\u017ei\u0165 tento projekt na experimentovanie s <strong>generick\u00fdmi rie\u0161eniami na portovanie sie\u0165ov\u00e9ho k\u00f3du na web<\/strong>. Zvy\u010dajne sa to vykon\u00e1va nasleduj\u00facimi sp\u00f4sobmi:<\/p>\n<ul>\n<li><strong>XMLHttpRequest\/fetch<\/strong>, ak sie\u0165ov\u00e1 \u010das\u0165 pozost\u00e1va len z HTTP po\u017eiadaviek, alebo<\/li>\n<li><strong>WebSockets<\/strong>.<\/li>\n<\/ul>\n<p>\nObe rie\u0161enia vy\u017eaduj\u00fa hos\u0165ovanie serverov\u00e9ho komponentu na strane servera a \u017eiadne z nich neumo\u017e\u0148uje pou\u017e\u00edva\u0165 ako prenosov\u00fd protokol <noindex><a rel=\"nofollow\" href=\"https:\/\/en.wikipedia.org\/wiki\/User_Datagram_Protocol\">UDP<\/a><\/noindex>. To je d\u00f4le\u017eit\u00e9 pre aplik\u00e1cie v re\u00e1lnom \u010dase, ako je softv\u00e9r na videokonferencie a hry, preto\u017ee z\u00e1ruky dodania a poradia paketov protokolu <noindex><a rel=\"nofollow\" href=\"https:\/\/en.wikipedia.org\/wiki\/Transmission_Control_Protocol\">TCP<\/a><\/noindex> m\u00f4\u017eu by\u0165 prek\u00e1\u017ekou pre n\u00edzke latencie.<\/p>\n<p>Existuje aj tretia cesta \u2014 pou\u017e\u00edvanie siete z prehliada\u010da: <noindex><a rel=\"nofollow\" href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Glossary\/WebRTC\"><strong>WebRTC<\/strong><\/a><\/noindex>.<\/p>\n<p><noindex><a rel=\"nofollow\" href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/API\/RTCDataChannel\"><strong>RTCDataChannel<\/strong><\/a><\/noindex> podporuje ako spo\u013eahliv\u00fd, tak aj nespo\u013eahliv\u00fd prenos (v poslednom pr\u00edpade sa sna\u017e\u00ed pou\u017ei\u0165 ako prenosov\u00fd protokol UDP, ak je to mo\u017en\u00e9), a m\u00f4\u017ee by\u0165 pou\u017eit\u00fd aj s vzdialen\u00fdm serverom, aj medzi prehliada\u010dmi. <strong>To znamen\u00e1, \u017ee m\u00f4\u017eeme portova\u0165 do prehliada\u010da cel\u00fa aplik\u00e1ciu, vr\u00e1tane serverov\u00e9ho komponentu!<\/strong><\/p>\n<p>Av\u0161ak s t\u00fdm sa sp\u00e1ja dodato\u010dn\u00e1 \u0165a\u017ekost: predt\u00fdm, ako si dva WebRTC peer m\u00f4\u017eu vymie\u0148a\u0165 d\u00e1ta, musia prejs\u0165 relat\u00edvne zlo\u017eit\u00fdm procesom \u201epodania r\u00fak\u201c (handshake) na pripojenie, na \u010do s\u00fa potrebn\u00e9 niektor\u00e9 extern\u00e9 entity (sign\u00e1lny server a jeden alebo viac serverov <noindex><a rel=\"nofollow\" href=\"https:\/\/en.wikipedia.org\/wiki\/STUN\">STUN<\/a><\/noindex>\/<noindex><a rel=\"nofollow\" href=\"https:\/\/en.wikipedia.org\/wiki\/Traversal_Using_Relays_around_NAT\">TURN<\/a><\/noindex>).<\/p>\n<p>Ide\u00e1lne by sme chceli vytvori\u0165 sie\u0165ov\u00e9 API, vo vn\u00fatri pou\u017e\u00edvaj\u00face WebRTC, ale \u010do najviac podobn\u00e9 rozhraniu UDP Sockets, ktor\u00e9 nepotrebuje vytv\u00e1ra\u0165 pripojenie.<\/p>\n<p>To n\u00e1m umo\u017en\u00ed vyu\u017ei\u0165 v\u00fdhody WebRTC bez potreby zverej\u0148ova\u0165 zlo\u017eit\u00e9 detaily k\u00f3du aplik\u00e1cie (ktor\u00fd sme v na\u0161om projekte chceli zmeni\u0165 \u010do najmenej).<\/p>\n<h1>Minim\u00e1lne WebRTC<\/h1>\n<p>\nWebRTC je sada API dostupn\u00e1 v prehliada\u010doch, ktor\u00e1 umo\u017e\u0148uje prenos peer-to-peer zvuku, videa a r\u00f4znych d\u00e1t.<\/p>\n<p>Spojenie medzi peer-ami sa vytv\u00e1ra (aj v pr\u00edpade, \u017ee s\u00fa pr\u00edtomn\u00e9 NAT z jednej alebo oboch str\u00e1n) pomocou serverov STUN a\/alebo TURN prostredn\u00edctvom mechanizmu naz\u00fdvan\u00e9ho ICE. Peer-i si vymie\u0148aj\u00fa inform\u00e1cie ICE a parametre kan\u00e1lov prostredn\u00edctvom pon\u00fak a odpoved\u00ed protokolu SDP.<\/p>\n<p>Wow! Ko\u013eko skratiek naraz. Po\u010fme kr\u00e1tko vysvetli\u0165, \u010do tieto pojmy znamenaj\u00fa:<\/p>\n<ul>\n<li><noindex><a rel=\"nofollow\" href=\"https:\/\/en.wikipedia.org\/wiki\/STUN\"><strong>Session Traversal Utilities for NAT<\/strong> (<strong>STUN<\/strong>)<\/a><\/noindex> \u2014 protokol na ob\u00eddenie NAT a z\u00edskanie p\u00e1ru (IP, port) na v\u00fdmenu d\u00e1t priamo s hostite\u013eom. Ak sa mu podar\u00ed splni\u0165 svoju \u00falohu, peer-i si m\u00f4\u017eu samostatne vymie\u0148a\u0165 d\u00e1ta.<\/li>\n<li><noindex><a rel=\"nofollow\" href=\"https:\/\/en.wikipedia.org\/wiki\/Traversal_Using_Relays_around_NAT\"><strong>Traversal Using Relays around NAT<\/strong> (<strong>TURN<\/strong>)<\/a><\/noindex> tie\u017e sa pou\u017e\u00edva na ob\u00eddenie NAT, ale realizuje to presmerovan\u00edm d\u00e1t cez proxy, ktor\u00e1 je vidite\u013en\u00e1 obom peer-om. Prid\u00e1va oneskorenie a je n\u00e1kladnej\u0161\u00ed na vykonanie ako STUN (preto\u017ee sa pou\u017e\u00edva po\u010das celej rel\u00e1cie), ale niekedy je to jedin\u00e1 mo\u017en\u00e1 mo\u017enos\u0165.<\/li>\n<li><noindex><a rel=\"nofollow\" href=\"https:\/\/en.wikipedia.org\/wiki\/Interactive_Connectivity_Establishment\"><strong>Interactive Connectivity Establishment<\/strong> (<strong>ICE<\/strong>)<\/a><\/noindex> sa pou\u017e\u00edva na v\u00fdber najlep\u0161ieho mo\u017en\u00e9ho sp\u00f4sobu spojenia dvoch peer-ov na z\u00e1klade inform\u00e1ci\u00ed z\u00edskan\u00fdch pri priamom spojen\u00ed peer-ov, ako aj inform\u00e1ci\u00ed z\u00edskan\u00fdch z ak\u00e9hoko\u013evek mno\u017estva serverov STUN a TURN.<\/li>\n<li><noindex><a rel=\"nofollow\" href=\"https:\/\/en.wikipedia.org\/wiki\/Session_Description_Protocol\"><strong>Session Description Protocol<\/strong> (<strong>SDP<\/strong>)<\/a><\/noindex> \u2014 je to form\u00e1t na opis parametrov pripojovacieho kan\u00e1la, ako s\u00fa ICE kandid\u00e1ti, multimedi\u00e1lne kodeky (v pr\u00edpade audio\/video kan\u00e1la), at\u010f. Jeden z peer-ov posiela SDP ponuku (\"ponuku\"), a druh\u00fd odpoved\u00e1 SDP odpove\u010fou (\"odpove\u010fou\"). Potom sa vytvor\u00ed kan\u00e1l.<\/li>\n<\/ul>\n<p>\nAby sa tak\u00e9to spojenie vytvorilo, peer-om je potrebn\u00e9 zhroma\u017edi\u0165 inform\u00e1cie, ktor\u00e9 z\u00edskali od serverov STUN a TURN, a vymeni\u0165 si ich navz\u00e1jom.<\/p>\n<p>Probl\u00e9m je v tom, \u017ee zatia\u013e nemaj\u00fa mo\u017enos\u0165 vymie\u0148a\u0165 si d\u00e1ta priamo, a preto mus\u00ed existova\u0165 offline mechanizmus: sign\u00e1lny server.<\/p>\n<p>Sign\u00e1lny server m\u00f4\u017ee by\u0165 ve\u013emi jednoduch\u00fd, preto\u017ee jeho jedinou \u00falohou je presmerovanie d\u00e1t medzi peer-ami v etape \"podania ruky\" (ako je zn\u00e1zornen\u00e9 na sch\u00e9me ni\u017e\u0161ie).<\/p>\n<p><img decoding=\"async\" alt=\"Portujeme multiplayerov\u00fa hru z C++ na web s Cheerp, WebRTC a Firebase\" src=\"\/wp-content\/uploads\/2019\/09\/0fc39ba5e0942bc19183e8cf06c60868.jpg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<i>Zjednodu\u0161en\u00e1 sch\u00e9ma postupnosti \"podania ruky\" WebRTC<\/i><\/p>\n<h1>Preh\u013ead sie\u0165ov\u00e9ho modelu Teeworlds<\/h1>\n<p>\nSie\u0165ov\u00e1 architekt\u00fara Teeworlds je ve\u013emi jednoduch\u00e1:<\/p>\n<ul>\n<li>Komponenty klienta a servera s\u00fa dve r\u00f4zne programy.<\/li>\n<li>Klienti sa zap\u00e1jaj\u00fa do hry pripojen\u00edm k jedn\u00e9mu z viacer\u00fdch serverov, z ktor\u00fdch ka\u017ed\u00fd host\u00ed len jednu hru naraz.<\/li>\n<li>Cel\u00e1 v\u00fdmena d\u00e1t v hre prebieha prostredn\u00edctvom servera.<\/li>\n<li>\u0160peci\u00e1lny master-server sl\u00fa\u017ei na zhroma\u017e\u010fovanie zoznamu v\u0161etk\u00fdch verejn\u00fdch serverov, ktor\u00e9 sa zobrazuj\u00fa v hernom klienskom programe.<\/li>\n<\/ul>\n<p>\nV\u010faka vyu\u017eitiu WebRTC na v\u00fdmenu d\u00e1t m\u00f4\u017eeme presun\u00fa\u0165 serverov\u00fa komponenent do prehliada\u010da, kde sa nach\u00e1dza klient. To n\u00e1m d\u00e1va skvel\u00fa pr\u00edle\u017eitos\u0165...<\/p>\n<h1>Zbavi\u0165 sa serverov<\/h1>\n<p>\nAbsencia serverovej logiky m\u00e1 pr\u00edjemn\u00fa v\u00fdhodu: m\u00f4\u017eeme nasadi\u0165 cel\u00fa aplik\u00e1ciu ako statick\u00fd obsah na Github Pages alebo na vlastnom hardv\u00e9ri za Cloudflare, \u010d\u00edm si zabezpe\u010d\u00edme r\u00fdchle na\u010d\u00edtanie a vysok\u00fa dostupnos\u0165 bez n\u00e1kladov. V podstate na ne m\u00f4\u017eeme zabudn\u00fa\u0165 a ak budeme ma\u0165 \u0161\u0165astie, \u017ee hra sa stane popul\u00e1rnou, nebude potrebn\u00e9 modernizova\u0165 infra\u0161trukt\u00faru.<\/p>\n<p>Av\u0161ak, aby syst\u00e9m fungoval, budeme aj tak musie\u0165 pou\u017ei\u0165 extern\u00fa architekt\u00faru:<\/p>\n<ul>\n<li>Jeden alebo viac STUN serverov: m\u00e1me na v\u00fdber z nieko\u013ek\u00fdch bezplatn\u00fdch mo\u017enost\u00ed.<\/li>\n<li>Aspo\u0148 jeden TURN server: tu nie s\u00fa k dispoz\u00edcii bezplatn\u00e9 mo\u017enosti, tak\u017ee si m\u00f4\u017eeme bu\u010f nastavi\u0165 svoj, alebo plati\u0165 za slu\u017ebu. Na\u0161\u0165astie v\u00e4\u010d\u0161inu \u010dasu sa pripojenie d\u00e1 nadviaza\u0165 cez STUN servery (a zabezpe\u010di\u0165 prav\u00e9 p2p), ale TURN je potrebn\u00fd ako z\u00e1lo\u017en\u00e1 alternat\u00edva.<\/li>\n<li>Sign\u00e1lny server: na rozdiel od dvoch \u010fal\u0161\u00edch aspektov nie je signalizovanie \u0161tandardizovan\u00e9. To, za \u010do vlastne bude zodpovedn\u00fd sign\u00e1lny server, do ur\u010ditej miery z\u00e1vis\u00ed od aplik\u00e1cie. V na\u0161om pr\u00edpade je potrebn\u00e9 vymeni\u0165 si mal\u00e9 mno\u017estvo d\u00e1t pred nadviazan\u00edm spojenia.<\/li>\n<li>Master-server Teeworlds: pou\u017e\u00edvaj\u00fa ho in\u00e9 servery na upozornenie na svoju existenciu a klienti na vyh\u013ead\u00e1vanie verejn\u00fdch serverov. Hoci nie je povinn\u00fd (klienti sa v\u017edy m\u00f4\u017eu manu\u00e1lne pripoji\u0165 k zn\u00e1memu serveru), bolo by dobr\u00e9 ho ma\u0165, aby hr\u00e1\u010di mohli s\u00fa\u0165a\u017ei\u0165 v hr\u00e1ch s n\u00e1hodn\u00fdmi \u013eu\u010fmi.<\/li>\n<\/ul>\n<p>\nRozhodli sme sa pou\u017ei\u0165 bezplatn\u00e9 STUN servery od spolo\u010dnosti Google, zatia\u013e \u010do jeden TURN server sme si nasadili sami.<\/p>\n<p>Na dva posledn\u00e9 body sme pou\u017eili <noindex><a rel=\"nofollow\" href=\"https:\/\/en.wikipedia.org\/wiki\/Firebase\">Firebase<\/a><\/noindex>:<\/p>\n<ul>\n<li>Master server Teeworlds je implementovan\u00fd ve\u013emi jednoducho: ako zoznam objektov, ktor\u00fd obsahuje inform\u00e1cie (meno, IP, mapa, re\u017eim, \u2026) ka\u017ed\u00e9ho akt\u00edvneho servera. Servery publikuj\u00fa a aktualizuj\u00fa svoj vlastn\u00fd objekt, zatia\u013e \u010do klienti ber\u00fa cel\u00fd zoznam a zobrazuj\u00fa ho hr\u00e1\u010dovi. Zoznam tie\u017e zobrazuje na domovskej str\u00e1nke ako HTML, aby hr\u00e1\u010di mohli jednoducho klikn\u00fa\u0165 na server a priamo sa dosta\u0165 do hry.<\/li>\n<li>Signaliz\u00e1cia je \u00fazko spojen\u00e1 s na\u0161ou implement\u00e1ciou socketov, op\u00edsanou v nasleduj\u00facej \u010dasti.<\/li>\n<\/ul>\n<p>\n<img decoding=\"async\" alt=\"Portujeme multiplayerov\u00fa hru z C++ na web s Cheerp, WebRTC a Firebase\" src=\"\/wp-content\/uploads\/2019\/09\/5e30bc22643f8baecf850053bf69e130.jpg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<i>Zoznam serverov v hre a na domovskej str\u00e1nke<\/i><\/p>\n<h1>Implement\u00e1cia socketov<\/h1>\n<p>\nChceme vytvori\u0165 API, \u010do najbli\u017e\u0161ie k Posix UDP Sockets, aby sme minimalizovali po\u010det potrebn\u00fdch zmien.<\/p>\n<p>Takisto chceme implementova\u0165 nevyhnutn\u00e9 minimum, potrebn\u00e9 na najjednoduch\u0161iu v\u00fdmenu \u00fadajov v sieti.<\/p>\n<p>Napr\u00edklad, nepotrebujeme skuto\u010dn\u00e9 smerovanie: v\u0161etky peerov\u00e9 zariadenia s\u00fa v jednej \u201evirtu\u00e1lnej LAN\u201c, spojenej s konkr\u00e9tnym in\u0161tan\u010dn\u00fdm datab\u00e1zov\u00fdm syst\u00e9mom Firebase.<\/p>\n<p>Preto nepotrebujeme jedine\u010dn\u00e9 IP adresy: na jedine\u010dn\u00fa identifik\u00e1ciu peerov sta\u010d\u00ed pou\u017ei\u0165 jedine\u010dn\u00e9 hodnoty k\u013e\u00fa\u010dov Firebase (podobne ako dom\u00e9nov\u00e9 men\u00e1), a ka\u017ed\u00fd peer lok\u00e1lne prira\u010fuje \u201efalo\u0161n\u00e9\u201c IP adresy ka\u017ed\u00e9mu k\u013e\u00fa\u010du, ktor\u00fd je potrebn\u00e9 previes\u0165. To n\u00e1s \u00faplne zbavuje potreby glob\u00e1lneho prira\u010fovania IP adries, \u010do je netrivi\u00e1lna \u00faloha.<\/p>\n<p>Tu je minim\u00e1lne API, ktor\u00e9 mus\u00edme implementova\u0165:<\/p>\n<pre><code class=\"cpp\">\/\/ Create and destroy a socket\nint socket();\nint close(int fd);\n\/\/ Bind a socket to a port, and publish it on Firebase\nint bind(int fd, AddrInfo* addr);\n\/\/ Send a packet. This lazily create a WebRTC connection to the \n\/\/ peer when necessary\nint sendto(int fd, uint8_t* buf, int len, const AddrInfo* addr);\n\/\/ Receive the packets destined to this socket\nint recvfrom(int fd, uint8_t* buf, int len, AddrInfo* addr);\n\/\/ Be notified when new packets arrived\nint recvCallback(Callback cb);\n\/\/ Obtain a local ip address for this peer key\nuint32_t resolve(client::String* key);\n\/\/ Get the peer key for this ip\nString* reverseResolve(uint32_t addr);\n\/\/ Get the local peer key\nString* local_key();\n\/\/ Initialize the library with the given Firebase database and \n\/\/ WebRTc connection options\nvoid init(client::FirebaseConfig* fb, client::RTCConfiguration* ice);<\/code><\/pre>\n<p>\nAPI je jednoduch\u00e9 a podobn\u00e9 API Posix Sockets, ale m\u00e1 nieko\u013eko d\u00f4le\u017eit\u00fdch rozdielov: <strong>registr\u00e1cia sp\u00e4tn\u00fdch volan\u00ed, priradenie lok\u00e1lnych IP a \u201eleniv\u00e1\u201c konektivita<\/strong>.<\/p>\n<h2>Registr\u00e1cia sp\u00e4tn\u00fdch volan\u00ed<\/h2>\n<p>\nAj ke\u010f p\u00f4vodn\u00fd program pou\u017e\u00edva neblokuj\u00faci vstup\/v\u00fdstup, pre spustenie v webovom prehliada\u010di je potrebn\u00e9 k\u00f3d refaktorova\u0165.<\/p>\n<p>D\u00f4vodom je to, \u017ee slu\u010dka udalost\u00ed v prehliada\u010di je skryt\u00e1 pred programom (\u010di u\u017e je to JavaScript alebo WebAssembly).<\/p>\n<p>V nat\u00edvnom prostred\u00ed m\u00f4\u017eeme p\u00edsa\u0165 k\u00f3d takto<\/p>\n<pre><code class=\"cpp\">while(running) {\n  select(...); \/\/ \u010dakajte na I\/O udalosti\n  while(true) {\n    int r = readfrom(...); \/\/ pokus o \u010d\u00edtanie\n    if (r &lt; 0 &amp;&amp; errno == EWOULDBLOCK) \/\/ nie s\u00fa k dispoz\u00edcii \u010fal\u0161ie \u00fadaje\n      break;\n    ...\n  }\n  ...\n}<\/code><\/pre>\n<p>\nAk je slu\u010dka udalost\u00ed pre n\u00e1s skryt\u00e1, mus\u00edme ju premeni\u0165 na nie\u010do podobn\u00e9:<\/p>\n<pre><code class=\"cpp\">auto cb = []() { \/\/ toto sa zavol\u00e1, ke\u010f s\u00fa nov\u00e9 \u00fadaje k dispoz\u00edcii\n  while(true) {\n    int r = readfrom(...); \/\/ pokus o \u010d\u00edtanie\n    if (r &lt; 0 &amp;&amp; errno == EWOULDBLOCK) \/\/ nie s\u00fa k dispoz\u00edcii \u010fal\u0161ie \u00fadaje\n      break;\n    ...\n  }\n  ...\n};\nrecvCallback(cb); \/\/ registr\u00e1cia sp\u00e4tn\u00e9ho volania<\/code><\/pre>\n<p><\/p>\n<h2>Priradenie lok\u00e1lnych IP<\/h2>\n<p>\nIdentifik\u00e1tory uzlov v na\u0161ej \u2018sieti\u2019 nie s\u00fa IP adresy, ale Firebase k\u013e\u00fa\u010de (to s\u00fa re\u0165azce, ktor\u00e9 vyzeraj\u00fa takto: <code>-LmEC50PYZLCiCP-vqde<\/code> ).<\/p>\n<p>Je to pohodln\u00e9, preto\u017ee nepotrebujeme mechanizmus na priradenie IP a kontrolu ich jedine\u010dnosti (ako aj ich likvid\u00e1ciu po odpojen\u00ed klienta), ale ve\u013emi \u010dasto je potrebn\u00e9 identifikova\u0165 peer pod\u013ea \u010d\u00edselnej hodnoty.<\/p>\n<p>Na tento \u00fa\u010del sa pou\u017e\u00edvaj\u00fa funkcie <code>resolve<\/code> a <code>reverseResolve<\/code>: aplik\u00e1cia z\u00edska re\u0165azcov\u00fa hodnotu k\u013e\u00fa\u010da (cez vstup pou\u017e\u00edvate\u013ea alebo cez majstrovsk\u00fd server) a m\u00f4\u017ee ju previes\u0165 na IP adresu na vn\u00fatorn\u00e9 pou\u017eitie. Zvy\u0161ok API pre jednoduchos\u0165 tie\u017e z\u00edska t\u00fato hodnotu namiesto re\u0165azca.<\/p>\n<p>Je to podobn\u00e9 ako DNS vyh\u013ead\u00e1vanie, ale vykon\u00e1va sa lok\u00e1lne u klienta.<\/p>\n<p>To znamen\u00e1, \u017ee IP adresy nem\u00f4\u017eu by\u0165 zdie\u013ean\u00e9 medzi r\u00f4znymi klientmi, a ak potrebujeme nejak\u00fd glob\u00e1lny identifik\u00e1tor, bude ho potrebn\u00e9 generova\u0165 in\u00fdm sp\u00f4sobom.<\/p>\n<h2>Leniv\u00e9 pripojenie<\/h2>\n<p>\nUDP nevy\u017eaduje pripojenie, ale, ako sme videli, sk\u00f4r ako za\u010dneme pren\u00e1\u0161a\u0165 \u00fadaje medzi dvoma peerami, WebRTC vy\u017eaduje dlh\u00fd proces pripojenia.<\/p>\n<p>Ak chceme zabezpe\u010di\u0165 rovnak\u00fa \u00farove\u0148 abstrakcie, (<code>sendto<\/code>\/<code>recvfrom<\/code> s \u013eubovo\u013en\u00fdmi peerami bez predch\u00e1dzaj\u00faceho pripojenia), mus\u00edme vykona\u0165 \u201eleniv\u00e9\u201c (oneskoren\u00e9) pripojenie v r\u00e1mci API.<\/p>\n<p>Toto je to, \u010do sa deje pri be\u017enej v\u00fdmene \u00fadajov medzi \u201eserverom\u201c a \u201eklientom\u201c v pr\u00edpade pou\u017eitia UDP, a \u010do by mala na\u0161a kni\u017enica vykon\u00e1va\u0165:<\/p>\n<ul>\n<li>Server vol\u00e1 <code>bind()<\/code>, aby informoval opera\u010dn\u00fd syst\u00e9m, \u017ee chce prij\u00edma\u0165 pakety na zadan\u00fd port.<\/li>\n<\/ul>\n<p>\nNamiesto toho zverejn\u00edme otvoren\u00fd port vo Firebase pod k\u013e\u00fa\u010dom servera a budeme po\u010d\u00fava\u0165 udalosti v jeho podstromu.<\/p>\n<ul>\n<li>Server vol\u00e1 <code>recvfrom()<\/code>, prij\u00edmaj\u00faci na tento port pakety poch\u00e1dzaj\u00face od ak\u00e9hoko\u013evek hostite\u013ea.<\/li>\n<\/ul>\n<p>\nV na\u0161om pr\u00edpade je potrebn\u00e9 kontrolova\u0165 prich\u00e1dzaj\u00facu frontu paketov, ktor\u00e9 boli odoslan\u00e9 na tento port.<\/p>\n<p>Ka\u017ed\u00fd port m\u00e1 svoju vlastn\u00fa frontu, a prid\u00e1vame na za\u010diatok d\u00e1tagramov WebRTC p\u00f4vodn\u00e9 a cie\u013eov\u00e9 porty, aby sme vedeli, do ktorej fronty presmerova\u0165 nov\u00fd paket pri jeho pr\u00edchode.<\/p>\n<p>Volanie je neblo\u010dka, tak\u017ee ak nie s\u00fa pakety, jednoducho vr\u00e1time -1 a nastav\u00edme <code>errno=EWOULDBLOCK<\/code>.<\/p>\n<ul>\n<li>Klient z\u00edska extern\u00fdmi prostriedkami IP a port servera a vol\u00e1 <code>sendto()<\/code>. Tie\u017e pritom prebieha vn\u00fatorn\u00e9 volanie <code>bind()<\/code>, tak\u017ee nasleduj\u00faci <code>recvfrom()<\/code> z\u00edska odpove\u010f bez explicitn\u00e9ho vykonania bind.<\/li>\n<\/ul>\n<p>\nV na\u0161om pr\u00edpade klient extern\u00fdm sp\u00f4sobom z\u00edskava re\u0165azov\u00fd k\u013e\u00fa\u010d a pou\u017e\u00edva funkciu <code>resolve()<\/code> na z\u00edskanie IP adresy.<\/p>\n<p>V tejto f\u00e1ze za\u010d\u00edname \u201epodanie ruky\u201c WebRTC, ak sa dva peer e\u0161te navz\u00e1jom nespojili. Pripojenia na r\u00f4zne porty jedn\u00e9ho peer pou\u017e\u00edvaj\u00fa rovnak\u00fd DataChannel WebRTC.<\/p>\n<p>Taktie\u017e vykon\u00e1vame nepriamy <code>bind()<\/code>, aby server mohol obnovi\u0165 pripojenie v nasleduj\u00facom <code>sendto()<\/code> v pr\u00edpade, \u017ee sa z nejak\u00e9ho d\u00f4vodu uzavrie.<\/p>\n<p>Server je informovan\u00fd o pripojen\u00ed klienta, ke\u010f klient zapisuje svoj SDP offer pod inform\u00e1ciou o porte servera vo Firebase, a server tam odpoved\u00e1 svoj\u00edm odpovedom.<\/p>\n<p>\nNa ni\u017e\u0161ie uvedenom sch\u00e9me je uk\u00e1\u017eka prenosu spr\u00e1v pre sch\u00e9mu soketov a prenos prv\u00e9ho spr\u00e1vy od klienta k serveru:<\/p>\n<p><img decoding=\"async\" alt=\"Portujeme multiplayerov\u00fa hru z C++ na web s Cheerp, WebRTC a Firebase\" src=\"\/wp-content\/uploads\/2019\/09\/7dc48e816c09e33daf28c307cad0c258.jpg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<i>\u00dapln\u00e1 sch\u00e9ma f\u00e1zy pripojenia medzi klientom a serverom<\/i><\/p>\n<h1>Z\u00e1ver<\/h1>\n<p>\nAk ste do\u010d\u00edtali a\u017e do konca, pravdepodobne v\u00e1s zauj\u00edma pozrie\u0165 sa na te\u00f3riu v praxi. Hru si m\u00f4\u017eete zahra\u0165 na <noindex><a rel=\"nofollow\" href=\"https:\/\/teeworlds.leaningtech.com\">teeworlds.leaningtech.com<\/a><\/noindex>, vysk\u00fa\u0161ajte!<\/p>\n<p><center><iframe loading=\"lazy\" width=\"560\" height=\"315\" src=\"https:\/\/gfycat.com\/ifr\/newjaggedcoyote\" frameborder=\"0\" allowfullscreen><\/iframe><\/center><br \/>\n<i>Priate\u013esk\u00fd z\u00e1pas medzi kolegami<\/i><\/p>\n<p>K\u00f3d sie\u0165ovej kni\u017enice je vo\u013ene dostupn\u00fd na <noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/leaningtech\/cheerpnet\">Github<\/a><\/noindex>. Pridajte sa k diskusii na na\u0161om kan\u00e1li v <noindex><a rel=\"nofollow\" href=\"https:\/\/gitter.im\/leaningtech\/cheerp\">Gitter<\/a><\/noindex>!<br \/>\n<br \/>Zdroj: <a content=\"nofollow\" rel=\"nofollow\" href=\"https:\/\/habr.com\/ru\/post\/468031\/\">habr.com<\/a><\/p>","protected":false,"gt_translate_keys":[{"key":"rendered","format":"html"}]},"excerpt":{"rendered":"<p>\u0412\u0432\u0435\u0434\u0435\u043d\u0438\u0435 \u041d\u0430\u0448\u0430 \u043a\u043e\u043c\u043f\u0430\u043d\u0438\u044f Leaning Technologies \u043f\u0440\u0435\u0434\u043e\u0441\u0442\u0430\u0432\u043b\u044f\u0435\u0442 \u0440\u0435\u0448\u0435\u043d\u0438\u044f \u043f\u043e \u043f\u043e\u0440\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044e \u0442\u0440\u0430\u0434\u0438\u0446\u0438\u043e\u043d\u043d\u044b\u0445 desktop-\u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0439 \u0432 \u0432\u0435\u0431. \u041d\u0430\u0448 \u043a\u043e\u043c\u043f\u0438\u043b\u044f\u0442\u043e\u0440 C++ Cheerp \u0433\u0435\u043d\u0435\u0440\u0438\u0440\u0443\u0435\u0442 \u0441\u043e\u0447\u0435\u0442\u0430\u043d\u0438\u0435 WebAssembly \u0438 JavaScript, \u0447\u0442\u043e \u043e\u0431\u0435\u0441\u043f\u0435\u0447\u0438\u0432\u0430\u0435\u0442 \u0438 \u043f\u0440\u043e\u0441\u0442\u043e\u0435 \u0432\u0437\u0430\u0438\u043c\u043e\u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0435 \u0441 \u0431\u0440\u0430\u0443\u0437\u0435\u0440\u043e\u043c, \u0438 \u0432\u044b\u0441\u043e\u043a\u0443\u044e \u043f\u0440\u043e\u0438\u0437\u0432\u043e\u0434\u0438\u0442\u0435\u043b\u044c\u043d\u043e\u0441\u0442\u044c. \u0412 \u043a\u0430\u0447\u0435\u0441\u0442\u0432\u0435 \u043f\u0440\u0438\u043c\u0435\u0440\u0430 \u0435\u0433\u043e \u043f\u0440\u0438\u043c\u0435\u043d\u0435\u043d\u0438\u044f \u043c\u044b \u0440\u0435\u0448\u0438\u043b\u0438 \u043f\u043e\u0440\u0442\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0434\u043b\u044f \u0432\u0435\u0431\u0430 \u043c\u043d\u043e\u0433\u043e\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c\u0441\u043a\u0443\u044e \u0438\u0433\u0440\u0443 \u0438 \u0432\u044b\u0431\u0440\u0430\u043b\u0438 \u0434\u043b\u044f \u044d\u0442\u043e\u0433\u043e Teeworlds. Teeworlds \u2014 \u044d\u0442\u043e \u043c\u043d\u043e\u0433\u043e\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c\u0441\u043a\u0430\u044f \u0434\u0432\u0443\u0445\u043c\u0435\u0440\u043d\u0430\u044f \u0440\u0435\u0442\u0440\u043e-\u0438\u0433\u0440\u0430 [&hellip;]<\/p>\n","protected":false,"gt_translate_keys":[{"key":"rendered","format":"html"}]},"author":1,"featured_media":28801,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[688],"tags":[],"class_list":["post-38370","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-administrirovanie"],"aioseo_notices":[],"aioseo_head":"\n\t\t<!-- All in One SEO 5.0.4 - aioseo.com -->\n\t<meta name=\"description\" content=\"\u0412\u0432\u0435\u0434\u0435\u043d\u0438\u0435 \u041d\u0430\u0448\u0430 \u043a\u043e\u043c\u043f\u0430\u043d\u0438\u044f Leaning Technologies \u043f\u0440\u0435\u0434\u043e\u0441\u0442\u0430\u0432\u043b\u044f\u0435\u0442 \u0440\u0435\u0448\u0435\u043d\u0438\u044f \u043f\u043e \u043f\u043e\u0440\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044e \u0442\u0440\u0430\u0434\u0438\u0446\u0438\u043e\u043d\u043d\u044b\u0445 desktop-\u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0439 \u0432 \u0432\u0435\u0431.\" \/>\n\t<meta name=\"robots\" content=\"max-image-preview:large\" \/>\n\t<meta name=\"author\" content=\"Yuri Gagarin\"\/>\n\t<link rel=\"canonical\" href=\"https:\/\/prohoster.info\/sk\/blog\/administrirovanie\/portiruem-mnogopolzovatelskuyu-igru-s-s-na-veb-c-cheerp-webrtc-i-firebase\" \/>\n\t\t<meta name=\"generator\" content=\"All in One SEO (AIOSEO) 5.0.4\" \/>\n\t\t<meta property=\"og:locale\" content=\"sk_SK\" \/>\n\t\t<meta property=\"og:site_name\" content=\"ProHoster | \u041a\u0443\u043f\u0438\u0442\u044c \u043d\u0430\u0434\u0435\u0436\u043d\u044b\u0439 \u0445\u043e\u0441\u0442\u0438\u043d\u0433 \u0434\u043b\u044f \u0441\u0430\u0439\u0442\u043e\u0432 \u0441 \u0437\u0430\u0449\u0438\u0442\u043e\u0439 \u043e\u0442 DDoS, VPS VDS \u0441\u0435\u0440\u0432\u0435\u0440\u044b\" \/>\n\t\t<meta property=\"og:type\" content=\"article\" \/>\n\t\t<meta property=\"og:title\" content=\"\ud83e\udd47\u041f\u043e\u0440\u0442\u0438\u0440\u0443\u0435\u043c \u043c\u043d\u043e\u0433\u043e\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c\u0441\u043a\u0443\u044e \u0438\u0433\u0440\u0443 \u0441 \u0421++ \u043d\u0430 \u0432\u0435\u0431 c Cheerp, WebRTC \u0438 Firebase | ProHoster\" \/>\n\t\t<meta property=\"og:description\" content=\"\u0412\u0432\u0435\u0434\u0435\u043d\u0438\u0435 \u041d\u0430\u0448\u0430 \u043a\u043e\u043c\u043f\u0430\u043d\u0438\u044f Leaning Technologies \u043f\u0440\u0435\u0434\u043e\u0441\u0442\u0430\u0432\u043b\u044f\u0435\u0442 \u0440\u0435\u0448\u0435\u043d\u0438\u044f \u043f\u043e \u043f\u043e\u0440\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044e \u0442\u0440\u0430\u0434\u0438\u0446\u0438\u043e\u043d\u043d\u044b\u0445 desktop-\u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0439 \u0432 \u0432\u0435\u0431.\" \/>\n\t\t<meta property=\"og:url\" content=\"https:\/\/prohoster.info\/sk\/blog\/administrirovanie\/portiruem-mnogopolzovatelskuyu-igru-s-s-na-veb-c-cheerp-webrtc-i-firebase\" \/>\n\t\t<meta property=\"og:image\" content=\"https:\/\/prohoster.info\/wp-content\/uploads\/2021\/11\/logo-350.jpg\" \/>\n\t\t<meta property=\"og:image:secure_url\" content=\"https:\/\/prohoster.info\/wp-content\/uploads\/2021\/11\/logo-350.jpg\" \/>\n\t\t<meta property=\"og:image:width\" content=\"350\" \/>\n\t\t<meta property=\"og:image:height\" content=\"350\" \/>\n\t\t<meta property=\"article:published_time\" content=\"2019-10-31T19:23:18+00:00\" \/>\n\t\t<meta property=\"article:modified_time\" content=\"2019-10-31T19:23:18+00:00\" \/>\n\t\t<meta property=\"article:publisher\" content=\"https:\/\/www.facebook.com\/prohoster\" \/>\n\t\t<meta property=\"article:author\" content=\"https:\/\/www.facebook.com\/prohoster\" \/>\n\t\t<!-- All in One SEO -->\n\n","aioseo_head_json":{"title":"\ud83e\udd47Portujeme viacu\u017e\u00edvate\u013esk\u00fa hru z C++ na web s Cheerp, WebRTC a Firebase | ProHoster","description":"\u00davod Na\u0161a spolo\u010dnos\u0165 Leaning Technologies poskytuje rie\u0161enia na portovanie tradi\u010dn\u00fdch desktopov\u00fdch aplik\u00e1ci\u00ed do webu.","canonical_url":"https:\/\/prohoster.info\/sk\/blog\/administrirovanie\/portiruem-mnogopolzovatelskuyu-igru-s-s-na-veb-c-cheerp-webrtc-i-firebase","robots":"max-image-preview:large","keywords":"","webmasterTools":{"miscellaneous":""},"schema":null,"og:locale":"sk_SK","og:site_name":"ProHoster | \u041a\u0443\u043f\u0438\u0442\u044c \u043d\u0430\u0434\u0435\u0436\u043d\u044b\u0439 \u0445\u043e\u0441\u0442\u0438\u043d\u0433 \u0434\u043b\u044f \u0441\u0430\u0439\u0442\u043e\u0432 \u0441 \u0437\u0430\u0449\u0438\u0442\u043e\u0439 \u043e\u0442 DDoS, VPS VDS \u0441\u0435\u0440\u0432\u0435\u0440\u044b","og:type":"article","og:title":"\ud83e\udd47\u041f\u043e\u0440\u0442\u0438\u0440\u0443\u0435\u043c \u043c\u043d\u043e\u0433\u043e\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c\u0441\u043a\u0443\u044e \u0438\u0433\u0440\u0443 \u0441 \u0421++ \u043d\u0430 \u0432\u0435\u0431 c Cheerp, WebRTC \u0438 Firebase | ProHoster","og:description":"\u0412\u0432\u0435\u0434\u0435\u043d\u0438\u0435 \u041d\u0430\u0448\u0430 \u043a\u043e\u043c\u043f\u0430\u043d\u0438\u044f Leaning Technologies \u043f\u0440\u0435\u0434\u043e\u0441\u0442\u0430\u0432\u043b\u044f\u0435\u0442 \u0440\u0435\u0448\u0435\u043d\u0438\u044f \u043f\u043e \u043f\u043e\u0440\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044e \u0442\u0440\u0430\u0434\u0438\u0446\u0438\u043e\u043d\u043d\u044b\u0445 desktop-\u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0439 \u0432 \u0432\u0435\u0431.","og:url":"https:\/\/prohoster.info\/sk\/blog\/administrirovanie\/portiruem-mnogopolzovatelskuyu-igru-s-s-na-veb-c-cheerp-webrtc-i-firebase","og:image":"https:\/\/prohoster.info\/wp-content\/uploads\/2021\/11\/logo-350.jpg","og:image:secure_url":"https:\/\/prohoster.info\/wp-content\/uploads\/2021\/11\/logo-350.jpg","og:image:width":350,"og:image:height":350,"article:published_time":"2019-10-31T19:23:18+00:00","article:modified_time":"2019-10-31T19:23:18+00:00","article:publisher":"https:\/\/www.facebook.com\/prohoster","article:author":"https:\/\/www.facebook.com\/prohoster"},"aioseo_meta_data":{"post_id":"38370","title":null,"description":null,"keywords":null,"keyphrases":null,"primary_term":null,"canonical_url":null,"og_title":null,"og_description":null,"og_object_type":"default","og_image_type":"default","og_image_url":null,"og_image_width":null,"og_image_height":null,"og_image_custom_url":null,"og_image_custom_fields":null,"og_video":null,"og_custom_url":null,"og_article_section":null,"og_article_tags":null,"twitter_use_og":false,"twitter_card":"default","twitter_image_type":"default","twitter_image_url":null,"twitter_image_custom_url":null,"twitter_image_custom_fields":null,"twitter_title":null,"twitter_description":null,"schema":{"blockGraphs":[],"customGraphs":[],"default":{"data":{"Article":[],"Course":[],"Dataset":[],"FAQPage":[],"Movie":[],"Person":[],"Product":[],"ProductReview":[],"Car":[],"Recipe":[],"Service":[],"SoftwareApplication":[],"WebPage":[]},"graphName":"","isEnabled":true},"graphs":[]},"schema_type":null,"schema_type_options":null,"pillar_content":false,"robots_default":true,"robots_noindex":false,"robots_noarchive":false,"robots_nosnippet":false,"robots_nofollow":false,"robots_noimageindex":false,"robots_noodp":false,"robots_notranslate":false,"robots_max_snippet":null,"robots_max_videopreview":null,"robots_max_imagepreview":"large","priority":null,"frequency":null,"local_seo":null,"seo_analyzer_scan_date":"2026-01-23 21:44:19","breadcrumb_settings":null,"limit_modified_date":false,"reviewed_by":null,"ai":null,"created":"2021-03-01 01:10:24","updated":"2026-01-23 21:44:19","focus_keyword":null,"additional_keywords":null,"truseo_locale":null},"gt_translate_keys":[{"key":"link","format":"url"}],"_links":{"self":[{"href":"https:\/\/prohoster.info\/sk\/wp-json\/wp\/v2\/posts\/38370","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/prohoster.info\/sk\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/prohoster.info\/sk\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/prohoster.info\/sk\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/prohoster.info\/sk\/wp-json\/wp\/v2\/comments?post=38370"}],"version-history":[{"count":0,"href":"https:\/\/prohoster.info\/sk\/wp-json\/wp\/v2\/posts\/38370\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/prohoster.info\/sk\/wp-json\/wp\/v2\/media\/28801"}],"wp:attachment":[{"href":"https:\/\/prohoster.info\/sk\/wp-json\/wp\/v2\/media?parent=38370"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/prohoster.info\/sk\/wp-json\/wp\/v2\/categories?post=38370"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/prohoster.info\/sk\/wp-json\/wp\/v2\/tags?post=38370"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}