fix(network): prevent null multiplayer peer crashes offline/teardown and fix Nakama RPC session validation
This commit is contained in:
+23
-17
@@ -63,7 +63,7 @@ func _ready():
|
||||
# Auto-start game if coming from lobby (already connected to match)
|
||||
# Works for both Nakama mode and LAN direct mode (ENet).
|
||||
var is_lan_connected = LobbyManager.is_lan_mode and multiplayer.has_multiplayer_peer() and multiplayer.multiplayer_peer.get_connection_status() == MultiplayerPeer.CONNECTION_CONNECTED
|
||||
if (NakamaManager.is_connected_to_nakama() or is_lan_connected) and multiplayer.get_unique_id() != 0:
|
||||
if multiplayer.has_multiplayer_peer() and (NakamaManager.is_connected_to_nakama() or is_lan_connected) and multiplayer.get_unique_id() != 0:
|
||||
print("Coming from lobby - auto-starting game...")
|
||||
await get_tree().process_frame
|
||||
_auto_start_from_lobby()
|
||||
@@ -221,7 +221,7 @@ func _setup_effect_elevation():
|
||||
|
||||
func _on_goal_count_updated(peer_id: int, count: int):
|
||||
# Only update for local player
|
||||
if peer_id == multiplayer.get_unique_id():
|
||||
if multiplayer.has_multiplayer_peer() and peer_id == multiplayer.get_unique_id():
|
||||
ui_manager.update_goal_count_label(count)
|
||||
|
||||
func _init_managers():
|
||||
@@ -564,15 +564,16 @@ func _process(delta):
|
||||
func _on_match_joined(match_id: String):
|
||||
var network_panel = get_node_or_null("PauseMenu/Panel/NetworkPanel")
|
||||
if network_panel:
|
||||
network_panel.get_node("NetworkInfo/UniquePeerID").text = str(multiplayer.get_unique_id())
|
||||
if multiplayer.is_server():
|
||||
network_panel.get_node("NetworkInfo/UniquePeerID").text = str(multiplayer.get_unique_id() if multiplayer and multiplayer.has_multiplayer_peer() else 0)
|
||||
if multiplayer and multiplayer.has_multiplayer_peer() and multiplayer.is_server():
|
||||
network_panel.get_node("NetworkInfo/NetworkSideDisplay").text = "Server (Match: %s)" % match_id
|
||||
_setup_host_game()
|
||||
else:
|
||||
network_panel.get_node("NetworkInfo/NetworkSideDisplay").text = "Client"
|
||||
_setup_client_game()
|
||||
else:
|
||||
if multiplayer.is_server():
|
||||
# If the UI isn't ready yet, still need to setup logic based on mode
|
||||
if multiplayer.has_multiplayer_peer() and multiplayer.is_server():
|
||||
_setup_host_game()
|
||||
else:
|
||||
_setup_client_game()
|
||||
@@ -683,7 +684,9 @@ func _spawn_lobby_client_sync(peer_id: int):
|
||||
|
||||
func _setup_client_game():
|
||||
"""Setup client when transitioning from lobby."""
|
||||
var my_id = multiplayer.get_unique_id()
|
||||
var my_id = 0
|
||||
if multiplayer and multiplayer.has_multiplayer_peer():
|
||||
my_id = multiplayer.get_unique_id()
|
||||
print("Client setup - my peer ID: ", my_id)
|
||||
|
||||
# INITIALIZE ARENA SIZE for Stop n Go locally to prevent out-of-bounds before sync arrives
|
||||
@@ -766,8 +769,8 @@ func _auto_start_from_lobby():
|
||||
# Update NetworkPanel in PauseMenu (if exists)
|
||||
var network_panel = get_node_or_null("PauseMenu/Panel/NetworkPanel")
|
||||
if network_panel:
|
||||
network_panel.get_node("NetworkInfo/UniquePeerID").text = str(multiplayer.get_unique_id())
|
||||
if multiplayer.is_server():
|
||||
network_panel.get_node("NetworkInfo/UniquePeerID").text = str(multiplayer.get_unique_id() if multiplayer and multiplayer.has_multiplayer_peer() else 0)
|
||||
if multiplayer and multiplayer.has_multiplayer_peer() and multiplayer.is_server():
|
||||
network_panel.get_node("NetworkInfo/NetworkSideDisplay").text = "Host (Match: %s)" % short_id
|
||||
else:
|
||||
network_panel.get_node("NetworkInfo/NetworkSideDisplay").text = "Client (Match: %s)" % short_id
|
||||
@@ -1385,7 +1388,7 @@ func add_player_character(peer_id: int, is_bot: bool = false):
|
||||
print("[Main] Set player %d name to %s from Lobby data" % [peer_id, p_name])
|
||||
break
|
||||
|
||||
if peer_id == multiplayer.get_unique_id():
|
||||
if multiplayer.has_multiplayer_peer() and peer_id == multiplayer.get_unique_id():
|
||||
GameStateManager.local_player_character = player_character
|
||||
ui_manager.set_local_player(player_character)
|
||||
if touch_controls:
|
||||
@@ -1608,7 +1611,7 @@ func sync_playerboard(player_id: int, new_playerboard: Array):
|
||||
player.playerboard = new_playerboard.duplicate()
|
||||
|
||||
# Update UI for local player
|
||||
if player_id == multiplayer.get_unique_id() and GameStateManager.local_player_character:
|
||||
if multiplayer.has_multiplayer_peer() and player_id == multiplayer.get_unique_id() and GameStateManager.local_player_character:
|
||||
ui_manager.update_playerboard_ui()
|
||||
|
||||
@rpc("any_peer", "call_local", "reliable")
|
||||
@@ -1628,7 +1631,7 @@ func sync_playerboard_slot(player_id: int, slot_index: int, item_id: int):
|
||||
return
|
||||
|
||||
# Update UI for local player only
|
||||
if player_id == multiplayer.get_unique_id() and GameStateManager.local_player_character:
|
||||
if multiplayer.has_multiplayer_peer() and player_id == multiplayer.get_unique_id() and GameStateManager.local_player_character:
|
||||
ui_manager.update_playerboard_ui()
|
||||
|
||||
|
||||
@@ -1649,7 +1652,7 @@ func verify_all_connections():
|
||||
@rpc
|
||||
func connection_verify(expected_players: Array):
|
||||
for peer_id in expected_players:
|
||||
if peer_id != multiplayer.get_unique_id() and not has_node(str(peer_id)):
|
||||
if multiplayer.has_multiplayer_peer() and peer_id != multiplayer.get_unique_id() and not has_node(str(peer_id)):
|
||||
rpc_id(1, "request_specific_player_data", peer_id)
|
||||
|
||||
@rpc("any_peer")
|
||||
@@ -1721,9 +1724,9 @@ func create_specific_player(data: Dictionary):
|
||||
# Force collision update
|
||||
if player_character.has_node("CollisionShape3D"):
|
||||
player_character.get_node("CollisionShape3D").disabled = false
|
||||
|
||||
# Wait for player managers to initialize (player.gd has 0.1s await in _ready)
|
||||
# Check if this is the local player (client's own player)
|
||||
var is_local_player = (peer_id == multiplayer.get_unique_id())
|
||||
var is_local_player = multiplayer.has_multiplayer_peer() and (peer_id == multiplayer.get_unique_id())
|
||||
if is_local_player and GameStateManager.local_player_character == null:
|
||||
GameStateManager.local_player_character = player_character
|
||||
ui_manager.set_local_player(player_character)
|
||||
@@ -2326,7 +2329,7 @@ func _show_game_over_panel():
|
||||
canvas_layer.add_child(panel)
|
||||
|
||||
# Populate data
|
||||
var local_peer_id = multiplayer.get_unique_id()
|
||||
var local_peer_id = multiplayer.get_unique_id() if multiplayer.has_multiplayer_peer() else 0
|
||||
panel.setup(all_player_scores, local_peer_id)
|
||||
|
||||
# --- REPORT NAKAMA MATCH STATS ---
|
||||
@@ -2351,7 +2354,8 @@ func _show_game_over_panel():
|
||||
# Connect signals
|
||||
panel.back_pressed.connect(_on_back_to_menu_pressed)
|
||||
panel.rematch_pressed.connect(func():
|
||||
LobbyManager.request_rematch.rpc(multiplayer.get_unique_id())
|
||||
if multiplayer.has_multiplayer_peer():
|
||||
LobbyManager.request_rematch.rpc(multiplayer.get_unique_id())
|
||||
)
|
||||
|
||||
LobbyManager.rematch_votes_updated.connect(func(count, required):
|
||||
@@ -2479,7 +2483,9 @@ func _render_leaderboard_entries(sorted_player_data: Array):
|
||||
if not vbox: vbox = leaderboard_panel.get_node_or_null("VBox")
|
||||
if not vbox: return
|
||||
|
||||
var my_id = multiplayer.get_unique_id()
|
||||
var my_id = 0
|
||||
if multiplayer and multiplayer.has_multiplayer_peer():
|
||||
my_id = multiplayer.get_unique_id()
|
||||
var my_index = -1
|
||||
for i in range(sorted_player_data.size()):
|
||||
if sorted_player_data[i].peer_id == my_id:
|
||||
|
||||
+20
-16
@@ -33,7 +33,7 @@ var display_name: String:
|
||||
|
||||
# Helper to check network status
|
||||
func can_rpc() -> bool:
|
||||
if not multiplayer.has_multiplayer_peer(): return false
|
||||
if not is_inside_tree() or not multiplayer or not multiplayer.has_multiplayer_peer(): return false
|
||||
if multiplayer.multiplayer_peer.get_class() == "OfflineMultiplayerPeer": return false
|
||||
return multiplayer.multiplayer_peer.get_connection_status() == MultiplayerPeer.CONNECTION_CONNECTED
|
||||
|
||||
@@ -81,7 +81,7 @@ var is_charged_strike: bool = false:
|
||||
_refresh_player_visuals()
|
||||
|
||||
# Sync to others if we are the authority
|
||||
if is_multiplayer_authority() and can_rpc():
|
||||
if is_inside_tree() and is_multiplayer_authority() and can_rpc() and multiplayer.has_multiplayer_peer():
|
||||
rpc("sync_charged_strike", is_charged_strike)
|
||||
|
||||
@rpc("any_peer", "call_local", "reliable")
|
||||
@@ -219,8 +219,8 @@ func _ready():
|
||||
# This prevents "grounding" friction and gravity from fighting our Tweens
|
||||
motion_mode = CharacterBody3D.MOTION_MODE_FLOATING
|
||||
|
||||
print("[Player] initialized: ", name, " Authority: ", get_multiplayer_authority())
|
||||
# name = str(get_multiplayer_authority()) # CRITICAL FIX: Do NOT overwrite name. Bots have authority 1 but unique names (IDs).
|
||||
var authority_id = get_multiplayer_authority() if multiplayer and multiplayer.has_multiplayer_peer() else 0
|
||||
print("[Player] initialized: ", name, " Authority: ", authority_id)
|
||||
# Look up player's display name from LobbyManager
|
||||
var my_id = get_multiplayer_authority()
|
||||
|
||||
@@ -255,7 +255,7 @@ func _ready():
|
||||
var mat = ShaderMaterial.new()
|
||||
mat.shader = pointer_shader
|
||||
|
||||
if name == str(multiplayer.get_unique_id()):
|
||||
if multiplayer and multiplayer.has_multiplayer_peer() and name == str(multiplayer.get_unique_id()):
|
||||
mat.set_shader_parameter("pointer_color", Color(0.0, 1.0, 0.0, 1.0)) # Green
|
||||
if has_node("Name"):
|
||||
$Name.modulate = Color(0.2, 0.8, 0.2, 1.0) # Pleasant Green for Name
|
||||
@@ -308,7 +308,7 @@ func _ready():
|
||||
current_position = _find_random_spawn_position()
|
||||
update_player_position(current_position)
|
||||
spawn_point_selected = true
|
||||
if can_rpc():
|
||||
if can_rpc() and multiplayer.has_multiplayer_peer():
|
||||
rpc("set_spawn_position", current_position)
|
||||
rpc("notify_spawn_selected", current_position)
|
||||
|
||||
@@ -318,11 +318,11 @@ func _ready():
|
||||
var char_index = (bot_id_val - 1) % bot_characters.size()
|
||||
var bot_char_name = bot_characters[char_index]
|
||||
set_character(bot_char_name)
|
||||
if is_multiplayer_authority() and can_rpc():
|
||||
if can_rpc() and multiplayer.has_multiplayer_peer():
|
||||
rpc("sync_character", bot_char_name)
|
||||
|
||||
# Sync bot status to network
|
||||
if is_multiplayer_authority() and can_rpc():
|
||||
if can_rpc() and multiplayer.has_multiplayer_peer():
|
||||
rpc("sync_bot_status", true)
|
||||
|
||||
# Continue to manager initialization...
|
||||
@@ -347,7 +347,7 @@ func _ready():
|
||||
current_position.y * 1 + 1 * 0.5
|
||||
)
|
||||
target_visual_position = global_position
|
||||
if is_multiplayer_authority() and can_rpc():
|
||||
if can_rpc() and multiplayer.has_multiplayer_peer():
|
||||
rpc("sync_position", current_position)
|
||||
else:
|
||||
target_visual_position = global_position
|
||||
@@ -417,7 +417,9 @@ func play_scatter_knock():
|
||||
func play_playerboard_scatter():
|
||||
"""Show the one-shot scatter VFX over the playerboard UI, but only on the
|
||||
local human player's own client (the board UI belongs to them)."""
|
||||
var is_local = name == str(multiplayer.get_unique_id())
|
||||
var is_local = false
|
||||
if multiplayer and multiplayer.has_multiplayer_peer():
|
||||
is_local = name == str(multiplayer.get_unique_id())
|
||||
var is_bot_check = is_bot or is_in_group("Bots")
|
||||
if not is_local or is_bot_check:
|
||||
return
|
||||
@@ -672,7 +674,7 @@ func _setup_character() -> void:
|
||||
return
|
||||
|
||||
var character_name = "Masbro" # Default
|
||||
var player_authority_id = get_multiplayer_authority()
|
||||
var player_authority_id = get_multiplayer_authority() if multiplayer and multiplayer.has_multiplayer_peer() else name.to_int()
|
||||
|
||||
# Look up character from LobbyManager for this player (works for all players)
|
||||
if LobbyManager:
|
||||
@@ -896,7 +898,9 @@ func sync_playerboard(new_playerboard: Array):
|
||||
|
||||
# Only update UI if this is the LOCAL HUMAN PLAYER
|
||||
# Bots managed by server (ID 1) might sync here, but we must NOT update UI for them
|
||||
var is_local = name == str(multiplayer.get_unique_id())
|
||||
var is_local = false
|
||||
if multiplayer and multiplayer.has_multiplayer_peer():
|
||||
is_local = name == str(multiplayer.get_unique_id())
|
||||
var is_bot_check = is_bot or is_in_group("Bots")
|
||||
|
||||
# DEBUG: Trace why UI updates might be happening
|
||||
@@ -1376,7 +1380,7 @@ func _process(delta):
|
||||
if LobbyManager.get_randomize_spawn() and spawn_point_selected and not visible:
|
||||
visible = true
|
||||
|
||||
if not multiplayer.has_multiplayer_peer():
|
||||
if not multiplayer or not multiplayer.has_multiplayer_peer():
|
||||
return
|
||||
|
||||
if is_multiplayer_authority():
|
||||
@@ -1478,7 +1482,7 @@ var last_sent_position: Vector3
|
||||
|
||||
func _physics_process(delta):
|
||||
# Sync position periodically (Heartbeat / Smoothing)
|
||||
if not multiplayer.has_multiplayer_peer():
|
||||
if not multiplayer or not multiplayer.has_multiplayer_peer():
|
||||
return
|
||||
|
||||
if is_multiplayer_authority():
|
||||
@@ -2018,7 +2022,7 @@ func bot_try_grab_item() -> bool:
|
||||
#if empty_slot == -1:
|
||||
#return false
|
||||
#
|
||||
#if is_multiplayer_authority():
|
||||
#if is_multiplayer_authority() and can_rpc() and multiplayer.has_multiplayer_peer():
|
||||
#rpc("bot_grab_item", grid_position, empty_slot, cell.x, cell.y, cell.z)
|
||||
#return true
|
||||
#
|
||||
@@ -2065,7 +2069,7 @@ func bot_try_grab_item() -> bool:
|
||||
#return false # no space
|
||||
#
|
||||
## Perform the grab and auto-place
|
||||
#if is_multiplayer_authority():
|
||||
#if is_multiplayer_authority() and can_rpc() and multiplayer.has_multiplayer_peer():
|
||||
## Update gridmap: remove item
|
||||
#rpc("sync_grid_item", cell.x, cell.y, cell.z, -1)
|
||||
## Update playerboard
|
||||
|
||||
@@ -4,12 +4,12 @@
|
||||
[ext_resource type="Theme" uid="uid://cxab3xxy00" path="res://assets/themes/GUI_Tekton.tres" id="1_pti1t"]
|
||||
[ext_resource type="Texture2D" uid="uid://jqvv6s55mlsk" path="res://assets/graphics/gui/BG.png" id="3_3vyn0"]
|
||||
[ext_resource type="FontFile" uid="uid://xnjx058n4tsw" path="res://assets/fonts/Nougat-ExtraBlack.ttf" id="3_font"]
|
||||
[ext_resource type="PackedScene" uid="uid://ejeamn0pyey4" path="res://assets/characters/Bob.glb" id="4_bob"]
|
||||
[ext_resource type="PackedScene" uid="uid://d4cul3w3wem5w" path="res://assets/characters/Gatot.glb" id="4_gatot"]
|
||||
[ext_resource type="PackedScene" uid="uid://1vk0mjnwkngi" path="res://assets/characters/Masbro.glb" id="4_masbro"]
|
||||
[ext_resource type="PackedScene" uid="uid://bmln7v6v5kvxg" path="res://assets/characters/Oldpop.glb" id="4_oldpop"]
|
||||
[ext_resource type="PackedScene" uid="uid://5qdk1umx2rjf" path="res://assets/characters/Bob.glb" id="4_bob"]
|
||||
[ext_resource type="PackedScene" uid="uid://bfujakntxa0v6" path="res://assets/characters/Gatot.glb" id="4_gatot"]
|
||||
[ext_resource type="PackedScene" uid="uid://cfjx66gthp1c5" path="res://assets/characters/Masbro.glb" id="4_masbro"]
|
||||
[ext_resource type="PackedScene" uid="uid://cxvbrdybeglt5" path="res://assets/characters/Oldpop.glb" id="4_oldpop"]
|
||||
[ext_resource type="Texture2D" uid="uid://brhn1dhp1gm13" path="res://assets/graphics/character_selection/sc_characters/sc_copper.png" id="4_pti1t"]
|
||||
[ext_resource type="AnimationLibrary" uid="uid://c3pyopnwibckj" path="res://assets/characters/animations/animation-pack.res" id="5_animlib"]
|
||||
[ext_resource type="AnimationLibrary" path="res://assets/characters/animations/animation-pack.res" id="5_animlib"]
|
||||
[ext_resource type="Texture2D" uid="uid://b5pp08fke7ptd" path="res://assets/graphics/gui/lobby/gold.png" id="tex_gold"]
|
||||
[ext_resource type="Texture2D" uid="uid://d0ouvm3x8h42c" path="res://assets/graphics/gui/lobby/star.png" id="tex_star"]
|
||||
|
||||
@@ -388,7 +388,7 @@ unique_name_in_owner = true
|
||||
own_world_3d = true
|
||||
transparent_bg = true
|
||||
handle_input_locally = false
|
||||
size = Vector2i(651, 554)
|
||||
size = Vector2i(651, 558)
|
||||
render_target_update_mode = 4
|
||||
|
||||
[node name="WorldEnvironment" type="WorldEnvironment" parent="MainMargin/MainHBox/CenterCol/ViewportWrapper/ViewportContainer/PreviewViewport" unique_id=1660814495]
|
||||
|
||||
Reference in New Issue
Block a user