ΠŸΠΎΡ€Ρ‚ΠΈΡ€ΡƒΠ΅ΠΌ ΠΌΠ½ΠΎΠ³ΠΎΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚Π΅Π»ΡŒΡΠΊΡƒΡŽ ΠΈΠ³Ρ€Ρƒ с Π‘++ Π½Π° Π²Π΅Π± c Cheerp, WebRTC ΠΈ Firebase

Π’Π²Π΅Π΄Π΅Π½ΠΈΠ΅

Наша компания Leaning Technologies прСдоставляСт Ρ€Π΅ΡˆΠ΅Π½ΠΈΡ ΠΏΠΎ ΠΏΠΎΡ€Ρ‚ΠΈΡ€ΠΎΠ²Π°Π½ΠΈΡŽ Ρ‚Ρ€Π°Π΄ΠΈΡ†ΠΈΠΎΠ½Π½Ρ‹Ρ… desktop-ΠΏΡ€ΠΈΠ»ΠΎΠΆΠ΅Π½ΠΈΠΉ Π² Π²Π΅Π±. Наш компилятор C++ Cheerp Π³Π΅Π½Π΅Ρ€ΠΈΡ€ΡƒΠ΅Ρ‚ сочСтаниС WebAssembly ΠΈ JavaScript, Ρ‡Ρ‚ΠΎ обСспСчиваСт ΠΈ простоС взаимодСйствиС с Π±Ρ€Π°ΡƒΠ·Π΅Ρ€ΠΎΠΌ, ΠΈ Π²Ρ‹ΡΠΎΠΊΡƒΡŽ ΠΏΡ€ΠΎΠΈΠ·Π²ΠΎΠ΄ΠΈΡ‚Π΅Π»ΡŒΠ½ΠΎΡΡ‚ΡŒ.

Π’ качСствС ΠΏΡ€ΠΈΠΌΠ΅Ρ€Π° Π΅Π³ΠΎ примСнСния ΠΌΡ‹ Ρ€Π΅ΡˆΠΈΠ»ΠΈ ΠΏΠΎΡ€Ρ‚ΠΈΡ€ΠΎΠ²Π°Ρ‚ΡŒ для Π²Π΅Π±Π° ΠΌΠ½ΠΎΠ³ΠΎΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚Π΅Π»ΡŒΡΠΊΡƒΡŽ ΠΈΠ³Ρ€Ρƒ ΠΈ Π²Ρ‹Π±Ρ€Π°Π»ΠΈ для этого Teeworlds. Teeworlds β€” это ΠΌΠ½ΠΎΠ³ΠΎΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚Π΅Π»ΡŒΡΠΊΠ°Ρ двухмСрная Ρ€Π΅Ρ‚Ρ€ΠΎ-ΠΈΠ³Ρ€Π° с нСбольшим, Π½ΠΎ Π°ΠΊΡ‚ΠΈΠ²Π½Ρ‹ΠΌ сообщСством ΠΈΠ³Ρ€ΠΎΠΊΠΎΠ² (Π² ΠΈΡ… числС ΠΈ я!). Она ΠΌΠ°Π»Π° ΠΊΠ°ΠΊ с Ρ‚ΠΎΡ‡ΠΊΠΈ зрСния скачиваСмых рСсурсов, Ρ‚Π°ΠΊ ΠΈ Ρ‚Ρ€Π΅Π±ΠΎΠ²Π°Π½ΠΈΠΉ ΠΊ ЦП ΠΈ GPU β€” ΠΈΠ΄Π΅Π°Π»ΡŒΠ½Ρ‹ΠΉ ΠΊΠ°Π½Π΄ΠΈΠ΄Π°Ρ‚.

ΠŸΠΎΡ€Ρ‚ΠΈΡ€ΡƒΠ΅ΠΌ ΠΌΠ½ΠΎΠ³ΠΎΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚Π΅Π»ΡŒΡΠΊΡƒΡŽ ΠΈΠ³Ρ€Ρƒ с Π‘++ Π½Π° Π²Π΅Π± c Cheerp, WebRTC ΠΈ Firebase
Π Π°Π±ΠΎΡ‚Π°ΡŽΡ‰Π°Ρ Π² Π±Ρ€Π°ΡƒΠ·Π΅Ρ€Π΅ Teeworlds

ΠœΡ‹ Ρ€Π΅ΡˆΠΈΠ»ΠΈ ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚ΡŒ этот ΠΏΡ€ΠΎΠ΅ΠΊΡ‚, Ρ‡Ρ‚ΠΎΠ±Ρ‹ ΠΏΠΎΡΠΊΡΠΏΠ΅Ρ€ΠΈΠΌΠ΅Π½Ρ‚ΠΈΡ€ΠΎΠ²Π°Ρ‚ΡŒ с ΠΎΠ±Ρ‰ΠΈΠΌΠΈ Ρ€Π΅ΡˆΠ΅Π½ΠΈΡΠΌΠΈ ΠΏΠΎ ΠΏΠΎΡ€Ρ‚ΠΈΡ€ΠΎΠ²Π°Π½ΠΈΡŽ сСтСвого ΠΊΠΎΠ΄Π° ΠΏΠΎΠ΄ Π²Π΅Π±. ΠžΠ±Ρ‹Ρ‡Π½ΠΎ это выполняСтся ΡΠ»Π΅Π΄ΡƒΡŽΡ‰ΠΈΠΌΠΈ способами:

  • XMLHttpRequest/fetch, Ссли сСтСвая Ρ‡Π°ΡΡ‚ΡŒ состоит Ρ‚ΠΎΠ»ΡŒΠΊΠΎ ΠΈΠ· HTTP-запросов, ΠΈΠ»ΠΈ
  • WebSockets.

Оба Ρ€Π΅ΡˆΠ΅Π½ΠΈΡ Ρ‚Ρ€Π΅Π±ΡƒΡŽΡ‚ Ρ…ΠΎΡΡ‚ΠΈΡ‚ΡŒ сСрвСрный ΠΊΠΎΠΌΠΏΠΎΠ½Π΅Π½Ρ‚ Π½Π° сторонС сСрвСра, ΠΈ Π½ΠΈ ΠΎΠ΄ΠΈΠ½ ΠΈΠ· Π½ΠΈΡ… Π½Π΅ позволяСт ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚ΡŒ Π² качСствС транспортного ΠΏΡ€ΠΎΡ‚ΠΎΠΊΠΎΠ»Π° UDP. Π­Ρ‚ΠΎ Π²Π°ΠΆΠ½ΠΎ для ΠΏΡ€ΠΈΠ»ΠΎΠΆΠ΅Π½ΠΈΠΉ Ρ€Π΅Π°Π»ΡŒΠ½ΠΎΠ³ΠΎ Π²Ρ€Π΅ΠΌΠ΅Π½ΠΈ, Ρ‚Π°ΠΊΠΈΡ… ΠΊΠ°ΠΊ софт для Π²ΠΈΠ΄Π΅ΠΎΠΊΠΎΠ½Ρ„Π΅Ρ€Π΅Π½Ρ†ΠΈΠΉ ΠΈ ΠΈΠ³Ρ€Ρ‹, ΠΏΠΎΡ‚ΠΎΠΌΡƒ Ρ‡Ρ‚ΠΎ Π³Π°Ρ€Π°Π½Ρ‚ΠΈΠΈ доставки ΠΈ порядка ΠΏΠ°ΠΊΠ΅Ρ‚ΠΎΠ² ΠΏΡ€ΠΎΡ‚ΠΎΠΊΠΎΠ»Π° TCP ΠΌΠΎΠ³ΡƒΡ‚ ΡΡ‚Π°Ρ‚ΡŒ ΠΏΠΎΠΌΠ΅Ρ…ΠΎΠΉ для Π½ΠΈΠ·ΠΊΠΈΡ… Π·Π°Π΄Π΅Ρ€ΠΆΠ΅ΠΊ.

БущСствуСт ΠΈ Ρ‚Ρ€Π΅Ρ‚ΠΈΠΉ ΠΏΡƒΡ‚ΡŒ β€” ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚ΡŒ ΡΠ΅Ρ‚ΡŒ ΠΈΠ· Π±Ρ€Π°ΡƒΠ·Π΅Ρ€Π°: WebRTC.

