Skip to content

Commit 99f6e67

Browse files
authored
Merge pull request #335 from Faless/webrtc/initial
Add two WebRTC demo
2 parents 0db440b + 2a57c64 commit 99f6e67

File tree

22 files changed

+1513
-0
lines changed

22 files changed

+1513
-0
lines changed
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
webrtc
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
# A local signaling server. Add this to autoloads with name "Signaling" (/root/Signaling)
2+
extends Node
3+
4+
# We will store the two peers here
5+
var peers = []
6+
7+
func register(path):
8+
assert(peers.size() < 2)
9+
peers.append(path)
10+
if peers.size() == 2:
11+
get_node(peers[0]).peer.create_offer()
12+
13+
func _find_other(path):
14+
# Find the other registered peer.
15+
for p in peers:
16+
if p != path:
17+
return p
18+
return ""
19+
20+
func send_session(path, type, sdp):
21+
var other = _find_other(path)
22+
assert(other != "")
23+
get_node(other).peer.set_remote_description(type, sdp)
24+
25+
func send_candidate(path, mid, index, sdp):
26+
var other = _find_other(path)
27+
assert(other != "")
28+
get_node(other).peer.add_ice_candidate(mid, index, sdp)

networking/webrtc_minimal/chat.gd

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
# An example p2p chat client
2+
extends Node
3+
4+
var peer = WebRTCPeerConnection.new()
5+
6+
# Create negotiated data channel
7+
var channel = peer.create_data_channel("chat", {"negotiated": true, "id": 1})
8+
9+
func _ready():
10+
# Connect all functions
11+
peer.connect("ice_candidate_created", self, "_on_ice_candidate")
12+
peer.connect("session_description_created", self, "_on_session")
13+
14+
# Register to the local signaling server (see below for the implementation)
15+
Signaling.register(get_path())
16+
17+
func _on_ice_candidate(mid, index, sdp):
18+
# Send the ICE candidate to the other peer via signaling server
19+
Signaling.send_candidate(get_path(), mid, index, sdp)
20+
21+
func _on_session(type, sdp):
22+
# Send the session to other peer via signaling server
23+
Signaling.send_session(get_path(), type, sdp)
24+
# Set generated description as local
25+
peer.set_local_description(type, sdp)
26+
27+
func _process(delta):
28+
# Always poll the connection frequently
29+
peer.poll()
30+
if channel.get_ready_state() == WebRTCDataChannel.STATE_OPEN:
31+
while channel.get_available_packet_count() > 0:
32+
print(get_path(), " received: ", channel.get_packet().get_string_from_utf8())
33+
34+
func send_message(message):
35+
channel.put_packet(message.to_utf8())

