Initial commit
This commit is contained in:
parent
308f09916b
commit
b915552a64
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -4,3 +4,4 @@ export_presets.cfg
|
||||||
*.translation
|
*.translation
|
||||||
.mono
|
.mono
|
||||||
data_*/
|
data_*/
|
||||||
|
/webrtc/
|
||||||
|
|
64
client_ui.gd
Normal file
64
client_ui.gd
Normal file
|
@ -0,0 +1,64 @@
|
||||||
|
extends Control
|
||||||
|
|
||||||
|
onready var client = $Client
|
||||||
|
|
||||||
|
func _ready():
|
||||||
|
client.connect("lobby_joined", self, "_lobby_joined")
|
||||||
|
client.connect("lobby_sealed", self, "_lobby_sealed")
|
||||||
|
client.connect("connected", self, "_connected")
|
||||||
|
client.connect("disconnected", self, "_disconnected")
|
||||||
|
client.rtc_mp.connect("peer_connected", self, "_mp_peer_connected")
|
||||||
|
client.rtc_mp.connect("peer_disconnected", self, "_mp_peer_disconnected")
|
||||||
|
client.rtc_mp.connect("server_disconnected", self, "_mp_server_disconnect")
|
||||||
|
client.rtc_mp.connect("connection_succeeded", self, "_mp_connected")
|
||||||
|
|
||||||
|
func _process(delta):
|
||||||
|
client.rtc_mp.poll()
|
||||||
|
while client.rtc_mp.get_available_packet_count() > 0:
|
||||||
|
_log(client.rtc_mp.get_packet().get_string_from_utf8())
|
||||||
|
|
||||||
|
func _connected(id):
|
||||||
|
_log("Signaling server connected with ID: %d" % id)
|
||||||
|
|
||||||
|
func _disconnected():
|
||||||
|
_log("Signaling server disconnected: %d - %s" % [client.code, client.reason])
|
||||||
|
|
||||||
|
func _lobby_joined(lobby):
|
||||||
|
_log("Joined lobby %s" % lobby)
|
||||||
|
|
||||||
|
func _lobby_sealed():
|
||||||
|
_log("Lobby has been sealed")
|
||||||
|
|
||||||
|
func _mp_connected():
|
||||||
|
_log("Multiplayer is connected (I am %d)" % client.rtc_mp.get_unique_id())
|
||||||
|
|
||||||
|
func _mp_server_disconnect():
|
||||||
|
_log("Multiplayer is disconnected (I am %d)" % client.rtc_mp.get_unique_id())
|
||||||
|
|
||||||
|
func _mp_peer_connected(id: int):
|
||||||
|
_log("Multiplayer peer %d connected" % id)
|
||||||
|
|
||||||
|
func _mp_peer_disconnected(id: int):
|
||||||
|
_log("Multiplayer peer %d disconnected" % id)
|
||||||
|
|
||||||
|
func _log(msg):
|
||||||
|
print(msg)
|
||||||
|
$VBoxContainer/TextEdit.text += str(msg) + "\n"
|
||||||
|
|
||||||
|
func ping():
|
||||||
|
_log(client.rtc_mp.put_packet("ping".to_utf8()))
|
||||||
|
|
||||||
|
func _on_Peers_pressed():
|
||||||
|
var d = client.rtc_mp.get_peers()
|
||||||
|
_log(d)
|
||||||
|
for k in d:
|
||||||
|
_log(client.rtc_mp.get_peer(k))
|
||||||
|
|
||||||
|
func start():
|
||||||
|
client.start($VBoxContainer/Connect/Host.text, $VBoxContainer/Connect/RoomSecret.text)
|
||||||
|
|
||||||
|
func _on_Seal_pressed():
|
||||||
|
client.seal_lobby()
|
||||||
|
|
||||||
|
func stop():
|
||||||
|
client.stop()
|
107
client_ui.tscn
Normal file
107
client_ui.tscn
Normal file
|
@ -0,0 +1,107 @@
|
||||||
|
[gd_scene load_steps=3 format=2]
|
||||||
|
|
||||||
|
[ext_resource path="res://client_ui.gd" type="Script" id=1]
|
||||||
|
[ext_resource path="res://multiplayer_client.gd" type="Script" id=2]
|
||||||
|
|
||||||
|
[node name="ClientUI" type="Control"]
|
||||||
|
margin_right = 1024.0
|
||||||
|
margin_bottom = 600.0
|
||||||
|
size_flags_horizontal = 3
|
||||||
|
size_flags_vertical = 3
|
||||||
|
script = ExtResource( 1 )
|
||||||
|
__meta__ = {
|
||||||
|
"_edit_use_anchors_": true
|
||||||
|
}
|
||||||
|
|
||||||
|
[node name="Client" type="Node" parent="."]
|
||||||
|
script = ExtResource( 2 )
|
||||||
|
|
||||||
|
[node name="VBoxContainer" type="VBoxContainer" parent="."]
|
||||||
|
anchor_right = 1.0
|
||||||
|
anchor_bottom = 1.0
|
||||||
|
custom_constants/separation = 8
|
||||||
|
__meta__ = {
|
||||||
|
"_edit_use_anchors_": false
|
||||||
|
}
|
||||||
|
|
||||||
|
[node name="Connect" type="HBoxContainer" parent="VBoxContainer"]
|
||||||
|
margin_right = 1024.0
|
||||||
|
margin_bottom = 24.0
|
||||||
|
|
||||||
|
[node name="Label" type="Label" parent="VBoxContainer/Connect"]
|
||||||
|
margin_top = 5.0
|
||||||
|
margin_right = 73.0
|
||||||
|
margin_bottom = 19.0
|
||||||
|
text = "Connect to:"
|
||||||
|
|
||||||
|
[node name="Host" type="LineEdit" parent="VBoxContainer/Connect"]
|
||||||
|
margin_left = 77.0
|
||||||
|
margin_right = 921.0
|
||||||
|
margin_bottom = 24.0
|
||||||
|
size_flags_horizontal = 3
|
||||||
|
text = "ws://localhost:9080"
|
||||||
|
|
||||||
|
[node name="Room" type="Label" parent="VBoxContainer/Connect"]
|
||||||
|
margin_left = 925.0
|
||||||
|
margin_right = 962.0
|
||||||
|
margin_bottom = 24.0
|
||||||
|
size_flags_vertical = 5
|
||||||
|
text = "Room"
|
||||||
|
valign = 1
|
||||||
|
|
||||||
|
[node name="RoomSecret" type="LineEdit" parent="VBoxContainer/Connect"]
|
||||||
|
margin_left = 966.0
|
||||||
|
margin_right = 1024.0
|
||||||
|
margin_bottom = 24.0
|
||||||
|
placeholder_text = "secret"
|
||||||
|
|
||||||
|
[node name="HBoxContainer" type="HBoxContainer" parent="VBoxContainer"]
|
||||||
|
margin_top = 32.0
|
||||||
|
margin_right = 1024.0
|
||||||
|
margin_bottom = 52.0
|
||||||
|
custom_constants/separation = 10
|
||||||
|
__meta__ = {
|
||||||
|
"_edit_use_anchors_": false
|
||||||
|
}
|
||||||
|
|
||||||
|
[node name="Start" type="Button" parent="VBoxContainer/HBoxContainer"]
|
||||||
|
margin_right = 41.0
|
||||||
|
margin_bottom = 20.0
|
||||||
|
text = "Start"
|
||||||
|
|
||||||
|
[node name="Stop" type="Button" parent="VBoxContainer/HBoxContainer"]
|
||||||
|
margin_left = 51.0
|
||||||
|
margin_right = 91.0
|
||||||
|
margin_bottom = 20.0
|
||||||
|
text = "Stop"
|
||||||
|
|
||||||
|
[node name="Seal" type="Button" parent="VBoxContainer/HBoxContainer"]
|
||||||
|
margin_left = 101.0
|
||||||
|
margin_right = 139.0
|
||||||
|
margin_bottom = 20.0
|
||||||
|
text = "Seal"
|
||||||
|
|
||||||
|
[node name="Ping" type="Button" parent="VBoxContainer/HBoxContainer"]
|
||||||
|
margin_left = 149.0
|
||||||
|
margin_right = 188.0
|
||||||
|
margin_bottom = 20.0
|
||||||
|
text = "Ping"
|
||||||
|
|
||||||
|
[node name="Peers" type="Button" parent="VBoxContainer/HBoxContainer"]
|
||||||
|
margin_left = 198.0
|
||||||
|
margin_right = 280.0
|
||||||
|
margin_bottom = 20.0
|
||||||
|
text = "Print peers"
|
||||||
|
|
||||||
|
[node name="TextEdit" type="TextEdit" parent="VBoxContainer"]
|
||||||
|
margin_top = 60.0
|
||||||
|
margin_right = 1024.0
|
||||||
|
margin_bottom = 600.0
|
||||||
|
size_flags_vertical = 3
|
||||||
|
readonly = true
|
||||||
|
|
||||||
|
[connection signal="pressed" from="VBoxContainer/HBoxContainer/Start" to="." method="start"]
|
||||||
|
[connection signal="pressed" from="VBoxContainer/HBoxContainer/Stop" to="." method="stop"]
|
||||||
|
[connection signal="pressed" from="VBoxContainer/HBoxContainer/Seal" to="." method="_on_Seal_pressed"]
|
||||||
|
[connection signal="pressed" from="VBoxContainer/HBoxContainer/Ping" to="." method="ping"]
|
||||||
|
[connection signal="pressed" from="VBoxContainer/HBoxContainer/Peers" to="." method="_on_Peers_pressed"]
|
|
@ -1,5 +1,7 @@
|
||||||
[gd_resource type="Environment" load_steps=2 format=2]
|
[gd_resource type="Environment" load_steps=2 format=2]
|
||||||
|
|
||||||
[sub_resource type="ProceduralSky" id=1]
|
[sub_resource type="ProceduralSky" id=1]
|
||||||
|
|
||||||
[resource]
|
[resource]
|
||||||
background_mode = 2
|
background_mode = 2
|
||||||
background_sky = SubResource( 1 )
|
background_sky = SubResource( 1 )
|
||||||
|
|
14
main.gd
Normal file
14
main.gd
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
extends Control
|
||||||
|
|
||||||
|
func _ready():
|
||||||
|
if OS.get_name() == "HTML5":
|
||||||
|
$VBoxContainer/Signaling.hide()
|
||||||
|
|
||||||
|
func _on_listen_toggled(button_pressed):
|
||||||
|
if button_pressed:
|
||||||
|
$Server.listen(int($VBoxContainer/Signaling/Port.value))
|
||||||
|
else:
|
||||||
|
$Server.stop()
|
||||||
|
|
||||||
|
func _on_LinkButton_pressed():
|
||||||
|
OS.shell_open("https://github.com/godotengine/webrtc-native/releases")
|
98
main.tscn
Normal file
98
main.tscn
Normal file
|
@ -0,0 +1,98 @@
|
||||||
|
[gd_scene load_steps=3 format=2]
|
||||||
|
|
||||||
|
[ext_resource path="res://main.gd" type="Script" id=1]
|
||||||
|
[ext_resource path="res://client_ui.tscn" type="PackedScene" id=2]
|
||||||
|
|
||||||
|
[node name="Control" type="Control"]
|
||||||
|
anchor_left = 0.0136719
|
||||||
|
anchor_top = 0.0166667
|
||||||
|
anchor_right = 0.986328
|
||||||
|
anchor_bottom = 0.983333
|
||||||
|
margin_top = 4.32134e-07
|
||||||
|
margin_bottom = -9.53674e-06
|
||||||
|
script = ExtResource( 1 )
|
||||||
|
__meta__ = {
|
||||||
|
"_edit_use_anchors_": true
|
||||||
|
}
|
||||||
|
|
||||||
|
[node name="VBoxContainer" type="VBoxContainer" parent="."]
|
||||||
|
anchor_right = 1.0
|
||||||
|
anchor_bottom = 1.0
|
||||||
|
custom_constants/separation = 50
|
||||||
|
__meta__ = {
|
||||||
|
"_edit_use_anchors_": true
|
||||||
|
}
|
||||||
|
|
||||||
|
[node name="Signaling" type="HBoxContainer" parent="VBoxContainer"]
|
||||||
|
margin_right = 995.0
|
||||||
|
margin_bottom = 24.0
|
||||||
|
|
||||||
|
[node name="Label" type="Label" parent="VBoxContainer/Signaling"]
|
||||||
|
margin_top = 5.0
|
||||||
|
margin_right = 104.0
|
||||||
|
margin_bottom = 19.0
|
||||||
|
text = "Signaling server:"
|
||||||
|
|
||||||
|
[node name="Port" type="SpinBox" parent="VBoxContainer/Signaling"]
|
||||||
|
margin_left = 108.0
|
||||||
|
margin_right = 182.0
|
||||||
|
margin_bottom = 24.0
|
||||||
|
min_value = 1025.0
|
||||||
|
max_value = 65535.0
|
||||||
|
value = 9080.0
|
||||||
|
|
||||||
|
[node name="ListenButton" type="Button" parent="VBoxContainer/Signaling"]
|
||||||
|
margin_left = 186.0
|
||||||
|
margin_right = 237.0
|
||||||
|
margin_bottom = 24.0
|
||||||
|
toggle_mode = true
|
||||||
|
text = "Listen"
|
||||||
|
|
||||||
|
[node name="CenterContainer" type="CenterContainer" parent="VBoxContainer/Signaling"]
|
||||||
|
margin_left = 241.0
|
||||||
|
margin_right = 995.0
|
||||||
|
margin_bottom = 24.0
|
||||||
|
size_flags_horizontal = 3
|
||||||
|
size_flags_vertical = 3
|
||||||
|
|
||||||
|
[node name="LinkButton" type="LinkButton" parent="VBoxContainer/Signaling/CenterContainer"]
|
||||||
|
margin_left = 104.0
|
||||||
|
margin_top = 5.0
|
||||||
|
margin_right = 650.0
|
||||||
|
margin_bottom = 19.0
|
||||||
|
text = "Make sure to download the GDNative WebRTC Plugin and place it in the project folder"
|
||||||
|
|
||||||
|
[node name="Clients" type="GridContainer" parent="VBoxContainer"]
|
||||||
|
margin_top = 74.0
|
||||||
|
margin_right = 995.0
|
||||||
|
margin_bottom = 579.0
|
||||||
|
size_flags_horizontal = 3
|
||||||
|
size_flags_vertical = 3
|
||||||
|
custom_constants/vseparation = 15
|
||||||
|
custom_constants/hseparation = 15
|
||||||
|
columns = 2
|
||||||
|
|
||||||
|
[node name="ClientUI" parent="VBoxContainer/Clients" instance=ExtResource( 2 )]
|
||||||
|
margin_right = 490.0
|
||||||
|
margin_bottom = 245.0
|
||||||
|
|
||||||
|
[node name="ClientUI2" parent="VBoxContainer/Clients" instance=ExtResource( 2 )]
|
||||||
|
margin_left = 505.0
|
||||||
|
margin_right = 995.0
|
||||||
|
margin_bottom = 245.0
|
||||||
|
|
||||||
|
[node name="ClientUI3" parent="VBoxContainer/Clients" instance=ExtResource( 2 )]
|
||||||
|
margin_top = 260.0
|
||||||
|
margin_right = 490.0
|
||||||
|
margin_bottom = 505.0
|
||||||
|
|
||||||
|
[node name="ClientUI4" parent="VBoxContainer/Clients" instance=ExtResource( 2 )]
|
||||||
|
margin_left = 505.0
|
||||||
|
margin_top = 260.0
|
||||||
|
margin_right = 995.0
|
||||||
|
margin_bottom = 505.0
|
||||||
|
|
||||||
|
[node name="Server" type="Node" parent="."]
|
||||||
|
|
||||||
|
[connection signal="toggled" from="VBoxContainer/Signaling/ListenButton" to="." method="_on_listen_toggled"]
|
||||||
|
[connection signal="pressed" from="VBoxContainer/Signaling/CenterContainer/LinkButton" to="." method="_on_LinkButton_pressed"]
|
86
multiplayer_client.gd
Normal file
86
multiplayer_client.gd
Normal file
|
@ -0,0 +1,86 @@
|
||||||
|
extends "ws_webrtc_client.gd"
|
||||||
|
|
||||||
|
var rtc_mp: WebRTCMultiplayer = WebRTCMultiplayer.new()
|
||||||
|
var sealed = false
|
||||||
|
|
||||||
|
func _init():
|
||||||
|
connect("connected", self, "connected")
|
||||||
|
connect("disconnected", self, "disconnected")
|
||||||
|
|
||||||
|
connect("offer_received", self, "offer_received")
|
||||||
|
connect("answer_received", self, "answer_received")
|
||||||
|
connect("candidate_received", self, "candidate_received")
|
||||||
|
|
||||||
|
connect("lobby_joined", self, "lobby_joined")
|
||||||
|
connect("lobby_sealed", self, "lobby_sealed")
|
||||||
|
connect("peer_connected", self, "peer_connected")
|
||||||
|
connect("peer_disconnected", self, "peer_disconnected")
|
||||||
|
|
||||||
|
func start(url, lobby = ""):
|
||||||
|
stop()
|
||||||
|
sealed = false
|
||||||
|
self.lobby = lobby
|
||||||
|
connect_to_url(url)
|
||||||
|
|
||||||
|
func stop():
|
||||||
|
rtc_mp.close()
|
||||||
|
close()
|
||||||
|
|
||||||
|
func _create_peer(id):
|
||||||
|
var peer: WebRTCPeerConnection = WebRTCPeerConnection.new()
|
||||||
|
peer.initialize({
|
||||||
|
"iceServers": [ { "urls": ["stun:stun.l.google.com:19302"] } ]
|
||||||
|
})
|
||||||
|
peer.connect("session_description_created", self, "_offer_created", [id])
|
||||||
|
peer.connect("ice_candidate_created", self, "_new_ice_candidate", [id])
|
||||||
|
rtc_mp.add_peer(peer, id)
|
||||||
|
if id > rtc_mp.get_unique_id():
|
||||||
|
peer.create_offer()
|
||||||
|
return peer
|
||||||
|
|
||||||
|
func _new_ice_candidate(mid_name, index_name, sdp_name, id):
|
||||||
|
send_candidate(id, mid_name, index_name, sdp_name)
|
||||||
|
|
||||||
|
func _offer_created(type, data, id):
|
||||||
|
if not rtc_mp.has_peer(id):
|
||||||
|
return
|
||||||
|
print("created", type)
|
||||||
|
rtc_mp.get_peer(id).connection.set_local_description(type, data)
|
||||||
|
if type == "offer": send_offer(id, data)
|
||||||
|
else: send_answer(id, data)
|
||||||
|
|
||||||
|
func connected(id):
|
||||||
|
print("Connected %d" % id)
|
||||||
|
rtc_mp.initialize(id, true)
|
||||||
|
|
||||||
|
func lobby_joined(lobby):
|
||||||
|
self.lobby = lobby
|
||||||
|
|
||||||
|
func lobby_sealed():
|
||||||
|
sealed = true
|
||||||
|
|
||||||
|
func disconnected():
|
||||||
|
print("Disconnected: %d: %s" % [code, reason])
|
||||||
|
if not sealed:
|
||||||
|
stop() # Unexpected disconnect
|
||||||
|
|
||||||
|
func peer_connected(id):
|
||||||
|
print("Peer connected %d" % id)
|
||||||
|
_create_peer(id)
|
||||||
|
|
||||||
|
func peer_disconnected(id):
|
||||||
|
if rtc_mp.has_peer(id): rtc_mp.remove_peer(id)
|
||||||
|
|
||||||
|
func offer_received(id, offer):
|
||||||
|
print("Got offer: %d" % id)
|
||||||
|
if rtc_mp.has_peer(id):
|
||||||
|
rtc_mp.get_peer(id).connection.set_remote_description("offer", offer)
|
||||||
|
|
||||||
|
func answer_received(id, answer):
|
||||||
|
print("Got answer: %d" % id)
|
||||||
|
if rtc_mp.has_peer(id):
|
||||||
|
rtc_mp.get_peer(id).connection.set_remote_description("answer", answer)
|
||||||
|
|
||||||
|
func candidate_received(id, mid, index, sdp):
|
||||||
|
if rtc_mp.has_peer(id):
|
||||||
|
rtc_mp.get_peer(id).connection.add_ice_candidate(mid, index, sdp)
|
|
@ -11,8 +11,13 @@ config_version=4
|
||||||
[application]
|
[application]
|
||||||
|
|
||||||
config/name="kdt"
|
config/name="kdt"
|
||||||
|
run/main_scene="res://main.tscn"
|
||||||
config/icon="res://icon.png"
|
config/icon="res://icon.png"
|
||||||
|
|
||||||
|
[gdnative]
|
||||||
|
|
||||||
|
singletons=[ "res://webrtc/webrtc.tres" ]
|
||||||
|
|
||||||
[physics]
|
[physics]
|
||||||
|
|
||||||
common/enable_pause_aware_picking=true
|
common/enable_pause_aware_picking=true
|
||||||
|
|
|
@ -1,3 +1,7 @@
|
||||||
|
# Setup
|
||||||
|
|
||||||
|
- Download the latest WebRTC GDNative plugin from https://github.com/godotengine/webrtc-native/releases and extract it in the project root.
|
||||||
|
|
||||||
# Signalling Server
|
# Signalling Server
|
||||||
|
|
||||||
deno run -A server.ts
|
deno run -A server.ts
|
||||||
|
|
109
server.ts
109
server.ts
|
@ -101,10 +101,14 @@ class Lobby {
|
||||||
const assigned = this.getPeerId(peer);
|
const assigned = this.getPeerId(peer);
|
||||||
const close = assigned === 1;
|
const close = assigned === 1;
|
||||||
this.peers.forEach((p) => {
|
this.peers.forEach((p) => {
|
||||||
// Room host disconnected, must close.
|
try {
|
||||||
if (close) p.ws.close(4000, STR_HOST_DISCONNECTED);
|
// Room host disconnected, must close.
|
||||||
// Notify peer disconnect.
|
if (close) p.ws.close(4000, STR_HOST_DISCONNECTED);
|
||||||
else p.ws.send(`D: ${assigned}\n`);
|
// Notify peer disconnect.
|
||||||
|
else p.ws.send(`D: ${assigned}\n`);
|
||||||
|
} catch (e) {
|
||||||
|
console.error(`Error when leaving: ${e}`);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
this.peers.splice(idx, 1);
|
this.peers.splice(idx, 1);
|
||||||
if (close && this.closeTimer >= 0) {
|
if (close && this.closeTimer >= 0) {
|
||||||
|
@ -217,50 +221,57 @@ function parseMsg(peer: Peer, msg: string) {
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log(`Server running on port ${PORT}`);
|
console.log(`Server running on port ${PORT}`);
|
||||||
const conn = Deno.listen({ port: PORT });
|
const server = Deno.listen({ port: PORT });
|
||||||
const httpConn = Deno.serveHttp(await conn.accept());
|
for await (const conn of server) {
|
||||||
const e = await httpConn.nextRequest();
|
(async () => {
|
||||||
if (e) {
|
const httpConn = Deno.serveHttp(conn);
|
||||||
const { socket, response } = Deno.upgradeWebSocket(e.request);
|
for await (const requestEvent of httpConn) {
|
||||||
const id = randomId();
|
if (requestEvent) {
|
||||||
const peer = new Peer(id, socket);
|
const { socket, response } = Deno.upgradeWebSocket(
|
||||||
socket.onopen = (_ev) => {
|
requestEvent.request,
|
||||||
if (peersCount >= MAX_PEERS) {
|
);
|
||||||
socket.close(4000, STR_TOO_MANY_PEERS);
|
const id = randomId();
|
||||||
return;
|
const peer = new Peer(id, socket);
|
||||||
|
socket.onopen = (_ev) => {
|
||||||
|
if (peersCount >= MAX_PEERS) {
|
||||||
|
socket.close(4000, STR_TOO_MANY_PEERS);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
peersCount++;
|
||||||
|
};
|
||||||
|
socket.onmessage = (ev) => {
|
||||||
|
try {
|
||||||
|
parseMsg(peer, ev.data);
|
||||||
|
} catch (e) {
|
||||||
|
const code = e.code || 4000;
|
||||||
|
console.log(`Error parsing message from ${id}:\n` + ev.data);
|
||||||
|
socket.close(code, e.message);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
socket.onclose = (ev) => {
|
||||||
|
peersCount--;
|
||||||
|
console.log(
|
||||||
|
`Connection with peer ${peer.id} closed ` +
|
||||||
|
`with reason: ${ev.reason}`,
|
||||||
|
);
|
||||||
|
if (
|
||||||
|
peer.lobby &&
|
||||||
|
lobbies.has(peer.lobby) &&
|
||||||
|
lobbies.get(peer.lobby).leave(peer)
|
||||||
|
) {
|
||||||
|
lobbies.delete(peer.lobby);
|
||||||
|
console.log(`Deleted lobby ${peer.lobby}`);
|
||||||
|
console.log(`Open lobbies: ${lobbies.size}`);
|
||||||
|
peer.lobby = "";
|
||||||
|
}
|
||||||
|
if (peer.timeout >= 0) {
|
||||||
|
clearTimeout(peer.timeout);
|
||||||
|
peer.timeout = -1;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
socket.onerror = (e) => console.error("WebSocket error:", e);
|
||||||
|
requestEvent.respondWith(response);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
peersCount++;
|
})();
|
||||||
};
|
|
||||||
socket.onmessage = (ev) => {
|
|
||||||
try {
|
|
||||||
parseMsg(peer, ev.data);
|
|
||||||
} catch (e) {
|
|
||||||
const code = e.code || 4000;
|
|
||||||
console.log(`Error parsing message from ${id}:\n` + ev.data);
|
|
||||||
socket.close(code, e.message);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
socket.onclose = (ev) => {
|
|
||||||
peersCount--;
|
|
||||||
console.log(
|
|
||||||
`Connection with peer ${peer.id} closed ` +
|
|
||||||
`with reason: ${ev.reason}`,
|
|
||||||
);
|
|
||||||
if (
|
|
||||||
peer.lobby &&
|
|
||||||
lobbies.has(peer.lobby) &&
|
|
||||||
lobbies.get(peer.lobby).leave(peer)
|
|
||||||
) {
|
|
||||||
lobbies.delete(peer.lobby);
|
|
||||||
console.log(`Deleted lobby ${peer.lobby}`);
|
|
||||||
console.log(`Open lobbies: ${lobbies.size}`);
|
|
||||||
peer.lobby = "";
|
|
||||||
}
|
|
||||||
if (peer.timeout >= 0) {
|
|
||||||
clearTimeout(peer.timeout);
|
|
||||||
peer.timeout = -1;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
socket.onerror = (e) => console.error("WebSocket error:", e);
|
|
||||||
e.respondWith(response);
|
|
||||||
}
|
}
|
||||||
|
|
116
ws_webrtc_client.gd
Normal file
116
ws_webrtc_client.gd
Normal file
|
@ -0,0 +1,116 @@
|
||||||
|
extends Node
|
||||||
|
|
||||||
|
export var autojoin = true
|
||||||
|
export var lobby = "" # Will create a new lobby if empty.
|
||||||
|
|
||||||
|
var client: WebSocketClient = WebSocketClient.new()
|
||||||
|
var code = 1000
|
||||||
|
var reason = "Unknown"
|
||||||
|
|
||||||
|
signal lobby_joined(lobby)
|
||||||
|
signal connected(id)
|
||||||
|
signal disconnected()
|
||||||
|
signal peer_connected(id)
|
||||||
|
signal peer_disconnected(id)
|
||||||
|
signal offer_received(id, offer)
|
||||||
|
signal answer_received(id, answer)
|
||||||
|
signal candidate_received(id, mid, index, sdp)
|
||||||
|
signal lobby_sealed()
|
||||||
|
|
||||||
|
func _init():
|
||||||
|
client.connect("data_received", self, "_parse_msg")
|
||||||
|
client.connect("connection_established", self, "_connected")
|
||||||
|
client.connect("connection_closed", self, "_closed")
|
||||||
|
client.connect("connection_error", self, "_closed")
|
||||||
|
client.connect("server_close_request", self, "_close_request")
|
||||||
|
|
||||||
|
func connect_to_url(url):
|
||||||
|
close()
|
||||||
|
code = 1000
|
||||||
|
reason = "Unknown"
|
||||||
|
client.connect_to_url(url)
|
||||||
|
|
||||||
|
func close():
|
||||||
|
client.disconnect_from_host()
|
||||||
|
|
||||||
|
func _closed(was_clean = false):
|
||||||
|
emit_signal("disconnected")
|
||||||
|
|
||||||
|
func _close_request(code, reason):
|
||||||
|
self.code = code
|
||||||
|
self.reason = reason
|
||||||
|
|
||||||
|
func _connected(protocol = ""):
|
||||||
|
client.get_peer(1).set_write_mode(WebSocketPeer.WRITE_MODE_TEXT)
|
||||||
|
if autojoin:
|
||||||
|
join_lobby(lobby)
|
||||||
|
|
||||||
|
func _parse_msg():
|
||||||
|
var pkt_str: String = client.get_peer(1).get_packet().get_string_from_utf8()
|
||||||
|
|
||||||
|
var req: PoolStringArray = pkt_str.split("\n", true, 1)
|
||||||
|
if req.size() != 2: # Invalid request size
|
||||||
|
return
|
||||||
|
|
||||||
|
var type: String = req[0]
|
||||||
|
if type.length() < 3: # Invalid type size
|
||||||
|
return
|
||||||
|
|
||||||
|
if type.begins_with("J: "):
|
||||||
|
emit_signal("lobby_joined", type.substr(3, type.length() - 3))
|
||||||
|
return
|
||||||
|
elif type.begins_with("S: "):
|
||||||
|
emit_signal("lobby_sealed")
|
||||||
|
return
|
||||||
|
|
||||||
|
var src_str: String = type.substr(3, type.length() - 3)
|
||||||
|
if not src_str.is_valid_integer(): # Source id is not an integer
|
||||||
|
return
|
||||||
|
|
||||||
|
var src_id: int = int(src_str)
|
||||||
|
|
||||||
|
if type.begins_with("I: "):
|
||||||
|
emit_signal("connected", src_id)
|
||||||
|
elif type.begins_with("N: "):
|
||||||
|
# Client connected
|
||||||
|
emit_signal("peer_connected", src_id)
|
||||||
|
elif type.begins_with("D: "):
|
||||||
|
# Client connected
|
||||||
|
emit_signal("peer_disconnected", src_id)
|
||||||
|
elif type.begins_with("O: "):
|
||||||
|
# Offer received
|
||||||
|
emit_signal("offer_received", src_id, req[1])
|
||||||
|
elif type.begins_with("A: "):
|
||||||
|
# Answer received
|
||||||
|
emit_signal("answer_received", src_id, req[1])
|
||||||
|
elif type.begins_with("C: "):
|
||||||
|
# Candidate received
|
||||||
|
var candidate: PoolStringArray = req[1].split("\n", false)
|
||||||
|
if candidate.size() != 3:
|
||||||
|
return
|
||||||
|
if not candidate[1].is_valid_integer():
|
||||||
|
return
|
||||||
|
emit_signal("candidate_received", src_id, candidate[0], int(candidate[1]), candidate[2])
|
||||||
|
|
||||||
|
func join_lobby(lobby):
|
||||||
|
return client.get_peer(1).put_packet(("J: %s\n" % lobby).to_utf8())
|
||||||
|
|
||||||
|
func seal_lobby():
|
||||||
|
return client.get_peer(1).put_packet("S: \n".to_utf8())
|
||||||
|
|
||||||
|
func send_candidate(id, mid, index, sdp) -> int:
|
||||||
|
return _send_msg("C", id, "\n%s\n%d\n%s" % [mid, index, sdp])
|
||||||
|
|
||||||
|
func send_offer(id, offer) -> int:
|
||||||
|
return _send_msg("O", id, offer)
|
||||||
|
|
||||||
|
func send_answer(id, answer) -> int:
|
||||||
|
return _send_msg("A", id, answer)
|
||||||
|
|
||||||
|
func _send_msg(type, id, data) -> int:
|
||||||
|
return client.get_peer(1).put_packet(("%s: %d\n%s" % [type, id, data]).to_utf8())
|
||||||
|
|
||||||
|
func _process(delta):
|
||||||
|
var status: int = client.get_connection_status()
|
||||||
|
if status == WebSocketClient.CONNECTION_CONNECTING or status == WebSocketClient.CONNECTION_CONNECTED:
|
||||||
|
client.poll()
|
Loading…
Reference in a new issue