RTCDataChannel ΠΏΠΎΠ΄Π΄Π΅Ρ€ΠΆΠΈΠ²Π°Π΅Ρ‚ ΠΈ Π½Π°Π΄Ρ‘ΠΆΠ½ΡƒΡŽ, ΠΈ Π½Π΅Π½Π°Π΄Ρ‘ΠΆΠ½ΡƒΡŽ ΠΏΠ΅Ρ€Π΅Π΄Π°Ρ‡Ρƒ (Π² послСднСм случаС ΠΎΠ½ ΠΏΠΎ возмоТности пытаСтся ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚ΡŒ Π² качСствС транспортного ΠΏΡ€ΠΎΡ‚ΠΎΠΊΠΎΠ»Π° UDP), ΠΈ ΠΌΠΎΠΆΠ΅Ρ‚ ΠΏΡ€ΠΈΠΌΠ΅Π½ΡΡ‚ΡŒΡΡ ΠΈ с ΡƒΠ΄Π°Π»Ρ‘Π½Π½Ρ‹ΠΌ сСрвСром, ΠΈ ΠΌΠ΅ΠΆΠ΄Ρƒ Π±Ρ€Π°ΡƒΠ·Π΅Ρ€Π°ΠΌΠΈ. Π­Ρ‚ΠΎ Π·Π½Π°Ρ‡ΠΈΡ‚, Ρ‡Ρ‚ΠΎ ΠΌΡ‹ ΠΌΠΎΠΆΠ΅ΠΌ ΠΏΠΎΡ€Ρ‚ΠΈΡ€ΠΎΠ²Π°Ρ‚ΡŒ Π² Π±Ρ€Π°ΡƒΠ·Π΅Ρ€ всё ΠΏΡ€ΠΈΠ»ΠΎΠΆΠ΅Π½ΠΈΠ΅, Π² Ρ‚ΠΎΠΌ числС ΠΈ сСрвСрный ΠΊΠΎΠΌΠΏΠΎΠ½Π΅Π½Ρ‚!

Однако с этим связана Π΄ΠΎΠΏΠΎΠ»Π½ΠΈΡ‚Π΅Π»ΡŒΠ½Π°Ρ Ρ‚Ρ€ΡƒΠ΄Π½ΠΎΡΡ‚ΡŒ: ΠΏΡ€Π΅ΠΆΠ΄Π΅ Ρ‡Π΅ΠΌ Π΄Π²Π° ΠΏΠΈΡ€Π° WebRTC смогут ΠΎΠ±ΠΌΠ΅Π½ΠΈΠ²Π°Ρ‚ΡŒΡΡ Π΄Π°Π½Π½Ρ‹ΠΌΠΈ, ΠΈΠΌ Π½ΡƒΠΆΠ½ΠΎ Π²Ρ‹ΠΏΠΎΠ»Π½ΠΈΡ‚ΡŒ ΠΎΡ‚Π½ΠΎΡΠΈΡ‚Π΅Π»ΡŒΠ½ΠΎ ΡΠ»ΠΎΠΆΠ½ΡƒΡŽ ΠΏΡ€ΠΎΡ†Π΅Π΄ΡƒΡ€Ρƒ «рукопоТатия» (handshake) для ΠΏΠΎΠ΄ΠΊΠ»ΡŽΡ‡Π΅Π½ΠΈΡ, для Ρ‡Π΅Π³ΠΎ трСбуСтся нСсколько сторонних сущностСй (ΡΠΈΠ³Π½Π°Π»ΡŒΠ½Ρ‹ΠΉ сСрвСр ΠΈ ΠΎΠ΄ΠΈΠ½ ΠΈΠ»ΠΈ нСсколько сСрвСров STUN/TURN).

Π’ ΠΈΠ΄Π΅Π°Π»Π΅ ΠΌΡ‹ Π±Ρ‹ Ρ…ΠΎΡ‚Π΅Π»ΠΈ ΡΠΎΠ·Π΄Π°Ρ‚ΡŒ сСтСвой API, Π²Π½ΡƒΡ‚Ρ€ΠΈ ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΡƒΡŽΡ‰ΠΈΠΉ WebRTC, Π½ΠΎ ΠΊΠ°ΠΊ ΠΌΠΎΠΆΠ½ΠΎ Π±ΠΎΠ»Π΅Π΅ Π±Π»ΠΈΠ·ΠΊΠΈΠΉ ΠΊ интСрфСйсу UDP Sockets, ΠΊΠΎΡ‚ΠΎΡ€ΠΎΠΌΡƒ Π½Π΅ Π½ΡƒΠΆΠ½ΠΎ ΡƒΡΡ‚Π°Π½Π°Π²Π»ΠΈΠ²Π°Ρ‚ΡŒ соСдинСниС.

Π­Ρ‚ΠΎ ΠΏΠΎΠ·Π²ΠΎΠ»ΠΈΡ‚ Π½Π°ΠΌ ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚ΡŒ прСимущСства WebRTC Π±Π΅Π· нСобходимости раскрытия слоТных подробностСй ΠΊΠΎΠ΄Ρƒ прилоТСния (ΠΊΠΎΡ‚ΠΎΡ€Ρ‹ΠΉ Π² своём ΠΏΡ€ΠΎΠ΅ΠΊΡ‚Π΅ ΠΌΡ‹ Ρ…ΠΎΡ‚Π΅Π»ΠΈ ΠΈΠ·ΠΌΠ΅Π½ΡΡ‚ΡŒ ΠΊΠ°ΠΊ ΠΌΠΎΠΆΠ½ΠΎ мСньшС).

ΠœΠΈΠ½ΠΈΠΌΠ°Π»ΡŒΠ½Ρ‹ΠΉ WebRTC

WebRTC β€” это ΠΈΠΌΠ΅ΡŽΡ‰ΠΈΠΉΡΡ Π² Π±Ρ€Π°ΡƒΠ·Π΅Ρ€Π°Ρ… Π½Π°Π±ΠΎΡ€ API, ΠΎΠ±Π΅ΡΠΏΠ΅Ρ‡ΠΈΠ²Π°ΡŽΡ‰ΠΈΠΉ ΠΏΠ΅Ρ€Π΅Π΄Π°Ρ‡Ρƒ peer-to-peer Π·Π²ΡƒΠΊΠ°, Π²ΠΈΠ΄Π΅ΠΎ ΠΈ ΠΏΡ€ΠΎΠΈΠ·Π²ΠΎΠ»ΡŒΠ½Ρ‹Ρ… Π΄Π°Π½Π½Ρ‹Ρ….

Π‘ΠΎΠ΅Π΄ΠΈΠ½Π΅Π½ΠΈΠ΅ ΠΌΠ΅ΠΆΠ΄Ρƒ ΠΏΠΈΡ€Π°ΠΌΠΈ устанавливаСтся (Π΄Π°ΠΆΠ΅ Π² случаС наличия NAT с ΠΎΠ΄Π½ΠΎΠΉ ΠΈΠ»ΠΈ ΠΎΠ±Π΅ΠΈΡ… сторон) ΠΏΡ€ΠΈ ΠΏΠΎΠΌΠΎΡ‰ΠΈ сСрвСров STUN ΠΈ/ΠΈΠ»ΠΈ TURN Ρ‡Π΅Ρ€Π΅Π· ΠΌΠ΅Ρ…Π°Π½ΠΈΠ·ΠΌ ΠΏΠΎΠ΄ Π½Π°Π·Π²Π°Π½ΠΈΠ΅ΠΌ ICE. ΠŸΠΈΡ€Ρ‹ ΠΎΠ±ΠΌΠ΅Π½ΠΈΠ²Π°ΡŽΡ‚ΡΡ ΠΈΠ½Ρ„ΠΎΡ€ΠΌΠ°Ρ†ΠΈΠ΅ΠΉ ICE ΠΈ ΠΏΠ°Ρ€Π°ΠΌΠ΅Ρ‚Ρ€Π°ΠΌΠΈ ΠΊΠ°Π½Π°Π»ΠΎΠ² Ρ‡Π΅Ρ€Π΅Π· offer ΠΈ answer ΠΏΡ€ΠΎΡ‚ΠΎΠΊΠΎΠ»Π° SDP.

