Big refactor with functional peering

This commit is contained in:
Daniel Flanagan 2021-12-09 21:10:17 -06:00
parent 7094ea299c
commit 5e7735a815
Signed by: lytedev
GPG key ID: 5B2020A0F9921EF4
10 changed files with 114 additions and 118 deletions

View file

@ -26,7 +26,7 @@ _global_script_class_icons={
[application]
config/name="kdt"
config/name="webrtc_tech_demo"
run/main_scene="res://screens/main_menu.tscn"
config/icon="res://assets/img/icon.png"
@ -36,7 +36,7 @@ Global="*res://scripts/global/global.gd"
[gdnative]
singletons=[ "res://webrtc/webrtc_debug.tres" ]
singletons=[ "res://webrtc/webrtc.tres" ]
[physics]

View file

@ -1,6 +1,7 @@
[gd_scene load_steps=4 format=2]
[gd_scene load_steps=5 format=2]
[ext_resource path="res://assets/fonts/iosevkalyte/iosevkalyte-regular.ttf" type="DynamicFontData" id=1]
[ext_resource path="res://assets/img/cross.png" type="Texture" id=2]
[ext_resource path="res://scripts/screens/main_menu.gd" type="Script" id=3]
[sub_resource type="DynamicFont" id=1]
@ -19,16 +20,16 @@ __meta__ = {
"_edit_use_anchors_": false
}
[node name="VBoxContainer" type="VBoxContainer" parent="."]
[node name="v" type="VBoxContainer" parent="."]
margin_right = 984.0
margin_bottom = 560.0
[node name="HBoxContainer" type="HBoxContainer" parent="VBoxContainer"]
[node name="h" type="HBoxContainer" parent="v"]
margin_right = 984.0
margin_bottom = 50.0
alignment = 1
[node name="quit" type="Button" parent="VBoxContainer/HBoxContainer"]
[node name="quit" type="Button" parent="v/h"]
margin_right = 325.0
margin_bottom = 50.0
rect_min_size = Vector2( 150, 50 )
@ -36,11 +37,13 @@ hint_tooltip = "Yeah, let's get out of here."
size_flags_horizontal = 3
size_flags_vertical = 3
text = "Quit"
icon = ExtResource( 2 )
expand_icon = true
__meta__ = {
"_edit_use_anchors_": false
}
[node name="multiplayer" type="Button" parent="VBoxContainer/HBoxContainer"]
[node name="multiplayer" type="Button" parent="v/h"]
margin_left = 329.0
margin_right = 654.0
margin_bottom = 50.0
@ -50,7 +53,7 @@ size_flags_horizontal = 3
size_flags_vertical = 3
text = "Multiplayer"
[node name="Singleplayer" type="Button" parent="VBoxContainer/HBoxContainer"]
[node name="singleplayer" type="Button" parent="v/h"]
margin_left = 658.0
margin_right = 984.0
margin_bottom = 50.0
@ -65,20 +68,20 @@ __meta__ = {
"_edit_use_anchors_": false
}
[node name="Container" type="CenterContainer" parent="VBoxContainer"]
[node name="c" type="CenterContainer" parent="v"]
margin_top = 54.0
margin_right = 984.0
margin_bottom = 560.0
size_flags_horizontal = 3
size_flags_vertical = 3
[node name="VBoxContainer" type="VBoxContainer" parent="VBoxContainer/Container"]
[node name="v" type="VBoxContainer" parent="v/c"]
margin_left = 124.0
margin_top = 153.0
margin_right = 859.0
margin_bottom = 352.0
[node name="Label" type="Label" parent="VBoxContainer/Container/VBoxContainer"]
[node name="title" type="Label" parent="v/c/v"]
margin_right = 735.0
margin_bottom = 181.0
custom_fonts/font = SubResource( 1 )
@ -87,26 +90,26 @@ Multiplayer Tech Demo"
align = 1
valign = 1
[node name="HBoxContainer" type="HBoxContainer" parent="VBoxContainer/Container/VBoxContainer"]
[node name="h" type="HBoxContainer" parent="v/c/v"]
margin_top = 185.0
margin_right = 735.0
margin_bottom = 199.0
alignment = 1
[node name="Label" type="Label" parent="VBoxContainer/Container/VBoxContainer/HBoxContainer"]
[node name="made_by" type="Label" parent="v/c/v/h"]
margin_left = 312.0
margin_right = 371.0
margin_bottom = 14.0
text = "made by "
[node name="LinkButton" type="LinkButton" parent="VBoxContainer/Container/VBoxContainer/HBoxContainer"]
[node name="link" type="LinkButton" parent="v/c/v/h"]
margin_left = 375.0
margin_right = 422.0
margin_bottom = 14.0
hint_tooltip = "Open https://lyte.dev"
text = "lytedev"
[connection signal="pressed" from="VBoxContainer/HBoxContainer/quit" to="." method="_on_JoinLobbyButton_pressed"]
[connection signal="pressed" from="VBoxContainer/HBoxContainer/multiplayer" to="." method="_on_multiplayer_pressed"]
[connection signal="pressed" from="VBoxContainer/HBoxContainer/Singleplayer" to="." method="_on_Singleplayer_pressed"]
[connection signal="pressed" from="VBoxContainer/Container/VBoxContainer/HBoxContainer/LinkButton" to="." method="_on_LinkButton_pressed"]
[connection signal="pressed" from="v/h/quit" to="." method="_on_JoinLobbyButton_pressed"]
[connection signal="pressed" from="v/h/multiplayer" to="." method="_on_multiplayer_pressed"]
[connection signal="pressed" from="v/h/singleplayer" to="." method="_on_Singleplayer_pressed"]
[connection signal="pressed" from="v/c/v/h/link" to="." method="_on_LinkButton_pressed"]

View file

@ -27,6 +27,7 @@ func check_onetime_flag(flag):
return result
func _ready():
signaller_client.connect("connected", self, "_signaller_connected")
negotiator.signaller_client = signaller_client
negotiator.ice_servers = ice_servers
add_child(negotiator)
@ -63,7 +64,6 @@ func _signaller_connected():
func lobby_browser():
negotiator.close()
signaller_client.connect("connected", self, "_signaller_connected")
negotiator.connect_to_signaller()
func lobby():
@ -72,3 +72,9 @@ func lobby():
func quit():
negotiator.close()
get_tree().quit()
func key_shortcut(code):
var sc = ShortCut.new()
sc.shortcut = InputEventKey.new()
sc.shortcut.scancode = code
return sc

View file

@ -10,14 +10,15 @@ const DISPLAY_NAME_FILE = "user://display_name.txt"
const DEFAULT_DISPLAY_NAME = "UnnamedPlayer"
# lobby data
signal lobby_data_received(lobbies)
signal lobby_data(lobbies)
signal lobby_joined(lobby_and_peer)
signal lobby_left(id)
signal lobby_delete(id)
# peer data
signal peer_data_received(peers)
signal peer_data(peers)
signal peer_left(id)
signal peer_init(peer_id)
# WebRTC negotiations
signal candidate_received(cand)
@ -36,6 +37,8 @@ onready var client_id = null
onready var display_name = DEFAULT_DISPLAY_NAME setget set_display_name
onready var peer_id = null
onready var lobby_data = null
var websocket_url = null
func _init(url):
@ -96,6 +99,8 @@ func send_offer(peerId, offer) -> int:
func send_answer(peerId, answer) -> int:
return _send_json({"peerId": peerId, "answer": answer }, "answer")
func get_lobby_name(): return lobby_data.name
func _closed(code):
print("WebSocket closed: %s: " % code)
emit_signal("disconnected")
@ -123,44 +128,45 @@ func _handle_data():
_process_message(data.result)
var _result = websocket.get_peer(1).put_packet("".to_utf8())
else:
print("Unhandled message: %s" % msg)
var d = msg.split(":", false, 2)
_process_message({type = d[0], data = d[1]})
func _process_message(data: Dictionary):
match data["type"]:
"init":
client_id = data.id
print("Signaller Received init response: %s" % data)
"your_peer_id":
peer_id = int(data.peerId)
emit_signal("peer_id_set", peer_id)
"init_peer":
print("init_peer: %s" % data)
peer_id = int(data.data)
emit_signal("peer_init", peer_id)
"lobby_data": emit_signal("lobby_data_received", data.data)
"lobby_data": emit_signal("lobby_data", data.data)
"lobby_delete": emit_signal("lobby_delete", data.id)
"lobby_joined":
peer_id = data.peerId
print("lobby_joined: %s" % data)
lobby_data = data
emit_signal("lobby_joined", data)
"lobby_left":
peer_id = null
emit_signal("lobby_left", data["id"])
"peer_data":
print(typeof(data), data)
if data.get("id") != null: emit_signal("peer_joined", [data])
else: emit_signal("peer_joined", data["data"])
emit_signal("peer_data", [data] if data.has("id") else data["data"])
"peer_left":
print("Peer Left: %s" % data)
emit_signal("peer_left", data)
"candidate":
print("Candidate received - Data: %s" % JSON.print(data["data"]))
emit_signal("candidate_received", data["data"])
print("Candidate received - Data: %s" % JSON.print(data))
emit_signal("candidate_received", data)
"offer":
print("Offer received - Data: %s" % JSON.print(data["data"]))
emit_signal("offer_received", data["data"])
print("Offer received - Data: %s" % JSON.print(data))
emit_signal("offer_received", data)
"answer":
print("Answer received - Data: %s" % JSON.print(data["data"]))
emit_signal("answer_received", data["data"])
print("Answer received - Data: %s" % JSON.print(data))
emit_signal("answer_received", data)
"ping":
_send("pong")
_: print("Unhandled Message - Data: %s" % JSON.print(data))

View file

@ -12,23 +12,20 @@ var ice_servers = null
func _init(_ice_servers, _signaller_client):
ice_servers = _ice_servers
signaller_client = _signaller_client
print("Initializing WebRTCNegotiator with ICE servers: %s" % ice_servers)
func _ready():
signaller_client.connect("offer_received", self, "_offer_received")
signaller_client.connect("answer_received", self, "_answer_received")
signaller_client.connect("candidate_received", self, "_candidate_received")
signaller_client.connect("connected", self, "_signaller_connected")
# signaller_client.connect("websocket_disconnected", self, "disconnected")
signaller_client.connect("peer_id_set", self, "_peer_id_set")
signaller_client.connect("peer_joined", self, "_peer_joined")
signaller_client.connect("peer_init", self, "_peer_id_set")
signaller_client.connect("peer_data", self, "_peer_data")
# webrtc.connect("peer_disconnected", self, "peer_disconnected")
# connect("lobby_sealed", self, "lobby_sealed")
add_child(self.signaller_client)
func _signaller_connected():
print("Connected to signaller. Will be using the following ICE servers: %s", ice_servers)
func close():
webrtc.close()
signaller_client.close()
@ -52,30 +49,27 @@ func _create_peer(peer_id):
print("Did not create offer: %d <= %d" % [peer_id, uid])
return peer
func _new_ice_candidate(mid_name, index_name, sdp_name, peer_id):
print("New ICE Candidate - MID: %s, Index: %s, SDP: %s, PeerID: %s" % [mid_name, index_name, sdp_name, peer_id])
signaller_client.send_candidate(peer_id, mid_name, index_name, sdp_name)
func _new_ice_candidate(mid, index, sdp, peer_id):
print("New ICE Candidate - MID: %s, Index: %s, SDP: %s, PeerID: %s" % [mid, index, sdp, peer_id])
signaller_client.send_candidate(peer_id, mid, index, sdp)
func _offer_created(type, data, peer_id):
print("Offer created - Type: %s, PeerID: %s, Data: %s" % [type, peer_id, JSON.print(data)])
if not webrtc.has_peer(peer_id):
return
print("%s created - PeerID: %s, Data: %s" % [type, peer_id, JSON.print(data)])
if not webrtc.has_peer(peer_id): return
webrtc.get_peer(peer_id).connection.set_local_description(type, data)
if type == "offer":
signaller_client.send_offer(peer_id, data)
else:
signaller_client.send_answer(peer_id, data)
if type == "offer": signaller_client.send_offer(peer_id, data)
else: signaller_client.send_answer(peer_id, data)
func _peer_id_set(peer_id):
print("Peer ID received %s - setting up to peer..." % [peer_id])
webrtc.initialize(peer_id, true)
get_tree().network_peer = webrtc
func _peer_joined(peers):
func _peer_data(peers):
for i in range(len(peers)):
var peer = peers[i]
if peer.has("peerId"):
var peer_id = peer["peerId"]
var peer_id = peer.peerId
if not webrtc.has_peer(peer_id):
_create_peer(peer_id)
@ -84,24 +78,24 @@ func peer_disconnected(id):
if webrtc.has_peer(id):
webrtc.remove_peer(id)
func offer_received(data):
var id = data["peerId"]
func _offer_received(data):
var id = data.peerId
if webrtc.has_peer(id):
print("Setting offer remote description: %s" % JSON.print(data))
webrtc.get_peer(id).connection.set_remote_description("offer", data["offer"])
print("Setting offer remote description: %s" % JSON.print(data.offer))
webrtc.get_peer(id).connection.set_remote_description("offer", data.offer)
else:
print("Received an offer for a peer with ID %s that hasn't been added" % id)
func answer_received(data):
var id = data["peerId"]
func _answer_received(data):
var id = data.peerId
if webrtc.has_peer(id):
print("Setting answer remote description: %s" % JSON.print(data))
webrtc.get_peer(id).connection.set_remote_description("answer", data["answer"])
webrtc.get_peer(id).connection.set_remote_description("answer", data.answer)
func candidate_received(data):
var id = data["peerId"]
func _candidate_received(data):
var id = data.peerId
if webrtc.has_peer(id):
print("Adding ice candidate: %s" % JSON.print(data))
webrtc.get_peer(id).connection.add_ice_candidate(data["mid"], data["index"], data["sdp"])
webrtc.get_peer(id).connection.add_ice_candidate(data.mid, data.index, data.sdp)
else:
print("Received candidate for non-existant peer %s" % id)

View file

@ -34,4 +34,4 @@ func set_current_players(n):
_update_text()
func _on_join_pressed():
Global.client.signaller.join_lobby(id)
Global.signaller_client.join_lobby(id)

View file

@ -15,23 +15,16 @@ onready var lobby = preload("res://objects/lobby.tscn")
func _ready():
display_name_edit.text = Global.signaller_client.display_name
Global.signaller_client.connect("lobby_new", self, "_lobby_new")
Global.signaller_client.connect("lobby_data", self, "_lobby_data")
Global.signaller_client.connect("lobby_delete", self, "_lobby_delete")
Global.signaller_client.connect("lobby_joined", self, "_lobby_joined")
Global.signaller_client.connect("lobby_left", self, "_lobby_left")
Global.signaller_client.connect("websocket_disconnected", self, "_signaller_disconnected")
Global.signaller_client.connect("disconnected", self, "_signaller_disconnected")
Global.signaller_client.request_lobby_list()
if Global.check_onetime_flag("create-lobby"):
Global.signaller_client.create_lobby()
"""
var ls = []
for n in range(200):
ls.push_back({id = n, name = n, currentPlayers = n, maxPlayers = n, locked = false})
call_deferred("_lobby_new", ls)
"""
# TODO: add search
func _input(ev):
pass
@ -46,7 +39,8 @@ func _input(ev):
func _signaller_disconnected():
Global.main_menu()
func _lobby_joined(_id, _peerId):
func _lobby_joined(data):
print("Lobby Joined: %s" % data)
Global.lobby()
func _lobby_left(_id):
@ -63,19 +57,14 @@ func _on_join_pressed():
if len(items) > 0:
Global.signaller_client.join_lobby(lobbies.get_item_metadata(items[0])["id"])
func _lobby_new(new_lobbies: Array):
func _lobby_data(new_lobbies: Array):
# TODO: handle scrolling so that the user is never too jarred (like chat)
for lobby_index in range(len(new_lobbies)):
var lobby_data = new_lobbies[lobby_index]
var id = lobby_data["id"]
if lobbies.has(id):
_update_lobby(id, lobby_data)
else:
_add_lobby(id, lobby_data)
for l in new_lobbies:
if lobbies.has(l.id): _update_lobby(l.id, l)
else: _add_lobby(l.id, l)
if Global.join_first_available_lobby:
Global.join_first_available_lobby = false
Global.signaller_client.join_lobby(id)
if Global.check_onetime_flag("join-first-available-lobby"):
Global.signaller_client.join_lobby(l.id)
func _lobby_delete(id: String):
print("Lobby Deleted: %s" % id)

View file

@ -1,6 +1,9 @@
extends Node
func _ready():
$v/h/quit.shortcut = Global.key_shortcut(KEY_ESCAPE)
$v/h/multiplayer.shortcut = Global.key_shortcut(KEY_M)
$v/h/singleplayer.shortcut = Global.key_shortcut(KEY_S)
if Global.check_onetime_flag("multiplayer"):
_on_multiplayer_pressed()

View file

@ -16,28 +16,27 @@ onready var peers = {}
onready var is_host = false
func _ready():
Global.client.signaller.connect("peer_joined", self, "_peer_joined")
Global.client.signaller.connect("peer_left", self, "_peer_left")
Global.client.signaller.connect("lobby_left", self, "_lobby_left")
Global.client.signaller.connect("lobby_new", self, "_lobby_update")
Global.signaller_client.connect("peer_data", self, "_peer_data")
Global.signaller_client.connect("peer_left", self, "_peer_left")
Global.signaller_client.connect("lobby_left", self, "_lobby_left")
Global.signaller_client.connect("lobby_data", self, "_lobby_data")
Global.client.signaller.connect("websocket_disconnected", self, "_signaller_disconnected")
Global.signaller_client.connect("disconnected", self, "_signaller_disconnected")
lobby_name.text = "%s" % Global.client.signaller.lobby_name
# lobby_name.text = "%s" % Global.signaller_client.lobby_name
Global.client.signaller.call_deferred("request_peer_list")
call_deferred("add_chat", "# Connected to %s" % Global.client.signaller.lobby_name)
Global.signaller_client.call_deferred("request_peer_list")
call_deferred("add_chat", "# Connected to %s" % Global.signaller_client.get_lobby_name())
# hide/show controls depending on whether or not we're the host
is_host = Global.client.signaller.peer_id == 1
if is_host:
ready_up.queue_free()
is_host = Global.signaller_client.is_host()
if is_host: ready_up.queue_free()
else:
lobby_name.editable = false
max_players.editable = false
start.queue_free()
lock.queue_free()
print("Setting up lobby... %s %s" % [Global.client.signaller.peer_id, is_host])
print("Setting up lobby... %s %s" % [Global.signaller_client.peer_id, is_host])
func _lobby_update(l):
for u in l:
@ -47,20 +46,15 @@ func _lobby_update(l):
print(l)
break
func _peer_joined(joined_peers):
func _peer_data(peers):
call_deferred("update_player_count")
if is_host: call_deferred("update_can_start")
for peer_index in range(len(joined_peers)):
var peer_data = joined_peers[peer_index]
var id = peer_data["id"]
if peers.has(id):
_update_peer(id, peer_data)
else:
_add_peer(id, peer_data)
for p in peers:
if peers.has(p.id): _update_peer(p.id, p)
else: _add_peer(p.id, p)
func _add_peer(id, peer_data):
call_deferred("update_player_count")
var new_peer = peer.instance()
new_peer.set_with_dict(peer_data)
peers_grid.add_child(new_peer)
@ -68,9 +62,10 @@ func _add_peer(id, peer_data):
add_chat("> %s joined the lobby" % new_peer.display_name)
func _update_peer(id, peer_data):
if peer_data.ready != peers[id].ready: add_chat("! %s is %s" % [peers[id].display_name, "now ready" if peer_data.ready else "no longer ready"])
if peer_data.ready != peers[id].ready:
var update_text = "now ready" if peer_data.ready else "no longer ready"
add_chat("! %s is %s" % [peers[id].display_name, update_text])
peers[id].set_with_dict(peer_data)
# TODO: announce changes in chat?
func _peer_left(ids: Array):
call_deferred("update_player_count")
@ -99,7 +94,7 @@ func preserve_chat_scroll(n, tl, fl, tc, fc):
func send_chat_message():
var message = chat_edit.text
if message != "":
rpc("add_chat", "%s: %s" % [Global.client.signaller.display_name, message])
rpc("add_chat", "%s: %s" % [Global.signaller_client.display_name, message])
chat_edit.text = ""
func update_can_start():
@ -114,11 +109,9 @@ func update_can_start():
start.disabled = !can_start
func _on_start_pressed():
print("Start %s" % is_host)
if is_host: rpc("start_game")
remotesync func start_game():
print("Starting game!")
Global.goto_game()
func _lobby_left(_id): Global.lobby_browser()
@ -126,6 +119,6 @@ func _on_leave_button_pressed(): Global.lobby_browser()
func _on_TextEdit_text_entered(_new_text): send_chat_message()
func update_player_count(): peers_list_label.text = "Players: %d" % peers.size()
func _on_Button_pressed(): send_chat_message()
func _on_ready_up_toggled(button_pressed: bool): Global.client.signaller.set_ready(button_pressed)
func _on_lobby_info_text_changed(new_text: String): Global.client.signaller.set_lobby_name(new_text)
func _on_ready_up_toggled(button_pressed: bool): Global.signaller_client.set_ready(button_pressed)
func _on_lobby_info_text_changed(new_text: String): Global.signaller_client.set_lobby_name(new_text)
func _signaller_disconnected(): Global.main_menu()

View file

@ -397,15 +397,17 @@ function onMessage(client: Client, ev: MessageEvent) {
/* falls through */
case "offer": {
if (!client.lobby) return;
console.log(`Received WebRTC Negotiation Message (type: ${msg.type})...`);
if (typeof msg.data !== "object") return;
const webrtcMessage = (msg.data as { peerId?: number });
if (!webrtcMessage.peerId || typeof webrtcMessage.peerId !== "number") {
return;
}
console.log(
`Received WebRTC Negotiation Message (type: ${msg.type}, from: ${client.peerId}, to: ${webrtcMessage.peerId})...`,
);
const destClient = client.lobby.getPeer(webrtcMessage.peerId);
if (!destClient || !destClient.peerId) return;
webrtcMessage.peerId = destClient.peerId;
webrtcMessage.peerId = client.peerId as number;
destClient.send(buildMessage(msg.type, webrtcMessage));
break;
}
@ -424,12 +426,12 @@ function onClientLeave(client: Client) {
}
function onSocketClose(client: Client, _ev: Event) {
console.log("Client Close");
console.log("Client Socket Close");
onClientLeave(client);
}
function onSocketError(client: Client, _ev: Event) {
console.log("Client Error");
console.log("Client Socket Error");
onClientLeave(client);
}