From 5a1695e2ab2b8e43ae6cbc4a9de8bc9256619b87 Mon Sep 17 00:00:00 2001 From: Shawn Date: Fri, 3 Jul 2026 16:09:59 -0400 Subject: [PATCH] Fix Godot 4.7 typed array crash: add freed-object filtering in PropertyPool and PerObjectHistory --- addons/netfox/servers/data/per-object-history.gd | 8 +++++++- addons/netfox/servers/data/property-pool.gd | 8 +++++++- scripts/headless_server.gd | 9 ++++++++- 3 files changed, 22 insertions(+), 3 deletions(-) diff --git a/addons/netfox/servers/data/per-object-history.gd b/addons/netfox/servers/data/per-object-history.gd index 6f0f9f8..ead6b4b 100644 --- a/addons/netfox/servers/data/per-object-history.gd +++ b/addons/netfox/servers/data/per-object-history.gd @@ -11,8 +11,14 @@ func _init(p_history_size: int): _history_size = p_history_size func subjects() -> Array[Object]: + # Filter out freed objects — Godot 4.7 stricter typed arrays + # reject invalid references and crash the engine. + var valid := [] + for o in _data.keys(): + if is_instance_valid(o): + valid.append(o) var result := [] as Array[Object] - result.assign(_data.keys()) + result.assign(valid) return result func is_auth(tick: int, subject: Object) -> bool: diff --git a/addons/netfox/servers/data/property-pool.gd b/addons/netfox/servers/data/property-pool.gd index 63db467..fa39a69 100644 --- a/addons/netfox/servers/data/property-pool.gd +++ b/addons/netfox/servers/data/property-pool.gd @@ -49,8 +49,14 @@ func get_properties_of(subject: Object) -> Array[NodePath]: return properties func get_subjects() -> Array[Object]: + # Filter out freed objects — Godot 4.7 stricter typed arrays + # reject invalid references and crash the engine. + var valid := [] + for s in _properties_by_subject.keys(): + if is_instance_valid(s): + valid.append(s) var subjects := [] as Array[Object] - subjects.assign(_properties_by_subject.keys()) + subjects.assign(valid) return subjects func is_empty() -> bool: diff --git a/scripts/headless_server.gd b/scripts/headless_server.gd index b476968..2e3d746 100644 --- a/scripts/headless_server.gd +++ b/scripts/headless_server.gd @@ -110,7 +110,14 @@ func _on_first_peer_join(_peer_id: int) -> void: return _first_client_connected = true - print("[HeadlessServer] First client connected — starting netfox tick loop...") + print("[HeadlessServer] First client connected — letting avatar spawn...") + + # Wait TWO frames so PlayerSpawner._handle_new_peer() creates the avatar + # BEFORE NetworkTime starts. The tick loop needs at least one subject. + await get_tree().process_frame + await get_tree().process_frame + + print("[HeadlessServer] Starting netfox tick loop with player subjects...") # Start netfox tick loop now that we have at least one player if NetworkTime and NetworkTime.has_method("start"):