Ого! Как ΠΌΠ½ΠΎΠ³ΠΎ Π°Π±Π±Ρ€Π΅Π²ΠΈΠ°Ρ‚ΡƒΡ€ Π·Π° ΠΎΠ΄ΠΈΠ½ Ρ€Π°Π·. Π”Π°Π²Π°ΠΉΡ‚Π΅ Π²ΠΊΡ€Π°Ρ‚Ρ†Π΅ объясним, Ρ‡Ρ‚ΠΎ Π·Π½Π°Ρ‡Π°Ρ‚ эти понятия:

  • Session Traversal Utilities for NAT (STUN) β€” ΠΏΡ€ΠΎΡ‚ΠΎΠΊΠΎΠ» для ΠΎΠ±Ρ…ΠΎΠ΄Π° NAT ΠΈ получСния ΠΏΠ°Ρ€Ρ‹ (IP, ΠΏΠΎΡ€Ρ‚) для ΠΎΠ±ΠΌΠ΅Π½Π° Π΄Π°Π½Π½Ρ‹ΠΌΠΈ нСпосрСдствСнно с хостом. Если Π΅ΠΌΡƒ удаётся Π²Ρ‹ΠΏΠΎΠ»Π½ΠΈΡ‚ΡŒ свою Π·Π°Π΄Π°Ρ‡Ρƒ, Ρ‚ΠΎ ΠΏΠΈΡ€Ρ‹ ΠΌΠΎΠ³ΡƒΡ‚ ΡΠ°ΠΌΠΎΡΡ‚ΠΎΡΡ‚Π΅Π»ΡŒΠ½ΠΎ ΠΎΠ±ΠΌΠ΅Π½ΠΈΠ²Π°Ρ‚ΡŒΡΡ Π΄Π°Π½Π½Ρ‹ΠΌΠΈ Π΄Ρ€ΡƒΠ³ с Π΄Ρ€ΡƒΠ³ΠΎΠΌ.
  • Traversal Using Relays around NAT (TURN) Ρ‚ΠΎΠΆΠ΅ ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΡƒΠ΅Ρ‚ΡΡ для ΠΎΠ±Ρ…ΠΎΠ΄Π° NAT, Π½ΠΎ ΠΎΠ½ Ρ€Π΅Π°Π»ΠΈΠ·ΡƒΠ΅Ρ‚ это, пСрСнаправляя Π΄Π°Π½Π½Ρ‹Π΅ Ρ‡Π΅Ρ€Π΅Π· прокси, Π²ΠΈΠ΄ΠΈΠΌΡ‹ΠΉ ΠΎΠ±ΠΎΠΈΠΌ ΠΏΠΈΡ€Π°ΠΌ. Он добавляСт Π·Π°Π΄Π΅Ρ€ΠΆΠΊΡƒ ΠΈ Π±ΠΎΠ»Π΅Π΅ Π·Π°Ρ‚Ρ€Π°Ρ‚Π΅Π½ Π² Π²Ρ‹ΠΏΠΎΠ»Π½Π΅Π½ΠΈΠΈ, Ρ‡Π΅ΠΌ STUN (ΠΏΠΎΡ‚ΠΎΠΌΡƒ Ρ‡Ρ‚ΠΎ примСняСтся Π½Π° протяТСнии всСго сСанса связи), Π½ΠΎ ΠΈΠ½ΠΎΠ³Π΄Π° это СдинствСнный Π²ΠΎΠ·ΠΌΠΎΠΆΠ½Ρ‹ΠΉ Π²Π°Ρ€ΠΈΠ°Π½Ρ‚.
  • Interactive Connectivity Establishment (ICE) ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΡƒΠ΅Ρ‚ΡΡ для Π²Ρ‹Π±ΠΎΡ€Π° Π½Π°ΠΈΠ»ΡƒΡ‡ΡˆΠ΅Π³ΠΎ Π²ΠΎΠ·ΠΌΠΎΠΆΠ½ΠΎΠ³ΠΎ способа соСдинСния Π΄Π²ΡƒΡ… ΠΏΠΈΡ€ΠΎΠ² Π½Π° основании ΠΈΠ½Ρ„ΠΎΡ€ΠΌΠ°Ρ†ΠΈΠΈ, ΠΏΠΎΠ»ΡƒΡ‡Π΅Π½Π½ΠΎΠΉ ΠΏΡ€ΠΈ нСпосрСдствСнном соСдинСнии ΠΏΠΈΡ€ΠΎΠ², Π° Ρ‚Π°ΠΊΠΆΠ΅ ΠΈΠ½Ρ„ΠΎΡ€ΠΌΠ°Ρ†ΠΈΠΈ, ΠΏΠΎΠ»ΡƒΡ‡Π΅Π½Π½ΠΎΠΉ Π»ΡŽΠ±Ρ‹ΠΌ количСством сСрвСров STUN ΠΈ TURN.
  • Session Description Protocol (SDP) β€” это Ρ„ΠΎΡ€ΠΌΠ°Ρ‚ описания ΠΏΠ°Ρ€Π°ΠΌΠ΅Ρ‚Ρ€ΠΎΠ² ΠΊΠ°Π½Π°Π»Π° ΠΏΠΎΠ΄ΠΊΠ»ΡŽΡ‡Π΅Π½ΠΈΡ, Π½Π°ΠΏΡ€ΠΈΠΌΠ΅Ρ€, ΠΊΠ°Π½Π΄ΠΈΠ΄Π°Ρ‚ΠΎΠ² ICE, ΠΊΠΎΠ΄Π΅ΠΊΠΎΠ² ΠΌΡƒΠ»ΡŒΡ‚ΠΈΠΌΠ΅Π΄ΠΈΠ° (Π² случаС Π·Π²ΡƒΠΊΠΎΠ²ΠΎΠ³ΠΎ/Π²ΠΈΠ΄Π΅ΠΎΠΊΠ°Π½Π°Π»Π°), ΠΈ Ρ‚.п… Один ΠΈΠ· ΠΏΠΈΡ€ΠΎΠ² отправляСт SDP Offer (Β«ΠΏΡ€Π΅Π΄Π»ΠΎΠΆΠ΅Π½ΠΈΠ΅Β»), Π° Π²Ρ‚ΠΎΡ€ΠΎΠΉ ΠΎΡ‚Π²Π΅Ρ‡Π°Π΅Ρ‚ SDP Answer (Β«ΠΎΡ‚ΠΊΠ»ΠΈΠΊΠΎΠΌΒ»). ПослС этого создаётся ΠΊΠ°Π½Π°Π».

Π§Ρ‚ΠΎΠ±Ρ‹ ΡΠΎΠ·Π΄Π°Ρ‚ΡŒ Ρ‚Π°ΠΊΠΎΠ΅ соСдинСниС, ΠΏΠΈΡ€Π°ΠΌ Π½ΡƒΠΆΠ½ΠΎ ΡΠΎΠ±Ρ€Π°Ρ‚ΡŒ ΠΈΠ½Ρ„ΠΎΡ€ΠΌΠ°Ρ†ΠΈΡŽ, ΠΏΠΎΠ»ΡƒΡ‡Π΅Π½Π½ΡƒΡŽ ΠΈΠΌΠΈ ΠΎΡ‚ сСрвСров STUN ΠΈ TURN, ΠΈ ΠΎΠ±ΠΌΠ΅Π½ΡΡ‚ΡŒΡΡ Сю Π΄Ρ€ΡƒΠ³ с Π΄Ρ€ΡƒΠ³ΠΎΠΌ.

ΠŸΡ€ΠΎΠ±Π»Π΅ΠΌΠ° Π² Ρ‚ΠΎΠΌ, Ρ‡Ρ‚ΠΎ Ρƒ Π½ΠΈΡ… ΠΏΠΎΠΊΠ° Π½Π΅Ρ‚ возмоТности ΠΎΠ±ΠΌΠ΅Π½ΠΈΠ²Π°Ρ‚ΡŒΡΡ Π΄Π°Π½Π½Ρ‹ΠΌΠΈ Π½Π°ΠΏΡ€ΡΠΌΡƒΡŽ, поэтому для ΠΎΠ±ΠΌΠ΅Π½Π° этими Π΄Π°Π½Π½Ρ‹ΠΌΠΈ Π΄ΠΎΠ»ΠΆΠ΅Π½ ΡΡƒΡ‰Π΅ΡΡ‚Π²ΠΎΠ²Π°Ρ‚ΡŒ внСполосной ΠΌΠ΅Ρ…Π°Π½ΠΈΠ·ΠΌ: ΡΠΈΠ³Π½Π°Π»ΡŒΠ½Ρ‹ΠΉ сСрвСр.

Π‘ΠΈΠ³Π½Π°Π»ΡŒΠ½Ρ‹ΠΉ сСрвСр ΠΌΠΎΠΆΠ΅Ρ‚ Π±Ρ‹Ρ‚ΡŒ ΠΎΡ‡Π΅Π½ΡŒ простым, ΠΏΠΎΡ‚ΠΎΠΌΡƒ Ρ‡Ρ‚ΠΎ Π΅Π³ΠΎ СдинствСнная Π·Π°Π΄Π°Ρ‡Π° β€” ΠΏΠ΅Ρ€Π΅Π½Π°ΠΏΡ€Π°Π²Π»Π΅Π½ΠΈΠ΅ Π΄Π°Π½Π½Ρ‹Ρ… ΠΌΠ΅ΠΆΠ΄Ρƒ ΠΏΠΈΡ€Π°ΠΌΠΈ Π½Π° этапС «рукопоТатия» (ΠΊΠ°ΠΊ ΠΏΠΎΠΊΠ°Π·Π°Π½ΠΎ Π½Π° схСмС Π½ΠΈΠΆΠ΅).

ΠŸΠΎΡ€Ρ‚ΠΈΡ€ΡƒΠ΅ΠΌ ΠΌΠ½ΠΎΠ³ΠΎΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚Π΅Π»ΡŒΡΠΊΡƒΡŽ ΠΈΠ³Ρ€Ρƒ с Π‘++ Π½Π° Π²Π΅Π± c Cheerp, WebRTC ΠΈ Firebase
Упрощённая схСма ΠΏΠΎΡΠ»Π΅Π΄ΠΎΠ²Π°Ρ‚Π΅Π»ΡŒΠ½ΠΎΡΡ‚ΠΈ «рукопоТатия» WebRTC

ΠžΠ±Π·ΠΎΡ€ сСтСвой ΠΌΠΎΠ΄Π΅Π»ΠΈ Teeworlds

