Work in progress

The content of this page was not yet updated for Godot 4.2 and may be outdated. If you know how to improve this page or you can confirm that it's up to date, feel free to open a pull request.

Веб RTC

HTML5, WebSocket, WebRTC

Однією з чудових функцій Godot є його здатність експортувати на платформу HTML5/WebAssembly, що дозволяє вашій грі запускатися безпосередньо у браузері, коли користувач відвідує вашу веб-сторінку.

Це чудова можливість як для демонстраційних, так і для повноцінних ігор, але раніше вони мали певні обмеження. У сфері мереж донедавна браузери підтримували лише HTTPRequests, коли спочатку в якості стандартів були запропоновані WebSocket, а потім WebRTC.

WebSocket

When the WebSocket protocol was standardized in December 2011, it allowed browsers to create stable and bidirectional connections to a WebSocket server. The protocol is a very powerful tool to send push notifications to browsers, and has been used to implement chats, turn-based games, etc.

Однак WebSocket-и все ще використовують TCP-з’єднання, яке надійне, але повільне, і тому не підходить для додатків у реальному часі, таких як VoIP та інші динамічні ігри.

Веб RTC

З цієї причини з 2010 року Google розпочала роботу над новою технологією під назвою WebRTC, яка згодом, у 2017 році, стала можливою рекомендацією від W3C. WebRTC — це набагато складніший набір специфікацій, який покладається на багато інших закулісних технологій (ICE, DTLS, SDP), щоб забезпечити швидке, безпечне спілкування в реальному часі між двома одноранговими вузлами.

Ідея полягає в тому, щоб знайти найшвидший маршрут між двома одноранговими вузлами та встановити, коли це можливо, прямий зв’язок (тобто спробувати уникнути сервер-ретранслятор).

Однак це має свою ціну, яка полягає в тому, що, перш ніж розпочнеться зв’язок, два однорангові вузли мають обмінятися між собою деякою медіа-інформацією (у формі Протоколу Опису Сеансу - тексти SDP). Зазвичай це має форму так званого Сервера Сигналізації WebRTC.

../../_images/webrtc_signaling.png

Однорангові вузли підключаються до сервера сигналізації (наприклад, сервера WebSocket) і надсилають свою медіа-інформацію. Потім сервер передає цю інформацію іншим одноранговим вузлам, дозволяючи їм встановити потрібний прямий зв’язок. Після виконання цього кроку однорангові вузли можуть відключитися від сервера сигналізації та зберегти пряме однорангове з’єднання (P2P) відкритим.

Використання WebRTC в Godot

WebRTC is implemented in Godot via two main classes WebRTCPeerConnection and WebRTCDataChannel, plus the multiplayer API implementation WebRTCMultiplayerPeer. See section on high-level multiplayer for more details.

Примітка

These classes are available automatically in HTML5, but require an external GDExtension plugin on native (non-HTML5) platforms. Check out the webrtc-native plugin repository for instructions and to get the latest release.

Попередження

Під час експорту в Android обов’язково ввімкніть дозвіл INTERNET у попередньо налаштованих експортах Android, перш ніж експортувати проект, або використовувати розгортання одним клацанням мишки. Інакше Android заблокує будь-який мережевий зв’язок.

Мінімальний приклад підключення

Цей приклад покаже вам, як створити з’єднання WebRTC між двома одноранговими вузлами в одній програмі. Це не дуже корисно в реальному житті, але дасть вам хороший огляд того, як налаштовано з’єднання WebRTC.

extends Node

# Create the two peers
var p1 = WebRTCPeerConnection.new()
var p2 = WebRTCPeerConnection.new()
# And a negotiated channel for each each peer
var ch1 = p1.create_data_channel("chat", {"id": 1, "negotiated": true})
var ch2 = p2.create_data_channel("chat", {"id": 1, "negotiated": true})

