Attention: Here be dragons

This is the latest (unstable) version of this documentation, which may document features not available in or compatible with released stable versions of Godot.

WebRTC

HTML5, WebSocket, WebRTC

Una delle grandi caratteristiche di Godot è la sua capacità di esportare sulla piattaforma HTML5/WebAssembly, consentendo al proprio gioco di essere eseguito direttamente nel browser quando un utente visita la propria pagina web.

Questa è un'ottima opportunità sia per le demo sia per i giochi completi, ma in passato presentava alcune limitazioni. Nell'ambito del networking, i browser supportavano solo HTTPRequest fino a poco tempo fa, quando WebSocket e poi WebRTC sono stati proposti come standard.

WebSocket

Quando il protocollo WebSocket è stato standardizzato nel dicembre 2011, ha consentito ai browser di creare connessioni stabili e bidirezionali a un server WebSocket. Il protocollo è uno strumento molto potente per inviare notifiche push ai browser ed è stato utilizzato per implementare chat, giochi a turni, ecc.

I WebSocket, tuttavia, utilizzano ancora una connessione TCP, che è buona per l'affidabilità ma non per la latenza, quindi non è adatta per applicazioni in tempo reale come VoIP e giochi frenetici.

WebRTC

Per questo motivo, dal 2010, Google ha iniziato a lavorare su una nuova tecnologia chiamata WebRTC, che in seguito, nel 2017, è diventata una raccomandazione candidata di W3C. WebRTC è un insieme di specifiche molto più complesso e si basa su molte altre tecnologie (ICE, DTLS, SDP) per una comunicazione veloce, in tempo reale e sicura tra due peer.

L'idea è quella di trovare il percorso più veloce tra i due peer e stabilire, ove possibile, una comunicazione diretta (vale a dire, cercare di evitare un server di relay).

Tuttavia, questo ha un prezzo: prima che la comunicazione possa iniziare, è necessario che alcune informazioni multimediali vengano scambiate tra i due peer (sotto forma di stringhe SDP - Session Description Protocol). Questo solitamente avviene tramite un cosiddetto WebRTC Signaling Server.

../../_images/webrtc_signaling.png

I peer si connettono a un server di segnalazione (ad esempio un server WebSocket) e inviano le proprie informazioni multimediali. Il server poi inoltra queste informazioni agli altri peer, consentendo loro di stabilire la comunicazione diretta desiderata. Una volta completato questo passaggio, i peer possono disconnettersi dal server di segnalazione e mantenere aperta la connessione Peer-to-Peer (P2P) diretta.

Utilizzare WebRTC in Godot

WebRTC è implementato in Godot tramite due classi principali WebRTCPeerConnection e WebRTCDataChannel, oltre all'implementazione dell'API multigiocatore WebRTCMultiplayerPeer. Consultare la sezione multigiocatore di alto livello per maggiori dettagli.

Nota

Queste classi sono disponibili automaticamente in HTML5, ma richiedono un'estensione GDExtension esterna su piattaforme native (non HTML5). Consultare il repository delle estensioni webrtc-native per istruzioni e per ottenere l'ultimo rilascio.

Avvertimento

When exporting to Android, make sure to enable the INTERNET permission in the Android export preset before exporting the project or using one-click deploy. Otherwise, network communication of any kind will be blocked by Android.

Esempio minimo di connessione

Questo esempio mostrerà come creare una connessione WebRTC tra due peer nella stessa applicazione. Non è molto utile nella vita reale, ma fornirà una buona panoramica di come è configurata una connessione 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())

Questo stamperà:

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

Esempio di segnalazione locale

Questo esempio espande quello precedente, separando i peer in due scene diverse e utilizzando un singleton come server di segnalazione.

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())

E ora, il server di segnalazione locale:

Nota

Questo server di segnalazione locale dovrebbe essere usato come singleton per connettere due peer nella stessa scena.

# 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)

Poi si può usare in questo modo:

# 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()))

Questo stamperà qualcosa di simile a questo:

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

Segnalazione remota con WebSocket

Una demo più avanzata che utilizza WebSocket per la segnalazione dei peer e per WebRTCMultiplayerPeer è disponibile nei progetti demo godot in networking/webrtc_signaling.