БСтСвая Π°Ρ€Ρ…ΠΈΡ‚Π΅ΠΊΡ‚ΡƒΡ€Π° Teeworlds ΠΎΡ‡Π΅Π½ΡŒ проста:

  • ΠšΠΎΠΌΠΏΠΎΠ½Π΅Π½Ρ‚Ρ‹ ΠΊΠ»ΠΈΠ΅Π½Ρ‚Π° ΠΈ сСрвСра β€” это Π΄Π²Π΅ Ρ€Π°Π·Π½Ρ‹Π΅ ΠΏΡ€ΠΎΠ³Ρ€Π°ΠΌΠΌΡ‹.
  • ΠšΠ»ΠΈΠ΅Π½Ρ‚Ρ‹ Π²ΡΡ‚ΡƒΠΏΠ°ΡŽΡ‚ Π² ΠΈΠ³Ρ€Ρƒ, ΠΏΠΎΠ΄ΠΊΠ»ΡŽΡ‡Π°ΡΡΡŒ ΠΊ ΠΎΠ΄Π½ΠΎΠΌΡƒ ΠΈΠ· Π½Π΅ΡΠΊΠΎΠ»ΡŒΠΊΠΈΡ… сСрвСров, ΠΊΠ°ΠΆΠ΄Ρ‹ΠΉ ΠΈΠ· ΠΊΠΎΡ‚ΠΎΡ€Ρ‹Ρ… Π·Π° Ρ€Π°Π· хостит Ρ‚ΠΎΠ»ΡŒΠΊΠΎ ΠΎΠ΄Π½Ρƒ ΠΈΠ³Ρ€Ρƒ.
  • Вся ΠΏΠ΅Ρ€Π΅Π΄Π°Ρ‡Π° Π΄Π°Π½Π½Ρ‹Ρ… Π² ΠΈΠ³Ρ€Π΅ вСдётся Ρ‡Π΅Ρ€Π΅Π· сСрвСр.
  • ΠžΡΠΎΠ±Ρ‹ΠΉ мастСр-сСрвСр ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΡƒΠ΅Ρ‚ΡΡ для сбора списка всСх ΠΏΡƒΠ±Π»ΠΈΡ‡Π½Ρ‹Ρ… сСрвСров, ΠΊΠΎΡ‚ΠΎΡ€Ρ‹Π΅ ΠΎΡ‚ΠΎΠ±Ρ€Π°ΠΆΠ°ΡŽΡ‚ΡΡ Π² ΠΈΠ³Ρ€ΠΎΠ²ΠΎΠΌ ΠΊΠ»ΠΈΠ΅Π½Ρ‚Π΅.

Благодаря использованию для ΠΎΠ±ΠΌΠ΅Π½Π° Π΄Π°Π½Π½Ρ‹ΠΌΠΈ WebRTC ΠΌΡ‹ ΠΌΠΎΠΆΠ΅ΠΌ пСрСнСсти сСрвСрный ΠΊΠΎΠΌΠΏΠΎΠ½Π΅Π½Ρ‚ ΠΈΠ³Ρ€Ρ‹ Π² Π±Ρ€Π°ΡƒΠ·Π΅Ρ€, Π³Π΄Π΅ находится ΠΊΠ»ΠΈΠ΅Π½Ρ‚. Π­Ρ‚ΠΎ Π΄Π°Ρ‘Ρ‚ Π½Π°ΠΌ ΠΏΡ€Π΅ΠΊΡ€Π°ΡΠ½ΡƒΡŽ Π²ΠΎΠ·ΠΌΠΎΠΆΠ½ΠΎΡΡ‚ΡŒβ€¦

Π˜Π·Π±Π°Π²ΠΈΡ‚ΡŒΡΡ ΠΎΡ‚ сСрвСров

ΠžΡ‚ΡΡƒΡ‚ΡΡ‚Π²ΠΈΠ΅ сСрвСрной Π»ΠΎΠ³ΠΈΠΊΠΈ ΠΈΠΌΠ΅Π΅Ρ‚ приятноС прСимущСство: ΠΌΡ‹ ΠΌΠΎΠΆΠ΅ΠΌ Ρ€Π°Π·Π²Π΅Ρ€Π½ΡƒΡ‚ΡŒ всё ΠΏΡ€ΠΈΠ»ΠΎΠΆΠ΅Π½ΠΈΠ΅ ΠΊΠ°ΠΊ статичный ΠΊΠΎΠ½Ρ‚Π΅Π½Ρ‚ Π½Π° Github Pages ΠΈΠ»ΠΈ Π½Π° собствСнном ΠΎΠ±ΠΎΡ€ΡƒΠ΄ΠΎΠ²Π°Π½ΠΈΠΈ Π·Π° Cloudflare, Ρ‚Π°ΠΊΠΈΠΌ ΠΎΠ±Ρ€Π°Π·ΠΎΠΌ бСсплатно обСспСчив сСбС быстрыС Π·Π°Π³Ρ€ΡƒΠ·ΠΊΠΈ ΠΈ высокий Π°ΠΏΡ‚Π°ΠΉΠΌ. По сути, ΠΌΠΎΠΆΠ½ΠΎ Π±ΡƒΠ΄Π΅Ρ‚ ΠΎ Π½ΠΈΡ… Π·Π°Π±Ρ‹Ρ‚ΡŒ, ΠΈ Ссли Π½Π°ΠΌ ΠΏΠΎΠ²Π΅Π·Ρ‘Ρ‚ ΠΈ ΠΈΠ³Ρ€Π° станСт популярной, Ρ‚ΠΎ инфраструктуру ΠΌΠΎΠ΄Π΅Ρ€Π½ΠΈΠ·ΠΈΡ€ΠΎΠ²Π°Ρ‚ΡŒ Π½Π΅ придётся.

Однако Ρ‡Ρ‚ΠΎΠ±Ρ‹ систСма Ρ€Π°Π±ΠΎΡ‚Π°Π»Π°, Π½Π°ΠΌ всё Ρ€Π°Π²Π½ΠΎ придётся ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚ΡŒ внСшнюю Π°Ρ€Ρ…ΠΈΡ‚Π΅ΠΊΡ‚ΡƒΡ€Ρƒ:

  • Один ΠΈΠ»ΠΈ нСсколько сСрвСров STUN: Ρƒ нас Π΅ΡΡ‚ΡŒ Π²Ρ‹Π±ΠΎΡ€ ΠΈΠ· Π½Π΅ΡΠΊΠΎΠ»ΡŒΠΊΠΈΡ… бСсплатных Π²Π°Ρ€ΠΈΠ°Π½Ρ‚ΠΎΠ².
  • По ΠΊΡ€Π°ΠΉΠ½Π΅ΠΉ ΠΌΠ΅Ρ€Π΅ ΠΎΠ΄ΠΈΠ½ сСрвСр TURN: здСсь бСсплатных Π²Π°Ρ€ΠΈΠ°Π½Ρ‚ΠΎΠ² Π½Π΅Ρ‚, поэтому ΠΌΡ‹ ΠΌΠΎΠΆΠ΅ΠΌ ΠΈΠ»ΠΈ Π½Π°ΡΡ‚Ρ€ΠΎΠΈΡ‚ΡŒ свой, ΠΈΠ»ΠΈ ΠΏΠ»Π°Ρ‚ΠΈΡ‚ΡŒ Π·Π° сСрвис. К ΡΡ‡Π°ΡΡ‚ΡŒΡŽ, Π±ΠžΠ»ΡŒΡˆΡƒΡŽ Ρ‡Π°ΡΡ‚ΡŒ Π²Ρ€Π΅ΠΌΠ΅Π½ΠΈ ΠΏΠΎΠ΄ΠΊΠ»ΡŽΡ‡Π΅Π½ΠΈΠ΅ ΠΌΠΎΠΆΠ½ΠΎ Π±ΡƒΠ΄Π΅Ρ‚ ΡƒΡΡ‚Π°Π½Π°Π²Π»ΠΈΠ²Π°Ρ‚ΡŒ Ρ‡Π΅Ρ€Π΅Π· сСрвСры STUN (ΠΈ ΠΎΠ±Π΅ΡΠΏΠ΅Ρ‡ΠΈΡ‚ΡŒ истинный p2p), Π½ΠΎ TURN Π½Π΅ΠΎΠ±Ρ…ΠΎΠ΄ΠΈΠΌ ΠΊΠ°ΠΊ запасной Π²Π°Ρ€ΠΈΠ°Π½Ρ‚.
  • Π‘ΠΈΠ³Π½Π°Π»ΡŒΠ½Ρ‹ΠΉ сСрвСр: Π² ΠΎΡ‚Π»ΠΈΡ‡ΠΈΠ΅ ΠΎΡ‚ Π΄Π²ΡƒΡ… Π΄Ρ€ΡƒΠ³ΠΈΡ… аспСктов, сигнализированиС Π½Π΅ стандартизировано. Π’ΠΎ, Π·Π° Ρ‡Ρ‚ΠΎ Π½Π° самом Π΄Π΅Π»Π΅ Π±ΡƒΠ΄Π΅Ρ‚ ΠΎΡ‚Π²Π΅Ρ‡Π°Ρ‚ΡŒ ΡΠΈΠ³Π½Π°Π»ΡŒΠ½Ρ‹ΠΉ сСрвСр, Π² Ρ‡Ρ‘ΠΌ-Ρ‚ΠΎ зависит ΠΎΡ‚ прилоТСния. Π’ нашСм случаС ΠΏΠ΅Ρ€Π΅Π΄ установкой соСдинСния Π½Π΅ΠΎΠ±Ρ…ΠΎΠ΄ΠΈΠΌΠΎ ΠΎΠ±ΠΌΠ΅Π½ΡΡ‚ΡŒΡΡ нСбольшим ΠΎΠ±ΡŠΡ‘ΠΌΠΎΠΌ Π΄Π°Π½Π½Ρ‹Ρ….
  • ΠœΠ°ΡΡ‚Π΅Ρ€-сСрвСр Teeworlds: ΠΎΠ½ ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΡƒΠ΅Ρ‚ΡΡ Π΄Ρ€ΡƒΠ³ΠΈΠΌΠΈ сСрвСрами для оповСщСния ΠΎ своём сущСствовании ΠΈ ΠΊΠ»ΠΈΠ΅Π½Ρ‚Π°ΠΌΠΈ для поиска ΠΏΡƒΠ±Π»ΠΈΡ‡Π½Ρ‹Ρ… сСрвСров. Π₯отя ΠΎΠ½ ΠΈ Π½Π΅ обязатСлСн (ΠΊΠ»ΠΈΠ΅Π½Ρ‚Ρ‹ всСгда ΠΌΠΎΠ³ΡƒΡ‚ ΠΏΠΎΠ΄ΠΊΠ»ΡŽΡ‡ΠΈΡ‚ΡŒΡΡ ΠΊ извСстному ΠΈΠΌ сСрвСру Π²Ρ€ΡƒΡ‡Π½ΡƒΡŽ), Π±Ρ‹Π»ΠΎ Π±Ρ‹ Ρ…ΠΎΡ€ΠΎΡˆΠΎ Π΅Π³ΠΎ ΠΈΠΌΠ΅Ρ‚ΡŒ, Ρ‡Ρ‚ΠΎΠ±Ρ‹ ΠΈΠ³Ρ€ΠΎΠΊΠΈ ΠΌΠΎΠ³Π»ΠΈ ΡƒΡ‡Π°ΡΡ‚Π²ΠΎΠ²Π°Ρ‚ΡŒ Π² ΠΈΠ³Ρ€Π°Ρ… со случайными людьми.

