From f51c49fcfa87a56d1c790b7286d39847c89f5f35 Mon Sep 17 00:00:00 2001 From: daddy Date: Sat, 5 Sep 2026 21:27:57 +0000 Subject: [PATCH] PC3 Phase 1.1: game boots clean from a bare clone Root cause: a stale .godot/global_script_class_cache.cfg was force-committed to the repo (last regenerated before the avatar system merged), and Godot does not rebuild the global class registry on a plain headless scene run -- only an editor scan / `--import` does. So every cross-file `class_name` reference in the avatar module failed to resolve from a fresh clone, and the AvatarSystem autoload failed to instantiate entirely. - Untrack .godot/ and sandbox_probe/.godot/ (already in .gitignore; were force-added during old WIP checkpoints) - Reference sibling avatar scripts via preload() constants instead of their global class_name, so the module parses independent of scan order. Keeps the class_name declarations for editor/tooling use. - avatar_loadout.gd's static from_json() factory self-references via a preload const too (a script can't resolve its own class_name mid-compile on a cold cache). - smoke_test_avatar_system.gd: same preload treatment so it runs from a bare `--script` invocation. Verified from a wiped .godot/: main_menu.tscn and game.tscn boot with zero script errors; smoke_test_avatar_system 10/10, smoke_test_player 15/15, smoke_test_main_menu_ui 13/13, smoke_test_test_level PASS. Co-Authored-By: Claude Sonnet 5 Claude-Session: https://claude.ai/code/session_015jtxAiu5dAW4WxCNdGkrkJ --- .godot/.gdignore | 1 - .godot/editor/filesystem_cache10 | 77 ------------- ...lding-3ad5c15c4f3250da0cc7c1af1770d85f.cfg | 5 - .godot/editor/project_metadata.cfg | 11 -- ...lding-73010d7d11a63b36042ee0ee3baaed9c.cfg | 5 - .godot/global_script_class_cache.cfg | 105 ------------------ .godot/uid_cache.bin | Bin 1348 -> 0 bytes sandbox_probe/.godot/.gdignore | 1 - scripts/avatar/avatar_catalog.gd | 3 + scripts/avatar/avatar_customization_ui.gd | 7 ++ scripts/avatar/avatar_loader.gd | 3 + scripts/avatar/avatar_loadout.gd | 11 +- scripts/avatar/avatar_system.gd | 10 ++ scripts/avatar/avatar_visualizer.gd | 5 + scripts/smoke_test_avatar_system.gd | 4 + 15 files changed, 42 insertions(+), 206 deletions(-) delete mode 100644 .godot/.gdignore delete mode 100644 .godot/editor/filesystem_cache10 delete mode 100644 .godot/editor/main.tscn-folding-3ad5c15c4f3250da0cc7c1af1770d85f.cfg delete mode 100644 .godot/editor/project_metadata.cfg delete mode 100644 .godot/editor/server.tscn-folding-73010d7d11a63b36042ee0ee3baaed9c.cfg delete mode 100644 .godot/global_script_class_cache.cfg delete mode 100644 .godot/uid_cache.bin delete mode 100644 sandbox_probe/.godot/.gdignore diff --git a/.godot/.gdignore b/.godot/.gdignore deleted file mode 100644 index 8b13789..0000000 --- a/.godot/.gdignore +++ /dev/null @@ -1 +0,0 @@ - diff --git a/.godot/editor/filesystem_cache10 b/.godot/editor/filesystem_cache10 deleted file mode 100644 index 85e1158..0000000 --- a/.godot/editor/filesystem_cache10 +++ /dev/null @@ -1,77 +0,0 @@ -63f7b34db8d8cdea90c76aacccf841ec -::res://::1787275036 -export_presets.cfg::TextFile::-1::1787272930::0::1::::<><><>0<>0<><>:: -README.md::TextFile::-1::1787272930::0::1::::<><><>0<>0<><>:: -::res://addons/::1787272930 -::res://assets/::1787272930 -::res://backend/::1787272930 -alembic.ini::TextFile::-1::1787272930::0::1::::<><><>0<>0<><>:: -docker-compose.yml::TextFile::-1::1787272930::0::1::::<><><>0<>0<><>:: -pytest.ini::TextFile::-1::1787272930::0::1::::<><><>0<>0<><>:: -requirements.txt::TextFile::-1::1787272930::0::1::::<><><>0<>0<><>:: -::res://backend/alembic/::1787272930 -::res://backend/alembic/versions/::1787272930 -::res://backend/app/::1787272930 -::res://backend/app/models/::1787272930 -::res://backend/app/routers/::1787272930 -::res://backend/app/services/::1787272930 -::res://backend/tests/::1787272930 -::res://docs/::1787272930 -::res://docs/architecture/::1787272930 -avatar.md::TextFile::-1::1787272930::0::1::::<><><>0<>0<><>:: -content_moderation.md::TextFile::-1::1787272930::0::1::::<><><>0<>0<><>:: -editor.md::TextFile::-1::1787272930::0::1::::<><><>0<>0<><>:: -networking.md::TextFile::-1::1787272930::0::1::::<><><>0<>0<><>:: -pc2_test_level.md::TextFile::-1::1787272930::0::1::::<><><>0<>0<><>:: -scripting_sandbox.md::TextFile::-1::1787272930::0::1::::<><><>0<>0<><>:: -world.md::TextFile::-1::1787272930::0::1::::<><><>0<>0<><>:: -::res://docs/checkpoints/::1787272930 -PC1.md::TextFile::-1::1787272930::0::1::::<><><>0<>0<><>:: -::res://docs/planning/::1787272930 -client-server-wiring.md::TextFile::-1::1787272930::0::1::::<><><>0<>0<><>:: -economy.md::TextFile::-1::1787272930::0::1::::<><><>0<>0<><>:: -social.md::TextFile::-1::1787272930::0::1::::<><><>0<>0<><>:: -::res://docs/testing/::1787275036 -README.md::TextFile::-1::1787272930::0::1::::<><><>0<>0<><>:: -test_inventory.md::TextFile::-1::1787275036::0::1::::<><><>0<>0<><>:: -::res://scenes/::1787275036 -game.tscn::PackedScene::-1::1787272930::0::1::::<><><>0<>0<><>::res://scenes/worlds/test_level.tscn<>res://scenes/player.tscn -main.tscn::PackedScene::-1::1787272930::0::1::::<><><>0<>0<><>::res://scripts/chunk_stream_manager.gd -main_menu.tscn::PackedScene::-1::1787272930::0::1::::<><><>0<>0<><>::res://scripts/main_menu.gd -player.tscn::PackedScene::-1::1787272930::0::1::::<><><>0<>0<><>::res://scripts/player.gd -server.tscn::PackedScene::-1::1787275036::0::1::::<><><>0<>0<><>::res://scripts/server.gd -server_player.tscn::PackedScene::-1::1787275036::0::1::::<><><>0<>0<><>::res://scripts/player_spawner.gd -world_list.tscn::PackedScene::-1::1787272930::0::1::::<><><>0<>0<><>::res://scripts/world_list.gd -::res://scenes/worlds/::1787272930 -test_level.tscn::PackedScene::-1::1787272930::0::1::::<><><>0<>0<><>:: -::res://scripts/::1787275098 -api_client.gd::GDScript::5412288119287126975::1787272930::0::1::::<>Node<><>0<>0<><>:: -chunk.gd::GDScript::7364239597441807588::1787272930::0::1::::Chunk<>Node3D<><>0<>0<><>:: -chunk_data_decoder.gd::GDScript::3922068956043581888::1787272930::0::1::::ChunkDataDecoder<>RefCounted<><>0<>0<><>:: -chunk_scene.gd::GDScript::9207602004456600331::1787272930::0::1::::ChunkScene<>Node3D<><>0<>0<><>:: -chunk_stream_manager.gd::GDScript::2366645676568508746::1787272930::0::1::::ChunkStreamManager<>Node<><>0<>0<><>:: -dynamic_object_node.gd::GDScript::8863349716576659118::1787272930::0::1::::DynamicObjectNode<>Node3D<><>0<>0<><>:: -main_menu.gd::GDScript::2834562513277893052::1787272930::0::1::::<>Control<><>0<>0<><>:: -network_client.gd::GDScript::5408296350353838185::1787275098::0::1::::NetworkClient<>Node<><>0<>0<><>:: -network_player_proxy.gd::GDScript::4787436336771232797::1787275036::0::1::::<>Node<><>0<>0<><>:: -network_receiver_proxy.gd::GDScript::6742760436861380520::1787275036::0::1::::<>Node<><>0<>0<><>:: -player.gd::GDScript::4510795778937304264::1787275036::0::1::::Player<>CharacterBody3D<><>0<>0<><>:: -player_spawner.gd::GDScript::3637338339553764865::1787275036::0::1::::PlayerNode<>CharacterBody3D<><>0<>0<><>:: -server.gd::GDScript::6336910781803684784::1787275036::0::1::::<>Node<><>0<>0<><>:: -smoke_test_api.gd::GDScript::1760923834350699002::1787272930::0::1::::<>SceneTree<><>0<>0<><>:: -smoke_test_auth.gd::GDScript::3027816090663230577::1787272930::0::1::::<>SceneTree<><>0<>0<><>:: -smoke_test_e2e.gd::GDScript::9086409960595132737::1787272930::0::1::::<>SceneTree<><>0<>0<><>:: -smoke_test_enter_world.gd::GDScript::4171815758762850910::1787275036::0::1::::<>SceneTree<><>0<>0<><>:: -smoke_test_main_menu_back.gd::GDScript::4436937414562273157::1787272930::0::1::::<>SceneTree<><>0<>0<><>:: -smoke_test_main_menu_full_flow.gd::GDScript::1948803640558784862::1787272930::0::1::::<>SceneTree<><>0<>0<><>:: -smoke_test_main_menu_ui.gd::GDScript::396938597300585484::1787272930::0::1::::<>SceneTree<><>0<>0<><>:: -smoke_test_network_client.gd::GDScript::3109386529184855644::1787275067::0::1::::<>SceneTree<><>0<>0<><>:: -smoke_test_player.gd::GDScript::3497042634752676337::1787272930::0::1::::<>SceneTree<><>0<>0<><>:: -smoke_test_test_level.gd::GDScript::2607555612829866949::1787272930::0::1::::<>SceneTree<><>0<>0<><>:: -smoke_test_world_list.gd::GDScript::4300757286283894147::1787272930::0::1::::<>SceneTree<><>0<>0<><>:: -static_object_node.gd::GDScript::5276389741929225897::1787272930::0::1::::StaticObjectNode<>Node3D<><>0<>0<><>:: -tile_def.gd::GDScript::2430528079284085109::1787272930::0::1::::TileDef<>RefCounted<><>0<>0<><>:: -tile_layer.gd::GDScript::4797906255131447844::1787272930::0::1::::TileLayer<>RefCounted<><>0<>0<><>:: -tile_layer_node.gd::GDScript::9033225196818247616::1787272930::0::1::::TileLayerNode<>Node3D<><>0<>0<><>:: -world_list.gd::GDScript::6119591848847211153::1787275036::0::1::::<>Control<><>0<>0<><>:: -world_object.gd::GDScript::6429674491464200335::1787272930::0::1::::WorldObject<>RefCounted<><>0<>0<><>:: diff --git a/.godot/editor/main.tscn-folding-3ad5c15c4f3250da0cc7c1af1770d85f.cfg b/.godot/editor/main.tscn-folding-3ad5c15c4f3250da0cc7c1af1770d85f.cfg deleted file mode 100644 index 82cb735..0000000 --- a/.godot/editor/main.tscn-folding-3ad5c15c4f3250da0cc7c1af1770d85f.cfg +++ /dev/null @@ -1,5 +0,0 @@ -[folding] - -node_unfolds=[NodePath("."), PackedStringArray("Layout"), NodePath("CenterContainer"), PackedStringArray("Layout"), NodePath("CenterContainer/VBoxContainer"), PackedStringArray("Layout", "Theme Overrides"), NodePath("CenterContainer/VBoxContainer/TitleLabel"), PackedStringArray("Layout", "Theme Overrides"), NodePath("CenterContainer/VBoxContainer/CustomizeButton"), PackedStringArray("Layout"), NodePath("CenterContainer/VBoxContainer/PlayButton"), PackedStringArray("Layout"), NodePath("CenterContainer/VBoxContainer/QuitButton"), PackedStringArray("Layout")] -resource_unfolds=[] -nodes_folded=[] diff --git a/.godot/editor/project_metadata.cfg b/.godot/editor/project_metadata.cfg deleted file mode 100644 index fbf5d53..0000000 --- a/.godot/editor/project_metadata.cfg +++ /dev/null @@ -1,11 +0,0 @@ -[game_view] - -select_mode=0 - -[editor_metadata] - -executable_path="/home/daddy/godot/godot" - -[recent_files] - -scenes=["res://scenes/main_menu.tscn", "res://scenes/server.tscn"] diff --git a/.godot/editor/server.tscn-folding-73010d7d11a63b36042ee0ee3baaed9c.cfg b/.godot/editor/server.tscn-folding-73010d7d11a63b36042ee0ee3baaed9c.cfg deleted file mode 100644 index 1d019fa..0000000 --- a/.godot/editor/server.tscn-folding-73010d7d11a63b36042ee0ee3baaed9c.cfg +++ /dev/null @@ -1,5 +0,0 @@ -[folding] - -node_unfolds=[] -resource_unfolds=[] -nodes_folded=[] diff --git a/.godot/global_script_class_cache.cfg b/.godot/global_script_class_cache.cfg deleted file mode 100644 index 68ebafa..0000000 --- a/.godot/global_script_class_cache.cfg +++ /dev/null @@ -1,105 +0,0 @@ -list=[{ -"base": &"Node3D", -"class": &"Chunk", -"icon": "", -"is_abstract": false, -"is_tool": false, -"language": &"GDScript", -"path": "res://scripts/chunk.gd" -}, { -"base": &"RefCounted", -"class": &"ChunkDataDecoder", -"icon": "", -"is_abstract": false, -"is_tool": false, -"language": &"GDScript", -"path": "res://scripts/chunk_data_decoder.gd" -}, { -"base": &"Node3D", -"class": &"ChunkScene", -"icon": "", -"is_abstract": false, -"is_tool": false, -"language": &"GDScript", -"path": "res://scripts/chunk_scene.gd" -}, { -"base": &"Node", -"class": &"ChunkStreamManager", -"icon": "", -"is_abstract": false, -"is_tool": false, -"language": &"GDScript", -"path": "res://scripts/chunk_stream_manager.gd" -}, { -"base": &"Node3D", -"class": &"DynamicObjectNode", -"icon": "", -"is_abstract": false, -"is_tool": false, -"language": &"GDScript", -"path": "res://scripts/dynamic_object_node.gd" -}, { -"base": &"Node", -"class": &"NetworkClient", -"icon": "", -"is_abstract": false, -"is_tool": false, -"language": &"GDScript", -"path": "res://scripts/network_client.gd" -}, { -"base": &"CharacterBody3D", -"class": &"Player", -"icon": "", -"is_abstract": false, -"is_tool": false, -"language": &"GDScript", -"path": "res://scripts/player.gd" -}, { -"base": &"CharacterBody3D", -"class": &"PlayerNode", -"icon": "", -"is_abstract": false, -"is_tool": false, -"language": &"GDScript", -"path": "res://scripts/player_spawner.gd" -}, { -"base": &"Node3D", -"class": &"StaticObjectNode", -"icon": "", -"is_abstract": false, -"is_tool": false, -"language": &"GDScript", -"path": "res://scripts/static_object_node.gd" -}, { -"base": &"RefCounted", -"class": &"TileDef", -"icon": "", -"is_abstract": false, -"is_tool": false, -"language": &"GDScript", -"path": "res://scripts/tile_def.gd" -}, { -"base": &"RefCounted", -"class": &"TileLayer", -"icon": "", -"is_abstract": false, -"is_tool": false, -"language": &"GDScript", -"path": "res://scripts/tile_layer.gd" -}, { -"base": &"Node3D", -"class": &"TileLayerNode", -"icon": "", -"is_abstract": false, -"is_tool": false, -"language": &"GDScript", -"path": "res://scripts/tile_layer_node.gd" -}, { -"base": &"RefCounted", -"class": &"WorldObject", -"icon": "", -"is_abstract": false, -"is_tool": false, -"language": &"GDScript", -"path": "res://scripts/world_object.gd" -}] diff --git a/.godot/uid_cache.bin b/.godot/uid_cache.bin deleted file mode 100644 index 91bf0931afdae1815823039f7eaa7cb72156b280..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1348 zcmb1RU|`rUty>c3FXJr@WEZ6tTj}c;Cl_TFloabH7G%aJ=VYelmFT6XJSj=4d&*&) zCWfIbIioZ$8>r&I%n7yc|Kyq}V^5?%8Qdz z^HPE8yedU>w=myOP{pOLq$o8pH$FEpFEJf#@Vc})1+zagRI6ZUNU6+A%*{-W&rixq zO)iPg1G)rg!5+(rh0~J5)TJ=g=O$+6#pkBxl>${|UU(SvK!ML&9z#`LYDsy1Q8pr| zWWR9l`<-0igy~|K(t@1C%G9Fxf};G2N}$;*)Yr8r^c707MH{WQw&gP;T%K9jo(GrwO07#}$)KrOA;=ZgwyPqx*<=Ms1<$0sEwgA#Jw z#JWFIySSzGiPM!1Y?g*mBZsX0JbHQ$xj`X0UB8m|s;ILGH? z7K2KNmG?|eG&fj!V&?zilEe~>Y*(rnE|>XZm!c$wlS(pkQh^0U8qiD?p*@oVr%iRj zES$iKz}X0>?7;fc8zp5iwF=nHg5)-6cAxm`r9iC4+Yn46QGC;HARGGVlvSiGhLI3u JkTL{lHvlxR@)!UB diff --git a/sandbox_probe/.godot/.gdignore b/sandbox_probe/.godot/.gdignore deleted file mode 100644 index 8b13789..0000000 --- a/sandbox_probe/.godot/.gdignore +++ /dev/null @@ -1 +0,0 @@ - diff --git a/scripts/avatar/avatar_catalog.gd b/scripts/avatar/avatar_catalog.gd index 898bc85..4c74bca 100644 --- a/scripts/avatar/avatar_catalog.gd +++ b/scripts/avatar/avatar_catalog.gd @@ -9,6 +9,9 @@ class_name AvatarCatalog extends RefCounted +# Preloaded rather than referenced by global class_name -- see avatar_loadout.gd. +const AvatarLoadout := preload("res://scripts/avatar/avatar_loadout.gd") + ## Emitted when the catalog is loaded successfully. signal catalog_loaded diff --git a/scripts/avatar/avatar_customization_ui.gd b/scripts/avatar/avatar_customization_ui.gd index 1602d74..df7aba9 100644 --- a/scripts/avatar/avatar_customization_ui.gd +++ b/scripts/avatar/avatar_customization_ui.gd @@ -12,6 +12,13 @@ class_name AvatarCustomizationUI extends Control +# Preloaded rather than referenced by global class_name -- see avatar_loadout.gd. +const AvatarLoadout := preload("res://scripts/avatar/avatar_loadout.gd") +const AvatarCatalog := preload("res://scripts/avatar/avatar_catalog.gd") +const AvatarLoader := preload("res://scripts/avatar/avatar_loader.gd") +const AvatarVisualizer := preload("res://scripts/avatar/avatar_visualizer.gd") +const AvatarSlotRegistry := preload("res://scripts/avatar/avatar_slot_registry.gd") + # --------------------------------------------------------------------------- # Signals diff --git a/scripts/avatar/avatar_loader.gd b/scripts/avatar/avatar_loader.gd index f104e85..629dc22 100644 --- a/scripts/avatar/avatar_loader.gd +++ b/scripts/avatar/avatar_loader.gd @@ -12,6 +12,9 @@ class_name AvatarLoader extends Node +# Preloaded rather than referenced by global class_name -- see avatar_loadout.gd. +const AvatarCatalog := preload("res://scripts/avatar/avatar_catalog.gd") + # --------------------------------------------------------------------------- # Constants diff --git a/scripts/avatar/avatar_loadout.gd b/scripts/avatar/avatar_loadout.gd index ba996d9..050527f 100644 --- a/scripts/avatar/avatar_loadout.gd +++ b/scripts/avatar/avatar_loadout.gd @@ -8,6 +8,15 @@ class_name AvatarLoadout extends RefCounted +# Sibling avatar scripts are referenced via preload rather than by their +# global class_name so this module parses correctly from a bare clone (a +# headless `godot ` run does not rebuild the global class registry; +# only an editor scan / `--import` does). See docs/architecture/avatar.md. +const AvatarSlotRegistry := preload("res://scripts/avatar/avatar_slot_registry.gd") +# Self-reference for the static factory below -- referring to our own +# class_name doesn't resolve during a cold-cache compile (see note above). +const _Self := preload("res://scripts/avatar/avatar_loadout.gd") + ## Maximum number of accessories allowed in the accessory slot. const MAX_ACCESSORIES: int = 8 @@ -58,7 +67,7 @@ func _init() -> void: ## Create a loadout from a JSON string. static func from_json(json_string: String) -> AvatarLoadout: - var loadout := AvatarLoadout.new() + var loadout := _Self.new() var data: Variant = JSON.parse_string(json_string) if not (data is Dictionary): loadout.validation_errors.append("Loadout JSON root is not an object") diff --git a/scripts/avatar/avatar_system.gd b/scripts/avatar/avatar_system.gd index 74e8bd0..0066cbb 100644 --- a/scripts/avatar/avatar_system.gd +++ b/scripts/avatar/avatar_system.gd @@ -10,6 +10,16 @@ extends Node +# Sibling avatar scripts are referenced via preload rather than by their global +# class_name so this autoload parses correctly from a bare clone -- a headless +# `godot ` run does not rebuild the global class registry (only an editor +# scan / `--import` does), and this script is parsed at startup before any such +# scan could have happened. See docs/architecture/avatar.md. +const AvatarCatalog := preload("res://scripts/avatar/avatar_catalog.gd") +const AvatarLoader := preload("res://scripts/avatar/avatar_loader.gd") +const AvatarLoadout := preload("res://scripts/avatar/avatar_loadout.gd") +const AvatarSlotRegistry := preload("res://scripts/avatar/avatar_slot_registry.gd") + # --------------------------------------------------------------------------- # Signals diff --git a/scripts/avatar/avatar_visualizer.gd b/scripts/avatar/avatar_visualizer.gd index eee9cb0..45e644d 100644 --- a/scripts/avatar/avatar_visualizer.gd +++ b/scripts/avatar/avatar_visualizer.gd @@ -23,6 +23,11 @@ class_name AvatarVisualizer extends Object +# Preloaded rather than referenced by global class_name -- see avatar_loadout.gd. +const AvatarLoadout := preload("res://scripts/avatar/avatar_loadout.gd") +const AvatarLoader := preload("res://scripts/avatar/avatar_loader.gd") +const AvatarSlotRegistry := preload("res://scripts/avatar/avatar_slot_registry.gd") + ## Emitted when the visualizer has finished building the render tree. signal build_complete diff --git a/scripts/smoke_test_avatar_system.gd b/scripts/smoke_test_avatar_system.gd index b342a9c..e7fac75 100644 --- a/scripts/smoke_test_avatar_system.gd +++ b/scripts/smoke_test_avatar_system.gd @@ -13,6 +13,10 @@ extends SceneTree ## Run with: ## godot --headless --path . --script res://scripts/smoke_test_avatar_system.gd +# Preloaded rather than referenced by global class_name so this test also +# runs from a cold cache (a bare `--script` run with no prior --import). +const AvatarLoadout := preload("res://scripts/avatar/avatar_loadout.gd") + var passed := 0 var failed := 0