Porting game bebarengan saka C ++ menyang web karo Cheerp, WebRTC lan Firebase

Pambuka

perusahaan kita Teknologi Leaning menehi solusi kanggo porting aplikasi desktop tradisional menyang web. Compiler C++ kita surak ngasilake kombinasi WebAssembly lan JavaScript, sing nyedhiyakake loro-lorone interaksi browser prasaja, lan kinerja dhuwur.

Minangka conto aplikasi, kita mutusaké kanggo port game bebarengan menyang web lan milih Teeworlds. Teeworlds minangka game retro 2D multiplier kanthi komunitas pemain cilik nanging aktif (kalebu aku!). Iku cilik saka segi sumber daya sing diunduh lan syarat CPU lan GPU - calon sing cocog.

Porting game bebarengan saka C ++ menyang web karo Cheerp, WebRTC lan Firebase
Mlaku ing browser Teeworlds

Kita mutusake nggunakake proyek iki kanggo eksperimen solusi umum kanggo porting kode jaringan menyang web. Iki biasane ditindakake kanthi cara ing ngisor iki:

  • XMLHttpRequest/fetch, yen bagean jaringan mung kasusun saka panjalukan HTTP, utawa
  • WebSocket.

Loro-lorone solusi mbutuhake hosting komponen server ing sisih server, lan ora ngidini digunakake minangka protokol transportasi UDP. Iki penting kanggo aplikasi wektu nyata kayata piranti lunak lan game konferensi video, amarga njamin pangiriman lan urutan paket protokol. TCP bisa dadi alangan kanggo latensi kurang.

Ana cara katelu - nggunakake jaringan saka browser: WebRTC.

Saluran RTCData Ndhukung transmisi sing bisa dipercaya lan ora bisa dipercaya (ing kasus terakhir nyoba nggunakake UDP minangka protokol transportasi yen bisa), lan bisa digunakake karo server remot lan ing antarane browser. Iki tegese kita bisa ngirim kabeh aplikasi menyang browser, kalebu komponen server!

Nanging, iki teka karo kangelan tambahan: sadurunge loro kanca WebRTC bisa komunikasi, padha kudu nindakake salaman relatif Komplek kanggo nyambung, kang mbutuhake sawetara entitas pihak katelu (server signaling lan siji utawa luwih server. STUN/NGABUK).

Saenipun, kita seneng nggawe API jaringan sing nggunakake WebRTC internal, nanging minangka cedhak antarmuka UDP Sockets sing ora perlu kanggo nggawe sambungan.

Iki bakal ngidini kita njupuk kauntungan saka WebRTC tanpa kudu mbukak rincian rumit menyang kode aplikasi (sing kita pengin ngganti sethithik ing proyek kita).

WebRTC minimal

WebRTC minangka sakumpulan API sing kasedhiya ing browser sing nyedhiyakake transmisi audio, video lan data sewenang-wenang peer-to-peer.

Sambungan antarane kanca-kanca ditetepake (sanajan ana NAT ing siji utawa loro-lorone) nggunakake STUN lan / utawa TURN server liwat mekanisme disebut ICE. Peers ngganti informasi ICE lan paramèter saluran liwat tawaran lan jawaban saka protokol SDP.

wah! Carane akeh singkatan ing siji wektu? Ayo diterangake kanthi ringkes apa tegese istilah kasebut:

  • Session Traversal Utilities kanggo NAT (STUN) - protokol kanggo ngliwati NAT lan entuk pasangan (IP, port) kanggo ijol-ijolan data langsung karo host. Yen dheweke bisa ngrampungake tugase, mula kanca-kancane bisa saling tukar data kanthi mandiri.
  • Traversal Nggunakake Relay watara NAT (NGABUK) uga digunakake kanggo traversal NAT, nanging ngleksanakake iki dening nerusake data liwat proxy sing katon kanggo loro ora pati cetho. Iku nambah latensi lan luwih larang kanggo dileksanakake tinimbang STUN (amarga ditrapake ing kabeh sesi komunikasi), nanging kadhangkala mung siji-sijine pilihan.
  • Pembentukan Konektivitas Interaktif (ICE) digunakake kanggo milih cara sing paling apik kanggo nyambungake rong kanca adhedhasar informasi sing dipikolehi saka nyambungake kanca-kanca kanthi langsung, uga informasi sing ditampa dening sawetara server STUN lan TURN.
  • Session Description Protokol (RDS) minangka format kanggo njlèntrèhaké paramèter saluran sambungan, contone, calon ICE, codec multimedia (ing kasus saluran audio / video), etc. .. Sawise iki, saluran digawe.

Kanggo nggawe sambungan kasebut, kanca-kanca kudu ngumpulake informasi sing ditampa saka server STUN lan TURN lan ijol-ijolan karo siji liyane.

