diff --git a/lobby.gd b/lobby.gd index d14b399..8049520 100644 --- a/lobby.gd +++ b/lobby.gd @@ -2,26 +2,39 @@ extends MarginContainer onready var peers = $v/body/peers/peers onready var lobby_info_label = $v/head/lobby_info +onready var peers_list_label = $v/body/peers/label onready var cursors = {} +onready var chat = $v/body/v/messages +onready var chat_edit = $v/body/v/h/chat func _ready(): Global.client.signaller.connect("peer_joined", self, "_peer_joined") Global.client.signaller.connect("peer_left", self, "_peer_left") + Global.client.connect("peer_disconnected", self, "_peer_left") Global.client.signaller.connect("lobby_left", self, "_lobby_left") - lobby_info_label.text = "%s (%s)" % [Global.client.signaller.lobby_id, get_tree().get_network_unique_id()] + lobby_info_label.text = "%s%s" % [Global.client.signaller.lobby_name, _host_suffix(get_tree().get_network_unique_id())] Global.client.signaller.connect("websocket_disconnected", self, "_signaller_disconnected") Global.client.signaller.request_peer_list() + call_deferred("add_chat", "# Connected to %s" % Global.client.signaller.lobby_name) + +func _host_suffix(id): + if id == 1: + return " (Host)" + else: + return "" func _signaller_disconnected(): Global.main_menu() func _peer_joined(joined_peers): + call_deferred("update_player_count") print("Joined Peers: %s" % [joined_peers]) for i in range(len(joined_peers)): var id = joined_peers[i]["id"] var exists = false for j in range(peers.get_item_count()): - if id == peers.get_item_metadata(j)["id"]: + var md = peers.get_item_metadata(j) + if md and md.has("id") and md["id"] == id: print("Already have this one") exists = true break @@ -29,34 +42,41 @@ func _peer_joined(joined_peers): var peerId = joined_peers[i]["peerId"] var name = joined_peers[i]["name"] print("New Lobby Peer ID: %s, Name: %s, PeerID: %s" % [id, name, peerId]) - peers.add_item("%s (%s)" % [name, peerId]) - peers.set_item_metadata(peers.get_item_count() - 1, { "id": id }) - + call_deferred("add_chat", "> %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 }) + func _peer_left(ids): + call_deferred("update_player_count") + print("Peer(s) Leaving: %s" % ids) for data in ids: + print(data) var id = data["id"] for i in range(peers.get_item_count()): - if id == peers.get_item_metadata(i)["id"]: + var md = peers.get_item_metadata(i) + if md and md.has("id") and md["id"] == id: peers.remove_item(i) + call_deferred("add_chat", "< %s left the lobby" % md["name"]) return + +func update_player_count(): + peers_list_label.text = "Players: %d" % peers.get_item_count() func _on_Button_pressed(): - rpc("shift_cursor") - # move cursor a bit + send_chat_message() -remotesync func shift_cursor(): - print("Shifting cursor...") - $shifter.rect_position.x += 10 - -func do_ping(): - print(rpc("ping")) - -remotesync func ping(): - print("pinged") - return 0 +remotesync func add_chat(message): + chat.add_item(message) func _lobby_left(_id): Global.lobby_browser() func _on_leave_button_pressed(): Global.lobby_browser() + +func _on_TextEdit_text_entered(_new_text): + send_chat_message() + +func send_chat_message(): + rpc("add_chat", "%s: %s" % [Global.client.signaller.display_name, chat_edit.text]) + chat_edit.text = "" diff --git a/lobby.tscn b/lobby.tscn index d2ea1a9..d735c52 100644 --- a/lobby.tscn +++ b/lobby.tscn @@ -28,6 +28,7 @@ margin_bottom = 50.0 margin_right = 150.0 margin_bottom = 50.0 rect_min_size = Vector2( 150, 50 ) +hint_tooltip = "Multiplayer Lobby List" text = "Leave" __meta__ = { "_edit_use_anchors_": false @@ -53,45 +54,71 @@ size_flags_horizontal = 3 size_flags_vertical = 3 [node name="peers" type="VBoxContainer" parent="v/body"] -margin_right = 128.0 +margin_right = 180.0 margin_bottom = 506.0 [node name="label" type="Label" parent="v/body/peers"] -margin_right = 128.0 +margin_right = 180.0 margin_bottom = 21.0 -text = "Players in Lobby" +text = "Players: 1" __meta__ = { "_edit_use_anchors_": false } [node name="peers" type="ItemList" parent="v/body/peers"] margin_top = 25.0 -margin_right = 128.0 +margin_right = 180.0 margin_bottom = 506.0 +rect_min_size = Vector2( 180, 0 ) size_flags_horizontal = 3 size_flags_vertical = 3 auto_height = true [node name="v" type="VBoxContainer" parent="v/body"] -margin_left = 132.0 +margin_left = 184.0 margin_right = 984.0 margin_bottom = 506.0 size_flags_horizontal = 3 size_flags_vertical = 3 [node name="label" type="Label" parent="v/body/v"] -margin_right = 852.0 +margin_right = 800.0 margin_bottom = 21.0 text = "Chat" [node name="messages" type="ItemList" parent="v/body/v"] margin_top = 25.0 -margin_right = 852.0 -margin_bottom = 506.0 +margin_right = 800.0 +margin_bottom = 471.0 size_flags_horizontal = 3 size_flags_vertical = 3 __meta__ = { "_edit_use_anchors_": false } +[node name="h" type="HBoxContainer" parent="v/body/v"] +margin_top = 475.0 +margin_right = 800.0 +margin_bottom = 506.0 +__meta__ = { +"_edit_use_anchors_": false +} + +[node name="chat" type="LineEdit" parent="v/body/v/h"] +margin_right = 688.0 +margin_bottom = 31.0 +size_flags_horizontal = 3 +size_flags_vertical = 3 +clear_button_enabled = true +placeholder_text = "Compose a chat message here, press Enter to send" + +[node name="Button" type="Button" parent="v/body/v/h"] +margin_left = 692.0 +margin_right = 800.0 +margin_bottom = 31.0 +size_flags_vertical = 3 +text = "Send Message" + [connection signal="pressed" from="v/head/leave_button" to="." method="_on_leave_button_pressed"] +[connection signal="text_entered" from="v/body/v/h/chat" to="." method="_on_TextEdit_text_entered"] +[connection signal="pressed" from="v/body/v/h/Button" to="." method="_on_Button_pressed"] diff --git a/main.tscn b/main.tscn index 5512c3c..a3ea0df 100644 --- a/main.tscn +++ b/main.tscn @@ -34,6 +34,7 @@ alignment = 1 margin_right = 325.0 margin_bottom = 50.0 rect_min_size = Vector2( 150, 50 ) +hint_tooltip = "Yeah, let's get out of here." size_flags_horizontal = 3 size_flags_vertical = 3 theme = ExtResource( 3 ) @@ -47,6 +48,7 @@ margin_left = 329.0 margin_right = 654.0 margin_bottom = 50.0 rect_min_size = Vector2( 150, 50 ) +hint_tooltip = "This is what you came for!" size_flags_horizontal = 3 size_flags_vertical = 3 theme = ExtResource( 3 ) @@ -57,9 +59,13 @@ margin_left = 658.0 margin_right = 984.0 margin_bottom = 50.0 rect_min_size = Vector2( 150, 50 ) +hint_tooltip = "This is a multiplayer tech demo, you dunce. + +And no, you may not ask why the button is here." size_flags_horizontal = 3 size_flags_vertical = 3 theme = ExtResource( 3 ) +disabled = true text = "Singleplayer" __meta__ = { "_edit_use_anchors_": false @@ -73,34 +79,38 @@ size_flags_horizontal = 3 size_flags_vertical = 3 [node name="VBoxContainer" type="VBoxContainer" parent="VBoxContainer/Container"] -margin_left = 282.0 -margin_top = 196.0 -margin_right = 702.0 -margin_bottom = 310.0 +margin_left = 124.0 +margin_top = 150.0 +margin_right = 859.0 +margin_bottom = 356.0 [node name="Label" type="Label" parent="VBoxContainer/Container/VBoxContainer"] -margin_right = 420.0 -margin_bottom = 89.0 +margin_right = 735.0 +margin_bottom = 181.0 custom_fonts/font = SubResource( 1 ) -text = "Wizard Farts" +text = "Godot HTML5 WebRTC +Multiplayer Tech Demo" +align = 1 +valign = 1 [node name="HBoxContainer" type="HBoxContainer" parent="VBoxContainer/Container/VBoxContainer"] -margin_top = 93.0 -margin_right = 420.0 -margin_bottom = 114.0 +margin_top = 185.0 +margin_right = 735.0 +margin_bottom = 206.0 alignment = 1 [node name="Label" type="Label" parent="VBoxContainer/Container/VBoxContainer/HBoxContainer"] -margin_left = 104.0 -margin_right = 248.0 +margin_left = 305.0 +margin_right = 369.0 margin_bottom = 21.0 -text = "made with love at " +text = "made by " [node name="LinkButton" type="LinkButton" parent="VBoxContainer/Container/VBoxContainer/HBoxContainer"] -margin_left = 252.0 -margin_right = 316.0 +margin_left = 373.0 +margin_right = 429.0 margin_bottom = 21.0 -text = "lyte.dev" +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_CreateLobbyButton_pressed"] diff --git a/multiplayer.gd b/multiplayer.gd index f251d96..0fadb3e 100644 --- a/multiplayer.gd +++ b/multiplayer.gd @@ -7,8 +7,10 @@ onready var is_loaded = false onready var lobbies = $v/body/lobbies onready var join_button = $v/head/join onready var lobbies_label = $v/subhead/label +onready var display_name_edit = $v/subhead/display_name func _ready(): + display_name_edit.text = Global.client.signaller.display_name join_button.disabled = true Global.client.signaller.connect("lobby_new", self, "_lobby_new") Global.client.signaller.connect("lobby_delete", self, "_lobby_delete") @@ -67,3 +69,6 @@ func _on_lobbies_item_activated(_index): func _on_lobbies_item_selected(_index): join_button.disabled = false + +func _on_display_name_text_changed(new_text): + Global.client.signaller.set_display_name(new_text) diff --git a/multiplayer.tscn b/multiplayer.tscn index 477bbe2..e169320 100644 --- a/multiplayer.tscn +++ b/multiplayer.tscn @@ -40,6 +40,7 @@ margin_bottom = 50.0 grow_horizontal = 2 grow_vertical = 2 rect_min_size = Vector2( 150, 50 ) +hint_tooltip = "Main Menu" size_flags_horizontal = 3 size_flags_vertical = 3 text = "Back" @@ -52,6 +53,7 @@ margin_left = 329.0 margin_right = 654.0 margin_bottom = 50.0 rect_min_size = Vector2( 150, 50 ) +hint_tooltip = "Create your own public lobby that any player may join" size_flags_horizontal = 3 size_flags_vertical = 3 text = "Create Lobby" @@ -64,6 +66,7 @@ margin_left = 658.0 margin_right = 984.0 margin_bottom = 50.0 rect_min_size = Vector2( 150, 50 ) +hint_tooltip = "Join the highlighted lobby ID below" size_flags_horizontal = 3 size_flags_vertical = 3 text = "Join" @@ -74,15 +77,25 @@ __meta__ = { [node name="subhead" type="HBoxContainer" parent="v"] margin_top = 54.0 margin_right = 984.0 -margin_bottom = 75.0 +margin_bottom = 85.0 [node name="label" type="Label" parent="v/subhead"] -margin_right = 136.0 -margin_bottom = 21.0 +margin_right = 830.0 +margin_bottom = 31.0 +size_flags_horizontal = 3 +size_flags_vertical = 3 text = "Active Lobbies: 0" +valign = 1 + +[node name="display_name" type="LineEdit" parent="v/subhead"] +margin_left = 834.0 +margin_right = 984.0 +margin_bottom = 31.0 +rect_min_size = Vector2( 150, 0 ) +placeholder_text = "Your Name Here" [node name="body" type="ScrollContainer" parent="v"] -margin_top = 79.0 +margin_top = 89.0 margin_right = 984.0 margin_bottom = 560.0 size_flags_horizontal = 3 @@ -90,7 +103,8 @@ size_flags_vertical = 3 [node name="lobbies" type="ItemList" parent="v/body"] margin_right = 984.0 -margin_bottom = 481.0 +margin_bottom = 471.0 +hint_tooltip = "You can double-click a lobby listing to join it." size_flags_horizontal = 3 size_flags_vertical = 3 same_column_width = true @@ -101,6 +115,7 @@ __meta__ = { [connection signal="pressed" from="v/head/back" to="." method="_on_back_pressed"] [connection signal="pressed" from="v/head/create_lobby" to="." method="_on_create_lobby_pressed"] [connection signal="pressed" from="v/head/join" to="." method="_on_join_pressed"] +[connection signal="text_changed" from="v/subhead/display_name" to="." method="_on_display_name_text_changed"] [connection signal="item_activated" from="v/body/lobbies" to="." method="_on_lobbies_item_activated"] [connection signal="item_selected" from="v/body/lobbies" to="." method="_on_lobbies_item_selected"] [connection signal="nothing_selected" from="v/body/lobbies" to="." method="_on_lobbies_nothing_selected"] diff --git a/multiplayer_client.gd b/multiplayer_client.gd index c2fb433..4dac680 100644 --- a/multiplayer_client.gd +++ b/multiplayer_client.gd @@ -18,17 +18,16 @@ onready var mp = WebRTCMultiplayer.new() onready var signaller = SignallerClient.new() func _ready(): - # signaller.connect("websocket_connected", self, "connected") - # signaller.connect("websocket_disconnected", self, "disconnected") - signaller.connect("offer_received", self, "offer_received") signaller.connect("answer_received", self, "answer_received") signaller.connect("candidate_received", self, "candidate_received") + # signaller.connect("websocket_connected", self, "connected") + # signaller.connect("websocket_disconnected", self, "disconnected") signaller.connect("peer_id_set", self, "_peer_id_set") - # connect("lobby_sealed", self, "lobby_sealed") signaller.connect("peer_joined", self, "_peer_joined") - # connect("peer_disconnected", self, "peer_disconnected") + # mp.connect("peer_disconnected", self, "peer_disconnected") + # connect("lobby_sealed", self, "lobby_sealed") add_child(signaller) func close(): @@ -44,8 +43,8 @@ func _create_peer(peer_id): var peer: WebRTCPeerConnection = WebRTCPeerConnection.new() print("New Net Peer with peer_id %d" % [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]) + var _result = peer.connect("session_description_created", self, "_offer_created", [peer_id]) + _result = 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: diff --git a/server.ts b/server.ts index 15505c7..d55b019 100644 --- a/server.ts +++ b/server.ts @@ -105,14 +105,14 @@ class Client { this.send(buildMessage("lobby_delete", { id })); } - lobbyCreate() { + lobbyCreate(name?: string) { if (this.lobby) { this.send( `[info] cannot create lobby (already in lobby ${this.lobby.id})`, ); return; } - new Lobby(this); + new Lobby(this, name || `${this.name}'s lobby`); } lobbyJoin(lobby: Lobby) { @@ -123,7 +123,11 @@ class Client { this.lobby = lobby; lobby.addClient(this); this.send( - buildMessage("lobby_joined", { id: lobby.id, peerId: this.peerId }), + buildMessage("lobby_joined", { + id: lobby.id, + name: lobby.name, + peerId: this.peerId, + }), ); } @@ -209,7 +213,7 @@ class Lobby { } interface ClientMessage { - type: "candidate" | "offer" | "answer"; + type: "candidate" | "offer" | "answer" | "init" | "lobby_create"; data: ServerDataObject; } @@ -231,6 +235,10 @@ function onMessage(client: Client, ev: MessageEvent) { client.peerList(); } } + if (msg.startsWith("update_display_name:")) { + const displayName = msg.substr("update_display_name:".indexOf(":") + 1); + client.name = displayName; + } if (msg.startsWith("lobby_join:")) { const id = msg.substr(11); const lobby = allLobbies.get(id); @@ -241,7 +249,18 @@ function onMessage(client: Client, ev: MessageEvent) { } if (msg.startsWith("json:")) { const data: ClientMessage = JSON.parse(msg.substr(5)); - if (["candidate", "answer", "offer"].includes(data.type)) { + if (data.type === "init") { + if (data.data.name) { + client.name = data.data.name.toString(); + } + client.send(buildMessage("init", { id: client.id, name: client.name })); + } else if (data.type === "lobby_create") { + client.lobbyCreate(data.data?.name?.toString()); + if (data.data.name) { + client.name = data.data.name.toString(); + } + client.send(buildMessage("init", { id: client.id, name: client.name })); + } else if (["candidate", "answer", "offer"].includes(data.type)) { console.log("Received WebRTC Negotiation Message..."); if (typeof data.data === "object") { const subdata = data.data; diff --git a/signaller_client.gd b/signaller_client.gd index f67a735..f24ff85 100644 --- a/signaller_client.gd +++ b/signaller_client.gd @@ -25,8 +25,16 @@ onready var ws: WebSocketClient = WebSocketClient.new() onready var client_id = null onready var lobby_id = null onready var peer_id = null +onready var display_name = "UnnamedPlayer" +onready var lobby_name = null + +const DISPLAY_NAME_FILE = "user://display_name.txt" func _ready(): + var dnf = File.new() + if dnf.open(DISPLAY_NAME_FILE, File.READ) == OK: + display_name = dnf.get_as_text() + dnf.close() var _result = ws.connect("data_received", self, "_parse_msg") _result = ws.connect("connection_established", self, "_connected") _result = ws.connect("connection_closed", self, "_closed") @@ -61,7 +69,12 @@ func _connected(protocol = ""): print("WebSocket signaller connected via protocol %s" % protocol) ws.get_peer(1).set_write_mode(WebSocketPeer.WRITE_MODE_TEXT) emit_signal("websocket_connected") - _send("init") + _send("json:%s" % JSON.print({ + "type": "init", + "data": { + "name": display_name + } + })) func _process(_delta: float): var status: int = ws.get_connection_status() @@ -80,6 +93,14 @@ func request_lobby_list(): func request_peer_list(): return _send("request_peer_list") +func set_display_name(new_display_name: String): + display_name = new_display_name + _send("update_display_name:%s" % display_name) + var dnf = File.new() + if dnf.open(DISPLAY_NAME_FILE, File.WRITE) == OK: + dnf.store_string(display_name) + dnf.close() + func _send(s: String): return ws.get_peer(1).put_packet(s.to_utf8()) @@ -89,7 +110,7 @@ func _parse_msg(): var data = JSON.parse(msg.substr(5)) if data.error == OK: handle_message(data.result) - ws.get_peer(1).put_packet("".to_utf8()) + var _result = ws.get_peer(1).put_packet("".to_utf8()) else: print("Unhandled message: %s" % msg) @@ -108,10 +129,12 @@ func handle_message(data: Dictionary): "lobby_joined": lobby_id = data["id"] + lobby_name = data["name"] peer_id = data["peerId"] emit_signal("lobby_joined", data["id"], data["peerId"]) "lobby_left": lobby_id = null + lobby_name = null emit_signal("lobby_left", data["id"]) "peer_joined":