ΠœΡ‹ Ρ€Π΅ΡˆΠΈΠ»ΠΈ ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚ΡŒ бСсплатныС сСрвСры STUN ΠΊΠΎΠΌΠΏΠ°Π½ΠΈΠΈ Google, Π° ΠΎΠ΄ΠΈΠ½ сСрвСр TURN Ρ€Π°Π·Π²Π΅Ρ€Π½ΡƒΠ»ΠΈ ΡΠ°ΠΌΠΎΡΡ‚ΠΎΡΡ‚Π΅Π»ΡŒΠ½ΠΎ.

Для Π΄Π²ΡƒΡ… послСдних ΠΏΡƒΠ½ΠΊΡ‚ΠΎΠ² ΠΌΡ‹ использовали Firebase:

  • ΠœΠ°ΡΡ‚Π΅Ρ€-сСрвСр Teeworlds Ρ€Π΅Π°Π»ΠΈΠ·ΠΎΠ²Π°Π½ ΠΎΡ‡Π΅Π½ΡŒ просто: ΠΊΠ°ΠΊ список ΠΎΠ±ΡŠΠ΅ΠΊΡ‚ΠΎΠ², содСрТащих ΠΈΠ½Ρ„ΠΎΡ€ΠΌΠ°Ρ†ΠΈΡŽ (имя, IP, ΠΊΠ°Ρ€Ρ‚Π°, Ρ€Π΅ΠΆΠΈΠΌ, …) ΠΊΠ°ΠΆΠ΄ΠΎΠ³ΠΎ Π°ΠΊΡ‚ΠΈΠ²Π½ΠΎΠ³ΠΎ сСрвСра. Π‘Π΅Ρ€Π²Π΅Ρ€Ρ‹ ΠΏΡƒΠ±Π»ΠΈΠΊΡƒΡŽΡ‚ ΠΈ ΠΎΠ±Π½ΠΎΠ²Π»ΡΡŽΡ‚ свой собствСнный ΠΎΠ±ΡŠΠ΅ΠΊΡ‚, Π° ΠΊΠ»ΠΈΠ΅Π½Ρ‚Ρ‹ Π±Π΅Ρ€ΡƒΡ‚ вСсь список ΠΈ ΠΎΡ‚ΠΎΠ±Ρ€Π°ΠΆΠ°ΡŽΡ‚ Π΅Π³ΠΎ ΠΈΠ³Ρ€ΠΎΠΊΡƒ. Π’Π°ΠΊΠΆΠ΅ ΠΌΡ‹ ΠΎΡ‚ΠΎΠ±Ρ€Π°ΠΆΠ°Π΅ΠΌ список Π½Π° домашнСй страницС ΠΊΠ°ΠΊ HTML, Ρ‡Ρ‚ΠΎΠ±Ρ‹ ΠΈΠ³Ρ€ΠΎΠΊΠΈ ΠΌΠΎΠ³Π»ΠΈ просто Π½Π°ΠΆΠ°Ρ‚ΡŒ Π½Π° сСрвСр ΠΈ прямиком ΠΏΠΎΠΏΠ°ΡΡ‚ΡŒ Π² ΠΈΠ³Ρ€Ρƒ.
  • Π‘ΠΈΠ³Π½Π°Π»ΠΈΠ·ΠΈΡ€ΠΎΠ²Π°Π½ΠΈΠ΅ тСсно связано с нашСй Ρ€Π΅Π°Π»ΠΈΠ·Π°Ρ†ΠΈΠ΅ΠΉ сокСтов, описанной Π² ΡΠ»Π΅Π΄ΡƒΡŽΡ‰Π΅ΠΌ Ρ€Π°Π·Π΄Π΅Π»Π΅.

ΠŸΠΎΡ€Ρ‚ΠΈΡ€ΡƒΠ΅ΠΌ ΠΌΠ½ΠΎΠ³ΠΎΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚Π΅Π»ΡŒΡΠΊΡƒΡŽ ΠΈΠ³Ρ€Ρƒ с Π‘++ Π½Π° Π²Π΅Π± c Cheerp, WebRTC ΠΈ Firebase
Бписок сСрвСров Π²Π½ΡƒΡ‚Ρ€ΠΈ ΠΈΠ³Ρ€Ρ‹ ΠΈ Π½Π° домашнСй страницС

РСализация сокСтов

ΠœΡ‹ Ρ…ΠΎΡ‚ΠΈΠΌ ΡΠΎΠ·Π΄Π°Ρ‚ΡŒ API, ΠΊΠ°ΠΊ ΠΌΠΎΠΆΠ½ΠΎ Π±ΠΎΠ»Π΅Π΅ Π±Π»ΠΈΠ·ΠΊΠΈΠΉ ΠΊ Posix UDP Sockets, Ρ‡Ρ‚ΠΎΠ±Ρ‹ ΠΌΠΈΠ½ΠΈΠΌΠΈΠ·ΠΈΡ€ΠΎΠ²Π°Ρ‚ΡŒ количСство Π½Π΅ΠΎΠ±Ρ…ΠΎΠ΄ΠΈΠΌΡ‹Ρ… ΠΈΠ·ΠΌΠ΅Π½Π΅Π½ΠΈΠΉ.

Π’Π°ΠΊ ΠΆΠ΅ ΠΌΡ‹ Ρ…ΠΎΡ‚ΠΈΠΌ Ρ€Π΅Π°Π»ΠΈΠ·ΠΎΠ²Π°Ρ‚ΡŒ Π½Π΅ΠΎΠ±Ρ…ΠΎΠ΄ΠΈΠΌΡ‹ΠΉ ΠΌΠΈΠ½ΠΈΠΌΡƒΠΌ, Ρ‚Ρ€Π΅Π±ΡƒΡŽΡ‰ΠΈΠΉΡΡ для ΠΏΡ€ΠΎΡΡ‚Π΅ΠΉΡˆΠ΅Π³ΠΎ ΠΎΠ±ΠΌΠ΅Π½Π° Π΄Π°Π½Π½Ρ‹ΠΌΠΈ ΠΏΠΎ сСти.

НапримСр, Π½Π°ΠΌ Π½Π΅ Π½ΡƒΠΆΠ½Π° настоящая ΠΌΠ°Ρ€ΡˆΡ€ΡƒΡ‚ΠΈΠ·Π°Ρ†ΠΈΡ: всС ΠΏΠΈΡ€Ρ‹ находятся Π² ΠΎΠ΄Π½ΠΎΠΉ Β«Π²ΠΈΡ€Ρ‚ΡƒΠ°Π»ΡŒΠ½ΠΎΠΉ LANΒ», связанной с ΠΊΠΎΠ½ΠΊΡ€Π΅Ρ‚Π½Ρ‹ΠΌ экзСмпляром Π±Π°Π·Ρ‹ Π΄Π°Π½Π½Ρ‹Ρ… Firebase.

