{"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\/et\/blog\/administrirovanie\/portiruem-mnogopolzovatelskuyu-igru-s-s-na-veb-c-cheerp-webrtc-i-firebase","title":{"rendered":"Kandideerime mitme kasutajaga m\u00e4ngu C++-st veebiversiooni Cheerp, WebRTC ja Firebase abil","gt_translate_keys":[{"key":"rendered","format":"text"}]},"content":{"rendered":"<h2>Sissejuhatus<\/h2>\n<p>\nMeie ettev\u00f5te <noindex><a rel=\"nofollow\" href=\"https:\/\/leaningtech.com\">Leaning Technologies<\/a><\/noindex> pakub lahendusi traditsiooniliste t\u00f6\u00f6lauarakenduste veebiversioonide jaoks. Meie C++ kompass <noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/leaningtech\/cheerp-meta\">Cheerp<\/a><\/noindex> genereerib WebAssembly ja JavaScripti kombinatsiooni, mis tagab nii <noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/leaningtech\/cheerp-meta\/wiki\/Cheerp-Tutorial%3A-Mixed-mode-C++-to-WebAssembly-and-JavaScript\">lihtsa suhtluse brauseriga<\/a><\/noindex>, kui ka k\u00f5rge j\u00f5udluse.<\/p>\n<p>Kuna n\u00e4ide selle kasutamisest otsustasime portida veebile jaoks mitmikm\u00e4ngu ja valisime selle jaoks <noindex><a rel=\"nofollow\" href=\"https:\/\/www.teeworlds.com\/\"><strong>Teeworlds<\/strong><\/a><\/noindex>. Teeworlds on kahem\u00f5\u00f5tmeline retro-mitmik m\u00e4ng v\u00e4ikese, kuid aktiivse m\u00e4ngijate kogukonnaga (sealhulgas mina!). See on v\u00e4ike nii alla laadimise ressurssides kui ka CPU ja GPU n\u00f5uetes \u2014 ideaalne kandidaat.<\/p>\n<p><img decoding=\"async\" alt=\"Kandideerime mitme kasutajaga m\u00e4ngu C++-st veebiversiooni Cheerp, WebRTC ja Firebase abil\" src=\"\/wp-content\/uploads\/2019\/09\/30bff36ee1158ddea2e76d8b11e48d2b.jpg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<i>Teeworlds, mis t\u00f6\u00f6tab brauseris<\/i><br \/>\n<noindex><a rel=\"nofollow\" name=\"habracut\"><\/a><\/noindex><br \/>\nOtsustasime kasutada seda projekti, et eksperimenteerida <strong>\u00fcldiste lahendustega v\u00f5rgu koodi portimiseks veebisse<\/strong>. Tavaliselt teostatakse seda j\u00e4rgmiste viisidega:<\/p>\n<ul>\n<li><strong>XMLHttpRequest\/fetch<\/strong>, kui v\u00f5rguosa koosneb ainult HTTP-p\u00e4ringutest, v\u00f5i<\/li>\n<li><strong>WebSockets<\/strong>.<\/li>\n<\/ul>\n<p>\nM\u00f5lemad lahendused n\u00f5uavad, et serveri komponent oleks serveripoolse hostimisega, ja kumbki ei luba kasutada transportprotokollina <noindex><a rel=\"nofollow\" href=\"https:\/\/en.wikipedia.org\/wiki\/User_Datagram_Protocol\">UDP<\/a><\/noindex>. See on oluline reaalajas rakenduste, nagu videokonverentside tarkvara ja m\u00e4ngude jaoks, sest protokolli pakkumise ja j\u00e4rjekorra tagamise garantiid <noindex><a rel=\"nofollow\" href=\"https:\/\/en.wikipedia.org\/wiki\/Transmission_Control_Protocol\">TCP<\/a><\/noindex> v\u00f5ivad segada madala latentsusega.<\/p>\n<p>On olemas ka kolmas tee \u2014 kasutada brauseriv\u00f5rku: <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> toetab nii usaldusv\u00e4\u00e4rset kui ka usaldusv\u00e4\u00e4rset edastamist (viimasel juhul p\u00fc\u00fcab see v\u00f5imalusel kasutada transpordiprotokollina UDP-d) ning v\u00f5ib t\u00f6\u00f6tada nii kaugserveriga kui ka brauserite vahel. <strong>See t\u00e4hendab, et saame portida kogu rakenduse brauserisse, sealhulgas serverikomponendi!<\/strong><\/p>\n<p>Siiski on sellega seotud lisaraskus: enne kui kaks WebRTC peer'i saavad andmeid vahetada, peavad nad l\u00e4bima suhteliselt keerulise \u201ek\u00e4tlemise\u201c protseduuri \u00fchendamiseks, milleks on vajalik mitu kolmanda osapoole erinevat \u00fcksust (signaaliserver ja \u00fcks v\u00f5i mitu serverit <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>Ideaalis tahaksime luua v\u00f5rguteabe, mis kasutab siseselt WebRTC-d, kuid on nii l\u00e4hedane UDP Sockets liidesele, et see ei n\u00f5ua \u00fchenduse loomist.<\/p>\n<p>See aitab meil kasutada WebRTC eeliseid, ilma et peaksime rakenduse koodi keerulisi \u00fcksikasju paljastama (mida soovisime oma projektis v\u00e4hem muuta).<\/p>\n<h1>Minimaalne WebRTC<\/h1>\n<p>\nWebRTC on brauserites olemasolev API komplekt, mis v\u00f5imaldab peer-to-peer heli, video ja mis tahes andmete edastamist.<\/p>\n<p>\u00dchendus peer\u2019ide vahel luuakse (isegi kui NAT on \u00fchelt v\u00f5i kahelt poolt) STUN ja\/v\u00f5i TURN serverite abil, kasutades mehhanismi nimega ICE. Peer\u2019id vahetavad ICE teavet ja kanaliparameetreid SDP protokolli pakkumise ja vastuse kaudu.<\/p>\n<p>Vau! Nii palju l\u00fchendeid korraga. Selgiteks l\u00fchidalt, mida need m\u00f5isted t\u00e4hendavad:<\/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 protokoll NAT-i \u00fcletamiseks ja (IP, port) p\u00e1ru hankimiseks, et andmeid otse hostiga vahetada. Kui see suudab oma \u00fclesande t\u00e4ita, saavad peer\u2019id omavahel andmeid iseseisvalt vahetada.<\/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> kasutatakse ka NAT-i \u00fcletamiseks, kuid see teeb seda, suunates andmeid kaudu vaheproksi, mis on m\u00f5lemale peerile n\u00e4htav. See lisab viivituse ja on t\u00e4itmisel kulukam kui STUN (kuna see rakendatakse kogu suhtlusseansi v\u00e4ltel), kuid m\u00f5nikord on see ainus v\u00f5imalik variant.<\/li>\n<li><noindex><a rel=\"nofollow\" href=\"https:\/\/en.wikipedia.org\/wiki\/Interactive_Connectivity_Establishment\"><strong>Interaktiivne \u00dchenduse Loomine<\/strong> (<strong>ICE<\/strong>)<\/a><\/noindex> kasutatakse kahe peer'i parima \u00fchendusviisi valimiseks, tuginedes teabele, mis on saadud otsese \u00fchenduse loomisel peer'ide vahel, ning teabele, mis on saadud igasugustelt STUN ja TURN serveritelt.<\/li>\n<li><noindex><a rel=\"nofollow\" href=\"https:\/\/en.wikipedia.org\/wiki\/Session_Description_Protocol\"><strong>Seansi Kirjeldamise Protokoll<\/strong> (<strong>SDP<\/strong>)<\/a><\/noindex> on \u00fchenduskanali parameetrite, n\u00e4iteks ICE kandidaatidest, meedia koodekidest (heli\/videokanali puhul) jne, kirjeldamise formaat. \u00dcks peer saadab SDP pakkumise (\"pakkumine\"), teine vastab SDP vastusega (\"vastus\"). P\u00e4rast seda luuakse kanal.<\/li>\n<\/ul>\n<p>\nSelle \u00fchenduse loomiseks peavad peer'id koguma teavet, mille nad on saanud STUN ja TURN serveritelt, ja vahetama seda omavahel.<\/p>\n<p>Probleem on selles, et neil pole veel v\u00f5imalust andmeid otse vahetada, seega peab andmete vahetamiseks olema olemas vaheline mehhanism: signaaliserver.<\/p>\n<p>Signaalserver v\u00f5ib olla v\u00e4ga lihtne, kuna selle ainus \u00fclesanne on edastada andmeid paaride vahel \u201ek\u00e4tlemise\u201d etapis (nagu on kujutatud alloleval skeemil).<\/p>\n<p><img decoding=\"async\" alt=\"Kandideerime mitme kasutajaga m\u00e4ngu C++-st veebiversiooni Cheerp, WebRTC ja Firebase abil\" src=\"\/wp-content\/uploads\/2019\/09\/0fc39ba5e0942bc19183e8cf06c60868.jpg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<i>Lihtsustatud WebRTC k\u00e4tlemise j\u00e4rjestuse skeem<\/i><\/p>\n<h1>Teeworldsi v\u00f5rgu mudeli \u00fclevaade<\/h1>\n<p>\nTeeworldsi v\u00f5rgu arhitektuur on v\u00e4ga lihtne:<\/p>\n<ul>\n<li>Kliendi ja serveri komponendid on kaks erinevat programmi.<\/li>\n<li>Kliendid liituvad m\u00e4nguga, \u00fchendudes \u00fche mitmest serverist, millest iga\u00fcks hostib korraga ainult \u00fchte m\u00e4ngu.<\/li>\n<li>K\u00f5ik m\u00e4ngus sisalduvad andmed edastatakse serveri kaudu.<\/li>\n<li>Eraldi meistriserverit kasutatakse k\u00f5igi avalike serverite nimekirja kogumiseks, mis kuvatakse m\u00e4ngu kliendis.<\/li>\n<\/ul>\n<p>\nAndmete vahetamiseks WebRTC kasutamine v\u00f5imaldab meil viia m\u00e4ngu serveri komponendi brauserisse, kus klient asub. See annab meile suurep\u00e4rase v\u00f5imaluse\u2026<\/p>\n<h1>Vabaneda serveritest<\/h1>\n<p>\nServeri loogika puudumine toob kaasa meeldiva eelise: saame rakenduse paigutada staatilise sisuna GitHub Pages'i v\u00f5i oma seadmetesse Cloudflare'i tagant, kindlustades seel\u00e4bi tasuta kiire laadimise ja k\u00f5rge t\u00f6\u00f6aja. Tegelikult v\u00f5iksime neist t\u00e4ielikult unustada, ning kui meil vedama hakkab ja m\u00e4ng muutub populaarseks, siis infrastruktuuri uuendama ei pea.<\/p>\n<p>Kuid s\u00fcsteemi t\u00f5husaks t\u00f6\u00f6tamiseks peame siiski kasutama v\u00e4list arhitektuuri:<\/p>\n<ul>\n<li>\u00dcks v\u00f5i mitu STUN serverit: meil on valida mitme tasuta variandi vahel.<\/li>\n<li>K\u00f5ik v\u00e4hemalt \u00fcks TURN server: siin tasuta variante ei ole, seega v\u00f5ime kas seada oma v\u00f5i maksta teenuse eest. \u00d5nneks saab suurema osa ajast \u00fchenduse luua STUN serverite kaudu (tagades t\u00f5elise p2p), kuid TURN on vajalik varuv\u00f5imalusena.<\/li>\n<li>Signaaliserver: erinevalt teistest kaht aspektist ei ole signaalimise standardiseerimist. See, mille eest signaaliserver tegelikult vastutab, s\u00f5ltub osade kaupa rakendusest. Meie puhul tuleb enne \u00fchenduse loomist vahetada v\u00e4ike andmemaht.<\/li>\n<li>Teeworldsi meistriserver: seda kasutavad teised serverid oma olemasolu teatamiseks ning kliendid avalike serverite leidmiseks. Kuigi see ei ole kohustuslik (kliendid saavad alati manuaalselt \u00fchenduda tuttava serveriga), oleks see kasulik, et m\u00e4ngijad saaksid osaleda m\u00e4ngudes juhuslike inimestega.<\/li>\n<\/ul>\n<p>\nOtsustasime kasutada Google'i tasuta STUN-servereid, ning \u00fche TURN-serveri seadistasime ise.<\/p>\n<p>Kahe viimase punkti jaoks kasutasime <noindex><a rel=\"nofollow\" href=\"https:\/\/en.wikipedia.org\/wiki\/Firebase\">Firebase<\/a><\/noindex>:<\/p>\n<ul>\n<li>Teeworldsi meistriserver on v\u00e4ga lihtsalt rakendatud: see on objektiloetelu, mis sisaldab teavet (nimi, IP, kaart, re\u017eiim jne) iga aktiivse serveri kohta. Serverid avaldavad ja uuendavad oma objekti ning kliendid saavad kogu loendi ning kuvavad selle m\u00e4ngijale. Samuti kuvame loendi avalehel HTMLina, et m\u00e4ngijad saaksid lihtsalt serverile klikkida ja otse m\u00e4ngu siseneda.<\/li>\n<li>Signaalimine on tihedalt seotud meie soklite rakendusega, mida kirjeldatakse j\u00e4rgmises jaotises.<\/li>\n<\/ul>\n<p>\n<img decoding=\"async\" alt=\"Kandideerime mitme kasutajaga m\u00e4ngu C++-st veebiversiooni Cheerp, WebRTC ja Firebase abil\" src=\"\/wp-content\/uploads\/2019\/09\/5e30bc22643f8baecf850053bf69e130.jpg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<i>Serverite loend m\u00e4ngus ja avalehel<\/i><\/p>\n<h1>Soklite rakendamine<\/h1>\n<p>\nSoovime luua API, mis oleks v\u00f5imalikult l\u00e4hedane Posix UDP sokkidele, et minimeerida vajalike muudatuste arvu.<\/p>\n<p>Samuti soovime rakendada vajalikku miinimumi, mis on vajalik k\u00f5ige lihtsama andmevahetuse jaoks \u00fcle v\u00f5rgu.<\/p>\n<p>N\u00e4iteks me ei vaja t\u00f5elist suunamist: k\u00f5ik peer\u2019id asuvad \u00fches \u201evirtuaalses LANis\u201c, mis on seotud konkreetse Firebase'i andmebaasi eksemplariga.<\/p>\n<p>Seet\u00f5ttu ei ole meil unikaalsed IP-aadressid vajalikud: piisab, kui kasutada unikaalseid Firebase v\u00f5tme v\u00e4\u00e4rtusi (sarnaselt domeeninimedele), ning iga peer m\u00e4\u00e4rab igale v\u00f5tmele kohalikult \"vale\" IP-aadressi, mida tuleb konverteerida. See vabastab meid globaalsete IP-aadresside m\u00e4\u00e4ramisest, mis on mitte triviaalne \u00fclesanne.<\/p>\n<p>Siin on minimaalne API, mille me peame rakendama:<\/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 on lihtne ja sarnaneb Posix Sockets API-le, kuid on mitmeid olulisi erinevusi: <strong>tagasikutsumiste registreerimine, kohalike IP-de m\u00e4\u00e4ramine ja \"laiska\" \u00fchendamine<\/strong>.<\/p>\n<h2>Tagasikutsumiste registreerimine<\/h2>\n<p>\nIsegi kui algne programm kasutab mittesummutavat I\/O-d, tuleb kood veebibrauseris t\u00f6\u00f6tamiseks refaktoorida.<\/p>\n<p>Selle p\u00f5hjuseks on see, et brauseris on s\u00fcndmuste ts\u00fckkel programmist varjatud (\u00fcksk\u00f5ik kas see on JavaScript v\u00f5i WebAssembly).<\/p>\n<p>Kohalikus keskkonnas saame koodi kirjutada j\u00e4rgmiselt<\/p>\n<pre><code class=\"cpp\">while(running) {\n  select(...); \/\/ oota I\/O s\u00fcndmusi\n  while(true) {\n    int r = readfrom(...); \/\/ proovi lugeda\n    if (r &lt; 0 &amp;&amp; errno == EWOULDBLOCK) \/\/ enam andmeid ei ole k\u00e4ttev\u00f5etav\n      break;\n    ...\n  }\n  ...\n}<\/code><\/pre>\n<p>\nKui s\u00fcndmuste ts\u00fckkel on meie jaoks varjatud, tuleb see muuta millegi sarnaseks:<\/p>\n<pre><code class=\"cpp\">auto cb = []() { \/\/ see this will be called when new data is available\n  while(true) {\n    int r = readfrom(...); \/\/ try to read\n    if (r &lt; 0 &amp;&amp; errno == EWOULDBLOCK) \/\/ no more data available\n      break;\n    ...\n  }\n  ...\n};\nrecvCallback(cb); \/\/ register the callback<\/code><\/pre>\n<p><\/p>\n<h2>Kohalikud IP-aadressid<\/h2>\n<p>\nMeie 'v\u00f5rgus' olevate s\u00f5lmede identifikaatorid ei ole IP-aadressid, vaid Firebase'i v\u00f5tmed (need on stringid, mis n\u00e4evad v\u00e4lja sellised: <code>-LmEC50PYZLCiCP-vqde<\/code> ).<\/p>\n<p>See on mugav, sest me ei vaja mehhanismi IP-de m\u00e4\u00e4ramiseks ja nende unikaalsuse kontrollimiseks (samuti nende utiliseerimiseks p\u00e4rast kliendi lahti\u00fchendamist), kuid tihti on vajalik identifitseerida peer'e numbriliste v\u00e4\u00e4rtuste j\u00e4rgi.<\/p>\n<p>Just selle jaoks kasutatakse funktsioone <code>resolve<\/code> ja <code>reverseResolve<\/code>: rakendus saab t\u00f5lkida v\u00f5tme stringi v\u00e4\u00e4rtuse (kasutaja sisendi kaudu v\u00f5i master-serveri kaudu) ja v\u00f5ib selle sisemiselt IP-aadressiks muuta. API \u00fclej\u00e4\u00e4nud osa saab samuti selle v\u00e4\u00e4rtuse stringina, et lihtsustada kasutamist.<\/p>\n<p>See on sarnane DNS-otsingule, kuid toimub kliendi kohal.<\/p>\n<p>See t\u00e4hendab, et IP-aadressid ei saa olla erinevate klientide jaoks \u00fchised ja kui on vajalik mingi globaalne identifikaator, tuleb see genereerida muul viisil.<\/p>\n<h2>Laisk \u00fchendus<\/h2>\n<p>\nUDP ei n\u00f5ua \u00fchendust, kuid nagu me oleme n\u00e4inud, enne andmete edastamise alustamist kahe peer'i vahel n\u00f5uab WebRTC pikka \u00fchendusprotsessi.<\/p>\n<p>Kui soovime tagada sama abstraktsiooni taseme, (<code>sendto<\/code>\/<code>recvfrom<\/code> ilma eelneva \u00fchenduseta juhuslike peer'idega), peame teostama \"laiska\" (edasil\u00fckatud) \u00fchendamist API sees.<\/p>\n<p>Nii toimub tavaline andmevahetus \"serveri\" ja \"kliendi\" vahel UDP kasutamisel ning mida peaks meie teek tegema:<\/p>\n<ul>\n<li>Server kutsub v\u00e4lja <code>bind()<\/code>, et teavitada operatsioonis\u00fcsteemi soovist saada pakette m\u00e4\u00e4ratud porti.<\/li>\n<\/ul>\n<p>\nSelle asemel avaldame Firebase'is avatud portaali serveri v\u00f5tme all ja kuulame s\u00fcndmusi selle alampuus.<\/p>\n<ul>\n<li>Server kutsub v\u00e4lja <code>recvfrom()<\/code>, et v\u00f5tta vastu pakette, mis saadetakse sellele portile mistahes hostilt.<\/li>\n<\/ul>\n<p>\nMeie puhul tuleb kontrollida sissetulevat pakettide j\u00e4rjekorda, mis on saadetud sellele portile.<\/p>\n<p>Igal pordil on oma j\u00e4rjekord ning me lisame WebRTC datagrammidele algusesse alg- ja l\u00f5pp-pordid, et teada, kuhu suunata uus pakett.<\/p>\n<p>V\u00e4ljakutse on mitteblokeeriv, seega, kui pakette pole, tagastame lihtsalt -1 ja m\u00e4\u00e4rame <code>errno=EWOULDBLOCK<\/code>.<\/p>\n<ul>\n<li>Kliendil on v\u00e4liste allikate kaudu IP ja serveri port, ning ta kutsub <code>sendto()<\/code>. Samuti toimub see ajal sisemine kutse <code>bind()<\/code>, seega j\u00e4rgnev <code>recvfrom()<\/code> saab vastuse ilma selge bind-kutsumise protsessita.<\/li>\n<\/ul>\n<p>\nMeie puhul saab klient v\u00e4liselt stringi v\u00f5tme ja kasutab funktsiooni <code>resolve()<\/code> IP-aadresse saamiseks.<\/p>\n<p>Selles etapis alustame WebRTC \"k\u00e4tlemist\", kui kaks peer-i veel ei ole \u00fcksteisega \u00fchendatud. \u00dchendused \u00fche peer-i erinevatele portidele kasutavad sama WebRTC DataChannelit.<\/p>\n<p>Me teostame ka kaudset <code>bind()<\/code>, et server saaks j\u00e4rgmises <code>sendto()<\/code> kui see mingil p\u00f5hjusel suletakse.<\/p>\n<p>Serverile antakse teada kliendi \u00fchendusest, kui klient salvestab oma SDP pakkumise serveri porti informatsiooniga Firebase'is, ja server vastab seal oma vastusega.<\/p>\n<p>\nAlltoodud skeemil on n\u00e4idatud s\u00f5numite liikumine socket-skeemi ja kliendi jaoks serveri esimese s\u00f5numi edastamine:<\/p>\n<p><img decoding=\"async\" alt=\"Kandideerime mitme kasutajaga m\u00e4ngu C++-st veebiversiooni Cheerp, WebRTC ja Firebase abil\" src=\"\/wp-content\/uploads\/2019\/09\/7dc48e816c09e33daf28c307cad0c258.jpg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<i>Kliendi ja serveri vahelise \u00fchenduse etapi t\u00e4ielik skeem<\/i><\/p>\n<h1>Kokkuv\u00f5te<\/h1>\n<p>\nKui olete l\u00f5puni lugenud, siis on teil ilmselt huvi n\u00e4ha teooriat praktikas. M\u00e4ngu saab m\u00e4ngida aadressil <noindex><a rel=\"nofollow\" href=\"https:\/\/teeworlds.leaningtech.com\">teeworlds.leaningtech.com<\/a><\/noindex>, proovige!<\/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>S\u00f5bralik m\u00e4ng kolleegide vahel<\/i><\/p>\n<p>V\u00f5rgu raamatukogu kood on vabalt saadaval <noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/leaningtech\/cheerpnet\">Github<\/a><\/noindex>. Liitu meie kanaliga suhtlemiseks <noindex><a rel=\"nofollow\" href=\"https:\/\/gitter.im\/leaningtech\/cheerp\">Gitter<\/a><\/noindex>!<br \/>\n<br \/>Allikas: <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 4.9.10 - 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. \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\" \/>\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\/et\/blog\/administrirovanie\/portiruem-mnogopolzovatelskuyu-igru-s-s-na-veb-c-cheerp-webrtc-i-firebase\" \/>\n\t<meta name=\"generator\" content=\"All in One SEO (AIOSEO) 4.9.10\" \/>\n\t\t<meta property=\"og:locale\" content=\"et_EE\" \/>\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. \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\" \/>\n\t\t<meta property=\"og:url\" content=\"https:\/\/prohoster.info\/et\/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\udd47Portime mitme m\u00e4ngijaga m\u00e4ngu C++-st veebis Cheerp, WebRTC ja Firebase abil | ProHoster","description":"Sissejuhatus Meie ettev\u00f5te Leaning Technologies pakub lahendusi traditsiooniliste desktop-rakenduste veebiversioonide jaoks. Meie C++ kompileerija Cheerp genereerib WebAssembly ja JavaScripti kombinatsiooni, mis tagab lihtsa suhtlemise brauseriga ning k\u00f5rge efektiivsuse. N\u00e4itena selle rakendamisest otsustasime portida veebiversiooniks mitme m\u00e4ngijaga m\u00e4ngu ja valisime selleks Teeworldsi. Teeworlds on mitme m\u00e4ngijaga 2D retro-m\u00e4ng","canonical_url":"https:\/\/prohoster.info\/et\/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":"et_EE","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. \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","og:url":"https:\/\/prohoster.info\/et\/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"},"gt_translate_keys":[{"key":"link","format":"url"}],"_links":{"self":[{"href":"https:\/\/prohoster.info\/et\/wp-json\/wp\/v2\/posts\/38370","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/prohoster.info\/et\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/prohoster.info\/et\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/prohoster.info\/et\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/prohoster.info\/et\/wp-json\/wp\/v2\/comments?post=38370"}],"version-history":[{"count":0,"href":"https:\/\/prohoster.info\/et\/wp-json\/wp\/v2\/posts\/38370\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/prohoster.info\/et\/wp-json\/wp\/v2\/media\/28801"}],"wp:attachment":[{"href":"https:\/\/prohoster.info\/et\/wp-json\/wp\/v2\/media?parent=38370"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/prohoster.info\/et\/wp-json\/wp\/v2\/categories?post=38370"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/prohoster.info\/et\/wp-json\/wp\/v2\/tags?post=38370"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}