diff --git a/scenes/game.gd b/scenes/game.gd index ba8cb0e..2353a2e 100644 --- a/scenes/game.gd +++ b/scenes/game.gd @@ -43,7 +43,10 @@ func _on_peer_connected(peer_id: int): if multiplayer.is_server(): assert(peer_id != 1, "This is a server, but the remote peer_id is 1.") Log.peer(self, "Initializing tracker for: " + str(peer_id)) - mp_tracker.spawn(peer_id) + mp_tracker.create(peer_id) + # Log.peer(self, "Setting multiplayer authority: " + str(peer_id)) + # var sp_tracker = mp_tracker.find(peer_id) + # sp_tracker.set_multiplayer_authority(peer_id) func _on_peer_disconnected(peer_id: int): Log.peer(self, "Peer disconnected: " + str(peer_id)) @@ -51,13 +54,28 @@ func _on_peer_disconnected(peer_id: int): assert(peer_id != 1, "This is a server, but the remote peer_id is 1.") Log.peer(self, "Deinitializing tracker for: " + str(peer_id)) mp_tracker.mark_disconnected.rpc(peer_id) + # Log.peer(self, "Unsetting multiplayer authority: " + str(peer_id)) + # var sp_tracker = mp_tracker.find(peer_id) + # sp_tracker.set_multiplayer_authority(1) -func _on_single_player_tracker_spawned(spawned_tracker: SinglePlayerTracker) -> void: +func _on_single_player_tracker_created(spawned_tracker: SinglePlayerTracker) -> void: # If we spawned ourselves, set our own identity if spawned_tracker.get_multiplayer_authority() == multiplayer.multiplayer_peer.get_unique_id(): + Log.peer(self, "Checking if we should notify our identity to everybody else...") spawned_tracker.update_identity.rpc(local_player_name, local_player_color) # If another player spawned, and they connected before us, send them our identity - else: + elif not multiplayer.is_server(): + Log.peer(self, "Checking if we should notify our identity to: " + str(spawned_tracker.get_multiplayer_authority())) var self_tracker = mp_tracker.find(multiplayer.multiplayer_peer.get_unique_id()) if self_tracker != null: self_tracker.notify_identity(spawned_tracker.get_multiplayer_authority()) + # If we're the server, broadcast information about disconnected players + else: + Log.peer(self, "Checking if we should notify identity for disconnected peers...") + for tracker in mp_tracker.find_children("", "SinglePlayerTracker", true, false): + Log.peer(self, str(tracker)) + if tracker.get_multiplayer_authority() == 1: + Log.peer(self, "I'm the authority, do notify.") + tracker.notify_identity(spawned_tracker.get_multiplayer_authority()) + else: + Log.peer(self, "I'm not the authority, do not notify.") diff --git a/scenes/game.tscn b/scenes/game.tscn index 97fcb7f..5bb39c6 100644 --- a/scenes/game.tscn +++ b/scenes/game.tscn @@ -8,5 +8,5 @@ script = ExtResource("1_cdtng") [node name="MultiplePlayersTracker" parent="." instance=ExtResource("2_f2gy2")] -[connection signal="spawned" from="MultiplePlayersTracker" to="." method="_on_single_player_tracker_spawned"] +[connection signal="created" from="MultiplePlayersTracker" to="." method="_on_single_player_tracker_created"] [connection signal="trackers_changed" from="MultiplePlayersTracker" to="." method="_on_trackers_changed"] diff --git a/scenes/lobby_menu.gd b/scenes/lobby_menu.gd index 157e522..140f66e 100644 --- a/scenes/lobby_menu.gd +++ b/scenes/lobby_menu.gd @@ -2,8 +2,17 @@ extends Control class_name LobbyMenu +signal leave_confirmed +signal start_confirmed + @onready var players_list: PlayersList = $"Layout/PlayersList" func _on_trackers_changed(trackers: Dictionary): players_list._on_trackers_changed(trackers) + +func _on_leave_button_pressed() -> void: + leave_confirmed.emit() + +func _on_start_button_pressed() -> void: + start_confirmed.emit() diff --git a/scenes/lobby_menu.tscn b/scenes/lobby_menu.tscn index 92365fd..64a8eea 100644 --- a/scenes/lobby_menu.tscn +++ b/scenes/lobby_menu.tscn @@ -38,6 +38,18 @@ layout_mode = 2 custom_minimum_size = Vector2(0, 16) layout_mode = 2 -[node name="Button" type="Button" parent="Layout"] +[node name="HBoxContainer" type="HBoxContainer" parent="Layout"] layout_mode = 2 -text = "Avvia partita" + +[node name="LeaveButton" type="Button" parent="Layout/HBoxContainer"] +layout_mode = 2 +size_flags_horizontal = 3 +text = "Esci" + +[node name="StartButton" type="Button" parent="Layout/HBoxContainer"] +layout_mode = 2 +size_flags_horizontal = 3 +text = "Avvia" + +[connection signal="pressed" from="Layout/HBoxContainer/LeaveButton" to="." method="_on_leave_button_pressed"] +[connection signal="pressed" from="Layout/HBoxContainer/StartButton" to="." method="_on_start_button_pressed"] diff --git a/scenes/main.gd b/scenes/main.gd index 43b226a..eb12adf 100644 --- a/scenes/main.gd +++ b/scenes/main.gd @@ -42,9 +42,7 @@ func init_server_game(server_port: int): smp.set_multiplayer_peer(peer) func deinit_server_game(): - var smp: SceneMultiplayer = scene_tree.get_multiplayer(^"/root/Main/Server") - smp.multiplayer_peer.close() - smp.multiplayer_peer = null + server_game_instance.multiplayer.multiplayer_peer = null scene_tree.set_multiplayer(multiplayer, ^"/root/Main/Server") server_game_instance.queue_free() @@ -67,14 +65,14 @@ func init_client_game(player_name: String, player_color: Color, server_address: smp.set_multiplayer_peer(peer) func deinit_client_game(): - var smp: SceneMultiplayer = scene_tree.get_multiplayer(^"/root/Main/Server") - smp.multiplayer_peer.close() - smp.multiplayer_peer = null + client_game_instance.multiplayer.multiplayer_peer = null scene_tree.set_multiplayer(multiplayer, ^"/root/Main/Client") client_game_instance.queue_free() func init_lobby_menu(): lobby_menu_instance = lobby_menu_scene.instantiate() + lobby_menu_instance.leave_confirmed.connect(_on_leave_confirmed) + lobby_menu_instance.start_confirmed.connect(_on_start_confirmed) interface_instance.add_child(lobby_menu_instance) func deinit_lobby_menu(): @@ -95,3 +93,13 @@ func _on_connecting_confirmed(player_name: String, player_color: Color, server_a init_client_game(player_name, player_color, server_address, server_port) init_lobby_menu() client_game_instance.trackers_changed.connect(lobby_menu_instance._on_trackers_changed) + +func _on_leave_confirmed(): + deinit_lobby_menu() + deinit_client_game() + if server_game_instance != null: + deinit_server_game() + init_main_menu() + +func _on_start_confirmed(): + pass diff --git a/scenes/multiple_players_tracker.gd b/scenes/multiple_players_tracker.gd index 199e501..478030f 100644 --- a/scenes/multiple_players_tracker.gd +++ b/scenes/multiple_players_tracker.gd @@ -1,38 +1,68 @@ -extends MultiplayerSpawner +extends Node class_name MultiplePlayersTracker -# Requires the peer_id as data for the spawn function. +## Manager of all the instances of [SinglePlayerTracker] for a given multiplayer room. +## +## Authority of this object is always given to the server (1). +## +## The server uses its authority to assign authority to the child [SinglePlayerTracker]s. -signal trackers_changed(trackers: Dictionary) +## Emitted when a new [SinglePlayerTracker] is created. +signal created(tracker: SinglePlayerTracker) -func emit_trackers_changed(): - trackers_changed.emit(trackers_by_peer_id) +## Emitted when any [SinglePlayerTracker] has changed. +signal changed(all_trackers: Array[SinglePlayerTracker]) +## Emitted when any [SinglePlayerTracker] is about to be destroyed. +signal before_destroyed(tracker: SinglePlayerTracker) -var trackers_by_peer_id: Dictionary = {} +## The scene to be instantiated. +const tracker_scene = preload("res://scenes/single_player_tracker.tscn") +## Return the [Array] of [SinglePlayerTrackers] managed by this object. +func find_all() -> Array[SinglePlayerTracker]: + return find_children("", "SinglePlayerTracker", false, false) as Array[SinglePlayerTracker] -func find(peer_id: int) -> SinglePlayerTracker: - return trackers_by_peer_id.get(peer_id) +## Find the first [SinglePlayerTracker] over which the given peer id has authority. +func find_id(peer_id: int) -> SinglePlayerTracker: + for tracker in find_all(): + if tracker.get_multiplayer_authority() == peer_id: + return tracker + return null +## Find the first [SinglePlayerTracker] over which the running instance has authority. +func find_self() -> SinglePlayerTracker: + var self_id = multiplayer.multiplayer_peer.get_unique_id() + return find_id(self_id) + +## Find the first [SinglePlayerTracker] representing a player with the given name. +func find_name(player_name: String) -> SinglePlayerTracker: + for tracker in find_all(): + if tracker.player_name == player_name: + return tracker + return null + +## Create a new [SinglePlayerTracker] for the given peer id, or return the one that already exists. @rpc("authority", "call_local", "reliable") -func mark_disconnected(peer_id: int): - var single_tracker_instance = find(peer_id) - single_tracker_instance.set_multiplayer_authority(1) - single_tracker_instance.peer_connected = false - emit_trackers_changed() +func create(peer_id: int) -> SinglePlayerTracker: + var tracker = find_id(peer_id) + if tracker != null: + return tracker + Log.peer(self, "Creating tracker for peer: %d" % peer_id) + tracker = tracker_scene.instantiate() + tracker.set_multiplayer_authority(peer_id) + created.emit(tracker) + var trackers = find_all() + changed.emit(trackers) + return tracker -func _ready(): - spawn_function = _spawn_tracker - -func _spawn_tracker(peer_id: int) -> Node: - var single_tracker_scene: PackedScene = load(get_spawnable_scene(0)) - var single_tracker_instance: SinglePlayerTracker = single_tracker_scene.instantiate() - single_tracker_instance.set_multiplayer_authority(peer_id) - single_tracker_instance.identity_updated.connect(_on_tracker_identity_updated) - trackers_by_peer_id[peer_id] = single_tracker_instance - emit_trackers_changed() - return single_tracker_instance - -func _on_tracker_identity_updated(_player_name: String, _player_color: Color): - emit_trackers_changed() +## Destroy the [SinglePlayerTracker] for the given peer id, or do nothing if it already exists. +func destroy(peer_id: int) -> void: + var tracker = find_id(peer_id) + if tracker == null: + return + Log.peer(self, "Destroying tracker for peer: %d" % peer_id) + before_destroyed.emit(tracker) + tracker.queue_free() + var trackers = find_all() + changed.emit(trackers) diff --git a/scenes/multiple_players_tracker.tscn b/scenes/multiple_players_tracker.tscn index c929dfd..2d7bb6b 100644 --- a/scenes/multiple_players_tracker.tscn +++ b/scenes/multiple_players_tracker.tscn @@ -2,8 +2,5 @@ [ext_resource type="Script" path="res://scenes/multiple_players_tracker.gd" id="1_fxysy"] -[node name="MultiplePlayersTracker" type="MultiplayerSpawner"] -_spawnable_scenes = PackedStringArray("res://scenes/single_player_tracker.tscn") -spawn_path = NodePath(".") -spawn_limit = 32 +[node name="MultiplePlayersTracker" type="Node"] script = ExtResource("1_fxysy") diff --git a/scenes/player_label.gd b/scenes/player_label.gd index d613824..264ed01 100644 --- a/scenes/player_label.gd +++ b/scenes/player_label.gd @@ -5,4 +5,4 @@ class_name PlayerLabel func update_from_tracker(tracker: SinglePlayerTracker): text = tracker.player_name add_theme_color_override("font_color", tracker.player_color) - modulate.a = 1.0 if tracker.peer_connected else 0.3 + modulate.a = 1.0 if tracker.get_multiplayer_authority() != 1 else 0.3 diff --git a/scenes/player_name_input.gd b/scenes/player_name_input.gd index 107bda1..a23a9ea 100644 --- a/scenes/player_name_input.gd +++ b/scenes/player_name_input.gd @@ -10,5 +10,5 @@ signal name_confirmed(selected_name: String) func _on_button_pressed(): name_confirmed.emit(line_edit.text) -func _on_line_edit_text_submitted(new_text: String): +func _on_line_edit_text_submitted(_new_text: String): name_confirmed.emit(line_edit.text) diff --git a/scenes/single_player_tracker.gd b/scenes/single_player_tracker.gd index efebe00..3b099ba 100644 --- a/scenes/single_player_tracker.gd +++ b/scenes/single_player_tracker.gd @@ -1,12 +1,9 @@ -extends Node +extends MultiplayerSynchronizer class_name SinglePlayerTracker - -signal identity_updated(player_name: String, player_color: Color) - - -## Whether the peer is connected or not. -var peer_connected: bool = true +## Node representative of a player connected to the room. +## +## The peer of the player this node represents has authority over it. ## The player's name. var player_name: String = "Player" @@ -14,16 +11,5 @@ var player_name: String = "Player" ## The player's color. var player_color: Color = Color.WHITE -## This player's score, with an item per hole played. -var strokes_per_hole: Array[int] = [] - - -@rpc("authority", "call_local", "reliable") -func update_identity(new_player_name: String, new_player_color: Color): - player_name = new_player_name - player_color = new_player_color - identity_updated.emit(player_name, player_color) - -func notify_identity(peer_id: int): - Log.peer(self, "Notifying of our identity: " + str(peer_id)) - update_identity.rpc_id(peer_id, player_name, player_color) +## Whether this player is currently connected or not. +var player_connected: bool = false diff --git a/scenes/single_player_tracker.tscn b/scenes/single_player_tracker.tscn index ec5116e..ce11fab 100644 --- a/scenes/single_player_tracker.tscn +++ b/scenes/single_player_tracker.tscn @@ -1,6 +1,25 @@ -[gd_scene load_steps=2 format=3 uid="uid://drccgvtcng3ju"] +[gd_scene load_steps=3 format=3 uid="uid://drccgvtcng3ju"] [ext_resource type="Script" path="res://scenes/single_player_tracker.gd" id="1_jwlkv"] -[node name="SinglePlayerTracker" type="Node"] +[sub_resource type="SceneReplicationConfig" id="SceneReplicationConfig_755wr"] +properties/0/path = NodePath(".:player_connected") +properties/0/spawn = true +properties/0/replication_mode = 2 +properties/1/path = NodePath(".:player_name") +properties/1/spawn = true +properties/1/replication_mode = 2 +properties/2/path = NodePath(".:player_color") +properties/2/spawn = true +properties/2/replication_mode = 2 +properties/3/path = NodePath(".:strokes_per_hole") +properties/3/spawn = true +properties/3/replication_mode = 2 + +[node name="SinglePlayerTracker" type="MultiplayerSynchronizer"] +root_path = NodePath(".") +replication_interval = 1.0 +delta_interval = 0.125 +replication_config = SubResource("SceneReplicationConfig_755wr") +visibility_update_mode = 2 script = ExtResource("1_jwlkv")