Π‘Π»Π΅Π΄ΠΎΠ²Π°Ρ‚Π΅Π»ΡŒΠ½ΠΎ, Π½Π°ΠΌ Π½Π΅ Π½ΡƒΠΆΠ½Ρ‹ ΡƒΠ½ΠΈΠΊΠ°Π»ΡŒΠ½Ρ‹Π΅ IP-адрСса: для ΡƒΠ½ΠΈΠΊΠ°Π»ΡŒΠ½ΠΎΠΉ ΠΈΠ΄Π΅Π½Ρ‚ΠΈΡ„ΠΈΠΊΠ°Ρ†ΠΈΠΈ ΠΏΠΈΡ€ΠΎΠ² достаточно ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚ΡŒ ΡƒΠ½ΠΈΠΊΠ°Π»ΡŒΠ½Ρ‹Π΅ значСния ΠΊΠ»ΡŽΡ‡Π΅ΠΉ Firebase (Π°Π½Π°Π»ΠΎΠ³ΠΈΡ‡Π½ΠΎ Π΄ΠΎΠΌΠ΅Π½Π½Ρ‹ΠΌ ΠΈΠΌΠ΅Π½Π°ΠΌ), ΠΈ ΠΊΠ°ΠΆΠ΄Ρ‹ΠΉ ΠΏΠΈΡ€ локально Π½Π°Π·Π½Π°Ρ‡Π°Π΅Ρ‚ Β«Ρ„Π°Π»ΡŒΡˆΠΈΠ²Ρ‹Π΅Β» IP-адрСса ΠΊΠ°ΠΆΠ΄ΠΎΠΌΡƒ ΠΊΠ»ΡŽΡ‡Ρƒ, ΠΊΠΎΡ‚ΠΎΡ€Ρ‹ΠΉ Π½ΡƒΠΆΠ½ΠΎ ΠΏΡ€Π΅ΠΎΠ±Ρ€Π°Π·ΠΎΠ²Π°Ρ‚ΡŒ. Π­Ρ‚ΠΎ ΠΏΠΎΠ»Π½ΠΎΡΡ‚ΡŒΡŽ избавляСт нас ΠΎΡ‚ нСобходимости глобального назначСния IP-адрСсов, Ρ‡Ρ‚ΠΎ являСтся Π½Π΅Ρ‚Ρ€ΠΈΠ²ΠΈΠ°Π»ΡŒΠ½ΠΎΠΉ Π·Π°Π΄Π°Ρ‡Π΅ΠΉ.

Π’ΠΎΡ‚ ΠΌΠΈΠ½ΠΈΠΌΠ°Π»ΡŒΠ½Ρ‹ΠΉ API, ΠΊΠΎΡ‚ΠΎΡ€Ρ‹ΠΉ Π½Π°ΠΌ Π½ΡƒΠΆΠ½ΠΎ Ρ€Π΅Π°Π»ΠΈΠ·ΠΎΠ²Π°Ρ‚ΡŒ:

// 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 прост ΠΈ ΠΏΠΎΡ…ΠΎΠΆ Π½Π° API Posix Sockets, Π½ΠΎ ΠΈΠΌΠ΅Π΅Ρ‚ нСсколько Π²Π°ΠΆΠ½Ρ‹Ρ… ΠΎΡ‚Π»ΠΈΡ‡ΠΈΠΉ: рСгистрация ΠΎΠ±Ρ€Π°Ρ‚Π½Ρ‹Ρ… Π²Ρ‹Π·ΠΎΠ²ΠΎΠ², Π½Π°Π·Π½Π°Ρ‡Π΅Π½ΠΈΠ΅ Π»ΠΎΠΊΠ°Π»ΡŒΠ½Ρ‹Ρ… IP ΠΈ Β«Π»Π΅Π½ΠΈΠ²ΠΎΠ΅Β» соСдинСниС.

РСгистрация ΠΎΠ±Ρ€Π°Ρ‚Π½Ρ‹Ρ… Π²Ρ‹Π·ΠΎΠ²ΠΎΠ²

Π”Π°ΠΆΠ΅ Ссли исходная ΠΏΡ€ΠΎΠ³Ρ€Π°ΠΌΠΌΠ° ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΡƒΠ΅Ρ‚ Π½Π΅Π±Π»ΠΎΠΊΠΈΡ€ΡƒΡŽΡ‰ΠΈΠΉ Π²Π²ΠΎΠ΄-Π²Ρ‹Π²ΠΎΠ΄, для запуска Π² Π²Π΅Π±-Π±Ρ€Π°ΡƒΠ·Π΅Ρ€Π΅ ΠΊΠΎΠ΄ Π½ΡƒΠΆΠ½ΠΎ Ρ€Π΅Ρ„Π°ΠΊΡ‚ΠΎΡ€ΠΈΠ·ΠΈΡ€ΠΎΠ²Π°Ρ‚ΡŒ.

ΠŸΡ€ΠΈΡ‡ΠΈΠ½Π° этого Π·Π°ΠΊΠ»ΡŽΡ‡Π°Π΅Ρ‚ΡΡ Π² Ρ‚ΠΎΠΌ, Ρ‡Ρ‚ΠΎ Ρ†ΠΈΠΊΠ» событий Π² Π±Ρ€Π°ΡƒΠ·Π΅Ρ€Π΅ скрыт ΠΎΡ‚ ΠΏΡ€ΠΎΠ³Ρ€Π°ΠΌΠΌΡ‹ (Π±ΡƒΠ΄ΡŒ Ρ‚ΠΎ JavaScript ΠΈΠ»ΠΈ WebAssembly).

Π’ Π½Π°Ρ‚ΠΈΠ²Π½ΠΎΠΉ срСдС ΠΌΡ‹ ΠΌΠΎΠΆΠ΅ΠΌ ΠΏΠΈΡΠ°Ρ‚ΡŒ ΠΊΠΎΠ΄ Ρ‚Π°ΠΊΠΈΠΌ ΠΎΠ±Ρ€Π°Π·ΠΎΠΌ

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

Если Ρ†ΠΈΠΊΠ» событий для нас скрыт, Ρ‚ΠΎ Π½ΡƒΠΆΠ½ΠΎ ΠΏΡ€Π΅Π²Ρ€Π°Ρ‚ΠΈΡ‚ΡŒ Π΅Π³ΠΎ Π² Π½Π΅Ρ‡Ρ‚ΠΎ ΠΏΠΎΠ΄ΠΎΠ±Π½ΠΎΠ΅:

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

НазначСниС Π»ΠΎΠΊΠ°Π»ΡŒΠ½Ρ‹Ρ… IP

Π˜Π΄Π΅Π½Ρ‚ΠΈΡ„ΠΈΠΊΠ°Ρ‚ΠΎΡ€Ρ‹ ΡƒΠ·Π»ΠΎΠ² Π² нашСй «сСти» ΡΠ²Π»ΡΡŽΡ‚ΡΡ Π½Π΅ IP-адрСсами, Π° ΠΊΠ»ΡŽΡ‡Π°ΠΌΠΈ Firebase (это строки, ΠΊΠΎΡ‚ΠΎΡ€Ρ‹Π΅ выглядят Ρ‚Π°ΠΊ: -LmEC50PYZLCiCP-vqde ).

Π­Ρ‚ΠΎ ΡƒΠ΄ΠΎΠ±Π½ΠΎ, ΠΏΠΎΡ‚ΠΎΠΌΡƒ Ρ‡Ρ‚ΠΎ Π½Π°ΠΌ Π½Π΅ Π½ΡƒΠΆΠ΅Π½ ΠΌΠ΅Ρ…Π°Π½ΠΈΠ·ΠΌ для назначСния IP ΠΈ ΠΏΡ€ΠΎΠ²Π΅Ρ€ΠΊΠ° ΠΈΡ… ΡƒΠ½ΠΈΠΊΠ°Π»ΡŒΠ½ΠΎΡΡ‚ΠΈ (Π° Ρ‚Π°ΠΊΠΆΠ΅ ΠΈΡ… утилизация послС ΠΎΡ‚ΠΊΠ»ΡŽΡ‡Π΅Π½ΠΈΡ ΠΊΠ»ΠΈΠ΅Π½Ρ‚Π°), Π½ΠΎ часто Π±Ρ‹Π²Π°Π΅Ρ‚ Π½Π΅ΠΎΠ±Ρ…ΠΎΠ΄ΠΈΠΌΠΎ ΠΈΠ΄Π΅Π½Ρ‚ΠΈΡ„ΠΈΡ†ΠΈΡ€ΠΎΠ²Π°Ρ‚ΡŒ ΠΏΠΈΡ€ΠΎΠ² ΠΏΠΎ числовому Π·Π½Π°Ρ‡Π΅Π½ΠΈΡŽ.

ИмСнно для этого ΠΈ ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΡƒΡŽΡ‚ΡΡ Ρ„ΡƒΠ½ΠΊΡ†ΠΈΠΈ resolve ΠΈ reverseResolve: ΠΏΡ€ΠΈΠ»ΠΎΠΆΠ΅Π½ΠΈΠ΅ ΠΊΠ°ΠΊΠΈΠΌ-Ρ‚ΠΎ ΠΎΠ±Ρ€Π°Π·ΠΎΠΌ ΠΏΠΎΠ»ΡƒΡ‡Π°Π΅Ρ‚ строковоС Π·Π½Π°Ρ‡Π΅Π½ΠΈΠ΅ ΠΊΠ»ΡŽΡ‡Π° (Ρ‡Π΅Ρ€Π΅Π· Π²Π²ΠΎΠ΄ ΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚Π΅Π»Ρ ΠΈΠ»ΠΈ Ρ‡Π΅Ρ€Π΅Π· мастСр-сСрвСр), ΠΈ ΠΌΠΎΠΆΠ΅Ρ‚ ΠΏΡ€Π΅ΠΎΠ±Ρ€Π°Π·ΠΎΠ²Π°Ρ‚ΡŒ Π΅Π³ΠΎ Π² IP-адрСс для Π²Π½ΡƒΡ‚Ρ€Π΅Π½Π½Π΅Π³ΠΎ использования. ΠžΡΡ‚Π°Π»ΡŒΠ½Π°Ρ Ρ‡Π°ΡΡ‚ΡŒ API Ρ‚ΠΎΠΆΠ΅ для простоты ΠΏΠΎΠ»ΡƒΡ‡Π°Π΅Ρ‚ вмСсто строки это Π·Π½Π°Ρ‡Π΅Π½ΠΈΠ΅.

