From 08eb9534a312f54e218eb25c0eda209f37ebba21 Mon Sep 17 00:00:00 2001 From: Daniel Flanagan Date: Tue, 7 Dec 2021 16:09:50 -0600 Subject: [PATCH] Cleanup --- default_env.tres => assets/default_env.tres | 0 .../fonts/iosevkalyte/iosevkalyte.tres | 0 assets/img/check.png | Bin 0 -> 2251 bytes assets/img/check.png.import | 35 +++++ assets/img/cross.png | Bin 0 -> 3256 bytes assets/img/cross.png.import | 35 +++++ icon.png => assets/img/icon.png | Bin icon.png.import => assets/img/icon.png.import | 7 +- theme.tres => assets/theme.tres | 2 +- project.godot | 8 +- game.tscn => screens/game.tscn | 2 +- lobby.tscn => screens/lobby.tscn | 71 +++++++-- main.tscn => screens/main.tscn | 4 +- multiplayer.tscn => screens/multiplayer.tscn | 4 +- screens/test.tscn | 18 +++ game.gd => scripts/game.gd | 0 global.gd => scripts/global.gd | 0 lobby.gd => scripts/lobby.gd | 68 ++++++--- main.gd => scripts/main.gd | 0 multiplayer.gd => scripts/multiplayer.gd | 32 +++-- .../multiplayer_client.gd | 4 +- .../signaller_client.gd | 22 ++- server.ts | 136 +++++++++++++++--- 23 files changed, 365 insertions(+), 83 deletions(-) rename default_env.tres => assets/default_env.tres (100%) rename iosevkalyte.tres => assets/fonts/iosevkalyte/iosevkalyte.tres (100%) create mode 100644 assets/img/check.png create mode 100644 assets/img/check.png.import create mode 100644 assets/img/cross.png create mode 100644 assets/img/cross.png.import rename icon.png => assets/img/icon.png (100%) rename icon.png.import => assets/img/icon.png.import (68%) rename theme.tres => assets/theme.tres (50%) rename game.tscn => screens/game.tscn (90%) rename lobby.tscn => screens/lobby.tscn (66%) rename main.tscn => screens/main.tscn (96%) rename multiplayer.tscn => screens/multiplayer.tscn (95%) create mode 100644 screens/test.tscn rename game.gd => scripts/game.gd (100%) rename global.gd => scripts/global.gd (100%) rename lobby.gd => scripts/lobby.gd (62%) rename main.gd => scripts/main.gd (100%) rename multiplayer.gd => scripts/multiplayer.gd (70%) rename multiplayer_client.gd => scripts/multiplayer_client.gd (97%) rename signaller_client.gd => scripts/signaller_client.gd (90%) diff --git a/default_env.tres b/assets/default_env.tres similarity index 100% rename from default_env.tres rename to assets/default_env.tres diff --git a/iosevkalyte.tres b/assets/fonts/iosevkalyte/iosevkalyte.tres similarity index 100% rename from iosevkalyte.tres rename to assets/fonts/iosevkalyte/iosevkalyte.tres diff --git a/assets/img/check.png b/assets/img/check.png new file mode 100644 index 0000000000000000000000000000000000000000..e2199aef32adaa63c2aa7373ec68e3b9f8485a1e GIT binary patch literal 2251 zcmV;+2sHPJP)EX>4Tx04R}tkv&MmKpe$iQ>7vm2Q!E`WT;LS{6ZYH3Pq?8YK2xEOfLO`CWa)% z#ZhoAIQX$xb#QUk)xlK|1V2FB+?*6$q{ROvg%&X$9QWhhy~o`5#sAzoM#1}`*ZYYd7A+tiFk&YmP5QwJhkbV zocD=ER#G(LbK((;E=c^yb=BuL&P9&}o+(PSTuWmMoGPOCwRjWq2i0{jD>UnZAIt}+-o z=CJ`CisuLagWuiSg^6)LDVhK}UtITN2#D+gjhgFzAG>bj1PDI^S4PXfQ3s|!Nw2lE z*b&gX4P0EeG<6TS+yVNZOvO?>DNIu+6oB_L`lcMvcMEi{`n@&xaryvcsH@ZsaBv6= z7b$x!;N4yAz5RQp+20SCSaP-~rd==q000JJOGiWi{{a60|De66lK=n!32;bRa{vG? zBLDy{BLR4&KXw2B00(qQO+^Rg3=VsqD;6$~V6_CEXUbMCn#v{q}iR%^9ZYqeHu z{ZE(kMWJzL%kelF_sb^0102(p&?`VmR}m;yBf@D?;0ACz&;{HAI)OOg0_VVqt}`GH zl!AyzG-1S(0^JAn0)s#r=utq6z#gy*Z0hwuq!gP|0Uq$UqTCCN0As*}e(nWQ!jl8G zfHhzdcn_?Lgd9$$)LaViz$X;puYgCu*T57oE+uqJ8IB@-2>hkb#6(ISDB{k#b}A&A z0DK#88+ZUb1)c$q1^%|uAz5L7{UQZm2l%A4I0XvLp#UFzpCbMO_zw6+^6%E~V*#lg z2kyuphV&gK4ICL|0~6qZZ&xIzff-#-1nQRV-3jrrxX5YOYpB;H8Wem7a947F0sNrI z4_f6e43sOk-SOZFu@@{LJ=2fh;+klbg0m&*Bm5mO8JGhko2zX@!sCn#2ukZ3UQ zUBEE#y(0foZr>~Lu@LaCB3uBnGQK6?4X~_#p91;HW+>4h;9~+mBJeX7__T642)wH+ zFR33W>MOuoU|vLg5&@;gO96K_E>jzo>|ct2Db?}z5VJFKH+BD`YX1uGhhA4C|6`yK z)CxNd06rz~FRaKvROH*N`d$gXpr~%C+gq&Y{I;ct(=hP$CBO$i4m<~D1^#}G;7@=J zN&OeSE-K%TRRs!wTWvR(s3Z6;H8jsG@DC*WYT)w%xF+Cpz$@kYo>IcCWfo8$0zAX( zklQ~I_!%qm$q?`*fjzHv<12IQ-lWuYLsh?e3s*-i)w&E|C7Nnw{TN_jjZUnu`GMJJX8( zV|9Cd1W#ve*I!oj7gg)8wcK645%MBwu9!7HaIG0!`L?rsqH9iYhAwD$&IEW7SP&7% zH{tlT5#S#=o6}b_TQ}xav}SPUxU>0x2M^xoOw%(d6@(d|X?S)8+6*-IsW`ofj)3w* z$dsnBcLbtCMC28^%jvVm_*O+`i3nGs!n`^Z@p~#?(~9I>3k*_0 zR&w1^kG`Sz1}Hzs2#daHcNc+o`kq{r(f0``mwumiZO^rBH3yOHl%K#B@Snukw}z3O z_-FRXDrIj+xxb*nMm8$r3sQhNgYQ!0#zewBtAte^Ot|{{9{tUzyuloDbXJM}snX`1 zq#vpARZD@SI=HlqFGcvNjHwoJeXbM#05V4S8D3F<*Q9`J-{M9+5V8A(c*XlcQ=T)G zzBT#A5dqKcM73f8b@{|9Z{~oZWHmsw5D^C6$bU-)xE*!b3sZn;Nwd1C50Ym}zwe>^ zXgXFU%GFju5#NyX_v;ybp8zN0;?A1r7a+%) zPQ|dp^!%IpZnxe9IG5NCn=;pC?HA0(XTsY34dEYhI{1&Qyv3XCXVd`g-t@HRY6Mg# z#xzY`GRGm2Fd;&Q^gBo8bW;Sp)u?N$5%T}@2q-7R%ni&lyn{-OJ`q!r0ycH6%Vf{$ z(f40J0p+SN?Qk0Z5q8`Ck9Fm5a>`pD0{*LrWI&UZB- Ze*l@bVgfdft&IQx002ovPDHLkV1iJ3Cn5j< literal 0 HcmV?d00001 diff --git a/assets/img/check.png.import b/assets/img/check.png.import new file mode 100644 index 0000000..c8284f5 --- /dev/null +++ b/assets/img/check.png.import @@ -0,0 +1,35 @@ +[remap] + +importer="texture" +type="StreamTexture" +path="res://.import/check.png-77d9c6c18a6e8ab3e8f9b0823b5b1f3f.stex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://assets/img/check.png" +dest_files=[ "res://.import/check.png-77d9c6c18a6e8ab3e8f9b0823b5b1f3f.stex" ] + +[params] + +compress/mode=0 +compress/lossy_quality=0.7 +compress/hdr_mode=0 +compress/bptc_ldr=0 +compress/normal_map=0 +flags/repeat=0 +flags/filter=true +flags/mipmaps=false +flags/anisotropic=false +flags/srgb=2 +process/fix_alpha_border=true +process/premult_alpha=false +process/HDR_as_SRGB=false +process/invert_color=false +process/normal_map_invert_y=false +stream=false +size_limit=0 +detect_3d=true +svg/scale=1.0 diff --git a/assets/img/cross.png b/assets/img/cross.png new file mode 100644 index 0000000000000000000000000000000000000000..c026e68b37be4fac7b1a777d276455f443ba2019 GIT binary patch literal 3256 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 %s joined the lobby" % name) - peers.add_item("%s%s" % [name, _host_suffix(peerId)]) - peers.set_item_metadata(peers.get_item_count() - 1, { "id": id, "peerId": peerId, "name": name }) + for peer_index in range(len(joined_peers)): + var peer = joined_peers[peer_index] + var id = peer["id"] + var peer_text = "%s%s" % [peer["name"], _host_suffix(peer["peerId"])] + if peers_map.has(id): + var i = peers_map[id] + var old_peer = peers.get_item_metadata(i) + peers.set_item_metadata(i, peer) + # announce changes? + else: + call_deferred("add_chat", "> %s joined the lobby" % peer["name"]) + var ready = false + var tex = not_ready_tex + if peerId == 1: + ready = true + tex = ready_tex + peers.add_item(peer_text, tex) + var i = peers.get_item_count() - 1 + peers.set_item_metadata(i, peer) + peers_map[id] = i + if !ready: can_start = false func _peer_left(ids): call_deferred("update_player_count") @@ -64,7 +87,12 @@ func update_player_count(): peers_list_label.text = "Players: %d" % peers.get_item_count() func _on_Button_pressed(): - send_chat_message() + send_chkkkkkkat_message() + +func _on_ready_up_toggled(button_pressed: bool): + +func _on_lobby_info_text_changed(new_text: String): + Global.client.signaller.set_lobby_name(new_text) remotesync func add_chat(message): if auto_scroll_chk.pressed: call_deferred("scroll_chat_to_bottom") diff --git a/main.gd b/scripts/main.gd similarity index 100% rename from main.gd rename to scripts/main.gd diff --git a/multiplayer.gd b/scripts/multiplayer.gd similarity index 70% rename from multiplayer.gd rename to scripts/multiplayer.gd index 0fadb3e..f8cae5a 100644 --- a/multiplayer.gd +++ b/scripts/multiplayer.gd @@ -9,6 +9,8 @@ onready var join_button = $v/head/join onready var lobbies_label = $v/subhead/label onready var display_name_edit = $v/subhead/display_name +onready var lobbies_map = {} + func _ready(): display_name_edit.text = Global.client.signaller.display_name join_button.disabled = true @@ -44,13 +46,21 @@ func _on_join_pressed(): Global.client.signaller.join_lobby(lobbies.get_item_metadata(items[0])["id"]) func _lobby_new(new_lobbies): - for i in range(len(new_lobbies)): - var id = new_lobbies[i]["id"] - var name = new_lobbies[i]["name"] - print("New Lobby ", id, name) - # TODO: could keep an index of IDs and indexes - lobbies.add_item(name) - lobbies.set_item_metadata(lobbies.get_item_count() - 1, { "id": id }) + # TODO: handle scrolling so that the user is never too jarred (like chat) + for lobby_index in range(len(new_lobbies)): + var lobby = new_lobbies[lobby_index] + var id = lobby["id"] + var lobby_text = "%s (%d/%d players)" % [lobby["name"], lobby["currentPlayers"], lobby["maxPlayers"]] + if lobbies_map.has(id): + print("Updated Lobby ", lobby) + var i = lobbies_map[id] + lobbies.set_item_metadata(i, lobby) + lobbies.set_item_text(i, lobby_text) + else: + print("New Lobby ", lobby) + lobbies.add_item(lobby_text) + lobbies_map[id] = lobbies.get_item_count() - 1 + lobbies.set_item_metadata(lobbies_map[id], lobby) if Global.join_first_available_lobby: Global.join_first_available_lobby = false @@ -58,10 +68,10 @@ func _lobby_new(new_lobbies): lobbies_label.text = "Active Lobbies: %d" % lobbies.get_item_count() func _lobby_delete(id): - for i in range(lobbies.get_item_count()): - if id == lobbies.get_item_metadata(i)["id"]: - lobbies.remove_item(i) - return + if lobbies_map.has(id): + var i = lobbies_map[id] + # TODO: this will break our current mappings due to re-order + lobbies.remove_item(i) lobbies_label.text = "Active Lobbies: %d" % lobbies.get_item_count() func _on_lobbies_item_activated(_index): diff --git a/multiplayer_client.gd b/scripts/multiplayer_client.gd similarity index 97% rename from multiplayer_client.gd rename to scripts/multiplayer_client.gd index 05fcd22..4dac680 100644 --- a/multiplayer_client.gd +++ b/scripts/multiplayer_client.gd @@ -4,8 +4,8 @@ extends Node This module sets up WebRTC peer connections. """ -var multiplayer_url = "wss://webrtc-signaller.deno.dev:443" -# var multiplayer_url = "ws://localhost:8888" +# var multiplayer_url = "wss://webrtc-signaller.deno.dev:443" +var multiplayer_url = "ws://localhost:8888" # var multiplayer_url = "ws://echo.websocket.org" var webrtc_ice_servers = [ { "urls": ["stun:stun.l.google.com:19302"] }, diff --git a/signaller_client.gd b/scripts/signaller_client.gd similarity index 90% rename from signaller_client.gd rename to scripts/signaller_client.gd index b36c49d..d606af3 100644 --- a/signaller_client.gd +++ b/scripts/signaller_client.gd @@ -28,6 +28,8 @@ onready var peer_id = null onready var display_name = "UnnamedPlayer" onready var lobby_name = null +func is_host(): return peer_id == 1 + const DISPLAY_NAME_FILE = "user://display_name.txt" func _ready(): @@ -104,6 +106,18 @@ func set_display_name(new_display_name: String): func _send(s: String): return ws.get_peer(1).put_packet(s.to_utf8()) +func set_lobby_name(s: String): + if is_host(): + _send("json:%s" % JSON.print({"type": "update_lobby", "data": {"name": s}})) + +func lock_lobby(): + if is_host(): + _send("json:%s" % JSON.print({"type": "update_lobby", "data": {"locked": true}})) + +func set_lobby_max_players(n: int): + if is_host(): + _send("json:%s" % JSON.print({"type": "update_lobby", "data": {"maxPlayers": n}})) + func _parse_msg(): var msg: String = ws.get_peer(1).get_packet().get_string_from_utf8() if msg.begins_with("json:"): @@ -124,7 +138,7 @@ func handle_message(data: Dictionary): emit_signal("peer_id_set", peer_id) "lobby_list": emit_signal("lobby_new", data["data"]) - "lobby_new": emit_signal("lobby_new", [{"id": data["id"], "name": data["name"]}]) + "lobby_new": emit_signal("lobby_new", [data]) "lobby_delete": emit_signal("lobby_delete", data["id"]) "lobby_joined": @@ -139,10 +153,8 @@ func handle_message(data: Dictionary): "peer_joined": print(typeof(data), data) - if data.get("id") != null: - emit_signal("peer_joined", [{"id": data["id"], "name": data["name"], "peerId": data["peerId"]}]) - else: - emit_signal("peer_joined", data["data"]) + if data.get("id") != null: emit_signal("peer_joined", [data]) + else: emit_signal("peer_joined", data["data"]) "peer_left": print("Peer Left: %s" % data) emit_signal("peer_left", data["data"]) diff --git a/server.ts b/server.ts index 6f50384..2258dbc 100644 --- a/server.ts +++ b/server.ts @@ -1,6 +1,6 @@ // import { randomInt } from "./deps.ts"; -const SERVER_VERSION = "0.1.0"; +const SERVER_VERSION = "0.2.0"; // TODO: version comparison type ID = string; @@ -14,8 +14,11 @@ interface DataMessage { type: string; } -type ServerDataObject = Record; -type ServerData = ServerDataObject[] | ServerDataObject | string; +type ServerDataObject = Record< + string, + string | number | symbol | null | boolean +>; +type ServerData = ServerDataObject[] | ServerDataObject | string | boolean; type Message = string | DataMessage; const buildMessage = ( @@ -35,6 +38,7 @@ class Client { name: string; socket: WebSocket; lobby: Lobby | null; + ready: boolean; constructor(socket: WebSocket) { this.id = crypto.randomUUID(); @@ -42,6 +46,7 @@ class Client { this.peerId = null; this.name = "Client"; this.lobby = null; + this.ready = false; allClients.set(this.id, this); } @@ -50,6 +55,13 @@ class Client { return this.socket.readyState == WebSocket.OPEN; } + setReady(ready: boolean) { + this.ready = ready; + this.lobby?.broadcast( + buildMessage("ready_change", { id: this.id, ready: this.ready }), + ); + } + remove() { this.lobbyLeave(); if (this.isConnected()) { @@ -83,25 +95,54 @@ class Client { peerList() { if (!this.lobby) return; - const peers: { id: ID; name: string; peerId: number | null }[] = []; - this.lobby.clients.forEach(({ id, name, peerId }) => - peers.push({ id, name, peerId }) + const peers: { + id: ID; + name: string; + peerId: number | null; + ready: boolean; + }[] = []; + this.lobby.clients.forEach(({ id, name, peerId, ready }) => + peers.push({ id, name, peerId, ready }) ); this.send(buildMessage("peer_joined", peers)); // TODO: chunk async? } lobbyList() { - const netLobbies: { id: ID; name: string }[] = []; - allLobbies.forEach(({ id, name }) => netLobbies.push({ id, name })); + const netLobbies: { + id: ID; + name: string; + maxPlayers: number; + locked: boolean; + currentPlayers: number; + }[] = []; + allLobbies.forEach((lobby) => { + const { id, name, maxPlayers, locked } = lobby; + netLobbies.push({ + id, + name, + maxPlayers, + locked, + currentPlayers: lobby.clients.size, + }); + }); // TODO: chunk async? this.send(buildMessage("lobby_list", netLobbies)); } - lobbyNew({ id, name }: Lobby) { - // if the client is already in a lobby, we don't care about new lobbies + lobbyNew(lobby: Lobby) { + // if the client is already in a lobby, they don't care about new lobbies if (this.lobby) return; - this.send(buildMessage("lobby_new", { id, name })); + const { id, name, maxPlayers, locked } = lobby; + this.send( + buildMessage("lobby_new", { + id, + name, + maxPlayers, + locked, + currentPlayers: lobby.clients.size, + }), + ); } lobbyDelete({ id }: Lobby) { @@ -109,14 +150,14 @@ class Client { this.send(buildMessage("lobby_delete", { id })); } - lobbyCreate(name?: string) { + lobbyCreate(opts?: Partial) { if (this.lobby) { this.send( `[info] cannot create lobby (already in lobby ${this.lobby.id})`, ); return; } - new Lobby(this, name || `${this.name}'s lobby`); + new Lobby(this, opts?.name || `${this.name}'s lobby`, opts?.maxPlayers); } lobbyJoin(lobby: Lobby) { @@ -155,16 +196,54 @@ class Lobby { name: string; clients: Map; hostClientId: ID; + maxPlayers: number; + locked: boolean; - constructor(host: Client, name?: string) { + constructor(host: Client, name?: string, maxPlayers = 20) { this.id = crypto.randomUUID(); this.hostClientId = host.id; this.clients = new Map(); this.name = name || this.id; + this.maxPlayers = maxPlayers; + this.locked = false; allLobbies.set(this.id, this); host.peerId = 1; host.lobbyJoin(this); + this.notify(); + } + + update( + requestor: Client, + newValues: { name?: string; maxPlayers?: number; locked?: boolean }, + ) { + if (requestor.peerId === 1) { + for (const k in newValues) { + switch (k) { + case "name": + if (newValues?.name) this.name = newValues.name; + break; + case "maxPlayers": + if ( + newValues?.maxPlayers && typeof newValues.maxPlayers === "number" + ) { + this.maxPlayers = newValues.maxPlayers; + } + break; + case "locked": + if (newValues?.locked) this.locked = newValues.locked; + break; + } + } + this.notify(); + } else { + requestor.send( + "[info] you cannot update this lobby (you are not the host!)", + ); + } + } + + notify() { allClients.forEach((client) => client.lobbyNew(this)); } @@ -204,6 +283,7 @@ class Lobby { ), ); this.clients.set(client.id, client); + this.notify(); } removeClient({ id }: Client) { @@ -213,11 +293,18 @@ class Lobby { console.warn("Host left!"); this.remove(); } + this.notify(); } } interface ClientMessage { - type: "candidate" | "offer" | "answer" | "init" | "lobby_create"; + type: + | "candidate" + | "offer" + | "answer" + | "init" + | "lobby_create" + | "update_lobby"; data: ServerDataObject; } @@ -228,9 +315,18 @@ function onMessage(client: Client, ev: MessageEvent) { if (msg === "pong") return; console.log("Client Message Received", msg); if (msg === "init") { - client.send(buildMessage("init", { id: client.id, name: client.name })); + client.send( + buildMessage("init", { + id: client.id, + name: client.name, + serverVersion: SERVER_VERSION, + }), + ); } if (msg === "lobby_create") client.lobbyCreate(); + if (msg.startsWith("set_ready:")) { + client.setReady(JSON.parse(msg.split(":", 2)[1])); + } if (msg === "lobby_leave") client.lobbyLeave(); if (msg === "request_lobby_list") client.lobbyList(); if (msg === "request_peer_list") { @@ -259,8 +355,14 @@ function onMessage(client: Client, ev: MessageEvent) { client.name = data.data.name.toString(); } client.send(buildMessage("init", { id: client.id, name: client.name })); + } else if (data.type === "update_lobby") { + if (client.lobby) { + client.lobby.update(client, data["data"]); + } else { + client.send("[info] cannot update lobby (you're not even in one!)"); + } } else if (data.type === "lobby_create") { - client.lobbyCreate(data.data?.name?.toString()); + client.lobbyCreate({ name: data.data?.name?.toString() }); if (data.data.name) { client.name = data.data.name.toString(); }