Masalahe yaiku dheweke durung duwe kemampuan kanggo komunikasi langsung, mula mekanisme sing ora ana band kudu ana kanggo ngganti data iki: server sinyal.

A server signaling bisa banget prasaja amarga mung tugas kanggo nerusake data antarane kanca-kanca ing phase salaman (minangka ditampilake ing diagram ngisor).

Porting game bebarengan saka C ++ menyang web karo Cheerp, WebRTC lan Firebase
Diagram urutan jabat tangan WebRTC sing disederhanakake

Ringkesan Model Jaringan Teeworlds

Arsitektur jaringan Teeworlds gampang banget:

  • Komponen klien lan server minangka rong program sing beda.
  • Klien ngetik game kanthi nyambungake menyang salah siji saka sawetara server, saben kang sarwa dumadi mung siji game ing wektu.
  • Kabeh transfer data ing game ditindakake liwat server.
  • Server master khusus digunakake kanggo ngumpulake dhaptar kabeh server umum sing ditampilake ing klien game.

Thanks kanggo nggunakake WebRTC kanggo ijol-ijolan data, kita bisa nransfer komponen server game kanggo browser ngendi klien dumunung. Iki menehi kita kesempatan gedhe ...

Njaluk nyisihaken saka server

Kekurangan logika server nduweni kauntungan sing apik: kita bisa nyebarake kabeh aplikasi minangka konten statis ing Github Pages utawa ing hardware kita dhewe ing mburi Cloudflare, saéngga njamin download cepet lan uptime dhuwur kanthi gratis. Nyatane, kita bisa lali babagan dheweke, lan yen kita beruntung lan game kasebut dadi populer, mula infrastruktur kasebut ora kudu dimodernisasi.

Nanging, supaya sistem bisa digunakake, kita isih kudu nggunakake arsitektur eksternal:

  • Siji utawa luwih server STUN: Kita duwe sawetara opsi gratis sing bisa dipilih.
  • Paling ora siji server TURN: ora ana opsi gratis ing kene, supaya kita bisa nyetel dhewe utawa mbayar layanan kasebut. Begjanipun, paling wektu sambungan bisa ditetepake liwat server STUN (lan nyedhiyani p2p bener), nanging TURN dibutuhake minangka pilihan fallback.
  • Server Sinyal: Ora kaya rong aspek liyane, sinyal ora standar. Apa server signaling bener bakal tanggung jawab kanggo gumantung ing aplikasi. Ing kasus kita, sadurunge nggawe sambungan, perlu kanggo ngganti jumlah data sing cilik.
  • Server Master Teeworlds: Iki digunakake dening server liyane kanggo ngiklanake eksistensi lan klien kanggo nemokake server umum. Nalika iku ora dibutuhake (klien tansah bisa nyambung menyang server padha ngerti bab manual), iku bakal dadi apik kanggo duwe supaya pemain bisa melu ing game karo wong acak.

Kita mutusake kanggo nggunakake server STUN gratis Google, lan masang server TURN dhewe.

Kanggo rong TCTerms pungkasan kita digunakake Firebase:

  • Server master Teeworlds ditindakake kanthi gampang: minangka dhaptar obyek sing ngemot informasi (jeneng, IP, peta, mode, ...) saben server sing aktif. Server nerbitake lan nganyari obyek dhewe, lan klien njupuk kabeh dhaftar lan nampilake menyang pamuter. Kita uga nampilake dhaptar ing kaca ngarep minangka HTML supaya pemain mung bisa klik ing server lan langsung dijupuk menyang game.
  • Sinyal ana hubungane karo implementasi soket, sing diterangake ing bagean sabanjure.

Porting game bebarengan saka C ++ menyang web karo Cheerp, WebRTC lan Firebase
Dhaptar server nang game lan ing kaca ngarep

Implementasi saka sockets

Kita pengin nggawe API sing cedhak karo Posix UDP Sockets kanggo nyilikake jumlah owah-owahan sing dibutuhake.

Kita uga pengin ngetrapake minimal sing dibutuhake kanggo ijol-ijolan data sing paling gampang ing jaringan.

Contone, kita ora perlu nuntun nyata: kabeh kanca-kanca ana ing "LAN virtual" padha sing digandhengake karo conto database Firebase tartamtu.

Mula, kita ora butuh alamat IP unik: nilai kunci Firebase sing unik (padha karo jeneng domain) cukup kanggo ngenali kanca-kanca kanthi unik, lan saben kanca lokal menehi alamat IP "palsu" kanggo saben kunci sing kudu diterjemahake. Iki rampung ngilangi kabutuhan alamat IP global, sing minangka tugas sing ora pati penting.

Iki API minimal sing kudu ditindakake:

// 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);