Π­Ρ‚ΠΎ ΠΏΠΎΡ…ΠΎΠΆΠ΅ Π½Π° DNS-поиск, Ρ‚ΠΎΠ»ΡŒΠΊΠΎ выполняСтся локально Ρƒ ΠΊΠ»ΠΈΠ΅Π½Ρ‚Π°.

Π’ΠΎ Π΅ΡΡ‚ΡŒ IP-адрСса Π½Π΅ ΠΌΠΎΠ³ΡƒΡ‚ Π±Ρ‹Ρ‚ΡŒ ΠΎΠ±Ρ‰ΠΈΠΌΠΈ для Ρ€Π°Π·Π½Ρ‹Ρ… ΠΊΠ»ΠΈΠ΅Π½Ρ‚ΠΎΠ², ΠΈ Ссли Π½ΡƒΠΆΠ΅Π½ ΠΊΠ°ΠΊΠΎΠΉ-Ρ‚ΠΎ Π³Π»ΠΎΠ±Π°Π»ΡŒΠ½Ρ‹ΠΉ ΠΈΠ΄Π΅Π½Ρ‚ΠΈΡ„ΠΈΠΊΠ°Ρ‚ΠΎΡ€, Ρ‚ΠΎ Π΅Π³ΠΎ придётся Π³Π΅Π½Π΅Ρ€ΠΈΡ€ΠΎΠ²Π°Ρ‚ΡŒ ΠΈΠ½Ρ‹ΠΌ способом.

Π›Π΅Π½ΠΈΠ²ΠΎΠ΅ соСдинСниС

UDP Π½Π΅ Π½ΡƒΠΆΠ½ΠΎ ΠΏΠΎΠ΄ΠΊΠ»ΡŽΡ‡Π΅Π½ΠΈΠ΅, Π½ΠΎ, ΠΊΠ°ΠΊ ΠΌΡ‹ Π²ΠΈΠ΄Π΅Π»ΠΈ, ΠΏΡ€Π΅ΠΆΠ΄Π΅ Ρ‡Π΅ΠΌ Π½Π°Ρ‡Π°Ρ‚ΡŒ ΠΏΠ΅Ρ€Π΅Π΄Π°Ρ‡Ρƒ Π΄Π°Π½Π½Ρ‹Ρ… ΠΌΠ΅ΠΆΠ΄Ρƒ двумя ΠΏΠΈΡ€Π°ΠΌΠΈ, WebRTC Ρ‚Ρ€Π΅Π±ΡƒΠ΅Ρ‚ Π΄Π»ΠΈΡ‚Π΅Π»ΡŒΠ½Ρ‹ΠΉ процСсс ΠΏΠΎΠ΄ΠΊΠ»ΡŽΡ‡Π΅Π½ΠΈΡ.

Если ΠΌΡ‹ Ρ…ΠΎΡ‚ΠΈΠΌ ΠΎΠ±Π΅ΡΠΏΠ΅Ρ‡ΠΈΡ‚ΡŒ Ρ‚ΠΎΡ‚ ΠΆΠ΅ ΡƒΡ€ΠΎΠ²Π΅Π½ΡŒ абстракции, (sendto/recvfrom с ΠΏΡ€ΠΎΠΈΠ·Π²ΠΎΠ»ΡŒΠ½Ρ‹ΠΌΠΈ ΠΏΠΈΡ€Π°ΠΌΠΈ Π±Π΅Π· ΠΏΡ€Π΅Π΄Π²Π°Ρ€ΠΈΡ‚Π΅Π»ΡŒΠ½ΠΎΠ³ΠΎ ΠΏΠΎΠ΄ΠΊΠ»ΡŽΡ‡Π΅Π½ΠΈΡ), Ρ‚ΠΎ Π΄ΠΎΠ»ΠΆΠ½Ρ‹ Π²Ρ‹ΠΏΠΎΠ»Π½ΡΡ‚ΡŒ Β«Π»Π΅Π½ΠΈΠ²ΠΎΠ΅Β» (ΠΎΡ‚Π»ΠΎΠΆΠ΅Π½Π½ΠΎΠ΅) ΠΏΠΎΠ΄ΠΊΠ»ΡŽΡ‡Π΅Π½ΠΈΠ΅ Π²Π½ΡƒΡ‚Ρ€ΠΈ API.

Π’ΠΎΡ‚ Ρ‡Ρ‚ΠΎ происходит ΠΏΡ€ΠΈ ΠΎΠ±Ρ‹Ρ‡Π½ΠΎΠΌ ΠΎΠ±ΠΌΠ΅Π½Π΅ Π΄Π°Π½Π½Ρ‹ΠΌΠΈ ΠΌΠ΅ΠΆΠ΄Ρƒ «сСрвСром» ΠΈ Β«ΠΊΠ»ΠΈΠ΅Π½Ρ‚ΠΎΠΌΒ» Π² случаС использования UDP, ΠΈ Ρ‡Ρ‚ΠΎ Π΄ΠΎΠ»ΠΆΠ½Π° Π²Ρ‹ΠΏΠΎΠ»Π½ΡΡ‚ΡŒ наша Π±ΠΈΠ±Π»ΠΈΠΎΡ‚Π΅ΠΊΠ°:

  • Π‘Π΅Ρ€Π²Π΅Ρ€ Π²Ρ‹Π·Ρ‹Π²Π°Π΅Ρ‚ bind(), Ρ‡Ρ‚ΠΎΠ±Ρ‹ ΡΠΎΠΎΠ±Ρ‰ΠΈΡ‚ΡŒ ΠΎΠΏΠ΅Ρ€Π°Ρ†ΠΈΠΎΠ½Π½ΠΎΠΉ систСмС, Ρ‡Ρ‚ΠΎ Ρ…ΠΎΡ‡Π΅Ρ‚ ΠΏΠΎΠ»ΡƒΡ‡Π°Ρ‚ΡŒ ΠΏΠ°ΠΊΠ΅Ρ‚Ρ‹ Π² ΡƒΠΊΠ°Π·Π°Π½Π½Ρ‹ΠΉ ΠΏΠΎΡ€Ρ‚.

ВмСсто этого ΠΌΡ‹ ΠΎΠΏΡƒΠ±Π»ΠΈΠΊΡƒΠ΅ΠΌ ΠΎΡ‚ΠΊΡ€Ρ‹Ρ‚Ρ‹ΠΉ ΠΏΠΎΡ€Ρ‚ Π² Firebase ΠΏΠΎΠ΄ ΠΊΠ»ΡŽΡ‡ΠΎΠΌ сСрвСра ΠΈ Π±ΡƒΠ΄Π΅ΠΌ ΡΠ»ΡƒΡˆΠ°Ρ‚ΡŒ события Π² Π΅Π³ΠΎ ΠΏΠΎΠ΄Π΄Π΅Ρ€Π΅Π²Π΅.

  • Π‘Π΅Ρ€Π²Π΅Ρ€ Π²Ρ‹Π·Ρ‹Π²Π°Π΅Ρ‚ recvfrom(), принимая Π² этот ΠΏΠΎΡ€Ρ‚ ΠΏΠ°ΠΊΠ΅Ρ‚Ρ‹, ΠΏΠΎΡΡ‚ΡƒΠΏΠ°ΡŽΡ‰ΠΈΠ΅ ΠΎΡ‚ любого хоста.

Π’ нашСм случаС Π½ΡƒΠΆΠ½ΠΎ ΠΏΡ€ΠΎΠ²Π΅Ρ€ΡΡ‚ΡŒ Π²Ρ…ΠΎΠ΄ΡΡ‰ΡƒΡŽ ΠΎΡ‡Π΅Ρ€Π΅Π΄ΡŒ ΠΏΠ°ΠΊΠ΅Ρ‚ΠΎΠ², ΠΎΡ‚ΠΏΡ€Π°Π²Π»Π΅Π½Π½Ρ‹Ρ… Π² этот ΠΏΠΎΡ€Ρ‚.

