From 202c44cabf42ffdec79ea7286ca1df809281a22b Mon Sep 17 00:00:00 2001 From: Daniel Flanagan Date: Sat, 11 Dec 2021 00:08:28 -0600 Subject: [PATCH] More mplayer cleanup --- scripts/global/global.gd | 1 - .../global/signalled_webrtc_multiplayer.gd | 152 ++++++++++---- scripts/global/signaller_client.gd | 197 ------------------ scripts/global/webrtc_negotiator.gd | 136 ------------ scripts/objects/lobby.gd | 6 +- scripts/screens/lobby_browser.gd | 58 +++--- scripts/screens/multiplayer_lobby.gd | 80 ++++--- server.ts | 31 ++- 8 files changed, 209 insertions(+), 452 deletions(-) delete mode 100644 scripts/global/signaller_client.gd delete mode 100644 scripts/global/webrtc_negotiator.gd diff --git a/scripts/global/global.gd b/scripts/global/global.gd index 46a7cc6..6aa1b97 100644 --- a/scripts/global/global.gd +++ b/scripts/global/global.gd @@ -36,7 +36,6 @@ func goto_game(): goto_scene("game") func lobby_browser(): - client.close() goto_scene("lobby_browser") func lobby(): diff --git a/scripts/global/signalled_webrtc_multiplayer.gd b/scripts/global/signalled_webrtc_multiplayer.gd index f2e4f57..d0a00c2 100644 --- a/scripts/global/signalled_webrtc_multiplayer.gd +++ b/scripts/global/signalled_webrtc_multiplayer.gd @@ -6,17 +6,21 @@ const DEFAULT_SIGNALLER_URL = "ws://localhost:8888" # const DEFAULT_SIGNALLER_URL = "wss://webrtc-signaller.deno.dev:443" const DEFAULT_ICE_SERVERS = [ - { "urls": ["stun:[::1]:3478", "stun:stun.l.google.com:19302"] }, + # first element in this array is for STUN + { "urls": ["stun:localhost:3478", "stun:stun.l.google.com:19302"] }, # { "urls": ["stun:stun.l.google.com:19302"] }, # just google - # { "urls": ["stun:[::1]:3478"] }, # just localhost + # { "urls": ["stun:localhost:3478"] }, # just localhost + # {} # TURN servers ] signal lobby_data(lobbies) +signal lobby_begin_join(uuid) signal lobby_joined(lobby_and_peer) -signal lobby_left(id) -signal lobby_delete(id) +signal lobby_left(uuid) +signal lobby_delete(uuid) signal peer_data(peers) +signal peer_created(peer) signal peer_left(id) signal peer_init(id) @@ -34,7 +38,7 @@ signal signaller_connected signal signaller_connection_failure signal signaller_disconnected -onready var webrtc = WebRTCMultiplayer.new() +onready var webrtc = null onready var websocket = WebSocketClient.new() onready var display_name = DEFAULT_DISPLAY_NAME setget set_display_name @@ -50,10 +54,6 @@ func _init(): func _ready(): display_name = _load_display_name() - webrtc.connect("connection_succeeded", self, "_webrtc_connection_succeeded") - webrtc.connect("peer_connected", self, "_webrtc_peer_connected") - webrtc.connect("peer_disconnected", self, "_webrtc_peer_disconnected") - websocket.connect("connection_established", self, "_signaller_connected") websocket.connect("data_received", self, "_signaller_data") websocket.connect("server_close_request", self, "_signaller_close_request") @@ -61,7 +61,16 @@ func _ready(): websocket.connect("connection_error", self, "_signaller_error", [false]) websocket.connect("connection_failed", self, "_signaller_error", [false]) +func is_signaller_connected(): + return websocket.get_connection_status() in [ + websocket.CONNECTION_CONNECTED, + websocket.CONNECTION_CONNECTED, + ] + +func is_in_lobby(): return current_lobby != null + func connect_to_signaller(url = signaller_url): + if url == signaller_url and is_signaller_connected(): return signaller_url = url close() print("Attempting to connect to WebSocket signalling server at %s" % signaller_url) @@ -70,7 +79,7 @@ func connect_to_signaller(url = signaller_url): print("Failed to connect to WebSocket signalling server at %s: %s" % [signaller_url, result]) func close(): - webrtc.close() + if webrtc != null: webrtc.close() websocket.disconnect_from_host() func set_display_name(new_display_name: String): @@ -81,15 +90,19 @@ func request_lobby_list(): return _send("request_lobby_list") func join_lobby(id: String): - return _send("lobby_join:%s" % id) + emit_signal("lobby_begin_join", id) + call_deferred("_send", "lobby_join:%s" % id) + +func leave_lobby(): + return _send("lobby_leave") func get_lobby_name(): - return current_lobby.name + return current_lobby.name if current_lobby.has("name") else null func create_lobby(): - return _send("lobby_create") + _send("lobby_create") -func is_host(): return get_tree().get_network_unique_id() == 1 +func is_host(): return is_signaller_connected() and is_in_lobby() and get_tree().get_network_unique_id() == 1 func set_lobby_name(s: String): if is_host(): _send_json({"name": s}, "update_lobby") @@ -141,7 +154,7 @@ func _signaller_connected(protocol = ""): func _process(_delta: float): var status: int = websocket.get_connection_status() - if status == WebSocketClient.CONNECTION_CONNECTED or status == WebSocketClient.CONNECTION_CONNECTING: + if status in [WebSocketClient.CONNECTION_CONNECTED, WebSocketClient.CONNECTION_CONNECTING]: websocket.poll() func _signaller_data(): @@ -155,36 +168,105 @@ func _signaller_data(): var d = msg.split(":", false, 2) _process_signaller_message({type = d[0], data = d[1]}) +func _init_webrtc_peer(data): + webrtc = WebRTCMultiplayer.new() + webrtc.connect("connection_succeeded", self, "_webrtc_connection_succeeded") + webrtc.connect("peer_connected", self, "_webrtc_peer_connected") + webrtc.connect("peer_disconnected", self, "_webrtc_peer_disconnected") + webrtc.initialize(int(data), true) + get_tree().network_peer = webrtc + +func _deinit_webrtc_peer(): + webrtc.close() + get_tree().network_peer = null + webrtc = null + func _process_signaller_message(data: Dictionary): match data["type"]: # "init": - "init_peer": - webrtc.initialize(int(data.data), true) - get_tree().network_peer = webrtc + "init_peer": _init_webrtc_peer(data.data) "lobby_data": emit_signal("lobby_data", data.data) - "lobby_delete": emit_signal("lobby_delete", data.id) - "lobby_joined": - current_lobby = data - emit_signal("lobby_joined", data) - "lobby_left": emit_signal("lobby_left", data.id) + "lobby_delete": emit_signal("lobby_delete", data.uuid) + "lobby_joined": _lobby_joined(data) + "lobby_left": _lobby_left(data.uuid) - "peer_data": - emit_signal("peer_data", [data] if data.has("id") else data.data) - "peer_left": emit_signal("peer_left", data.data) + "peer_data": _signaller_peer_data([data] if data.has("id") else data.data) + "peer_left": _signaller_peer_left(data.id) - "candidate": - print("Candidate received - Data: %s" % JSON.print(data)) - emit_signal("candidate_received", data) - "offer": - print("Offer received - Data: %s" % JSON.print(data)) - emit_signal("offer_received", data) - "answer": - print("Answer received - Data: %s" % JSON.print(data)) - emit_signal("answer_received", data) + "candidate": _webrtc_candidate_received(data) + "offer": _webrtc_offer_received(data) + "answer": _webrtc_answer_received(data) "ping": _send("pong") _: print("Unhandled Message - Data: %s" % JSON.print(data)) +func _lobby_joined(lobby_data): + current_lobby = lobby_data + print("Lobby Joined: %s" % lobby_data) + emit_signal("lobby_joined", lobby_data) + +func _lobby_left(uuid): + current_lobby = null + print("Lobby Left: %s" % uuid) + emit_signal("lobby_left", uuid) + _deinit_webrtc_peer() + +func _create_peer(data): + var id = data.id + var peer: WebRTCPeerConnection = WebRTCPeerConnection.new() + print("Creating WebRTC Peer %s" % [id]) + peer.connect("session_description_created", self, "_webrtc_offer_created", [id]) + peer.connect("ice_candidate_created", self, "_new_ice_candidate", [id]) + peer.initialize({"iceServers": DEFAULT_ICE_SERVERS}) + webrtc.add_peer(peer, int(id)) + # this guarantees only one peer sends the offer and that offers are never + # sent to ourselves? + if int(id) > webrtc.get_unique_id(): peer.create_offer() + emit_signal("peer_created", data) + +func _webrtc_offer_created(type, data, id): + print("WebRTC %s created for peer %s" % [type, id]) + if not webrtc.has_peer(int(id)): return + print("WebRTC local description set for peer %s" % id) + webrtc.get_peer(id).connection.set_local_description(type, data) + print("--> Local Description: %s" % JSON.print(data)) + if type == "offer": send_offer(id, data) + else: send_answer(id, data) + +func _new_ice_candidate(mid, index, sdp, id): + print("New ICE candidate for peer %s" % id) + send_candidate(id, mid, index, sdp) + +func _signaller_peer_left(id): if peers.has(id): peers.delete(id) + +func _signaller_peer_data(peers): + for peer in peers: + if peer.has("id") and not webrtc.has_peer(int(peer.id)): + _create_peer(peer) + emit_signal("peer_data", peers) + +func _webrtc_offer_received(data): + if webrtc.has_peer(int(data.id)): + print("Setting offer remote description for peer %s" % data.id) + print("--> Offer: %s" % JSON.print(data.offer)) + webrtc.get_peer(data.id).connection.set_remote_description("offer", data.offer) + else: + print("Received an offer for a peer with ID %s that hasn't been added" % data.id) + +func _webrtc_answer_received(data): + if webrtc.has_peer(data.id): + print("Setting answer remote description for peer %s" % data.id) + print("--> Answer: %s" % JSON.print(data.answer)) + webrtc.get_peer(data.id).connection.set_remote_description("answer", data.answer) + +func _webrtc_candidate_received(data): + if webrtc.has_peer(data.id): + print("Adding ice candidate for peer %s" % data.id) + print("--> Candidate: %s" % JSON.print(data.candidate)) + webrtc.get_peer(data.id).connection.add_ice_candidate(data.mid, data.index, data.sdp) + else: + print("Received candidate for non-existant peer %s" % data.id) + func _load_display_name(): var _display_name = DEFAULT_DISPLAY_NAME var display_name_file = File.new() diff --git a/scripts/global/signaller_client.gd b/scripts/global/signaller_client.gd deleted file mode 100644 index d5d5938..0000000 --- a/scripts/global/signaller_client.gd +++ /dev/null @@ -1,197 +0,0 @@ -extends Node - -""" -This module is responsible for making a WebSocket connection to the signaller -in order to enable establishish WebRTC P2P connections. Another module is -expected to fully setup the peer connections. -""" - -const DISPLAY_NAME_FILE = "user://display_name.txt" -const DEFAULT_DISPLAY_NAME = "UnnamedPlayer" - -# lobby data -signal lobby_data(lobbies) -signal lobby_joined(lobby_and_peer) -signal lobby_left(id) -signal lobby_delete(id) - -# peer data -signal peer_data(peers) -signal peer_left(id) -signal peer_init(peer_id) - -# WebRTC negotiations -signal candidate_received(cand) -signal offer_received(data) -signal answer_received(data) - -# underlying websocket -signal received_message -signal connected -signal connection_failure -signal disconnected - -onready var websocket: WebSocketClient = WebSocketClient.new() -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): - websocket_url = url - -func _ready(): - display_name = _load_display_name() - websocket.connect("data_received", self, "_handle_data") - websocket.connect("connection_established", self, "_connected") - # websocket.connect("connection_succeeded", self, "_connection_succeeded") - websocket.connect("connection_closed", self, "_closed") - websocket.connect("connection_error", self, "_connection_error", [false]) - websocket.connect("connection_failed", self, "_connection_error", [false]) - websocket.connect("server_close_request", self, "_close_request") - -func connect_websocket(): - close() - print("Attempting to connect to WebSocket signalling server at %s" % websocket_url) - var result = websocket.connect_to_url(websocket_url) - if result != OK: - print("Failed to connect to WebSocket signalling server at %s: %s" % [websocket_url, result]) - -func close(): - websocket.disconnect_from_host() - -func set_display_name(new_display_name: String): - display_name = new_display_name - _send("update_display_name:%s" % display_name) - -func request_lobby_list(): - return _send("request_lobby_list") - -func join_lobby(id: String): - return _send("lobby_join:%s" % id) - -func create_lobby(): - return _send("lobby_create") - -func is_host(): return peer_id == 1 - -func set_lobby_name(s: String): - if is_host(): _send_json({"name": s}, "update_lobby") - -func lock_lobby(): - if is_host(): _send_json({"locked": true}, "update_lobby") - -func set_lobby_max_players(n: int): - if is_host(): _send_json({"maxPlayers": n}, "update_lobby") - -func request_peer_list(): _send("request_peer_list") - -func send_candidate(peerId, mid, index, sdp) -> int: - return _send_json({"peerId": peerId, "mid": mid, "index": index, "sdp": sdp}, "candidate") - -func send_offer(peerId, offer) -> int: - return _send_json({"peerId": peerId, "offer": offer }, "offer") - -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") - -func _close_request(code: int, reason: String): - print("Received WebSocket close request from signalling server - Code: %s, Reason: %s" % [code, reason]) - # TODO: does this fire _closed? - -func _connected(protocol = ""): - print("Signaller connected via WebSocket using protocol %s" % protocol) - websocket.get_peer(1).set_write_mode(WebSocketPeer.WRITE_MODE_TEXT) - emit_signal("connected") - _send_json({name = display_name}, "init") - -func _process(_delta: float): - var status: int = websocket.get_connection_status() - if status == WebSocketClient.CONNECTION_CONNECTED or status == WebSocketClient.CONNECTION_CONNECTING: - websocket.poll() - -func _handle_data(): - var msg: String = websocket.get_peer(1).get_packet().get_string_from_utf8() - if msg.begins_with("json:"): - var data = JSON.parse(msg.substr(5)) - if data.error == OK: - _process_message(data.result) - var _result = websocket.get_peer(1).put_packet("".to_utf8()) - else: - 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) - "init_peer": - print("init_peer: %s" % data) - peer_id = int(data.data) - emit_signal("peer_init", peer_id) - - "lobby_data": emit_signal("lobby_data", data.data) - "lobby_delete": emit_signal("lobby_delete", data.id) - - "lobby_joined": - 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": - emit_signal("peer_data", [data] if data.has("id") else data["data"]) - "peer_left": - print("Peer Left: %s" % data) - emit_signal("peer_left", data["data"]) - - "candidate": - print("Candidate received - Data: %s" % JSON.print(data)) - emit_signal("candidate_received", data) - "offer": - print("Offer received - Data: %s" % JSON.print(data)) - emit_signal("offer_received", data) - "answer": - print("Answer received - Data: %s" % JSON.print(data)) - emit_signal("answer_received", data) - "ping": - _send("pong") - _: print("Unhandled Message - Data: %s" % JSON.print(data)) - -func _load_display_name(): - var _display_name = DEFAULT_DISPLAY_NAME - var display_name_file = File.new() - if display_name_file.open(DISPLAY_NAME_FILE, File.READ) == OK: - _display_name = display_name_file.get_as_text() - else: - print("Failed to open %s for reading display_name, creating default" % DISPLAY_NAME_FILE) - _store_display_name() - display_name_file.close() - return _display_name - -func _store_display_name(): - var display_name_file = File.new() - if display_name_file.open(DISPLAY_NAME_FILE, File.WRITE) == OK: - display_name_file.store_string(display_name) - else: - print("Failed to open %s for writing display_name" % DISPLAY_NAME_FILE) - display_name_file.close() - -func _send(s: String): - return websocket.get_peer(1).put_packet(s.to_utf8()) - -func _send_json(data: Dictionary, type=null): - if type != null: data["type"] = type - _send("json:%s" % JSON.print(data)) diff --git a/scripts/global/webrtc_negotiator.gd b/scripts/global/webrtc_negotiator.gd deleted file mode 100644 index 41eee28..0000000 --- a/scripts/global/webrtc_negotiator.gd +++ /dev/null @@ -1,136 +0,0 @@ -extends Node - -""" -This module sets up WebRTC peer connections. -""" - -signal peer_connected(id) -signal peer_disconnected(id) -signal connection_succeeded() -signal signaller_disconnected() - -onready var webrtc = WebRTCMultiplayer.new() - -var signaller_client = null -var ice_servers = null -var peers = {} -var peers_id_mappings = {} - -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("disconnected", self, "_signaller_disconnected") - - 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("peer_data", self, "_peer_data") - - webrtc.connect("connection_succeeded", self, "_connection_succeeded") - webrtc.connect("peer_connected", self, "_peer_connected") - webrtc.connect("peer_disconnected", self, "_peer_disconnected") - - # connect("lobby_sealed", self, "lobby_sealed") - - add_child(self.signaller_client) - -func _peer_connected(peer_id): - print("FULLY CONNECTED PEER: %s" % peer_id) - emit_signal("peer_connected", peer_id) - -func _peer_disconnected(peer_id): - print("FULLY CONNECTED PEER DISCONNECTING: %s" % peer_id) - emit_signal("peer_disconnected") - -func _connection_succeeded(): - print("FULLY CONNECTED") - emit_signal("connection_succeeded") - -func _signaller_disconnected(): - print("SIGNALLER DISCONNECTED") - emit_signal("signaller_disconnected") - -func close(): - webrtc.close() - signaller_client.close() - -func connect_to_signaller(): - signaller_client.connect_websocket() - -func peer_by_peer_id(peer_id): - if peers_id_mappings.has(peer_id): - var p = peers_id_mappings[peer_id] - if peers.has(p): - return peers[p] - return null - -func _create_peer(peer_id): - # if peer_id == signaller_client.peer_id: return - var peer: WebRTCPeerConnection = WebRTCPeerConnection.new() - print("New Net Peer with peer_id %d" % [peer_id]) - var _peer_init_results = peer.initialize({"iceServers": ice_servers}) - var _result = peer.connect("session_description_created", self, "_offer_created", [peer_id]) - _result = peer.connect("ice_candidate_created", self, "_new_ice_candidate", [peer_id]) - webrtc.add_peer(peer, peer_id) - var uid = webrtc.get_unique_id() - if peer_id > uid: - var offer_result = peer.create_offer() - print("Create Offer Result: %s" % offer_result) - else: - print("Did not create offer: %d <= %d" % [peer_id, uid]) - return peer - -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("%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) - -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_data(peers): - for i in range(len(peers)): - var peer = peers[i] - if peer.has("peerId"): - var peer_id = peer.peerId - if not webrtc.has_peer(peer_id): - _create_peer(peer_id) - -func peer_disconnected(id): - print("Peer %s disconnected" % id) - if webrtc.has_peer(id): - webrtc.remove_peer(id) - -func _offer_received(data): - var id = data.peerId - if webrtc.has_peer(id): - 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 - 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) - -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) - else: - print("Received candidate for non-existant peer %s" % id) diff --git a/scripts/objects/lobby.gd b/scripts/objects/lobby.gd index b6730f1..b20a1ea 100644 --- a/scripts/objects/lobby.gd +++ b/scripts/objects/lobby.gd @@ -2,7 +2,7 @@ extends HBoxContainer class_name LobbyControl -export(String) var id = "Some UUID" +export(String) var uuid = "Some UUID" export(String) var display_name = "Lobby" setget set_display_name export(int) var max_players = 20 setget set_max_players export(int) var current_players = 1 setget set_current_players @@ -14,7 +14,7 @@ func _ready(): func _update_text(): $Label.text = "%s (%d/%d players)" % [display_name, current_players, max_players] -const keys = ["id", "display_name", "max_players", "current_players", "locked"] +const keys = ["uuid", "display_name", "max_players", "current_players", "locked"] func set_with_dict(dict): if dict.has("name"): self.display_name = dict["name"] for k in keys: @@ -34,4 +34,4 @@ func set_current_players(n): _update_text() func _on_join_pressed(): - Global.signaller_client.join_lobby(id) + Global.client.join_lobby(uuid) diff --git a/scripts/screens/lobby_browser.gd b/scripts/screens/lobby_browser.gd index 91ae1a5..224ab2e 100644 --- a/scripts/screens/lobby_browser.gd +++ b/scripts/screens/lobby_browser.gd @@ -15,14 +15,14 @@ func _ready(): display_name_edit.text = Global.client.display_name Global.client.connect("lobby_data", self, "_lobby_data") Global.client.connect("lobby_delete", self, "_lobby_delete") - Global.client.connect("lobby_joined", self, "_lobby_joined") + Global.client.connect("lobby_begin_join", self, "_lobby_begin_join") Global.client.connect("lobby_left", self, "_lobby_left") Global.client.connect("signaller_connected", self, "_signaller_connected") Global.client.connect("signaller_disconnected", self, "_signaller_disconnected") - Global.client.connect_to_signaller() - - if Global.check_onetime_flag("create-lobby"): - Global.client.create_lobby() + if Global.client.is_signaller_connected(): + _signaller_connected() + else: + Global.client.connect_to_signaller() # TODO: add search func _input(ev): @@ -39,59 +39,59 @@ func _signaller_disconnected(): Global.main_menu() func _signaller_connected(): - Global.client.request_lobby_list() + print("Lobby Browser: Signaller Connected") + if Global.check_onetime_flag("create-lobby"): + call_deferred("_on_create_lobby_pressed") + else: + Global.client.request_lobby_list() -func _lobby_joined(data): - print("Lobby Joined: %s" % data) +func _lobby_begin_join(data): + print("Joining Lobby: %s" % data) Global.lobby() -func _lobby_left(_id): +func _lobby_left(_uuid): Global.lobby_browser() func _on_back_pressed(): Global.main_menu() func _on_create_lobby_pressed(): + Global.lobby() Global.client.create_lobby() -func _on_join_pressed(): - var items = lobbies.get_selected_items() - if len(items) > 0: - Global.client.join_lobby(lobbies.get_item_metadata(items[0])["id"]) - func _lobby_data(new_lobbies: Array): for l in new_lobbies: - if lobbies.has(l.id): _update_lobby(l.id, l) - else: _add_lobby(l.id, l) + if lobbies.has(l.uuid): _update_lobby(l.uuid, l) + else: _add_lobby(l.uuid, l) if Global.check_onetime_flag("join-first-available-lobby"): - Global.client.join_lobby(l.id) + Global.client.join_lobby(l.uuid) -func _lobby_delete(id: String): - print("Lobby Deleted: %s" % id) - _delete_lobby(id) +func _lobby_delete(uuid: String): + print("Lobby Deleted: %s" % uuid) + _delete_lobby(uuid) -func _add_lobby(id, lobby_data): +func _add_lobby(uuid, lobby_data): call_deferred("_update_lobbies_text") print("New Lobby ", lobby_data) # if lobby_data.currentPlayers > 0: var new_lobby = lobby.instance() new_lobby.set_with_dict(lobby_data) lobbies_grid.add_child(new_lobby) - lobbies[id] = new_lobby + lobbies[uuid] = new_lobby -func _update_lobby(id, lobby_data): +func _update_lobby(uuid, lobby_data): call_deferred("_update_lobbies_text") print("Updated Lobby ", lobby_data) - lobbies[id].set_with_dict(lobby_data) + lobbies[uuid].set_with_dict(lobby_data) -func _delete_lobby(id): +func _delete_lobby(uuid): call_deferred("_update_lobbies_text") - if lobbies.has(id): + if lobbies.has(uuid): print("Removing lobby...") - lobbies_grid.remove_child(lobbies[id]) - lobbies[id].queue_free() - lobbies.erase(id) + lobbies_grid.remove_child(lobbies[uuid]) + lobbies[uuid].queue_free() + lobbies.erase(uuid) func _update_lobbies_text(): var n = lobbies.size() diff --git a/scripts/screens/multiplayer_lobby.gd b/scripts/screens/multiplayer_lobby.gd index 3320f15..cec830b 100644 --- a/scripts/screens/multiplayer_lobby.gd +++ b/scripts/screens/multiplayer_lobby.gd @@ -18,63 +18,50 @@ onready var peers_id_mappings = {} onready var is_host = false func _ready(): - Global.client.connect("peer_data", self, "_peer_data") + Global.client.connect("peer_created", self, "_peer_created") Global.client.connect("peer_left", self, "_peer_left") Global.client.connect("lobby_left", self, "_lobby_left") Global.client.connect("lobby_data", self, "_lobby_data") + Global.client.connect("lobby_joined", self, "_lobby_joined") Global.client.connect("signaller_disconnected", self, "_signaller_disconnected") - Global.client.connect("webrtc_connection_succeeded", self, "_connection_succeeded") + Global.client.connect("webrtc_connection_succeeded", self, "_webrtc_connection_succeeded") # lobby_name.text = "%s" % Global.client.lobby_name - Global.client.call_deferred("request_peer_list") - call_deferred("add_chat", "# Connected to %s" % Global.client.get_lobby_name()) + # call_deferred("add_chat", "# to %s" % Global.client.get_lobby_name()) # hide/show controls depending on whether or not we're the host - is_host = Global.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() - - # until we fully connect - ready_up.disabled = true - chat_edit.editable = false - send_button.disabled = true - print("Setting up lobby... %s %s" % [get_tree().get_network_unique_id(), is_host]) + _update_ui() -func _connection_succeeded(): - ready_up.disabled = false - send_button.disabled = false - chat_edit.editable = true +func _signaller_disconnected(): + Global.main_menu() -func _lobby_update(l): - for u in l: - if !is_host: - max_players.text = str(u.maxPlayers) - lobby_name.text = u.name - print(l) - break +func _lobby_joined(data): + add_chat("# Connected to %s" % data.name) + _update_ui() + Global.client.request_peer_list() -func _peer_data(peers): +func _webrtc_connection_succeeded(): + if !is_host: + ready_up.disabled = false + send_button.disabled = false + chat_edit.editable = true + Global.client.request_peer_list() + +func _peer_created(peer): call_deferred("update_player_count") if is_host: call_deferred("update_can_start") - for p in peers: - if peers.has(p.id): _update_peer(p.id, p) - else: _add_peer(p.id, p) + if peers.has(peer.id): _update_peer(peer.id, peer) + else: _add_peer(peer.id, peer) -func _add_peer(id, peer_data): +func _add_peer(id, p): call_deferred("update_player_count") var new_peer = peer.instance() - if peer_data.peerId == 1: peer_data.ready = true - new_peer.set_with_dict(peer_data) + if p.id == 1: p.ready = true + new_peer.set_with_dict(p) peers_grid.add_child(new_peer) peers[id] = new_peer - peers_id_mappings[int(peer_data.peerId)] = id add_chat("> %s joined the lobby" % new_peer.display_name) func _update_peer(id, peer_data): @@ -142,18 +129,29 @@ remotesync func start_game(): remotesync func set_ready(ready): var from = get_tree().get_rpc_sender_id() - print(from) - peer_by_peer_id(from).ready = ready + print("Set Ready: %s %s" % [from, ready]) + Global.client.peers[from].ready = ready if is_host: update_can_start() func _on_TextEdit_text_entered(_new_text): if !send_button.disabled: send_chat_message() +func _update_ui(): + is_host = Global.client.is_host() + ready_up.visible = !is_host + lobby_name.editable = is_host + max_players.editable = is_host + start.visible = is_host + lock.visible = is_host + var is_in_lobby = Global.client.is_in_lobby() + ready_up.disabled = !is_in_lobby + chat_edit.editable = is_in_lobby + send_button.disabled = !is_in_lobby + func _lobby_left(_id): Global.lobby_browser() -func _on_leave_button_pressed(): Global.lobby_browser() +func _on_leave_button_pressed(): Global.client.leave_lobby() 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): rpc("set_ready", button_pressed) func _on_lobby_info_text_changed(new_text: String): Global.client.set_lobby_name(new_text) -func _signaller_disconnected(): Global.main_menu() diff --git a/server.ts b/server.ts index fe49516..d6a8c18 100644 --- a/server.ts +++ b/server.ts @@ -3,8 +3,8 @@ const SERVER_VERSION = "0.2.0"; // TODO: version comparison -type UUID = string; type ID = number; +type UUID = string; // app state const allLobbies = new Map(); @@ -57,6 +57,7 @@ class Client { } remove() { + console.debug(`Removing Client ${this.uuid}`); this.lobbyLeave(); if (this.isConnected()) { this.socket.close(); @@ -73,6 +74,7 @@ class Client { : message, ); } + // TODO: maybe log when we try to send and we're not connected? } catch (e) { console.error( `Failed to send on socket ${this.socket} to client ${this.id}: ${ @@ -104,6 +106,7 @@ class Client { this.lobby.clients.forEach(({ id, name }) => { if (typeof id === "number") peers.push({ id, name }); }); + console.debug(`Sending peer list with ${peers.length} peers...`); this.send(buildMessage("peer_data", peers)); // TODO: chunk async? } @@ -172,12 +175,11 @@ class Client { this.send(`[info] cannot leave lobby (not in a lobby)`); return; } + console.debug(`Client ${this.uuid} leaving Lobby ${this.lobby?.uuid}`); + leavingLobby.removeClient(this); this.id = null; this.lobby = null; - if (this.isConnected()) { - this.send(buildMessage("lobby_left", { id: leavingLobby.uuid })); - } - leavingLobby.removeClient(this); + this.send(buildMessage("lobby_left", { uuid: leavingLobby.uuid })); } } @@ -201,6 +203,10 @@ class Lobby { this._shouldNotify = true; + console.debug( + `Lobby ${this.uuid} created by Client ${this.hostClientUuid}`, + ); + allLobbies.set(this.uuid, this); host.id = 1; host.lobbyJoin(this); @@ -221,7 +227,7 @@ class Lobby { requestor: Client, newValues: { name?: string; maxPlayers?: number; locked?: boolean }, ) { - if (requestor.id === 1) { + if (requestor.uuid === this.hostClientUuid) { for (const k in newValues) { switch (k) { case "name": @@ -254,6 +260,7 @@ class Lobby { remove() { this._shouldNotify = false; + console.debug(`Removing Lobby ${this.uuid}`); allLobbies.delete(this.uuid); allClients.forEach((client) => client.lobbyDelete(this)); this.clients.forEach((client) => { @@ -271,6 +278,9 @@ class Lobby { crypto.getRandomValues(arr); client.id = Math.abs(arr[0]); } + console.debug( + `Adding Client ${client.uuid} Lobby ${this.uuid} as peer ${client.id}`, + ); client.send(buildMessage("init_peer", client.id)); this.broadcast( buildMessage("peer_data", [{ @@ -293,9 +303,10 @@ class Lobby { removeClient({ id, uuid }: Client) { if (typeof id !== "number") return; this.clients.delete(id); - this.broadcast(buildMessage("peer_left", [{ id }])); + this.broadcast(buildMessage("peer_left", { id })); + console.debug(`Removing Client ${this.uuid} from Lobby ${this.uuid}`); if (uuid === this.hostClientUuid) { - console.warn("Host left!"); + console.warn(`Host left, removing Lobby ${this.uuid}`); this.remove(); } this.notify(); @@ -343,7 +354,7 @@ function onMessage(client: Client, ev: MessageEvent) { // TODO: log who from? IPs etc.? const msg = parseMessage(ev.data); if (msg.type === "pong") return; - console.log("Client Message Received", msg); + console.debug("Client Message Received", msg); switch (msg.type) { case "init": if (msg.data && (msg.data as { name: string })["name"]) { @@ -421,7 +432,7 @@ function onMessage(client: Client, ev: MessageEvent) { } function onSocketOpen(client: Client, _ev: Event) { - console.log("New Client", client.id); + console.log("New Client", client.uuid); } function onClientLeave(client: Client) {