API iku prasaja lan padha karo Posix Sockets API, nanging nduweni sawetara beda penting: logging callbacks, nemtokake IP lokal, lan sambungan kesed.

Ndhaptar Callbacks

Sanajan program asli nggunakake I/O non-blocking, kode kudu refactored kanggo mbukak ing browser web.

Alesan kanggo iki yaiku loop acara ing browser didhelikake saka program kasebut (dadi JavaScript utawa WebAssembly).

Ing lingkungan asli kita bisa nulis kode kaya iki

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;
    ...
  }
  ...
}

Yen loop acara didhelikake kanggo kita, mula kita kudu ngowahi dadi kaya iki:

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

Tugas IP lokal

ID simpul ing "jaringan" kita dudu alamat IP, nanging kunci Firebase (iku string sing katon kaya iki: -LmEC50PYZLCiCP-vqde ).

Iki trep amarga kita ora mbutuhake mekanisme kanggo nemtokake IP lan mriksa keunikane (uga mbuwang sawise klien medhot), nanging asring perlu kanggo ngenali kanca-kanca kanthi nilai numerik.

Iki persis apa fungsi digunakake kanggo. resolve и reverseResolve: Aplikasi Piye wae nampa nilai senar saka tombol (liwat input pangguna utawa liwat server master), lan bisa ngowahi menyang alamat IP kanggo nggunakake internal. Liyane saka API uga nampa nilai iki tinimbang senar kanggo gamblang.

Iki padha karo DNS lookup, nanging ditindakake sacara lokal ing klien.

Yaiku, alamat IP ora bisa dienggo bareng antarane klien sing beda-beda, lan yen sawetara jinis pengenal global dibutuhake, mula kudu digawe kanthi cara sing beda.

Sambungan malas

UDP ora mbutuhake sambungan, nanging kaya sing wis kita deleng, WebRTC mbutuhake proses sambungan sing dawa sadurunge bisa miwiti nransfer data antarane rong kanca.

Yen kita pengin nyedhiyakake tingkat abstraksi sing padha, (sendto/recvfrom karo ora pati cetho tanpa sambungan sadurunge), banjur padha kudu nindakake sambungan "kesed" (tundha) nang API.

Iki kedadeyan sajrone komunikasi normal antarane "server" lan "klien" nalika nggunakake UDP, lan apa sing kudu ditindakake perpustakaan:

  • Telpon server bind()kanggo ngandhani sistem operasi sing pengin nampa paket ing port kasebut.

Nanging, kita bakal nerbitake port mbukak menyang Firebase ing sangisore tombol server lan ngrungokake acara ing subtree kasebut.

  • Telpon server recvfrom(), nampa paket teka saka sembarang host ing port iki.

Ing kasus kita, kita kudu mriksa antrian mlebu paket sing dikirim menyang port iki.

Saben port wis antrian dhewe, lan kita nambah sumber lan bandar tujuan kanggo awal WebRTC datagrams supaya kita ngerti antrian sing arep diterusake nalika paket anyar teka.

Telpon ora mblokir, dadi yen ora ana paket, kita mung bali -1 lan nyetel errno=EWOULDBLOCK.

  • Klien nampa IP lan port server kanthi sawetara cara eksternal, lan nelpon sendto(). Iki uga nggawe telpon internal. bind(), mulane sabanjure recvfrom() bakal nampa respon tanpa eksplisit eksekusi bind.

Ing kasus kita, klien eksternal nampa kunci senar lan nggunakake fungsi kasebut resolve() kanggo entuk alamat IP.

Ing wektu iki, kita miwiti jabat tangan WebRTC yen loro kanca durung nyambung. Sambungan menyang port beda saka peer padha nggunakake WebRTC DataChannel padha.

Kita uga nindakake ora langsung bind()supaya server bisa nyambung maneh ing sabanjuré sendto() ing kasus ditutup kanggo sawetara alesan.

Server diwenehi kabar babagan sambungan klien nalika klien nulis tawaran SDP ing ngisor informasi port server ing Firebase, lan server nanggapi kanthi respon ing kana.

Diagram ing ngisor iki nuduhake conto aliran pesen kanggo skema soket lan transmisi pesen pisanan saka klien menyang server:

Porting game bebarengan saka C ++ menyang web karo Cheerp, WebRTC lan Firebase
Diagram lengkap fase sambungan antarane klien lan server

kesimpulan

Yen sampeyan wis maca nganti saiki, sampeyan bisa uga kepengin weruh teori kasebut ing tumindak. Game bisa diputer ing teeworlds.leaningtech.com, cobanen!


Pertandhingan loropaken antarane kolega

Kode perpustakaan jaringan kasedhiya gratis ing GitHub. Gabung obrolan ing saluran kita ing Gitter!

Source: www.habr.com

Add a comment