ΠšΠ°ΠΆΠ΄Ρ‹ΠΉ ΠΏΠΎΡ€Ρ‚ ΠΈΠΌΠ΅Π΅Ρ‚ ΡΠΎΠ±ΡΡ‚Π²Π΅Π½Π½ΡƒΡŽ ΠΎΡ‡Π΅Ρ€Π΅Π΄ΡŒ, ΠΈ ΠΌΡ‹ добавляСм Π² Π½Π°Ρ‡Π°Π»ΠΎ Π΄Π°Ρ‚Π°Π³Ρ€Π°ΠΌΠΌ WebRTC исходный ΠΈ ΠΊΠΎΠ½Π΅Ρ‡Π½Ρ‹ΠΉ ΠΏΠΎΡ€Ρ‚Ρ‹, Ρ‡Ρ‚ΠΎΠ±Ρ‹ Π·Π½Π°Ρ‚ΡŒ, Π² ΠΊΠ°ΠΊΡƒΡŽ ΠΎΡ‡Π΅Ρ€Π΅Π΄ΡŒ ΠΏΠ΅Ρ€Π΅Π½Π°ΠΏΡ€Π°Π²ΠΈΡ‚ΡŒ ΠΏΡ€ΠΈ поступлСнии Π½ΠΎΠ²Ρ‹ΠΉ ΠΏΠ°ΠΊΠ΅Ρ‚.

Π’Ρ‹Π·ΠΎΠ² являСтся Π½Π΅Π±Π»ΠΎΠΊΠΈΡ€ΡƒΡŽΡ‰ΠΈΠΌ, поэтому Ссли ΠΏΠ°ΠΊΠ΅Ρ‚ΠΎΠ² Π½Π΅Ρ‚, ΠΌΡ‹ просто Π²ΠΎΠ·Π²Ρ€Π°Ρ‰Π°Π΅ΠΌ -1 ΠΈ Π·Π°Π΄Π°Ρ‘ΠΌ errno=EWOULDBLOCK.

  • ΠšΠ»ΠΈΠ΅Π½Ρ‚ ΠΏΠΎΠ»ΡƒΡ‡Π°Π΅Ρ‚ Π½Π΅ΠΊΠΈΠΌΠΈ внСшними срСдствами IP ΠΈ ΠΏΠΎΡ€Ρ‚ сСрвСра, ΠΈ Π²Ρ‹Π·Ρ‹Π²Π°Π΅Ρ‚ sendto(). Π’Π°ΠΊΠΆΠ΅ ΠΏΡ€ΠΈ этом выполняСтся Π²Π½ΡƒΡ‚Ρ€Π΅Π½Π½ΠΈΠΉ Π²Ρ‹Π·ΠΎΠ² bind(), поэтому ΠΏΠΎΡΠ»Π΅Π΄ΡƒΡŽΡ‰ΠΈΠΉ recvfrom() ΠΏΠΎΠ»ΡƒΡ‡ΠΈΡ‚ ΠΎΡ‚Π²Π΅Ρ‚ Π±Π΅Π· явного выполнСния bind.

Π’ нашСм случаС ΠΊΠ»ΠΈΠ΅Π½Ρ‚ внСшним ΠΎΠ±Ρ€Π°Π·ΠΎΠΌ ΠΏΠΎΠ»ΡƒΡ‡Π°Π΅Ρ‚ строковый ΠΊΠ»ΡŽΡ‡ ΠΈ ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΡƒΠ΅Ρ‚ Ρ„ΡƒΠ½ΠΊΡ†ΠΈΡŽ resolve() для получСния IP-адрСса.

На этом этапС ΠΌΡ‹ Π½Π°Ρ‡ΠΈΠ½Π°Π΅ΠΌ Β«Ρ€ΡƒΠΊΠΎΠΏΠΎΠΆΠ°Ρ‚ΠΈΠ΅Β» WebRTC, Ссли Π΄Π²Π° ΠΏΠΈΡ€Π° Π΅Ρ‰Ρ‘ Π½Π΅ соСдинСны Π΄Ρ€ΡƒΠ³ с Π΄Ρ€ΡƒΠ³ΠΎΠΌ. ΠŸΠΎΠ΄ΠΊΠ»ΡŽΡ‡Π΅Π½ΠΈΡ ΠΊ Ρ€Π°Π·Π½Ρ‹ΠΌ ΠΏΠΎΡ€Ρ‚Π°ΠΌ ΠΎΠ΄Π½ΠΎΠ³ΠΎ ΠΏΠΈΡ€Π° ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΡƒΡŽΡ‚ ΠΎΠ΄ΠΈΠ½Π°ΠΊΠΎΠ²Ρ‹ΠΉ DataChannel WebRTC.

Π’Π°ΠΊΠΆΠ΅ ΠΌΡ‹ выполняСм косвСнный bind(), Ρ‡Ρ‚ΠΎΠ±Ρ‹ сСрвСр ΠΌΠΎΠ³ Π²ΠΎΡΡΡ‚Π°Π½ΠΎΠ²ΠΈΡ‚ΡŒ соСдинСниС Π² ΡΠ»Π΅Π΄ΡƒΡŽΡ‰Π΅ΠΌ sendto() Π½Π° случай, Ссли ΠΎΠ½ΠΎ ΠΏΠΎ ΠΊΠ°ΠΊΠΈΠΌ-Ρ‚ΠΎ ΠΏΡ€ΠΈΡ‡ΠΈΠ½Π°ΠΌ Π·Π°ΠΊΡ€Ρ‹Π»ΠΎΡΡŒ.

Π‘Π΅Ρ€Π²Π΅Ρ€ увСдомляСтся ΠΎ ΠΏΠΎΠ΄ΠΊΠ»ΡŽΡ‡Π΅Π½ΠΈΠΈ ΠΊΠ»ΠΈΠ΅Π½Ρ‚Π°, ΠΊΠΎΠ³Π΄Π° ΠΊΠ»ΠΈΠ΅Π½Ρ‚ записываСт свой SDP offer ΠΏΠΎΠ΄ ΠΈΠ½Ρ„ΠΎΡ€ΠΌΠ°Ρ†ΠΈΠ΅ΠΉ ΠΏΠΎΡ€Ρ‚Π° сСрвСра Π² Firebase, ΠΈ сСрвСр Ρ‚Π°ΠΌ ΠΆΠ΅ ΠΎΡ‚Π²Π΅Ρ‡Π°Π΅Ρ‚ своим ΠΎΡ‚ΠΊΠ»ΠΈΠΊΠΎΠΌ.

На ΠΏΠΎΠΊΠ°Π·Π°Π½Π½ΠΎΠΉ Π½ΠΈΠΆΠ΅ схСмС ΠΏΠΎΠΊΠ°Π·Π°Π½ ΠΏΡ€ΠΈΠΌΠ΅Ρ€ двиТСния сообщСний для схСмы сокСтов ΠΈ ΠΏΠ΅Ρ€Π΅Π΄Π°Ρ‡Π° ΠΎΡ‚ ΠΊΠ»ΠΈΠ΅Π½Ρ‚Π° сСрвСру ΠΏΠ΅Ρ€Π²ΠΎΠ³ΠΎ сообщСния:

ΠŸΠΎΡ€Ρ‚ΠΈΡ€ΡƒΠ΅ΠΌ ΠΌΠ½ΠΎΠ³ΠΎΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚Π΅Π»ΡŒΡΠΊΡƒΡŽ ΠΈΠ³Ρ€Ρƒ с Π‘++ Π½Π° Π²Π΅Π± c Cheerp, WebRTC ΠΈ Firebase
Полная схСма этапа ΠΏΠΎΠ΄ΠΊΠ»ΡŽΡ‡Π΅Π½ΠΈΡ ΠΌΠ΅ΠΆΠ΄Ρƒ ΠΊΠ»ΠΈΠ΅Π½Ρ‚ΠΎΠΌ ΠΈ сСрвСром

Π—Π°ΠΊΠ»ΡŽΡ‡Π΅Π½ΠΈΠ΅

Если Π²Ρ‹ Π΄ΠΎΡ‡ΠΈΡ‚Π°Π»ΠΈ Π΄ΠΎ ΠΊΠΎΠ½Ρ†Π°, Ρ‚ΠΎ Π²Π°ΠΌ Π½Π°Π²Π΅Ρ€Π½ΠΎ интСрСсно ΠΏΠΎΡΠΌΠΎΡ‚Ρ€Π΅Ρ‚ΡŒ Π½Π° Ρ‚Π΅ΠΎΡ€ΠΈΡŽ Π² дСйствии. Π’ ΠΈΠ³Ρ€Ρƒ ΠΌΠΎΠΆΠ½ΠΎ ΡΡ‹Π³Ρ€Π°Ρ‚ΡŒ Π½Π° teeworlds.leaningtech.com, ΠΏΠΎΠΏΡ€ΠΎΠ±ΡƒΠΉΡ‚Π΅!


ДруТСский ΠΌΠ°Ρ‚Ρ‡ ΠΌΠ΅ΠΆΠ΄Ρƒ ΠΊΠΎΠ»Π»Π΅Π³Π°ΠΌΠΈ

Код сСтСвой Π±ΠΈΠ±Π»ΠΈΠΎΡ‚Π΅ΠΊΠΈ свободно доступСн Π½Π° Github. ΠŸΡ€ΠΈΡΠΎΠ΅Π΄ΠΈΠ½ΡΠΉΡ‚Π΅ΡΡŒ ΠΊ ΠΎΠ±Ρ‰Π΅Π½ΠΈΡŽ Π½Π° нашСм ΠΊΠ°Π½Π°Π»Π΅ Π² Gitter!

Π˜ΡΡ‚ΠΎΡ‡Π½ΠΈΠΊ: habr.com