Ittrasferixxi logħba multiplayer minn C++ għall-web b'Cheerp, WebRTC u Firebase

Introduzzjoni

kumpanija tagħna Teknoloġiji Leaning jipprovdi soluzzjonijiet għall-portabbiltà tal-applikazzjonijiet tradizzjonali tad-desktop fuq il-web. Il-kompilatur C++ tagħna cheerp jiġġenera taħlita ta 'WebAssembly u JavaScript, li jipprovdi t-tnejn interazzjoni sempliċi tal-browser, u prestazzjoni għolja.

Bħala eżempju tal-applikazzjoni tagħha, iddeċidejna li ntrasferixxu logħba multiplayer fuq il-web u għażilna Teeworlds. Teeworlds hija logħba retro multiplayer XNUMXD b'komunità żgħira iżda attiva ta' plejers (inkluż jien!). Huwa żgħir kemm f'termini ta 'riżorsi mniżżla kif ukoll rekwiżiti ta' CPU u GPU - kandidat ideali.

Ittrasferixxi logħba multiplayer minn C++ għall-web b'Cheerp, WebRTC u Firebase
Tħaddim fil-browser Teeworlds

Iddeċidejna li nużaw dan il-proġett biex nesperimentaw miegħu soluzzjonijiet ġenerali għall-portabbiltà tal-kodiċi tan-netwerk għall-web. Dan normalment isir bil-modi li ġejjin:

  • XMLHttpRequest/fetch, jekk il-parti tan-netwerk tikkonsisti biss minn talbiet HTTP, jew
  • sokits tal-web.

Iż-żewġ soluzzjonijiet jeħtieġu li jospitaw komponent ta 'server fuq in-naħa tas-server, u l-ebda waħda ma tippermetti l-użu bħala protokoll tat-trasport UDP. Dan huwa importanti għal applikazzjonijiet f'ħin reali bħal softwer u logħob tal-konferenzi bil-vidjo, minħabba li jiggarantixxi l-kunsinna u l-ordni tal-pakketti tal-protokoll TCP jista' jsir xkiel għal latenza baxxa.

Hemm it-tielet mod - uża n-netwerk mill-browser: WebRTC.