networking/webrtc_minimal/main.gd

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
extends Node
2+
3+
const Chat = preload("res://chat.gd")
4+
5+
func _ready():
6+
var p1 = Chat.new()
7+
var p2 = Chat.new()
8+
add_child(p1)
9+
add_child(p2)
10+
11+
# Wait a second and send message from P1
12+
yield(get_tree().create_timer(1), "timeout")
13+
p1.send_message("Hi from %s" % p1.get_path())
14+
15+
# Wait a second and send message from P2
16+
yield(get_tree().create_timer(1), "timeout")
17+
p2.send_message("Hi from %s" % p2.get_path())
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
[gd_scene load_steps=4 format=2]
2+
3+
[ext_resource path="res://minimal.tscn" type="PackedScene" id=1]
4+
[ext_resource path="res://main.gd" type="Script" id=2]
5+
6+
[sub_resource type="GDScript" id=1]
7+
script/source = "extends LinkButton
8+
9+
func _on_LinkButton_pressed():
10+
OS.shell_open(\"https://github.com/godotengine/webrtc-native/releases\")
11+
"
12+
13+
[node name="main" type="Node"]
14+
script = ExtResource( 2 )
15+
16+
[node name="minimal" parent="." instance=ExtResource( 1 )]
17+
18+
[node name="CenterContainer" type="CenterContainer" parent="."]
19+
anchor_right = 1.0
20+
anchor_bottom = 1.0
21+
__meta__ = {
22+
"_edit_use_anchors_": true
23+
}
24+
25+
[node name="LinkButton" type="LinkButton" parent="CenterContainer"]
26+
margin_left = 239.0
27+
margin_top = 293.0
28+
margin_right = 785.0
29+
margin_bottom = 307.0
30+
text = "Make sure to download the GDNative WebRTC Plugin and place it in the project folder"
31+
script = SubResource( 1 )
32+
__meta__ = {
33+
"_edit_use_anchors_": false
34+
}
35+
[connection signal="pressed" from="CenterContainer/LinkButton" to="CenterContainer/LinkButton" method="_on_LinkButton_pressed"]
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
# Main scene
2+
extends Node
3+
4+
# Create the two peers
5+
var p1 = WebRTCPeerConnection.new()
6+
var p2 = WebRTCPeerConnection.new()
7+
var ch1 = p1.create_data_channel("chat", {"id": 1, "negotiated": true})
8+
var ch2 = p2.create_data_channel("chat", {"id": 1, "negotiated": true})
9+
10+
func _ready():
11+
# Connect P1 session created to itself to set local description
12+
p1.connect("session_description_created", p1, "set_local_description")
13+
# Connect P1 session and ICE created to p2 set remote description and candidates
14+
p1.connect("session_description_created", p2, "set_remote_description")
15+
p1.connect("ice_candidate_created", p2, "add_ice_candidate")
16+
17+
# Same for P2
18+
p2.connect("session_description_created", p2, "set_local_description")
19+
p2.connect("session_description_created", p1, "set_remote_description")
20+
p2.connect("ice_candidate_created", p1, "add_ice_candidate")
21+
22+
# Let P1 create the offer
23+
p1.create_offer()
24+
25+
# Wait a second and send message from P1
26+
yield(get_tree().create_timer(1), "timeout")
27+
ch1.put_packet("Hi from P1".to_utf8())
28+
29+
# Wait a second and send message from P2
30+
yield(get_tree().create_timer(1), "timeout")
31+
ch2.put_packet("Hi from P2".to_utf8())
32+
33+
func _process(delta):
34+
p1.poll()
35+
p2.poll()
36+
if ch1.get_ready_state() == ch1.STATE_OPEN and ch1.get_available_packet_count() > 0:
37+
print("P1 received: ", ch1.get_packet().get_string_from_utf8())
38+
if ch2.get_ready_state() == ch2.STATE_OPEN and ch2.get_available_packet_count() > 0:
39+
print("P2 received: ", ch2.get_packet().get_string_from_utf8())
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
[gd_scene load_steps=2 format=2]
2+
3+
[ext_resource path="res://minimal.gd" type="Script" id=1]
4+
5+
[node name="minimal" type="Node"]
6+
script = ExtResource( 1 )
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
; Engine configuration file.
2+
; It's best edited using the editor UI and not directly,
3+
; since the parameters that go here are not all obvious.
4+
;
5+
; Format:
6+
; [section] ; section goes between []
7+
; param=value ; assign values to parameters
8+
9+
config_version=4
10+
11+
_global_script_classes=[ ]
12+
_global_script_class_icons={
13+
14+
}
15+
16+
[application]
17+
18+
config/name="WebRTC Minimal Connection"
19+
run/main_scene="res://main.tscn"
20+
21+
[autoload]
22+
23+
Signaling="*res://Signaling.gd"
24+
25+
[gdnative]
26+
27+
singletons=[ "res://webrtc/webrtc.tres" ]
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
webrtc
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
# A WebSocket signaling server/client for WebRTC.
2+
3+
This demo is devided in 4 parts:
4+
5+
- The `server` folder contains the signaling server implementation written in GDScript (so it can be run by a game server running Godot)
6+
- The `server_node` folder contains the signaling server implementation written in Node.js (if you don't plan to run a game server but only match-making).
7+
- The `client` part contains the client implementation in GDScript.
8+
- Itself divided into raw protocol and `WebRTCMultiplayer` handling.
9+
- The `demo` contains a small app that uses it.
10+
11+
**NOTE**: You must extract the [latest version](https://github.com/godotengine/webrtc-native/releases) of the WebRTC GDNative plugin in the project folder to run from desktop.
12+
13+
## Protocol
14+
15+
The protocol is text based, and composed by a command and possibly multiple payload arguments, each separated by a new line.
16+
17+
Messages without payload must still end with a newline and are the following:
18+
- `J: ` (or `J: <ROOM>`), must be sent by client immediately after connection to get a lobby assigned or join a known one.
19+
This messages is also sent by server back to the client to notify assigned lobby, or simply a successful join.
20+
- `I: <ID>`, sent by server to identify the client when it joins a room.
21+
- `N: <ID>`, sent by server to notify new peers in the same lobby.
22+
- `D: <ID>`, sent by server to notify when a peer in the same lobby disconnects.
23+
- `S: `, sent by client to seal the lobby (only the client that created it is allowed to seal a lobby).
24+
25+
When a lobby is sealed, no new client will be able to join, and the lobby will be destroyed (and clients disconnected) after 10 seconds.
26+
27+
Messages with payload (used to transfer WebRTC parameters) are:
28+
- `O: <ID>`, used to send an offer.
29+
- `A: <ID>`, used to send an answer.
30+
- `C: <ID>`, used to send a candidate.
31+
32+
When sending the parameter, a client will set `<ID>` as the destination peer, the server will replace it with the id of the sending peer, and rely it to the proper destination.

0 commit comments

Comments
 (0)