func _ready():
    # Connect P1 session created to itself to set local description.
    p1.session_description_created.connect(p1.set_local_description)
    # Connect P1 session and ICE created to p2 set remote description and candidates.
    p1.session_description_created.connect(p2.set_remote_description)
    p1.ice_candidate_created.connect(p2.add_ice_candidate)

    # Same for P2
    p2.session_description_created.connect(p2.set_local_description)
    p2.session_description_created.connect(p1.set_remote_description)
    p2.ice_candidate_created.connect(p1.add_ice_candidate)

    # Let P1 create the offer
    p1.create_offer()

    # Wait a second and send message from P1.
    await get_tree().create_timer(1).timeout
    ch1.put_packet("Hi from P1".to_utf8_buffer())

    # Wait a second and send message from P2.
    await get_tree().create_timer(1).timeout
    ch2.put_packet("Hi from P2".to_utf8_buffer())

func _process(_delta):
    # Poll connections
    p1.poll()
    p2.poll()

    # Check for messages
    if ch1.get_ready_state() == ch1.STATE_OPEN and ch1.get_available_packet_count() > 0:
        print("P1 received: ", ch1.get_packet().get_string_from_utf8())
    if ch2.get_ready_state() == ch2.STATE_OPEN and ch2.get_available_packet_count() > 0:
        print("P2 received: ", ch2.get_packet().get_string_from_utf8())

Буде виведено:

P1 received: Hi from P1
P2 received: Hi from P2

Приклад локальної сигналізації

Цей приклад розширює попередній, розділяючи однорангові вузли у двох різних сценах та використовуючи синглтон, як сервер сигналізації.

extends Node
# An example p2p chat client.

var peer = WebRTCPeerConnection.new()

# Create negotiated data channel.
var channel = peer.create_data_channel("chat", {"negotiated": true, "id": 1})

func _ready():
    # Connect all functions.
    peer.ice_candidate_created.connect(self._on_ice_candidate)
    peer.session_description_created.connect(self._on_session)

    # Register to the local signaling server (see below for the implementation).
    Signaling.register(String(get_path()))


func _on_ice_candidate(mid, index, sdp):
    # Send the ICE candidate to the other peer via signaling server.
    Signaling.send_candidate(String(get_path()), mid, index, sdp)


func _on_session(type, sdp):
    # Send the session to other peer via signaling server.
    Signaling.send_session(String(get_path()), type, sdp)
    # Set generated description as local.
    peer.set_local_description(type, sdp)


func _process(delta):
    # Always poll the connection frequently.
    peer.poll()
    if channel.get_ready_state() == WebRTCDataChannel.STATE_OPEN:
        while channel.get_available_packet_count() > 0:
            print(String(get_path()), " received: ", channel.get_packet().get_string_from_utf8())


func send_message(message):
    channel.put_packet(message.to_utf8_buffer())

А тепер для локального сервера сигналізації:

Примітка

Цей локальний сервер сигналізації має використовуватися, як синглтон для з’єднання двох однорангових вузлів в одній сцені.

# A local signaling server. Add this to autoloads with name "Signaling" (/root/Signaling)
extends Node

# We will store the two peers here
var peers = []

func register(path):
    assert(peers.size() < 2)
    peers.append(path)
    if peers.size() == 2:
        get_node(peers[0]).peer.create_offer()


func _find_other(path):
    # Find the other registered peer.
    for p in peers:
        if p != path:
            return p
    return ""


func send_session(path, type, sdp):
    var other = _find_other(path)
    assert(other != "")
    get_node(other).peer.set_remote_description(type, sdp)


func send_candidate(path, mid, index, sdp):
    var other = _find_other(path)
    assert(other != "")
    get_node(other).peer.add_ice_candidate(mid, index, sdp)

Тоді ви можете використовувати його так:

# Main scene (main.gd)
extends Node

const Chat = preload("res://chat.gd")

func _ready():
    var p1 = Chat.new()
    var p2 = Chat.new()
    add_child(p1)
    add_child(p2)

    # Wait a second and send message from P1
    await get_tree().create_timer(1).timeout
    p1.send_message("Hi from %s" % String(p1.get_path()))

    # Wait a second and send message from P2
    await get_tree().create_timer(1).timeout
    p2.send_message("Hi from %s" % String(p2.get_path()))

Це надрукує щось подібне до цього:

/root/main/@@3 received: Hi from /root/main/@@2
/root/main/@@2 received: Hi from /root/main/@@3

Віддалена сигналізація за допомогою WebSocket

A more advanced demo using WebSocket for signaling peers and WebRTCMultiplayerPeer is available in the godot demo projects under networking/webrtc_signaling.