fixed most of the sheisse

This commit is contained in:
2026-01-20 03:08:24 +01:00
parent 22cb66877c
commit 152992398c
2 changed files with 80 additions and 31 deletions

View File

@@ -35,9 +35,9 @@ const MAX_BUFFER_SIZE: int = 2 * 1024 * 1024 # 2MB buffer threshold
var last_buffer_check_time: float = 0.0
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}
const CLIENT_BUFFER_CHECK_INTERVAL: float = 0.2 # Check client buffers every 200ms
const CLIENT_BUFFER_SKIP_THRESHOLD: int = 1024 * 512 # Skip sending if buffer > 512KB
const CLIENT_BUFFER_SKIP_DURATION: float = 2.0 # Skip sending for 2 seconds if buffer is full
const CLIENT_BUFFER_CHECK_INTERVAL: float = 0.1 # Check client buffers every 100ms (more frequent)
const CLIENT_BUFFER_SKIP_THRESHOLD: int = 1024 * 256 # Skip sending if buffer > 256KB (lower threshold to catch earlier)
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 cached_corridor_mask: PackedInt32Array = PackedInt32Array()
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)
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)
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
else:
# 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)
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
# 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
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):
@@ -629,6 +636,10 @@ func _rpc_node_to_ready_peers(node: Node, method: String, args: Array = []):
if matchbox_conn_state == 3 or matchbox_conn_state == 4: # DISCONNECTED or FAILED
client_gameworld_ready.erase(peer_id)
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
# Note: Even with all checks, there's still a tiny race condition window,
@@ -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
if not dungeon_data.is_empty():
# 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)
_rpc_to_ready_peers("_client_ready_status_changed", [clients_ready.duplicate()])
@@ -1479,7 +1493,7 @@ func _check_tab_visibility():
if OS.get_name() == "Web":
# Use DisplayServer to check if window is focused/visible
# 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)
# 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
@@ -1495,7 +1509,7 @@ func _check_tab_visibility():
if time_since_last_process > 1000:
return false
return is_visible
return window_visible
return true # Always visible on non-web platforms
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_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 = {}
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
# Use the rebuilt data
corridor_mask = cached_corridor_mask
corridor_rooms = cached_corridor_rooms
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
var needs_tile_clear = corridor_state_changed or should_rebuild_corridor
if needs_tile_clear:
for y in range(map_size.y):
for x in range(map_size.x):
var idx = x + y * map_size.x
if idx < 0 or idx >= combined_seen.size():
continue
for y in range(map_size.y):
for x in range(map_size.x):
var idx = x + y * map_size.x
if idx < 0 or idx >= combined_seen.size():
continue
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?
var tile_room = _find_room_at_tile(Vector2i(x, y))
var in_allowed_room = false
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)
in_allowed_room = allowed_room_ids.has(room_id)
# Clear combined_seen for any tile not in corridor or allowed rooms
# 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 in_allowed_room = false
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)
in_allowed_room = allowed_room_ids.has(room_id)
# Clear combined_seen for any tile not in corridor or allowed rooms
if not tile_in_corridor and not in_allowed_room:
combined_seen[idx] = 0
combined_seen[idx] = 0
# Update last corridor fog update time
last_corridor_fog_update = Time.get_ticks_msec() / 1000.0
@@ -3766,6 +3781,12 @@ func _reassemble_dungeon_blob():
_update_camera()
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
print("=== GameWorld: Client - About to load HUD (call_deferred) ===")
call_deferred("_load_hud")
@@ -4323,15 +4344,18 @@ func _spawn_interactable_objects():
# Check broken_objects after object is fully spawned
if broken_objects.has(i):
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"))
# Use timer to break after object is fully ready (wait a bit to ensure sprite is set up)
# Use call_deferred as well, but with a timer backup to ensure it happens
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 both call_deferred and timer to ensure object is fully initialized
var obj_ref = obj # Capture reference
var obj_index = i # Capture index for logging
# Use timer to ensure object is fully initialized (wait 2 frames worth of time)
get_tree().create_timer(0.05).timeout.connect(func():
if is_instance_valid(obj_ref):
_break_spawned_object(obj_ref, obj_index)
# First try with call_deferred (runs at end of frame)
call_deferred("_break_spawned_object", obj_ref, obj_index)
# Also use timer as backup (wait longer to ensure sprite and all components are ready)
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)
@@ -4347,6 +4371,10 @@ func _break_spawned_object(obj: Node, obj_index: int):
if "is_broken" in obj and obj.is_broken:
print("GameWorld: Object at index ", obj_index, " (name: ", obj.name, ") is already broken")
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"):
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)

View File

@@ -380,6 +380,12 @@ func _on_peer_disconnected(id: int):
players_info.erase(id)
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)
if is_hosting and room_registry and not room_id.is_empty():
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)
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)
# Show chat message for reconnection attempt