RTCDataChannel Jappoġġja trasmissjoni kemm affidabbli kif ukoll mhux affidabbli (f'dan l-aħħar każ jipprova juża l-UDP bħala protokoll tat-trasport kull meta jkun possibbli), u jista 'jintuża kemm ma' server remot kif ukoll bejn browsers. Dan ifisser li nistgħu port l-applikazzjoni kollha għall-browser, inkluż il-komponent server!

Madankollu, dan jiġi b'diffikultà addizzjonali: qabel ma żewġ WebRTC pari jistgħu jikkomunikaw, jeħtieġ li jwettqu handshake relattivament kumpless biex jikkonnettjaw, li teħtieġ diversi entitajiet ta 'partijiet terzi (server ta' sinjalazzjoni u server wieħed jew aktar. STORD/DAWRAN).

Idealment, nixtiequ noħolqu API ta' netwerk li juża WebRTC internament, iżda li jkun qrib kemm jista' jkun ta' interface ta' Sockets UDP li m'għandux għalfejn jistabbilixxi konnessjoni.

Dan se jippermettilna nieħdu vantaġġ minn WebRTC mingħajr ma jkollna nesponu dettalji kumplessi għall-kodiċi tal-applikazzjoni (li ridna nibdlu mill-inqas possibbli fil-proġett tagħna).

WebRTC minimu

WebRTC huwa sett ta 'APIs disponibbli fil-browsers li jipprovdi trażmissjoni peer-to-peer ta' awdjo, vidjo u data arbitrarja.

Il-konnessjoni bejn il-pari hija stabbilita (anki jekk hemm NAT fuq naħa waħda jew iż-żewġ naħat) bl-użu ta 'servers STUN u/jew TURN permezz ta' mekkaniżmu msejjaħ ICE. Peers jiskambjaw informazzjoni ICE u parametri tal-kanal permezz ta 'offerta u tweġiba tal-protokoll SDP.

Ara naqra! Kemm abbrevjazzjonijiet f'ħin wieħed? Ejja nispjegaw fil-qosor xi jfissru dawn it-termini:

  • Sessjoni Traversal Utilities għal NAT (STORD) — protokoll biex jinqabeż NAT u jikseb par (IP, port) għall-iskambju tad-dejta direttament mal-host. Jekk jirnexxielu jlesti l-kompitu tiegħu, allura sħabhom jistgħu jiskambjaw id-dejta b'mod indipendenti ma 'xulxin.
  • Traversal bl-użu ta' Relays madwar NAT (DAWRAN) jintuża wkoll għal traversal NAT, iżda jimplimenta dan billi jibgħat data permezz ta 'prokura li hija viżibbli għaż-żewġ sħabhom. Iżżid il-latenza u tiswa aktar biex timplimenta minn STUN (għax tiġi applikata matul is-sessjoni ta 'komunikazzjoni kollha), iżda xi drabi hija l-unika għażla.
  • Stabbiliment ta' Konnettività Interattiva (ICE) użat biex jintgħażel l-aħjar metodu possibbli ta 'konnessjoni ta' żewġ peers ibbażat fuq informazzjoni miksuba mill-konnessjoni ta 'pari direttament, kif ukoll informazzjoni riċevuta minn kwalunkwe numru ta' servers STUN u TURN.
  • Deskrizzjoni tas-Sessjoni Protokoll (SDP) huwa format biex jiddeskrivi l-parametri tal-kanal tal-konnessjoni, pereżempju, kandidati ICE, codecs multimedjali (fil-każ ta 'kanal awdjo/vidjo), eċċ... Wieħed mill-pari jibgħat Offerta SDP, u t-tieni jirrispondi b'Tweġiba SDP . . Wara dan, jinħoloq kanal.

Biex tinħoloq konnessjoni bħal din, il-pari jridu jiġbru l-informazzjoni li jirċievu mis-servers STUN u TURN u jiskambjawha bejniethom.

Il-problema hija li għad m'għandhomx il-kapaċità li jikkomunikaw direttament, għalhekk irid jeżisti mekkaniżmu barra mill-banda biex jiskambjaw din id-dejta: server ta 'sinjalazzjoni.

Server ta 'sinjalazzjoni jista' jkun sempliċi ħafna għaliex l-uniku xogħol tiegħu huwa li jgħaddi data bejn sħabhom fil-fażi ta 'handshake (kif muri fid-dijagramma hawn taħt).

Ittrasferixxi logħba multiplayer minn C++ għall-web b'Cheerp, WebRTC u Firebase
Dijagramma simplifikata tas-sekwenza tal-handshake WebRTC

Ħarsa ġenerali tal-Mudell tan-Netwerk Teeworlds

L-arkitettura tan-netwerk Teeworlds hija sempliċi ħafna:

  • Il-komponenti tal-klijent u tas-server huma żewġ programmi differenti.
  • Il-klijenti jidħlu fil-logħba billi jikkonnettjaw ma 'wieħed minn diversi servers, li kull wieħed minnhom jospita logħba waħda biss kull darba.
  • It-trasferiment tad-dejta kollu fil-logħba jitwettaq permezz tas-server.
  • Jintuża master server speċjali biex tinġabar lista tas-servers pubbliċi kollha li jintwerew fil-klijent tal-logħba.

Grazzi għall-użu tal-WebRTC għall-iskambju tad-dejta, nistgħu nittrasferixxu l-komponent tas-server tal-logħba għall-browser fejn jinsab il-klijent. Dan jagħtina opportunità kbira...

Jeħles mis-servers

In-nuqqas ta 'loġika tas-server għandu vantaġġ sabiħ: nistgħu niskjeraw l-applikazzjoni kollha bħala kontenut statiku fuq Github Pages jew fuq il-ħardwer tagħna stess wara Cloudflare, u b'hekk niżguraw downloads veloċi u uptime għoli b'xejn. Fil-fatt, nistgħu ninsew dwarhom, u jekk aħna xxurtjati u l-logħba ssir popolari, allura l-infrastruttura ma jkollhiex għalfejn tiġi modernizzata.

Madankollu, biex is-sistema taħdem, għad irridu nużaw arkitettura esterna:

  • Server wieħed jew aktar STUN: Għandna diversi għażliet b'xejn minn fejn nagħżlu.
  • Mill-inqas server TURN wieħed: m'hemm l-ebda għażliet b'xejn hawn, għalhekk nistgħu jew inwaqqfu tagħna stess jew inħallsu għas-servizz. Fortunatament, il-biċċa l-kbira tal-ħin il-konnessjoni tista 'tiġi stabbilita permezz ta' servers STUN (u tipprovdi p2p vera), iżda TURN huwa meħtieġ bħala għażla fallback.
  • Server tas-Sinjali: B'differenza miż-żewġ aspetti l-oħra, is-sinjalar mhuwiex standardizzat. Dak li s-server tas-sinjalar fil-fatt ikun responsabbli jiddependi xi ftit fuq l-applikazzjoni. Fil-każ tagħna, qabel ma tistabbilixxi konnessjoni, huwa meħtieġ li tiskambja ammont żgħir ta 'dejta.
  • Teeworlds Master Server: Jintuża minn servers oħra biex jirreklamaw l-eżistenza tagħhom u mill-klijenti biex isibu servers pubbliċi. Filwaqt li mhuwiex meħtieġ (il-klijenti dejjem jistgħu jgħaqqdu ma 'server li jafu dwaru manwalment), ikun sabiħ li jkun hemm sabiex il-plejers jistgħu jipparteċipaw fil-logħob ma' nies każwali.

Iddeċidejna li nużaw is-servers STUN b'xejn ta' Google, u skjerajna server TURN wieħed aħna stess.

Għall-aħħar żewġ punti użajna Firebase:

  • Is-server prinċipali Teeworlds huwa implimentat b'mod sempliċi ħafna: bħala lista ta 'oġġetti li jkun fihom informazzjoni (isem, IP, mappa, mod, ...) ta' kull server attiv. Is-servers jippubblikaw u jaġġornaw l-oġġett tagħhom stess, u l-klijenti jieħdu l-lista kollha u juruha lill-plejer. Aħna wkoll nuru l-lista fuq il-paġna ewlenija bħala HTML sabiex il-plejers jistgħu sempliċement ikklikkja fuq is-server u jittieħdu direttament għal-logħba.
  • Is-sinjalar huwa relatat mill-qrib mal-implimentazzjoni tas-sokits tagħna, deskritta fit-taqsima li jmiss.

Ittrasferixxi logħba multiplayer minn C++ għall-web b'Cheerp, WebRTC u Firebase
Lista tas-servers ġewwa l-logħba u fuq il-paġna ewlenija

Implimentazzjoni ta' sokits

Irridu noħolqu API li tkun qrib Posix UDP Sockets kemm jista 'jkun biex timminimizza n-numru ta' bidliet meħtieġa.

Irridu wkoll nimplimentaw il-minimu meħtieġ meħtieġ għall-aktar skambju ta' data sempliċi fuq in-netwerk.

Pereżempju, m'għandniex bżonn rotta reali: il-pari kollha huma fuq l-istess "LAN virtwali" assoċjata ma' każ speċifiku tad-database Firebase.

Għalhekk, m'għandniex bżonn indirizzi IP uniċi: valuri uniċi taċ-ċavetta Firebase (simili għall-ismijiet tad-dominju) huma biżżejjed biex jidentifikaw sħabhom b'mod uniku, u kull peer jassenja lokalment indirizzi IP "foloz" għal kull ċavetta li trid tiġi tradotta. Dan jelimina kompletament il-ħtieġa għall-assenjazzjoni tal-indirizz IP globali, li hija kompitu mhux trivjali.

Hawn hu l-API minimu li għandna bżonn nimplimentaw:

// Create and destroy a socket
int socket();
int close(int fd);
// Bind a socket to a port, and publish it on Firebase
int bind(int fd, AddrInfo* addr);
// Send a packet. This lazily create a WebRTC connection to the 
// peer when necessary
int sendto(int fd, uint8_t* buf, int len, const AddrInfo* addr);
// Receive the packets destined to this socket
int recvfrom(int fd, uint8_t* buf, int len, AddrInfo* addr);
// Be notified when new packets arrived
int recvCallback(Callback cb);
// Obtain a local ip address for this peer key
uint32_t resolve(client::String* key);
// Get the peer key for this ip
String* reverseResolve(uint32_t addr);
// Get the local peer key
String* local_key();
// Initialize the library with the given Firebase database and 
// WebRTc connection options
void init(client::FirebaseConfig* fb, client::RTCConfiguration* ice);

L-API hija sempliċi u simili għall-API Posix Sockets, iżda għandha ftit differenzi importanti: illoggjar callbacks, jassenja IPs lokali, u konnessjonijiet għażżien.

Reġistrazzjoni ta' Callbacks

Anke jekk il-programm oriġinali juża I/O li ma jimblokkax, il-kodiċi għandu jiġi refactored biex jaħdem f'web browser.

Ir-raġuni għal dan hija li l-linja tal-avveniment fil-browser hija moħbija mill-programm (kemm jekk tkun JavaScript jew WebAssembly).

Fl-ambjent nattiv nistgħu niktbu kodiċi bħal dan

while(running) {
  select(...); // wait for I/O events
  while(true) {
    int r = readfrom(...); // try to read
    if (r < 0 && errno == EWOULDBLOCK) // no more data available
      break;
    ...
  }
  ...
}

Jekk il-linja tal-avvenimenti hija moħbija għalina, allura għandna bżonn nibdluh f'xi ħaġa bħal din:

auto cb = []() { // this will be called when new data is available
  while(true) {
    int r = readfrom(...); // try to read
    if (r < 0 && errno == EWOULDBLOCK) // no more data available
      break;
    ...
  }
  ...
};
recvCallback(cb); // register the callback

Assenjazzjoni IP lokali

L-IDs tan-nodi fin-"netwerk" tagħna mhumiex indirizzi IP, iżda ċwievet Firebase (huma strings li jidhru bħal dan: -LmEC50PYZLCiCP-vqde ).

Dan huwa konvenjenti għaliex m'għandniex bżonn mekkaniżmu għall-assenjazzjoni tal-IPs u l-iċċekkjar tal-uniċità tagħhom (kif ukoll ir-rimi tagħhom wara li l-klijent jiskonnettja), iżda ħafna drabi huwa meħtieġ li jiġu identifikati sħabhom b'valur numeriku.

Dan huwa eżattament għalxiex jintużaw il-funzjonijiet. resolve и reverseResolve: L-applikazzjoni b'xi mod tirċievi l-valur tas-sekwenza taċ-ċavetta (permezz tal-input tal-utent jew permezz tas-server prinċipali), u tista 'tikkonverti f'indirizz IP għal użu intern. Il-bqija tal-API jirċievi wkoll dan il-valur minflok string għas-sempliċità.

Dan huwa simili għal tfittxija DNS, iżda mwettqa lokalment fuq il-klijent.

Jiġifieri, l-indirizzi IP ma jistgħux jinqasmu bejn klijenti differenti, u jekk ikun meħtieġ xi tip ta 'identifikatur globali, ikollu jiġi ġġenerat b'mod differenti.

Konnessjoni għażżien

UDP m'għandux bżonn konnessjoni, iżda kif rajna, WebRTC jeħtieġ proċess ta 'konnessjoni twil qabel ma jkun jista' jibda jittrasferixxi d-dejta bejn żewġ sħabhom.

Jekk irridu nipprovdu l-istess livell ta' estrazzjoni, (sendto/recvfrom ma 'pari arbitrarji mingħajr konnessjoni minn qabel), allura għandhom iwettqu konnessjoni "għażżien" (ittardjata) ġewwa l-API.

Dan huwa dak li jiġri waqt il-komunikazzjoni normali bejn is-"server" u l-"klijent" meta tuża l-UDP, u x'għandha tagħmel il-librerija tagħna:

  • Sejħiet tas-server bind()biex tgħid lis-sistema operattiva li trid tirċievi pakketti fuq il-port speċifikat.

Minflok, aħna se nippubblikaw port miftuħ għal Firebase taħt iċ-ċavetta tas-server u nisimgħu għall-avvenimenti fis-subtree tagħha.

  • Sejħiet tas-server recvfrom(), jaċċetta pakketti li ġejjin minn kwalunkwe ospitanti fuq dan il-port.

Fil-każ tagħna, għandna bżonn niċċekkjaw il-kju deħlin ta 'pakketti mibgħuta lil dan il-port.

Kull port għandu l-kju tiegħu stess, u aħna nżidu l-portijiet tas-sors u tad-destinazzjoni mal-bidu tad-datagrams WebRTC sabiex inkunu nafu għal liema kju nibgħatu meta jasal pakkett ġdid.

Is-sejħa mhix imblukkata, għalhekk jekk ma jkunx hemm pakketti, aħna sempliċement nirritornaw -1 u nissettjaw errno=EWOULDBLOCK.

  • Il-klijent jirċievi l-IP u l-port tas-server b'xi mezzi esterni, u jsejjaħ sendto(). Dan jagħmel ukoll sejħa interna. bind(), għalhekk sussegwenti recvfrom() se jirċievi t-tweġiba mingħajr ma jeżegwixxi b'mod espliċitu bind.

Fil-każ tagħna, il-klijent esternament jirċievi ċ-ċavetta string u juża l-funzjoni resolve() biex tikseb indirizz IP.

F'dan il-punt, nibdew handshake WebRTC jekk iż-żewġ sħabhom għadhom mhumiex konnessi ma 'xulxin. Konnessjonijiet għal portijiet differenti tal-istess peer jużaw l-istess WebRTC DataChannel.

Aħna nwettqu wkoll indiretti bind()sabiex is-server jista 'jerġa' jgħaqqad fil-li jmiss sendto() fil-każ li għalqet għal xi raġuni.

Is-server jiġi nnotifikat bil-konnessjoni tal-klijent meta l-klijent jikteb l-offerta SDP tiegħu taħt l-informazzjoni tal-port tas-server f'Firebase, u s-server jirrispondi bir-rispons tiegħu hemmhekk.

Id-dijagramma ta’ hawn taħt turi eżempju ta’ fluss ta’ messaġġi għal skema ta’ socket u t-trażmissjoni tal-ewwel messaġġ mill-klijent għas-server:

Ittrasferixxi logħba multiplayer minn C++ għall-web b'Cheerp, WebRTC u Firebase
Dijagramma kompluta tal-fażi tal-konnessjoni bejn il-klijent u s-server

Konklużjoni

Jekk qrajt s'issa, probabbilment int interessat li tara t-teorija fl-azzjoni. Il-logħba tista' tintlagħab fuqha teeworlds.leaningtech.com, ippruvaha!


Loghba ta’ hbiberija bejn kollegi

Il-kodiċi tal-librerija tan-netwerk huwa disponibbli b'mod liberu fuq GitHub. Ingħaqad mal-konversazzjoni fuq il-kanal tagħna fuq Gitter!

Sors: www.habr.com

Żid kumment