diff --git a/networking/webrtc_minimal/.gitignore b/networking/webrtc_minimal/.gitignore new file mode 100644 index 00000000..d9bb4a8c --- /dev/null +++ b/networking/webrtc_minimal/.gitignore @@ -0,0 +1 @@ +webrtc diff --git a/networking/webrtc_minimal/Signaling.gd b/networking/webrtc_minimal/Signaling.gd new file mode 100644 index 00000000..af1fb756 --- /dev/null +++ b/networking/webrtc_minimal/Signaling.gd @@ -0,0 +1,28 @@ +# 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) \ No newline at end of file diff --git a/networking/webrtc_minimal/chat.gd b/networking/webrtc_minimal/chat.gd new file mode 100644 index 00000000..39678c3c --- /dev/null +++ b/networking/webrtc_minimal/chat.gd @@ -0,0 +1,35 @@ +# An example p2p chat client +extends Node + +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.connect("ice_candidate_created", self, "_on_ice_candidate") + peer.connect("session_description_created", self, "_on_session") + + # Register to the local signaling server (see below for the implementation) + Signaling.register(get_path()) + +func _on_ice_candidate(mid, index, sdp): + # Send the ICE candidate to the other peer via signaling server + Signaling.send_candidate(get_path(), mid, index, sdp) + +func _on_session(type, sdp): + # Send the session to other peer via signaling server + Signaling.send_session(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(get_path(), " received: ", channel.get_packet().get_string_from_utf8()) + +func send_message(message): + channel.put_packet(message.to_utf8()) \ No newline at end of file diff --git a/networking/webrtc_minimal/main.gd b/networking/webrtc_minimal/main.gd new file mode 100644 index 00000000..c61b3ab2 --- /dev/null +++ b/networking/webrtc_minimal/main.gd @@ -0,0 +1,17 @@ +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 + yield(get_tree().create_timer(1), "timeout") + p1.send_message("Hi from %s" % p1.get_path()) + + # Wait a second and send message from P2 + yield(get_tree().create_timer(1), "timeout") + p2.send_message("Hi from %s" % p2.get_path()) \ No newline at end of file diff --git a/networking/webrtc_minimal/main.tscn b/networking/webrtc_minimal/main.tscn new file mode 100644 index 00000000..12e8c344 --- /dev/null +++ b/networking/webrtc_minimal/main.tscn @@ -0,0 +1,35 @@ +[gd_scene load_steps=4 format=2] + +[ext_resource path="res://minimal.tscn" type="PackedScene" id=1] +[ext_resource path="res://main.gd" type="Script" id=2] + +[sub_resource type="GDScript" id=1] +script/source = "extends LinkButton + +func _on_LinkButton_pressed(): + OS.shell_open(\"https://github.com/godotengine/webrtc-native/releases\") +" + +[node name="main" type="Node"] +script = ExtResource( 2 ) + +[node name="minimal" parent="." instance=ExtResource( 1 )] + +[node name="CenterContainer" type="CenterContainer" parent="."] +anchor_right = 1.0 +anchor_bottom = 1.0 +__meta__ = { +"_edit_use_anchors_": true +} + +[node name="LinkButton" type="LinkButton" parent="CenterContainer"] +margin_left = 239.0 +margin_top = 293.0 +margin_right = 785.0 +margin_bottom = 307.0 +text = "Make sure to download the GDNative WebRTC Plugin and place it in the project folder" +script = SubResource( 1 ) +__meta__ = { +"_edit_use_anchors_": false +} +[connection signal="pressed" from="CenterContainer/LinkButton" to="CenterContainer/LinkButton" method="_on_LinkButton_pressed"] diff --git a/networking/webrtc_minimal/minimal.gd b/networking/webrtc_minimal/minimal.gd new file mode 100644 index 00000000..944c375d --- /dev/null +++ b/networking/webrtc_minimal/minimal.gd @@ -0,0 +1,39 @@ +# Main scene +extends Node + +# Create the two peers +var p1 = WebRTCPeerConnection.new() +var p2 = WebRTCPeerConnection.new() +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.connect("session_description_created", p1, "set_local_description") + # Connect P1 session and ICE created to p2 set remote description and candidates + p1.connect("session_description_created", p2, "set_remote_description") + p1.connect("ice_candidate_created", p2, "add_ice_candidate") + + # Same for P2 + p2.connect("session_description_created", p2, "set_local_description") + p2.connect("session_description_created", p1, "set_remote_description") + p2.connect("ice_candidate_created", p1, "add_ice_candidate") + + # Let P1 create the offer + p1.create_offer() + + # Wait a second and send message from P1 + yield(get_tree().create_timer(1), "timeout") + ch1.put_packet("Hi from P1".to_utf8()) + + # Wait a second and send message from P2 + yield(get_tree().create_timer(1), "timeout") + ch2.put_packet("Hi from P2".to_utf8()) + +func _process(delta): + p1.poll() + p2.poll() + 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()) \ No newline at end of file diff --git a/networking/webrtc_minimal/minimal.tscn b/networking/webrtc_minimal/minimal.tscn new file mode 100644 index 00000000..9e512bc6 --- /dev/null +++ b/networking/webrtc_minimal/minimal.tscn @@ -0,0 +1,6 @@ +[gd_scene load_steps=2 format=2] + +[ext_resource path="res://minimal.gd" type="Script" id=1] + +[node name="minimal" type="Node"] +script = ExtResource( 1 ) diff --git a/networking/webrtc_minimal/project.godot b/networking/webrtc_minimal/project.godot new file mode 100644 index 00000000..46973240 --- /dev/null +++ b/networking/webrtc_minimal/project.godot @@ -0,0 +1,27 @@ +; Engine configuration file. +; It's best edited using the editor UI and not directly, +; since the parameters that go here are not all obvious. +; +; Format: +; [section] ; section goes between [] +; param=value ; assign values to parameters + +config_version=4 + +_global_script_classes=[ ] +_global_script_class_icons={ + +} + +[application] + +config/name="WebRTC Minimal Connection" +run/main_scene="res://main.tscn" + +[autoload] + +Signaling="*res://Signaling.gd" + +[gdnative] + +singletons=[ "res://webrtc/webrtc.tres" ]