fix(network): prevent null multiplayer peer crashes offline/teardown and fix Nakama RPC session validation

This commit is contained in:
2026-07-10 17:36:26 +08:00
parent 2b4c9d9dcb
commit b30709de3d
84 changed files with 5291 additions and 982 deletions
+20 -16
View File
@@ -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