bugfix, desync, and add UI function

This commit is contained in:
2026-01-14 00:20:20 +08:00
parent 6948a4aed1
commit bee9c30f0e
14 changed files with 362 additions and 112 deletions
+50 -18
View File
@@ -59,9 +59,14 @@ func _init_managers():
screen_shake_manager.initialize($Camera3D)
# Touch controls for mobile
touch_controls = load("res://scripts/managers/touch_controls.gd").new()
touch_controls.name = "TouchControls"
add_child(touch_controls)
# Touch controls for mobile
touch_controls = get_node_or_null("TouchControls")
if not touch_controls:
print("TouchControls node not found in scene, creating instance...")
touch_controls = load("res://scripts/managers/touch_controls.gd").new()
touch_controls.name = "TouchControls"
add_child(touch_controls)
touch_controls.initialize(self)
# Connect signals for UI updates
@@ -180,8 +185,16 @@ func broadcast_message(player_name: String, message: String):
func _setup_global_match_timer_ui():
"""Create the global match timer display at the top of the screen."""
# Check if timer check is enabled in lobby settings
if not LobbyManager.enable_cycle_timer:
var existing = get_node_or_null("GlobalMatchTimer")
if existing:
existing.visible = false
return
var existing = get_node_or_null("GlobalMatchTimer")
if existing:
existing.visible = true
return
# Create timer panel
@@ -345,21 +358,40 @@ func _setup_client_game():
var my_id = multiplayer.get_unique_id()
print("Client setup - my peer ID: ", my_id)
# Create local player immediately
if not has_node(str(my_id)):
var player_character = PlayerManager.add_player_character(my_id)
add_child(player_character)
player_character.add_to_group("Players", true)
GameStateManager.add_player(my_id)
GameStateManager.local_player_character = player_character
ui_manager.set_local_player(player_character)
if touch_controls:
touch_controls.set_player(player_character)
ui_manager.update_button_states()
print("Created local player for client: ", my_id)
# Pre-spawn ALL players known from LobbyManager (including Host ID 1)
# This ensures nodes exist to receive RPCs (like 'set_spawn_position') that might arrive before full sync
var lobby_players = LobbyManager.get_players()
for player_data in lobby_players:
var p_id = player_data.get("id", 0)
if p_id != 0:
add_player_character(p_id)
print("Client: Pre-spawned player ", p_id)
# Wait for host to be ready, then request full sync
await get_tree().create_timer(2.0).timeout
# Pre-spawn potential bots (IDs 2 to MaxPlayers) to prevent RPC "Node not found" errors
# Bots use small integer IDs (2, 3, 4...) while clients use large unique IDs
if GameStateManager.enable_bots:
for i in range(2, GameStateManager.max_players + 1):
# Only spawn if not already existing (e.g. if a human somehow got this ID, though unlikely)
if not has_node(str(i)):
add_player_character(i)
get_node(str(i)).is_bot = true # Assume bot initially
get_node(str(i)).add_to_group("Bots", true)
print("Client: Pre-spawned potential bot ", i)
# Ensure local player setup (UI, controls) is verified
var player_character = get_node_or_null(str(my_id))
if player_character:
# If we just spawned it above, we need to set these locally too
if GameStateManager.local_player_character != player_character:
GameStateManager.local_player_character = player_character
ui_manager.set_local_player(player_character)
if touch_controls:
touch_controls.set_player(player_character)
ui_manager.update_button_states()
print("Client: Configured local player ", my_id)
# Wait shorter time for host to be ready, then request full sync to correct positions/state
await get_tree().create_timer(1.0).timeout
rpc_id(1, "request_full_player_sync", my_id)
func _auto_start_from_lobby():
@@ -483,7 +515,7 @@ func add_player_character(peer_id: int):
func _on_peer_connected(new_peer_id: int):
if multiplayer.is_server():
await get_tree().create_timer(1.5).timeout
await get_tree().create_timer(0.1).timeout
add_player_character(new_peer_id)
rpc("add_newly_connected_player_character", new_peer_id)
+89 -1
View File
@@ -1,4 +1,4 @@
[gd_scene load_steps=27 format=3 uid="uid://dxn87yj8qnfpp"]
[gd_scene load_steps=29 format=3 uid="uid://dxn87yj8qnfpp"]
[ext_resource type="MeshLibrary" uid="uid://kcv6ans86ug7" path="res://addons/enhanced_gridmap/meshlibrary/default.tres" id="1_110wo"]
[ext_resource type="Script" uid="uid://co1ads72by6na" path="res://scenes/main.gd" id="1_xcpe3"]
@@ -22,6 +22,8 @@
[ext_resource type="StyleBox" uid="uid://d3ruc8gytoovx" path="res://assets/styles/ribbon_selected_gui.tres" id="18_u5x6e"]
[ext_resource type="StyleBox" uid="uid://cdhnwvcklbyl8" path="res://assets/styles/ribbon_hovered_gui.tres" id="19_w1rqq"]
[ext_resource type="StyleBox" uid="uid://3yog1weaqhxb" path="res://assets/styles/ribbon_unselected_gui.tres" id="20_q6bc1"]
[ext_resource type="Script" uid="uid://b54tfa0n6kogi" path="res://scripts/managers/touch_controls.gd" id="touch_manager"]
[ext_resource type="Script" uid="uid://djiml4sh61dc1" path="res://scripts/ui/virtual_joystick.gd" id="virtual_joystick"]
[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_playerboard"]
content_margin_left = 8.0
@@ -9699,6 +9701,92 @@ custom_minimum_size = Vector2(0, 40)
layout_mode = 2
text = "Back"
[node name="TouchControls" type="CanvasLayer" parent="."]
layer = 10
script = ExtResource("touch_manager")
[node name="TouchControls" type="Control" parent="TouchControls"]
layout_mode = 3
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
offset_top = 318.0
grow_horizontal = 2
grow_vertical = 2
mouse_filter = 1
[node name="VirtualJoystick" type="Control" parent="TouchControls/TouchControls"]
layout_mode = 1
anchors_preset = 2
anchor_top = 1.0
anchor_bottom = 1.0
offset_left = 120.0
offset_top = -280.0
offset_right = 280.0
offset_bottom = -120.0
grow_vertical = 0
script = ExtResource("virtual_joystick")
[node name="GrabBtn" type="Button" parent="TouchControls/TouchControls"]
layout_mode = 1
anchors_preset = 3
anchor_left = 1.0
anchor_top = 1.0
anchor_right = 1.0
anchor_bottom = 1.0
offset_left = -232.0
offset_top = -286.0
offset_right = -162.0
offset_bottom = -216.0
grow_horizontal = 0
grow_vertical = 0
text = "👋"
[node name="PutBtn" type="Button" parent="TouchControls/TouchControls"]
layout_mode = 1
anchors_preset = 3
anchor_left = 1.0
anchor_top = 1.0
anchor_right = 1.0
anchor_bottom = 1.0
offset_left = -152.0
offset_top = -206.0
offset_right = -82.0
offset_bottom = -136.0
grow_horizontal = 0
grow_vertical = 0
text = "📦"
[node name="SpecialBtn" type="Button" parent="TouchControls/TouchControls"]
layout_mode = 1
anchors_preset = 3
anchor_left = 1.0
anchor_top = 1.0
anchor_right = 1.0
anchor_bottom = 1.0
offset_left = -232.0
offset_top = -126.0
offset_right = -162.0
offset_bottom = -56.0
grow_horizontal = 0
grow_vertical = 0
text = "⚡"
[node name="SettingsBtn" type="Button" parent="TouchControls/TouchControls"]
layout_mode = 1
anchors_preset = 3
anchor_left = 1.0
anchor_top = 1.0
anchor_right = 1.0
anchor_bottom = 1.0
offset_left = -69.0
offset_top = -69.0
offset_right = -19.0
offset_bottom = -19.0
grow_horizontal = 0
grow_vertical = 0
text = "⚙"
[connection signal="pressed" from="Menu/Host" to="." method="_on_host_pressed"]
[connection signal="pressed" from="Menu/Join" to="." method="_on_join_pressed"]
[connection signal="text_submitted" from="MessageInput" to="." method="_on_message_input_text_submitted"]
+26 -6
View File
@@ -78,6 +78,8 @@ var spawn_locations = [
var finish_locations: Array:
get: return race_manager.finish_locations if race_manager else []
var target_visual_position: Vector3 = Vector3.ZERO # For client-side smoothing
var spawn_point_selected = false
# Action for hilighter
@@ -145,8 +147,8 @@ func _ready():
if is_multiplayer_authority():
rpc("sync_display_name", display_name)
# Wait briefly to ensure proper scene setup
await get_tree().create_timer(0.1).timeout
# Wait briefly to ensure proper scene setup and server recognition
await get_tree().create_timer(0.5).timeout
# More robust way to get the main scene
var main_scene = get_tree().get_root().get_node_or_null("Main")
@@ -226,8 +228,11 @@ func _ready():
1.0,
current_position.y * cell_size.z + cell_size.z * 0.5
)
target_visual_position = global_position
if is_multiplayer_authority():
rpc("sync_position", current_position)
else:
target_visual_position = global_position
func _init_managers():
movement_manager = load("res://scripts/managers/player_movement_manager.gd").new()
@@ -609,6 +614,11 @@ func _process(delta):
if _verify_timer >= 3.0:
_verify_timer = 0.0
rpc("ping_existence")
else:
# Client-side visual smoothing
# Interpolate towards the target position received from authority
if global_position.distance_squared_to(target_visual_position) > 0.001:
global_position = global_position.lerp(target_visual_position, delta * 15.0)
# Delegate rotation to movement manager
if movement_manager:
@@ -942,7 +952,8 @@ static func reset_race_stats():
@rpc("any_peer", "call_local", "unreliable")
func remote_set_position(authority_position):
global_position = authority_position
# Don't snap directly, update target for interpolation
target_visual_position = authority_position
@rpc("any_peer", "call_local")
func display_message(message, type: int = 0):
@@ -1371,11 +1382,14 @@ func bot_arrange_item(from_slot: int, to_slot: int):
func update_visual_position():
# Ensure proper grid-aligned positioning
global_position = Vector3(
var new_pos = Vector3(
current_position.x * cell_size.x + cell_size.x * 0.5,
1.0,
current_position.y * cell_size.z + cell_size.z * 0.5
)
global_position = new_pos
target_visual_position = new_pos # Snap target too
if is_multiplayer_authority():
rpc("sync_position", current_position)
@@ -1383,11 +1397,14 @@ func update_visual_position():
func sync_position(pos: Vector2i):
current_position = pos
# Always update the visual position after position sync
global_position = Vector3(
var new_pos = Vector3(
current_position.x * cell_size.x + cell_size.x * 0.5,
cell_size.y,
current_position.y * cell_size.z + cell_size.z * 0.5
) + cell_offset
global_position = new_pos
target_visual_position = new_pos # Reset smoothing target to prevent fighting
@rpc("any_peer", "call_local", "reliable")
func set_spawn_position(pos: Vector2i):
@@ -1397,11 +1414,14 @@ func set_spawn_position(pos: Vector2i):
# Clear any spawn highlights
clear_spawn_highlights()
# Update visual position
global_position = Vector3(
var new_pos = Vector3(
current_position.x * cell_size.x + cell_size.x * 0.5,
cell_size.y,
current_position.y * cell_size.z + cell_size.z * 0.5
) + cell_offset
global_position = new_pos
target_visual_position = new_pos
@rpc("any_peer", "call_local", "reliable")