From 9c5b37685299ab54f1ce4f32f544911f0551e052 Mon Sep 17 00:00:00 2001 From: Daniel Flanagan Date: Wed, 17 Nov 2021 16:24:00 -0600 Subject: [PATCH] wth is happening --- global.gd | 7 ++- lobby.gd | 6 ++- multiplayer.gd | 12 ++--- multiplayer_client.gd | 83 ++++++++++++++++++-------------- server.ts | 54 +++++++++++++++++---- signaller_client.gd | 107 +++++++++++++++--------------------------- 6 files changed, 147 insertions(+), 122 deletions(-) diff --git a/global.gd b/global.gd index e5e2eb6..4c7beca 100644 --- a/global.gd +++ b/global.gd @@ -5,7 +5,9 @@ const MultiplayerClient = preload("multiplayer_client.gd") onready var client = MultiplayerClient.new() func _ready(): + # client.signaller.connect("websocket_connected", self, "signaller_disconnected") add_child(client) + client.signaller.connect("websocket_connected", self, "_signaller_connected") func goto_scene(scene_resource_name): var _result = get_tree().change_scene("res://%s.tscn" % scene_resource_name) @@ -18,9 +20,12 @@ func start_singleplayer_game(): client.close() goto_scene("game") +func _signaller_connected(): + goto_scene("multiplayer") + func lobby_browser(): client.close() - goto_scene("multiplayer") + Global.client.connect_to_signaller() func lobby(): goto_scene("lobby") diff --git a/lobby.gd b/lobby.gd index f414887..0c2fd53 100644 --- a/lobby.gd +++ b/lobby.gd @@ -7,8 +7,12 @@ func _ready(): Global.client.signaller.connect("peer_left", self, "_peer_left") Global.client.signaller.connect("lobby_left", self, "_lobby_left") $MarginContainer/Label.text = Global.client.signaller.lobby_id + Global.client.signaller.connect("websocket_disconnected", self, "_signaller_disconnected") Global.client.signaller.request_peer_list() +func _signaller_disconnected(): + Global.main_menu() + func _draw(): pass @@ -19,7 +23,7 @@ func _peer_joined(joined_peers): for i in range(len(joined_peers)): var id = joined_peers[i]["id"] var name = joined_peers[i]["name"] - print("New Peer ", id, name) + print("New Lobby Peer ", id, name) peers.add_item("%s" % name) peers.set_item_metadata(peers.get_item_count() - 1, { "id": id }) diff --git a/multiplayer.gd b/multiplayer.gd index 150adc3..3625507 100644 --- a/multiplayer.gd +++ b/multiplayer.gd @@ -9,18 +9,18 @@ func _ready(): Global.client.signaller.connect("lobby_delete", self, "_lobby_delete") Global.client.signaller.connect("lobby_joined", self, "_lobby_joined") Global.client.signaller.connect("lobby_left", self, "_lobby_left") - Global.client.signaller.connect("websocket_connected", self, "_signaller_connected") - Global.client.connect_to_signaller() + Global.client.signaller.connect("websocket_disconnected", self, "_signaller_disconnected") + Global.client.signaller.request_lobby_list() -func _lobby_joined(id): +func _signaller_disconnected(): + Global.main_menu() + +func _lobby_joined(_id, _peerId): Global.lobby() func _lobby_left(_id): Global.lobby_browser() -func _signaller_connected(): - Global.client.signaller.request_lobby_list() - func _on_back_pressed(): Global.main_menu() diff --git a/multiplayer_client.gd b/multiplayer_client.gd index 1197195..908763d 100644 --- a/multiplayer_client.gd +++ b/multiplayer_client.gd @@ -15,16 +15,16 @@ onready var mp = WebRTCMultiplayer.new() onready var signaller = SignallerClient.new() func _ready(): - # connect("connected", self, "connected") - # connect("disconnected", self, "disconnected") + # signaller.connect("websocket_connected", self, "connected") + # signaller.connect("websocket_disconnected", self, "disconnected") - # connect("offer_received", self, "offer_received") - # connect("answer_received", self, "answer_received") - # connect("candidate_received", self, "candidate_received") + signaller.connect("offer_received", self, "offer_received") + signaller.connect("answer_received", self, "answer_received") + signaller.connect("candidate_received", self, "candidate_received") - # connect("lobby_joined", self, "lobby_joined") + signaller.connect("lobby_joined", self, "_lobby_joined") # connect("lobby_sealed", self, "lobby_sealed") - # connect("peer_connected", self, "peer_connected") + signaller.connect("peer_joined", self, "_peer_joined") # connect("peer_disconnected", self, "peer_disconnected") add_child(signaller) @@ -36,26 +36,30 @@ func connect_to_signaller(): close() signaller.connect_to_websocket_signaller(multiplayer_url) -func _create_peer(id): +func _create_peer(peer_id): var peer: WebRTCPeerConnection = WebRTCPeerConnection.new() - peer.initialize({"iceServers": webrtc_ice_servers}) - peer.connect("session_description_created", self, "_offer_created", [id]) - peer.connect("ice_candidate_created", self, "_new_ice_candidate", [id]) - mp.add_peer(peer, id) - if id > mp.get_unique_id(): - # TODO: peer.create_offer() - pass + print("New Net Peer %s with peer_id %d" % [peer, peer_id]) + var _peer_init_results = peer.initialize({"iceServers": webrtc_ice_servers}) + peer.connect("session_description_created", self, "_offer_created", [peer_id]) + peer.connect("ice_candidate_created", self, "_new_ice_candidate", [peer_id]) + mp.add_peer(peer, peer_id) + var uid = mp.get_unique_id() + if peer_id > uid: + var offer_result = peer.create_offer() + print("Create Offer Result: ", offer_result) + else: + print("Did not create offer: %d <= %d" % [peer_id, uid]) return peer -func _new_ice_candidate(mid_name, index_name, sdp_name, id): - print("New ICE Candidate: ", mid_name, index_name, sdp_name, id) - # TODO: send_candidate(id, mid_name, index_name, sdp_name) +func _new_ice_candidate(mid_name, index_name, sdp_name, peer_id): + print("New ICE Candidate: ", mid_name, index_name, sdp_name, peer_id) + signaller.send_candidate(peer_id, mid_name, index_name, sdp_name) -func _offer_created(type, data, id): - if not mp.has_peer(id): +func _offer_created(type, data, peer_id): + print("Offer Created", type, data, peer_id) + if not mp.has_peer(peer_id): return - print("created", type) - mp.get_peer(id).connection.set_local_description(type, data) + mp.get_peer(peer_id).connection.set_local_description(type, data) if type == "offer": # TODO: send_offer(id, data) pass @@ -63,28 +67,35 @@ func _offer_created(type, data, id): # TODO: send_answer(id, data) pass -func connected(id): - print("Connected %d" % id) - mp.initialize(id, true) +func _lobby_joined(id, peer_id): + print("Connected to lobby %s as peer %d" % [id, peer_id]) + mp.initialize(peer_id, true) -func peer_connected(id): - print("Peer connected %d" % id) - _create_peer(id) +func _peer_joined(peers): + for i in range(len(peers)): + var peer_id = peers[i]["peerId"] + if not mp.has_peer(peer_id): + _create_peer(peer_id) func peer_disconnected(id): + print("Peer disconnected ", id) if mp.has_peer(id): mp.remove_peer(id) -func offer_received(id, offer): - print("Got offer: %d" % id) +func offer_received(data): + print("Got offer: ", data) + var id = data["peerId"] if mp.has_peer(id): - mp.get_peer(id).connection.set_remote_description("offer", offer) + mp.get_peer(id).connection.set_remote_description("offer", data["offer"]) -func answer_received(id, answer): - print("Got answer: %d" % id) +func answer_received(data): + print("Got answer: ", data) + var id = data["peerId"] if mp.has_peer(id): - mp.get_peer(id).connection.set_remote_description("answer", answer) + mp.get_peer(id).connection.set_remote_description("answer", data["offer"]) -func candidate_received(id, mid, index, sdp): +func candidate_received(data): + print("Candidate Received: ", data) + var id = data["peerId"] if mp.has_peer(id): - mp.get_peer(id).connection.add_ice_candidate(mid, index, sdp) + mp.get_peer(id).connection.add_ice_candidate(data["mid"], data["index"], data["sdp"]) diff --git a/server.ts b/server.ts index 7cf6bdc..64b3b9e 100644 --- a/server.ts +++ b/server.ts @@ -13,7 +13,8 @@ interface DataMessage { type: string; } -type ServerData = Record | string; +type ServerDataObject = Record; +type ServerData = ServerDataObject | string; type Message = string | DataMessage; const broadcast = (message: Message) => @@ -32,6 +33,7 @@ const buildMessage = ( class Client { id: ID; + peerId: number | null; name: string; socket: WebSocket; lobby: Lobby | null; @@ -39,11 +41,10 @@ class Client { constructor(socket: WebSocket) { this.id = crypto.randomUUID(); this.socket = socket; + this.peerId = null; this.name = "Client"; this.lobby = null; - this.send(buildMessage("your-id", this.id)); - console.log(this); allClients.set(this.id, this); } @@ -78,8 +79,10 @@ class Client { clientList() { if (!this.lobby) return; - const netClients: { id: ID; name: string }[] = []; - this.lobby.clients.forEach(({ id, name }) => netClients.push({ id, name })); + const netClients: { id: ID; name: string; peerId: number | null }[] = []; + this.lobby.clients.forEach(({ id, name, peerId }) => + netClients.push({ id, name, peerId }) + ); // TODO: chunk async? this.send(buildMessage("peer_list", netClients)); } @@ -118,8 +121,10 @@ class Client { return; } this.lobby = lobby; - this.send(buildMessage("lobby_joined", { id: lobby.id })); lobby.addClient(this); + this.send( + buildMessage("lobby_joined", { id: lobby.id, peerId: this.peerId }), + ); } lobbyLeave() { @@ -128,6 +133,7 @@ class Client { this.send(`[info] cannot leave lobby (not in a lobby)`); return; } + this.peerId = null; this.lobby = null; if (this.isConnected()) { this.send(buildMessage("lobby_left", { id: leavingLobby.id })); @@ -149,6 +155,7 @@ class Lobby { this.name = name || this.id; allLobbies.set(this.id, this); + host.peerId = 1; host.lobbyJoin(this); allClients.forEach((client) => client.lobbyNew(this)); } @@ -166,10 +173,19 @@ class Lobby { } addClient(client: Client) { - this.clients.set(client.id, client); + if (!client.peerId) { + const arr = new Int32Array(1); + crypto.getRandomValues(arr); + client.peerId = Math.abs(arr[0]); + } this.broadcast( - buildMessage("peer_joined", { id: client.id, name: client.name }), + buildMessage("peer_joined", { + id: client.id, + name: client.name, + peerId: client.peerId, + }), ); + this.clients.set(client.id, client); } removeClient({ id }: Client) { @@ -182,10 +198,16 @@ class Lobby { } } +interface ClientMessage { + type: "candidate" | "offer" | "answer"; + data: ServerDataObject; +} + // events function onMessage(client: Client, ev: MessageEvent) { // TODO: log who from? console.log("Client Message Received", ev.data); + if (ev.data === "init") client.send(buildMessage("your_id", client.id)); if (ev.data === "lobby_create") client.lobbyCreate(); if (ev.data === "lobby_leave") client.lobbyLeave(); if (ev.data === "request_lobby_list") client.lobbyList(); @@ -196,6 +218,22 @@ function onMessage(client: Client, ev: MessageEvent) { if (lobby) client.lobbyJoin(lobby); else client.send(`[info] could not find lobby ${id}`); } + if (ev.data.startsWith("json:")) { + const data: ClientMessage = JSON.parse(ev.data.substr(5)); + if (data.type in ["candidate", "answer", "offer"]) { + if (typeof data.data === "object") { + const subdata = data.data; + if (typeof subdata["peerId"] === "number") { + const peerId: number = subdata["peerId"]; + const payload = Object.assign({}, data); + payload.data.peerId = client.peerId; + for (const client of allClients.values()) { + client.send(ev.data); + } + } + } + } + } } function onSocketOpen(_client: Client, _ev: Event) { diff --git a/signaller_client.gd b/signaller_client.gd index a720785..a71cea9 100644 --- a/signaller_client.gd +++ b/signaller_client.gd @@ -8,16 +8,21 @@ expected to fully setup the peer connections. signal lobby_new(lobbiesList) signal lobby_delete(id) -signal lobby_joined(id) +signal lobby_joined(id, peer_id) signal lobby_left(id) signal peer_joined(id) signal peer_left(id) +signal candidate_received(data) +signal offer_received(data) +signal answer_received(data) signal websocket_connected signal websocket_disconnected onready var ws: WebSocketClient = WebSocketClient.new() +onready var client_id = null onready var lobby_id = null +onready var peer_id = null func _ready(): var _result = ws.connect("data_received", self, "_parse_msg") @@ -45,6 +50,7 @@ func _connected(protocol = ""): print("WebSocket signaller connected via protocol ", protocol) ws.get_peer(1).set_write_mode(WebSocketPeer.WRITE_MODE_TEXT) emit_signal("websocket_connected") + _send("init") func _process(_delta: float): var status: int = ws.get_connection_status() @@ -78,89 +84,50 @@ func _parse_msg(): func handle_message(data: Dictionary): match data["type"]: + "your_id": client_id = data["data"] "lobby_new": emit_signal("lobby_new", [{"id": data["id"], "name": data["name"]}]) "lobby_delete": emit_signal("lobby_delete", data["id"]) "lobby_joined": lobby_id = data["id"] - emit_signal("lobby_joined", data["id"]) + peer_id = data["peerId"] + emit_signal("lobby_joined", data["id"], data["peerId"]) "lobby_left": lobby_id = null emit_signal("lobby_left", data["id"]) "lobby_list": emit_signal("lobby_new", data["data"]) "peer_list": emit_signal("peer_joined", data["data"]) - "peer_joined": emit_signal("peer_joined", [{"id": data["id"], "name": data["name"]}]) + "peer_joined": emit_signal("peer_joined", [{"id": data["id"], "name": data["name"], "peerId": data["peerId"]}]) "peer_left": emit_signal("peer_left", data["id"]) + "candidate": + print("Candidate received ", data["data"]) + emit_signal("candidate_received", data["data"]) + "offer": + print("Offer received ", data["data"]) + emit_signal("offer_received", data["data"]) + "answer": + print("Answer received ", data["data"]) + emit_signal("answer_received", data["data"]) _: print("Unhandled Data Message: ", JSON.print(data)) +func send_candidate(peerId, mid, index, sdp) -> int: + return _send("json:%s" % JSON.print({ + "type": "candidate", + "data": { + "peerId": peerId, + "mid": mid, + "index": index, + "sdp": sdp + } + })) + +func send_offer(peerId, offer) -> int: + return _send("json:%s" % JSON.print({ "type": "offer", "data": {"peerId": peerId, "offer": offer }})) + +func send_answer(peerId, answer) -> int: + return _send("json:%s" % JSON.print({ "type": "answer", "data": {"peerId": peerId, "answer": answer }})) + """ -func _parse_msg(): - var pkt_str: String = ws.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: "): - print("Emitting lobby_joined") - 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: "): - print("Emitting connected") - emit_signal("connected", src_id) - elif type.begins_with("N: "): - # Client connected - print("Emitting peer_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 - print("Emitting offer_received") - emit_signal("offer_received", src_id, req[1]) - elif type.begins_with("A: "): - # Answer received - print("Emitting 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 - print("Emitting candidate_received") - emit_signal("candidate_received", src_id, candidate[0], int(candidate[1]), candidate[2]) - -func join_lobby(joined_lobby): - return ws.get_peer(1).put_packet(("J: %s\n" % joined_lobby).to_utf8()) - -func seal_lobby(): - return ws.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 ws.get_peer(1).put_packet(("%s: %d\n%s" % [type, id, data]).to_utf8()) """