fixed most of the sheisse
This commit is contained in:
@@ -35,9 +35,9 @@ const MAX_BUFFER_SIZE: int = 2 * 1024 * 1024 # 2MB buffer threshold
|
|||||||
var last_buffer_check_time: float = 0.0
|
var last_buffer_check_time: float = 0.0
|
||||||
const BUFFER_CHECK_INTERVAL: float = 0.1 # Check buffer every 100ms
|
const BUFFER_CHECK_INTERVAL: float = 0.1 # Check buffer every 100ms
|
||||||
var client_buffer_states: Dictionary = {} # peer_id -> {buffered: int, last_check: float, skip_until: float}
|
var client_buffer_states: Dictionary = {} # peer_id -> {buffered: int, last_check: float, skip_until: float}
|
||||||
const CLIENT_BUFFER_CHECK_INTERVAL: float = 0.2 # Check client buffers every 200ms
|
const CLIENT_BUFFER_CHECK_INTERVAL: float = 0.1 # Check client buffers every 100ms (more frequent)
|
||||||
const CLIENT_BUFFER_SKIP_THRESHOLD: int = 1024 * 512 # Skip sending if buffer > 512KB
|
const CLIENT_BUFFER_SKIP_THRESHOLD: int = 1024 * 256 # Skip sending if buffer > 256KB (lower threshold to catch earlier)
|
||||||
const CLIENT_BUFFER_SKIP_DURATION: float = 2.0 # Skip sending for 2 seconds if buffer is full
|
const CLIENT_BUFFER_SKIP_DURATION: float = 3.0 # Skip sending for 3 seconds if buffer is full (longer duration)
|
||||||
var fog_node: Node2D = null
|
var fog_node: Node2D = null
|
||||||
var cached_corridor_mask: PackedInt32Array = PackedInt32Array()
|
var cached_corridor_mask: PackedInt32Array = PackedInt32Array()
|
||||||
var cached_corridor_rooms: Array = []
|
var cached_corridor_rooms: Array = []
|
||||||
@@ -193,12 +193,18 @@ func _send_gameworld_ready():
|
|||||||
if retry_count < 50: # Try up to 50 times (10 seconds total)
|
if retry_count < 50: # Try up to 50 times (10 seconds total)
|
||||||
set_meta("gameworld_ready_retry_count", retry_count + 1)
|
set_meta("gameworld_ready_retry_count", retry_count + 1)
|
||||||
LogManager.log("GameWorld: Host peer (1) not in peers and Matchbox not connected yet, retrying (" + str(retry_count + 1) + "/50)...", LogManager.CATEGORY_NETWORK)
|
LogManager.log("GameWorld: Host peer (1) not in peers and Matchbox not connected yet, retrying (" + str(retry_count + 1) + "/50)...", LogManager.CATEGORY_NETWORK)
|
||||||
get_tree().create_timer(0.2).timeout.connect(func(): _send_gameworld_ready())
|
get_tree().create_timer(0.2).timeout.connect(func():
|
||||||
|
if is_instance_valid(self) and is_inside_tree():
|
||||||
|
_send_gameworld_ready()
|
||||||
|
)
|
||||||
return
|
return
|
||||||
else:
|
else:
|
||||||
# After max retries, log warning and continue retrying (but less frequently)
|
# After max retries, log warning and continue retrying (but less frequently)
|
||||||
LogManager.log("GameWorld: Max retries reached for gameworld_ready, continuing with slower retries (every 1 second)...", LogManager.CATEGORY_NETWORK)
|
LogManager.log("GameWorld: Max retries reached for gameworld_ready, continuing with slower retries (every 1 second)...", LogManager.CATEGORY_NETWORK)
|
||||||
get_tree().create_timer(1.0).timeout.connect(func(): _send_gameworld_ready())
|
get_tree().create_timer(1.0).timeout.connect(func():
|
||||||
|
if is_instance_valid(self) and is_inside_tree():
|
||||||
|
_send_gameworld_ready()
|
||||||
|
)
|
||||||
return
|
return
|
||||||
|
|
||||||
# Peer is in list OR Matchbox connection is ready - try to send RPC
|
# Peer is in list OR Matchbox connection is ready - try to send RPC
|
||||||
@@ -328,7 +334,8 @@ func _schedule_gameworld_ready_check(peer_id: int, local_count: int, attempts: i
|
|||||||
return
|
return
|
||||||
|
|
||||||
get_tree().create_timer(0.2).timeout.connect(func():
|
get_tree().create_timer(0.2).timeout.connect(func():
|
||||||
_schedule_gameworld_ready_check(peer_id, local_count, attempts + 1)
|
if is_instance_valid(self) and is_inside_tree():
|
||||||
|
_schedule_gameworld_ready_check(peer_id, local_count, attempts + 1)
|
||||||
)
|
)
|
||||||
|
|
||||||
func _send_initial_client_sync(peer_id: int, local_count: int):
|
func _send_initial_client_sync(peer_id: int, local_count: int):
|
||||||
@@ -630,6 +637,10 @@ func _rpc_node_to_ready_peers(node: Node, method: String, args: Array = []):
|
|||||||
client_gameworld_ready.erase(peer_id)
|
client_gameworld_ready.erase(peer_id)
|
||||||
continue
|
continue
|
||||||
|
|
||||||
|
# Check if client's buffer is full - skip sending if so (CRITICAL to prevent buffer overflow errors)
|
||||||
|
if _should_skip_client_due_to_buffer(peer_id):
|
||||||
|
continue # Skip sending to this client - their buffer is full
|
||||||
|
|
||||||
# All checks passed, send RPC
|
# All checks passed, send RPC
|
||||||
# Note: Even with all checks, there's still a tiny race condition window,
|
# Note: Even with all checks, there's still a tiny race condition window,
|
||||||
# but this minimizes it significantly. If channel closes between check and send,
|
# but this minimizes it significantly. If channel closes between check and send,
|
||||||
@@ -852,7 +863,10 @@ func _notify_client_ready(peer_id: int):
|
|||||||
# Wait a bit longer to ensure the client has fully loaded the game scene and spawned all players
|
# Wait a bit longer to ensure the client has fully loaded the game scene and spawned all players
|
||||||
if not dungeon_data.is_empty():
|
if not dungeon_data.is_empty():
|
||||||
# Delay positioning to ensure client scene is fully loaded
|
# Delay positioning to ensure client scene is fully loaded
|
||||||
get_tree().create_timer(0.5).timeout.connect(func(): _position_new_joiner_and_sync_positions(peer_id))
|
get_tree().create_timer(0.5).timeout.connect(func():
|
||||||
|
if is_instance_valid(self) and is_inside_tree():
|
||||||
|
_position_new_joiner_and_sync_positions(peer_id)
|
||||||
|
)
|
||||||
|
|
||||||
# Notify all players that a client is ready (so server players can check if all are ready)
|
# Notify all players that a client is ready (so server players can check if all are ready)
|
||||||
_rpc_to_ready_peers("_client_ready_status_changed", [clients_ready.duplicate()])
|
_rpc_to_ready_peers("_client_ready_status_changed", [clients_ready.duplicate()])
|
||||||
@@ -1479,7 +1493,7 @@ func _check_tab_visibility():
|
|||||||
if OS.get_name() == "Web":
|
if OS.get_name() == "Web":
|
||||||
# Use DisplayServer to check if window is focused/visible
|
# Use DisplayServer to check if window is focused/visible
|
||||||
# This is a simple check - on web, when tab is inactive, window loses focus
|
# This is a simple check - on web, when tab is inactive, window loses focus
|
||||||
var is_visible = DisplayServer.window_get_mode() != DisplayServer.WINDOW_MODE_MINIMIZED
|
var window_visible = DisplayServer.window_get_mode() != DisplayServer.WINDOW_MODE_MINIMIZED
|
||||||
# Also check if we can poll (if process is running, tab is likely active)
|
# Also check if we can poll (if process is running, tab is likely active)
|
||||||
# On web, when tab is inactive, _process may still run but less frequently
|
# On web, when tab is inactive, _process may still run but less frequently
|
||||||
# We'll track time between _process calls as a proxy for tab activity
|
# We'll track time between _process calls as a proxy for tab activity
|
||||||
@@ -1495,7 +1509,7 @@ func _check_tab_visibility():
|
|||||||
if time_since_last_process > 1000:
|
if time_since_last_process > 1000:
|
||||||
return false
|
return false
|
||||||
|
|
||||||
return is_visible
|
return window_visible
|
||||||
return true # Always visible on non-web platforms
|
return true # Always visible on non-web platforms
|
||||||
|
|
||||||
func _process(_delta):
|
func _process(_delta):
|
||||||
@@ -1818,12 +1832,13 @@ func _update_fog_of_war(delta: float) -> void:
|
|||||||
cached_corridor_rooms = _get_rooms_connected_to_corridor(cached_corridor_mask, player_tile)
|
cached_corridor_rooms = _get_rooms_connected_to_corridor(cached_corridor_mask, player_tile)
|
||||||
cached_corridor_player_tile = player_tile
|
cached_corridor_player_tile = player_tile
|
||||||
|
|
||||||
# Build a set of allowed room IDs for fast lookup
|
# Build a set of allowed room IDs for fast lookup
|
||||||
cached_corridor_allowed_room_ids = {}
|
cached_corridor_allowed_room_ids = {}
|
||||||
for room in cached_corridor_rooms:
|
for room in cached_corridor_rooms:
|
||||||
var room_id = str(room.x) + "," + str(room.y) + "," + str(room.w) + "," + str(room.h)
|
var room_id = str(room.x) + "," + str(room.y) + "," + str(room.w) + "," + str(room.h)
|
||||||
cached_corridor_allowed_room_ids[room_id] = true
|
cached_corridor_allowed_room_ids[room_id] = true
|
||||||
|
|
||||||
|
# Use the rebuilt data
|
||||||
corridor_mask = cached_corridor_mask
|
corridor_mask = cached_corridor_mask
|
||||||
corridor_rooms = cached_corridor_rooms
|
corridor_rooms = cached_corridor_rooms
|
||||||
allowed_room_ids = cached_corridor_allowed_room_ids
|
allowed_room_ids = cached_corridor_allowed_room_ids
|
||||||
@@ -1853,21 +1868,21 @@ func _update_fog_of_war(delta: float) -> void:
|
|||||||
# OPTIMIZATION: Only do expensive per-tile checks when corridor state changed or player moved significantly
|
# OPTIMIZATION: Only do expensive per-tile checks when corridor state changed or player moved significantly
|
||||||
var needs_tile_clear = corridor_state_changed or should_rebuild_corridor
|
var needs_tile_clear = corridor_state_changed or should_rebuild_corridor
|
||||||
if needs_tile_clear:
|
if needs_tile_clear:
|
||||||
for y in range(map_size.y):
|
for y in range(map_size.y):
|
||||||
for x in range(map_size.x):
|
for x in range(map_size.x):
|
||||||
var idx = x + y * map_size.x
|
var idx = x + y * map_size.x
|
||||||
if idx < 0 or idx >= combined_seen.size():
|
if idx < 0 or idx >= combined_seen.size():
|
||||||
continue
|
continue
|
||||||
var tile_in_corridor = idx < corridor_mask.size() and corridor_mask[idx] == 1
|
var tile_in_corridor = idx < corridor_mask.size() and corridor_mask[idx] == 1
|
||||||
# Check if this tile is in a room, and if so, is it an allowed room?
|
# Check if this tile is in a room, and if so, is it an allowed room?
|
||||||
var tile_room = _find_room_at_tile(Vector2i(x, y))
|
var tile_room = _find_room_at_tile(Vector2i(x, y))
|
||||||
var in_allowed_room = false
|
var in_allowed_room = false
|
||||||
if not tile_room.is_empty():
|
if not tile_room.is_empty():
|
||||||
var room_id = str(tile_room.x) + "," + str(tile_room.y) + "," + str(tile_room.w) + "," + str(tile_room.h)
|
var room_id = str(tile_room.x) + "," + str(tile_room.y) + "," + str(tile_room.w) + "," + str(tile_room.h)
|
||||||
in_allowed_room = allowed_room_ids.has(room_id)
|
in_allowed_room = allowed_room_ids.has(room_id)
|
||||||
# Clear combined_seen for any tile not in corridor or allowed rooms
|
# Clear combined_seen for any tile not in corridor or allowed rooms
|
||||||
if not tile_in_corridor and not in_allowed_room:
|
if not tile_in_corridor and not in_allowed_room:
|
||||||
combined_seen[idx] = 0
|
combined_seen[idx] = 0
|
||||||
|
|
||||||
# Update last corridor fog update time
|
# Update last corridor fog update time
|
||||||
last_corridor_fog_update = Time.get_ticks_msec() / 1000.0
|
last_corridor_fog_update = Time.get_ticks_msec() / 1000.0
|
||||||
@@ -3766,6 +3781,12 @@ func _reassemble_dungeon_blob():
|
|||||||
_update_camera()
|
_update_camera()
|
||||||
print("GameWorld: Client - Camera updated")
|
print("GameWorld: Client - Camera updated")
|
||||||
|
|
||||||
|
# Update fog of war to reveal area around player (CRITICAL for joiner to see the map)
|
||||||
|
await get_tree().process_frame
|
||||||
|
print("GameWorld: Client - Updating fog of war after player positioning...")
|
||||||
|
_update_fog_of_war(0.0) # Pass 0.0 for delta since we're in async context
|
||||||
|
print("GameWorld: Client - Fog of war updated")
|
||||||
|
|
||||||
# Load HUD
|
# Load HUD
|
||||||
print("=== GameWorld: Client - About to load HUD (call_deferred) ===")
|
print("=== GameWorld: Client - About to load HUD (call_deferred) ===")
|
||||||
call_deferred("_load_hud")
|
call_deferred("_load_hud")
|
||||||
@@ -4323,15 +4344,18 @@ func _spawn_interactable_objects():
|
|||||||
# Check broken_objects after object is fully spawned
|
# Check broken_objects after object is fully spawned
|
||||||
if broken_objects.has(i):
|
if broken_objects.has(i):
|
||||||
print("GameWorld: Object at index ", i, " (name: ", obj.name, ") is marked as broken, breaking it now")
|
print("GameWorld: Object at index ", i, " (name: ", obj.name, ") is marked as broken, breaking it now")
|
||||||
print("GameWorld: Object state - is_broken: ", obj.is_broken if "is_broken" in obj else "N/A", ", has _sync_break: ", obj.has_method("_sync_break"))
|
print("GameWorld: Object state - is_broken: ", obj.is_broken if "is_broken" in obj else "N/A", ", has _sync_break: ", obj.has_method("_sync_break"), ", is_destroyable: ", obj.is_destroyable if "is_destroyable" in obj else "N/A")
|
||||||
# Use timer to break after object is fully ready (wait a bit to ensure sprite is set up)
|
# Use both call_deferred and timer to ensure object is fully initialized
|
||||||
# Use call_deferred as well, but with a timer backup to ensure it happens
|
|
||||||
var obj_ref = obj # Capture reference
|
var obj_ref = obj # Capture reference
|
||||||
var obj_index = i # Capture index for logging
|
var obj_index = i # Capture index for logging
|
||||||
# Use timer to ensure object is fully initialized (wait 2 frames worth of time)
|
# First try with call_deferred (runs at end of frame)
|
||||||
get_tree().create_timer(0.05).timeout.connect(func():
|
call_deferred("_break_spawned_object", obj_ref, obj_index)
|
||||||
if is_instance_valid(obj_ref):
|
# Also use timer as backup (wait longer to ensure sprite and all components are ready)
|
||||||
_break_spawned_object(obj_ref, obj_index)
|
get_tree().create_timer(0.1).timeout.connect(func():
|
||||||
|
if is_instance_valid(obj_ref) and not obj_ref.is_queued_for_deletion():
|
||||||
|
if "is_broken" in obj_ref and not obj_ref.is_broken:
|
||||||
|
print("GameWorld: Timer backup - breaking object at index ", obj_index, " (name: ", obj_ref.name, ")")
|
||||||
|
_break_spawned_object(obj_ref, obj_index)
|
||||||
)
|
)
|
||||||
|
|
||||||
LogManager.log("GameWorld: Spawned " + str(objects.size()) + " interactable objects", LogManager.CATEGORY_DUNGEON)
|
LogManager.log("GameWorld: Spawned " + str(objects.size()) + " interactable objects", LogManager.CATEGORY_DUNGEON)
|
||||||
@@ -4347,6 +4371,10 @@ func _break_spawned_object(obj: Node, obj_index: int):
|
|||||||
if "is_broken" in obj and obj.is_broken:
|
if "is_broken" in obj and obj.is_broken:
|
||||||
print("GameWorld: Object at index ", obj_index, " (name: ", obj.name, ") is already broken")
|
print("GameWorld: Object at index ", obj_index, " (name: ", obj.name, ") is already broken")
|
||||||
return
|
return
|
||||||
|
# Check if object is destroyable (chests and pillars are not destroyable)
|
||||||
|
if "is_destroyable" in obj and not obj.is_destroyable:
|
||||||
|
print("GameWorld: Object at index ", obj_index, " (name: ", obj.name, ") is not destroyable, skipping break")
|
||||||
|
return
|
||||||
if obj.has_method("_sync_break"):
|
if obj.has_method("_sync_break"):
|
||||||
print("GameWorld: Breaking object at index ", obj_index, " (name: ", obj.name, ")")
|
print("GameWorld: Breaking object at index ", obj_index, " (name: ", obj.name, ")")
|
||||||
print("GameWorld: Object state before break - is_broken: ", obj.is_broken if "is_broken" in obj else "N/A", ", is_queued_for_deletion: ", obj.is_queued_for_deletion(), ", has sprite: ", "sprite" in obj and obj.sprite != null)
|
print("GameWorld: Object state before break - is_broken: ", obj.is_broken if "is_broken" in obj else "N/A", ", is_queued_for_deletion: ", obj.is_queued_for_deletion(), ", has sprite: ", "sprite" in obj and obj.sprite != null)
|
||||||
|
|||||||
@@ -380,6 +380,12 @@ func _on_peer_disconnected(id: int):
|
|||||||
players_info.erase(id)
|
players_info.erase(id)
|
||||||
player_disconnected.emit(id, player_info)
|
player_disconnected.emit(id, player_info)
|
||||||
|
|
||||||
|
# If joiner and host (peer ID 1) disconnected, trigger server disconnection logic
|
||||||
|
if not is_hosting and id == 1:
|
||||||
|
log_print("NetworkManager: Host (peer ID 1) disconnected, triggering reconnection...")
|
||||||
|
_on_server_disconnected()
|
||||||
|
return
|
||||||
|
|
||||||
# Update room registry if hosting (player count changed)
|
# Update room registry if hosting (player count changed)
|
||||||
if is_hosting and room_registry and not room_id.is_empty():
|
if is_hosting and room_registry and not room_id.is_empty():
|
||||||
var player_count = get_all_player_ids().size()
|
var player_count = get_all_player_ids().size()
|
||||||
@@ -745,6 +751,21 @@ func _attempt_reconnect():
|
|||||||
log_print("NetworkManager: Cannot reconnect - is_hosting: " + str(is_hosting) + ", reconnection_room_id: " + reconnection_room_id)
|
log_print("NetworkManager: Cannot reconnect - is_hosting: " + str(is_hosting) + ", reconnection_room_id: " + reconnection_room_id)
|
||||||
return
|
return
|
||||||
|
|
||||||
|
# Check if we're already connected to Matchbox (host might have reconnected and we're waiting for peer ID)
|
||||||
|
if matchbox_client and matchbox_client.has("is_network_connected") and matchbox_client.is_network_connected:
|
||||||
|
log_print("NetworkManager: Already connected to Matchbox, waiting for host to assign peer ID...")
|
||||||
|
# Cancel reconnection attempt - we're already connected, just waiting for host
|
||||||
|
reconnection_attempting = false
|
||||||
|
reconnection_timer = 0.0
|
||||||
|
|
||||||
|
# Show chat message
|
||||||
|
var game_world_waiting = get_tree().get_first_node_in_group("game_world")
|
||||||
|
if game_world_waiting:
|
||||||
|
var chat_ui = game_world_waiting.get_node_or_null("ChatUI")
|
||||||
|
if chat_ui and chat_ui.has_method("add_local_message"):
|
||||||
|
chat_ui.add_local_message("System", "Waiting for host to reconnect...")
|
||||||
|
return
|
||||||
|
|
||||||
log_print("NetworkManager: Attempting to reconnect to room: " + reconnection_room_id)
|
log_print("NetworkManager: Attempting to reconnect to room: " + reconnection_room_id)
|
||||||
|
|
||||||
# Show chat message for reconnection attempt
|
# Show chat message for reconnection attempt
|
||||||
|
|||||||
Reference in New Issue
Block a user