From 06c88179964e735f0ce2f4fc00a2184760fcbaf2 Mon Sep 17 00:00:00 2001 From: chrisbell Date: Tue, 21 Oct 2025 22:18:29 -0500 Subject: [PATCH] Datapacks system mostly complete --- sessionzero-client/datapack_test.tres | 22 ++ sessionzero-client/project.godot | 4 + .../scenes/desktop/main_ui.tscn | 6 +- .../scripts/datapacks/datapack_manager.gd | 76 ++++ .../scripts/datapacks/datapack_manager.gd.uid | 1 + .../scripts/datapacks/datapack_tests.gd | 374 ++++++++++++++++++ .../datapack_tests.gd.uid} | 0 .../datapacks/models/character_template.gd | 13 + .../models/character_template.gd.uid | 1 + .../models/datapack_dependency.gd | 0 .../models/datapack_dependency.gd.uid | 0 .../{ => datapacks}/models/datapack_model.gd | 0 .../models/datapack_model.gd.uid | 0 .../{ => datapacks}/models/dataset_model.gd | 0 .../models/dataset_model.gd.uid | 0 .../datapacks/models/session_template.gd | 13 + .../datapacks/models/session_template.gd.uid | 1 + .../{ => datapacks}/models/szobject.gd | 0 .../{ => datapacks}/models/szobject.gd.uid | 0 .../datapacks/models/template_model.gd | 7 + .../datapacks/models/template_model.gd.uid | 1 + .../other}/data_field_value.gd | 35 +- .../other}/data_field_value.gd.uid | 0 .../other}/dataset_entry.gd | 0 .../other}/dataset_entry.gd.uid | 0 .../other}/dataset_group.gd | 0 .../other}/dataset_group.gd.uid | 0 .../datapacks/other/dependency_source.gd | 20 + .../datapacks/other/dependency_source.gd.uid | 1 + .../datapacks/other/formula_resolver.gd | 52 +++ .../datapacks/other/formula_resolver.gd.uid | 1 + .../scripts/datapacks/other/formula_value.gd | 10 + .../datapacks/other/formula_value.gd.uid | 1 + .../scripts/datapacks/other/list_value.gd | 5 + .../scripts/datapacks/other/list_value.gd.uid | 1 + .../scripts/datapacks/other/template_field.gd | 22 ++ .../datapacks/other/template_field.gd.uid | 1 + .../scripts/datapacks/other/template_group.gd | 8 + .../datapacks/other/template_group.gd.uid | 1 + .../scripts/helpers/datapack_creator.gd | 69 +--- .../scripts/helpers/datapack_loader.gd | 15 +- sessionzero-client/scripts/test.gd | 12 - 42 files changed, 682 insertions(+), 91 deletions(-) create mode 100644 sessionzero-client/datapack_test.tres create mode 100644 sessionzero-client/scripts/datapacks/datapack_manager.gd create mode 100644 sessionzero-client/scripts/datapacks/datapack_manager.gd.uid create mode 100644 sessionzero-client/scripts/datapacks/datapack_tests.gd rename sessionzero-client/scripts/{test.gd.uid => datapacks/datapack_tests.gd.uid} (100%) create mode 100644 sessionzero-client/scripts/datapacks/models/character_template.gd create mode 100644 sessionzero-client/scripts/datapacks/models/character_template.gd.uid rename sessionzero-client/scripts/{ => datapacks}/models/datapack_dependency.gd (100%) rename sessionzero-client/scripts/{ => datapacks}/models/datapack_dependency.gd.uid (100%) rename sessionzero-client/scripts/{ => datapacks}/models/datapack_model.gd (100%) rename sessionzero-client/scripts/{ => datapacks}/models/datapack_model.gd.uid (100%) rename sessionzero-client/scripts/{ => datapacks}/models/dataset_model.gd (100%) rename sessionzero-client/scripts/{ => datapacks}/models/dataset_model.gd.uid (100%) create mode 100644 sessionzero-client/scripts/datapacks/models/session_template.gd create mode 100644 sessionzero-client/scripts/datapacks/models/session_template.gd.uid rename sessionzero-client/scripts/{ => datapacks}/models/szobject.gd (100%) rename sessionzero-client/scripts/{ => datapacks}/models/szobject.gd.uid (100%) create mode 100644 sessionzero-client/scripts/datapacks/models/template_model.gd create mode 100644 sessionzero-client/scripts/datapacks/models/template_model.gd.uid rename sessionzero-client/scripts/{models/schema => datapacks/other}/data_field_value.gd (83%) rename sessionzero-client/scripts/{models/schema => datapacks/other}/data_field_value.gd.uid (100%) rename sessionzero-client/scripts/{models/schema => datapacks/other}/dataset_entry.gd (100%) rename sessionzero-client/scripts/{models/schema => datapacks/other}/dataset_entry.gd.uid (100%) rename sessionzero-client/scripts/{models/schema => datapacks/other}/dataset_group.gd (100%) rename sessionzero-client/scripts/{models/schema => datapacks/other}/dataset_group.gd.uid (100%) create mode 100644 sessionzero-client/scripts/datapacks/other/dependency_source.gd create mode 100644 sessionzero-client/scripts/datapacks/other/dependency_source.gd.uid create mode 100644 sessionzero-client/scripts/datapacks/other/formula_resolver.gd create mode 100644 sessionzero-client/scripts/datapacks/other/formula_resolver.gd.uid create mode 100644 sessionzero-client/scripts/datapacks/other/formula_value.gd create mode 100644 sessionzero-client/scripts/datapacks/other/formula_value.gd.uid create mode 100644 sessionzero-client/scripts/datapacks/other/list_value.gd create mode 100644 sessionzero-client/scripts/datapacks/other/list_value.gd.uid create mode 100644 sessionzero-client/scripts/datapacks/other/template_field.gd create mode 100644 sessionzero-client/scripts/datapacks/other/template_field.gd.uid create mode 100644 sessionzero-client/scripts/datapacks/other/template_group.gd create mode 100644 sessionzero-client/scripts/datapacks/other/template_group.gd.uid delete mode 100644 sessionzero-client/scripts/test.gd diff --git a/sessionzero-client/datapack_test.tres b/sessionzero-client/datapack_test.tres new file mode 100644 index 0000000..780d4fb --- /dev/null +++ b/sessionzero-client/datapack_test.tres @@ -0,0 +1,22 @@ +[gd_resource type="Resource" script_class="DatapackModel" load_steps=8 format=3 uid="uid://cuyhpk5xnm735"] + +[ext_resource type="Script" uid="uid://1eht88nmv63j" path="res://scripts/datapacks/models/datapack_dependency.gd" id="1_0qrwo"] +[ext_resource type="Script" uid="uid://b2k68if5pcfyn" path="res://scripts/helpers/datetime.gd" id="1_jdefw"] +[ext_resource type="Script" uid="uid://dgj5rubcp6v00" path="res://scripts/datapacks/models/datapack_model.gd" id="2_3lhj8"] +[ext_resource type="Script" uid="uid://xiy5j06o8254" path="res://scripts/helpers/guid.gd" id="3_8vm13"] +[ext_resource type="Script" uid="uid://c8co7n1xvfmou" path="res://scripts/datapacks/models/szobject.gd" id="3_jdefw"] + +[sub_resource type="Resource" id="Resource_4o47k"] +script = ExtResource("1_jdefw") +metadata/_custom_type_script = "uid://b2k68if5pcfyn" + +[sub_resource type="Resource" id="Resource_rn2pn"] +script = ExtResource("3_8vm13") +metadata/_custom_type_script = "uid://xiy5j06o8254" + +[resource] +script = ExtResource("2_3lhj8") +guid = SubResource("Resource_rn2pn") +created_at = SubResource("Resource_4o47k") +sz_objects = Array[ExtResource("3_jdefw")]([null]) +metadata/_custom_type_script = "uid://dgj5rubcp6v00" diff --git a/sessionzero-client/project.godot b/sessionzero-client/project.godot index 9c0407a..941c834 100644 --- a/sessionzero-client/project.godot +++ b/sessionzero-client/project.godot @@ -15,6 +15,10 @@ run/main_scene="uid://fy5iji5t58jk" config/features=PackedStringArray("4.5", "GL Compatibility") config/icon="uid://bxsq8il8lgcq2" +[autoload] + +DatapackManager="*res://scripts/datapacks/datapack_manager.gd" + [gui] theme/custom="uid://mu3c8g7q4ygp" diff --git a/sessionzero-client/scenes/desktop/main_ui.tscn b/sessionzero-client/scenes/desktop/main_ui.tscn index f342df1..0ba2131 100644 --- a/sessionzero-client/scenes/desktop/main_ui.tscn +++ b/sessionzero-client/scenes/desktop/main_ui.tscn @@ -1,9 +1,8 @@ [gd_scene load_steps=2 format=3 uid="uid://fy5iji5t58jk"] -[ext_resource type="Script" uid="uid://b6xurr0segcug" path="res://scripts/test.gd" id="1_ffwby"] +[ext_resource type="Script" uid="uid://b6xurr0segcug" path="res://scripts/datapacks/datapack_tests.gd" id="1_ffwby"] [node name="MainUI" type="CanvasLayer"] -script = ExtResource("1_ffwby") [node name="Panel" type="Panel" parent="."] anchors_preset = 15 @@ -12,3 +11,6 @@ anchor_bottom = 1.0 grow_horizontal = 2 grow_vertical = 2 theme_type_variation = &"Background_Panel" + +[node name="DatapackTests" type="Node" parent="."] +script = ExtResource("1_ffwby") diff --git a/sessionzero-client/scripts/datapacks/datapack_manager.gd b/sessionzero-client/scripts/datapacks/datapack_manager.gd new file mode 100644 index 0000000..48ba843 --- /dev/null +++ b/sessionzero-client/scripts/datapacks/datapack_manager.gd @@ -0,0 +1,76 @@ +# datapack_manager.gd +extends Node + +var loaded_packs: Dictionary[String, DatapackModel] = {} + + +func clear_loaded_packs(): + loaded_packs.clear() + + +func register_pack(datapack: DatapackModel): + if datapack and datapack.guid: + var guid_str = datapack.guid.to_string() + loaded_packs[guid_str] = datapack + print("DatapackManager: Registered pack '%s' (%s)" % [datapack.name, guid_str]) + else: + push_error("DatapackManager: Attempted to register invalid or un-GUIDed datapack.") + + +func unregister_pack(pack_guid: String): + if loaded_packs.has(pack_guid): + loaded_packs.erase(pack_guid) + print("DatapackManager: Unregistered pack with GUID %s." % pack_guid) + +# --- DEPENDENCY RESOLUTION --- + +## Resolves a single SzObject ID across all loaded packs. +## Used by SPECIFIC_DATASET mode when the pack GUID is unknown (or referencing local content). +func resolve_object_dependency(object_id: String) -> SzObject: + for pack_guid in loaded_packs: + var pack = loaded_packs[pack_guid] + var obj = pack.get_object_by_id(object_id) + + if obj: + return obj + + push_error("Dependency Resolution Failed: Could not find object with ID '%s' in any loaded datapack." % object_id) + return null + + +## Finds all SzObjects (Datasets or Templates) across all loaded packs +## that match a given sz_type string. +## Used by DATASET_TYPE_WILDCARD and CHARACTER_TEMPLATE_REF modes. +func resolve_type_wildcard(sz_type: String) -> Array[SzObject]: + var matching_objects: Array[SzObject] = [] + for pack_guid in loaded_packs: + var pack = loaded_packs[pack_guid] + + for obj in pack.sz_objects: + if obj.sz_type == sz_type: + matching_objects.append(obj) + + if matching_objects.is_empty(): + push_warning("Wildcard Resolution Failed: Could not find any object with sz_type '%s' in loaded datapacks." % sz_type) + + return matching_objects + +## Main entry point for resolving a DependencySource defined in a Template. +## Returns an array of SzObjects (can be Datasets or Templates) +func resolve_dependency_source(source: DependencySource) -> Array: + match source.mode: + DependencySource.DependencyMode.SPECIFIC_DATASET: + if source.target_datapack_guid.is_empty(): + var target_obj = resolve_object_dependency(source.target_sz_object_id) + return [target_obj] if target_obj else [] + + var target_pack = loaded_packs.get(source.target_datapack_guid) + if target_pack: + var target_obj = target_pack.get_object_by_id(source.target_sz_object_id) + return [target_obj] if target_obj else [] + return [] + + DependencySource.DependencyMode.DATASET_TYPE_WILDCARD: + return resolve_type_wildcard(source.target_type_string) + + return [] diff --git a/sessionzero-client/scripts/datapacks/datapack_manager.gd.uid b/sessionzero-client/scripts/datapacks/datapack_manager.gd.uid new file mode 100644 index 0000000..b3e1b79 --- /dev/null +++ b/sessionzero-client/scripts/datapacks/datapack_manager.gd.uid @@ -0,0 +1 @@ +uid://chlpgt0jnkd52 diff --git a/sessionzero-client/scripts/datapacks/datapack_tests.gd b/sessionzero-client/scripts/datapacks/datapack_tests.gd new file mode 100644 index 0000000..ec585c1 --- /dev/null +++ b/sessionzero-client/scripts/datapacks/datapack_tests.gd @@ -0,0 +1,374 @@ + + + +# ------------------------------- WARNING -------------------------------- +# These tests were auto generated by an LLM according to a design document +# to simply test the workflow. These tests should not be completely relied +# on for tesing full functionality of the datapack system. +# ------------------------------------------------------------------------ + + +class_name DatapackTestNode +extends Node + +# --- PRIMARY PACK CONSTANTS --- +const PACK_NAME = "core_data_test_pack" +const ENTRY_ID = "test_item_sword" +const WEAPONS_DATASET_ID = "weapons_dataset" +const SKILLS_DATASET_ID = "skills_dataset" +const CHAR_TEMPLATE_ID = "base_char_template" +const SESSION_TEMPLATE_ID = "basic_session_template" + +# --- NEW CROSS-PACK CONSTANTS --- +const DEPENDENCY_PACK_NAME = "stat_core_pack" +const DEPENDENCY_DATASET_ID = "core_stats_dataset" +const DEPENDENCY_ENTRY_ID = "base_strength" +const DEPENDER_PACK_NAME = "class_pack" +const DEPENDER_DATASET_ID = "warrior_class" + + +func _ready() -> void: + + print("--- Starting Datapack Test ---") + + # --- Primary Test (Pack 1) --- + var creation_success = _create_and_save_datapack() + if not creation_success: + push_error("TEST FAILED: Datapack creation failed.") + return + + _load_and_verify_datapack() + _test_formula_resolution() + + # --- New Cross-Pack Dependency Test (Pack 2 & 3) --- + _test_cross_datapack_dependency() + + print("--- Datapack Test Complete ---") + +#---------------------------------------------------------------------- +## --- STEP 2: CREATION AND SAVING --- +#---------------------------------------------------------------------- +func _create_and_save_datapack() -> bool: + print("\n--- 2. Creating and Saving Datapack ---") + + # --- 0. Setup Core Data --- + var stats_group := DatasetGroup.new() + stats_group.id = "base_stats" + stats_group.name = "Base Stats" + + # Simple Numeric Field + var damage_field := DataFieldValue.new() + damage_field.field_type = DataFieldValue.DataFieldType.NUMBER + damage_field.value = 12 + stats_group.fields["damage"] = damage_field + + # --- FORMULA Field --- + var formula_res := FormulaValue.new() + formula_res.expression = "Base_Damage * Critical_Multiplier" + + var crit_damage_field := DataFieldValue.new() + crit_damage_field.field_type = DataFieldValue.DataFieldType.FORMULA + crit_damage_field.value = formula_res + stats_group.fields["critical_damage"] = crit_damage_field + + # --- LIST Field --- + var list_res := ListValue.new() + + # Add nested DataFieldValue items to the ListValue resource + var list_item_1 := DataFieldValue.new() + list_item_1.field_type = DataFieldValue.DataFieldType.NUMBER + list_item_1.value = 5 + + var list_item_2 := DataFieldValue.new() + list_item_2.field_type = DataFieldValue.DataFieldType.TEXT + list_item_2.value = "Stun" + + list_res.items.append(list_item_1) + list_res.items.append(list_item_2) + + var effect_list_field := DataFieldValue.new() + effect_list_field.field_type = DataFieldValue.DataFieldType.LIST + effect_list_field.value = list_res + stats_group.fields["on_hit_effects"] = effect_list_field + + # Create Dataset Entry + var sword_entry := DatasetEntry.new() + sword_entry.id = ENTRY_ID + sword_entry.name = "Longsword of Testing" + sword_entry.description = "A sword for unit testing." + sword_entry.groups.append(stats_group) + + # Create Dataset Model (Weapons) + var weapons_dataset := DatasetModel.new() + weapons_dataset.id = WEAPONS_DATASET_ID + weapons_dataset.name = "Weapons" + weapons_dataset.entries[sword_entry.id] = sword_entry + weapons_dataset.sz_type = "Weapon" + + # --- Dependency Setup: Skills Dataset --- + var skills_dataset := DatasetModel.new() + skills_dataset.id = SKILLS_DATASET_ID + skills_dataset.name = "Skills" + skills_dataset.sz_type = "Skill" + + # --- Dependency Setup: CharacterTemplate --- + var char_template := CharacterTemplate.new() + char_template.id = CHAR_TEMPLATE_ID + char_template.name = "Standard Character" + + var skills_dependency := DependencySource.new() + skills_dependency.mode = DependencySource.DependencyMode.SPECIFIC_DATASET + skills_dependency.target_sz_object_id = SKILLS_DATASET_ID + + char_template.data_dependencies["Skills_Source"] = skills_dependency + + # --- Dependency Setup: SessionTemplate --- + var session_template := SessionTemplate.new() + session_template.id = SESSION_TEMPLATE_ID + session_template.name = "Basic Campaign Session" + + var char_dep_source := DependencySource.new() + char_dep_source.mode = DependencySource.DependencyMode.SPECIFIC_DATASET + char_dep_source.target_sz_object_id = CHAR_TEMPLATE_ID + session_template.character_template_ref = char_dep_source + + # --- Create and Save Datapack Model --- + var datapack := DatapackModel.new() + datapack.name = "Test Pack" + datapack.version = "1.0.0" + + datapack.sz_objects.append(weapons_dataset) + datapack.sz_objects.append(skills_dataset) + datapack.sz_objects.append(char_template) + datapack.sz_objects.append(session_template) + + var creator = DatapackCreator.new() + var success = creator.save_datapack(datapack, PACK_NAME) + + return success + +#---------------------------------------------------------------------- +## --- STEP 3: LOADING AND VERIFICATION (Omitted for brevity, assumed stable) --- +#---------------------------------------------------------------------- +func _load_and_verify_datapack() -> void: + print("\n--- 3. Loading and Verification ---") + var loader = DatapackLoader.new() + + var loaded_pack: DatapackModel = loader.load_datapack(PACK_NAME) + + if not loaded_pack: + push_error("TEST FAILED: DatapackLoader returned null.") + return + + DatapackManager.register_pack(loaded_pack) + print("Verification: Datapack registered with DatapackManager.") + + var loaded_weapons_dataset: DatasetModel = loaded_pack.get_object_by_id(WEAPONS_DATASET_ID) + var loaded_entry: DatasetEntry = loaded_weapons_dataset.entries.get(ENTRY_ID) + var loaded_group: DatasetGroup = loaded_entry.groups.filter(func(g): return g.id == "base_stats").front() + var loaded_char_template: CharacterTemplate = loaded_pack.get_object_by_id(CHAR_TEMPLATE_ID) + var loaded_session_template: SessionTemplate = loaded_pack.get_object_by_id(SESSION_TEMPLATE_ID) + + # 1. SessionTemplate Character Reference + var char_ref_dep_source: DependencySource = loaded_session_template.character_template_ref + var resolved_char_template_array = DatapackManager.resolve_dependency_source(char_ref_dep_source) + if resolved_char_template_array.size() == 1 and resolved_char_template_array.front() == loaded_char_template: + print("✅ SUCCESS: SessionTemplate dependency link resolved.") + else: + push_error("TEST FAILED: SessionTemplate character_template_ref resolution failed.") + + # 2. CharacterTemplate Skills Dependency + var skills_dep_source: DependencySource = loaded_char_template.data_dependencies.get("Skills_Source") + var resolved_skills_array = DatapackManager.resolve_dependency_source(skills_dep_source) + var loaded_skills_dataset = loaded_pack.get_object_by_id(SKILLS_DATASET_ID) + if resolved_skills_array.size() == 1 and resolved_skills_array.front() == loaded_skills_dataset: + print("✅ SUCCESS: CharacterTemplate data dependency link resolved.") + else: + push_error("TEST FAILED: CharacterTemplate data dependency resolution failed.") + + # 3. Verify FORMULA field + var loaded_crit_field: DataFieldValue = loaded_group.fields.get("critical_damage") + if loaded_crit_field and loaded_crit_field.field_type == DataFieldValue.DataFieldType.FORMULA: + var formula: FormulaValue = loaded_crit_field.value + if formula is FormulaValue and formula.expression == "Base_Damage * Critical_Multiplier": + print("✅ SUCCESS: Found and verified FormulaValue resource.") + else: + push_error("TEST FAILED: FormulaValue verification failed (wrong type or expression).") + else: + push_error("TEST FAILED: Critical damage field not found or wrong type.") + + # 4. Verify LIST field + var loaded_list_field: DataFieldValue = loaded_group.fields.get("on_hit_effects") + if loaded_list_field and loaded_list_field.field_type == DataFieldValue.DataFieldType.LIST: + var list_val: ListValue = loaded_list_field.value + if list_val is ListValue and list_val.items.size() == 2: + if list_val.items[1] is DataFieldValue and list_val.items[1].value == "Stun": + print("✅ SUCCESS: Found and verified ListValue and its nested contents.") + else: + push_error("TEST FAILED: ListValue contents verification failed.") + else: + push_error("TEST FAILED: ListValue resource failed to load correctly.") + else: + push_error("TEST FAILED: On hit effects field not found or wrong type.") + + print("\nVerification: All checks completed.") + +#---------------------------------------------------------------------- +## --- STEP 4: FORMULA RESOLUTION TEST (Omitted for brevity, assumed stable) --- +#---------------------------------------------------------------------- +func _test_formula_resolution(): + print("\n--- 4. Testing Formula Resolver with Variable Substitution ---") + + var resolver := FormulaResolver.new() + var raw_expression := "Base_Damage * Critical_Multiplier + floor(Skill_Level / 2)" + + var context: Dictionary = { + "Base_Damage": 15, + "Critical_Multiplier": 2.5, + "Skill_Level": 7 + } + + var expected_result = 40.5 + var result = resolver.resolve_formula(raw_expression, context) + + if abs(result - expected_result) < 0.001: + print("✅ SUCCESS: FormulaResolver calculated the correct result: %f" % result) + else: + push_error("TEST FAILED: FormulaResolver result incorrect. Expected %f, Got %f" % [expected_result, result]) + + +#---------------------------------------------------------------------- +## --- STEP 5: CROSS-PACK DEPENDENCY RESOLUTION --- +#---------------------------------------------------------------------- +func _test_cross_datapack_dependency(): + print("\n--- 5. Testing Cross-Pack Dependency Resolution ---") + + DatapackManager.clear_loaded_packs() + print("DatapackManager cleared for cross-pack test.") + + # --- 5a. Create and Save DEPENDENCY PACK (Pack A: Stat Core) --- + # FIX: Capture the DatapackModel to get its GUID + var pack_a_model: DatapackModel = _create_dependency_pack_a() + if not pack_a_model: + push_error("TEST FAILED: Failed to create Dependency Pack A.") + return + + var dependency_pack_guid: Guid = pack_a_model.guid # <--- This is the Guid object needed + print("Pack A GUID captured: %s" % dependency_pack_guid.to_string()) + + # --- 5b. Create and Save DEPENDER PACK (Pack B: Class Pack) --- + # FIX: Pass the GUID object to the creator function + var success_b = _create_depender_pack_b(dependency_pack_guid) + if not success_b: + push_error("TEST FAILED: Failed to create Depender Pack B.") + return + + # --- 5c. Load and Register Both Packs --- + var loader = DatapackLoader.new() + var pack_a: DatapackModel = loader.load_datapack(DEPENDENCY_PACK_NAME) + var pack_b: DatapackModel = loader.load_datapack(DEPENDER_PACK_NAME) + + if not pack_a or not pack_b: + push_error("TEST FAILED: Failed to load one or both dependency test packs.") + return + + DatapackManager.register_pack(pack_a) + DatapackManager.register_pack(pack_b) + print("Registered both Dependency (A) and Depender (B) packs.") + + # --- 5d. Resolve Dependency in Pack B --- + var depender_dataset: DatasetModel = pack_b.get_object_by_id(DEPENDER_DATASET_ID) + if not depender_dataset: + push_error("TEST FAILED: Could not find Depender Dataset in Pack B.") + return + + var dependency_source: DependencySource = depender_dataset.get_meta("core_stats_source") + if not dependency_source: + push_error("TEST FAILED: Could not find the DependencySource object in Pack B.") + return + + var resolved_objects: Array = DatapackManager.resolve_dependency_source(dependency_source) + + # --- 5e. Verification --- + if resolved_objects.size() != 1: + push_error("TEST FAILED: Dependency resolution returned incorrect count: %d" % resolved_objects.size()) + return + + var resolved_dataset: DatasetModel = resolved_objects.front() + var expected_dataset: DatasetModel = pack_a.get_object_by_id(DEPENDENCY_DATASET_ID) + + if resolved_dataset == expected_dataset: + print("✅ SUCCESS: Cross-pack dependency successfully resolved.") + if resolved_dataset.entries.has(DEPENDENCY_ENTRY_ID): + print("✅ SUCCESS: Resolved dataset contains the expected entry '%s'." % DEPENDENCY_ENTRY_ID) + else: + push_error("TEST FAILED: Dependency resolved to the wrong object.") + + +# --- Helper Functions for Cross-Pack Test --- + +# UPDATED: Returns the DatapackModel for GUID retrieval +func _create_dependency_pack_a() -> DatapackModel: + # Create entry + var str_field := DataFieldValue.new() + str_field.field_type = DataFieldValue.DataFieldType.NUMBER + str_field.value = 10 + + var strength_entry := DatasetEntry.new() + strength_entry.id = DEPENDENCY_ENTRY_ID + strength_entry.name = "Strength" + strength_entry.top_level_fields["value"] = str_field + + # Create dataset + var core_stats_dataset := DatasetModel.new() + core_stats_dataset.id = DEPENDENCY_DATASET_ID + core_stats_dataset.name = "Core Stats" + core_stats_dataset.sz_type = "StatDefinition" + core_stats_dataset.entries[strength_entry.id] = strength_entry + + # Create datapack + var datapack := DatapackModel.new() + datapack.name = "Stat Core Pack" + datapack.version = "1.0.0" + datapack.sz_objects.append(core_stats_dataset) + + var creator = DatapackCreator.new() + var success = creator.save_datapack(datapack, DEPENDENCY_PACK_NAME) + + if success: + return datapack + else: + return null # Return null on failure + +# UPDATED: Accepts the GUID to correctly create the DatapackDependency +func _create_depender_pack_b(dependency_pack_guid: Guid) -> bool: + # Create DependencySource (internal reference by string ID) + var dependency_source := DependencySource.new() + dependency_source.mode = DependencySource.DependencyMode.SPECIFIC_DATASET + dependency_source.target_sz_object_id = DEPENDENCY_DATASET_ID + + # Create Depender Dataset + var warrior_class_dataset := DatasetModel.new() + warrior_class_dataset.id = DEPENDER_DATASET_ID + warrior_class_dataset.name = "Warrior Class" + warrior_class_dataset.sz_type = "ClassDefinition" + + warrior_class_dataset.set_meta("core_stats_source", dependency_source) + + # Create Datapack + var datapack := DatapackModel.new() + datapack.name = "Warrior Class Pack" + datapack.version = "1.0.0" + datapack.sz_objects.append(warrior_class_dataset) + + # Add the explicit pack dependency + var pack_dep := DatapackDependency.new() + # FIX: Assign the Guid object to the 'id' property + pack_dep.id = dependency_pack_guid + pack_dep.name = "Stat Core Pack" + pack_dep.version = "1.0.0" + datapack.dependencies.append(pack_dep) + + var creator = DatapackCreator.new() + var success = creator.save_datapack(datapack, DEPENDER_PACK_NAME) + return success diff --git a/sessionzero-client/scripts/test.gd.uid b/sessionzero-client/scripts/datapacks/datapack_tests.gd.uid similarity index 100% rename from sessionzero-client/scripts/test.gd.uid rename to sessionzero-client/scripts/datapacks/datapack_tests.gd.uid diff --git a/sessionzero-client/scripts/datapacks/models/character_template.gd b/sessionzero-client/scripts/datapacks/models/character_template.gd new file mode 100644 index 0000000..c8e2f2c --- /dev/null +++ b/sessionzero-client/scripts/datapacks/models/character_template.gd @@ -0,0 +1,13 @@ +# character_template.gd +class_name CharacterTemplate +extends SzObject +# sz_type will be set to "CharacterTemplate" + +# Core definitions for character sheet fields +@export var top_level_field_definitions: Dictionary = {} # TemplateFieldDefinition +@export var group_definitions: Array[TemplateGroup] = [] + +# --- SPECIALIZED DEPENDENCIES --- +# The Character Template needs to reference *Datasets* (like Weapons, Skills, etc.) +# Map of logical_name (e.g., "Weapon_Source") -> DependencySource (the new class below) +@export var data_dependencies: Dictionary[String, DependencySource] = {} diff --git a/sessionzero-client/scripts/datapacks/models/character_template.gd.uid b/sessionzero-client/scripts/datapacks/models/character_template.gd.uid new file mode 100644 index 0000000..b5c2c20 --- /dev/null +++ b/sessionzero-client/scripts/datapacks/models/character_template.gd.uid @@ -0,0 +1 @@ +uid://5k8j3av0kqg4 diff --git a/sessionzero-client/scripts/models/datapack_dependency.gd b/sessionzero-client/scripts/datapacks/models/datapack_dependency.gd similarity index 100% rename from sessionzero-client/scripts/models/datapack_dependency.gd rename to sessionzero-client/scripts/datapacks/models/datapack_dependency.gd diff --git a/sessionzero-client/scripts/models/datapack_dependency.gd.uid b/sessionzero-client/scripts/datapacks/models/datapack_dependency.gd.uid similarity index 100% rename from sessionzero-client/scripts/models/datapack_dependency.gd.uid rename to sessionzero-client/scripts/datapacks/models/datapack_dependency.gd.uid diff --git a/sessionzero-client/scripts/models/datapack_model.gd b/sessionzero-client/scripts/datapacks/models/datapack_model.gd similarity index 100% rename from sessionzero-client/scripts/models/datapack_model.gd rename to sessionzero-client/scripts/datapacks/models/datapack_model.gd diff --git a/sessionzero-client/scripts/models/datapack_model.gd.uid b/sessionzero-client/scripts/datapacks/models/datapack_model.gd.uid similarity index 100% rename from sessionzero-client/scripts/models/datapack_model.gd.uid rename to sessionzero-client/scripts/datapacks/models/datapack_model.gd.uid diff --git a/sessionzero-client/scripts/models/dataset_model.gd b/sessionzero-client/scripts/datapacks/models/dataset_model.gd similarity index 100% rename from sessionzero-client/scripts/models/dataset_model.gd rename to sessionzero-client/scripts/datapacks/models/dataset_model.gd diff --git a/sessionzero-client/scripts/models/dataset_model.gd.uid b/sessionzero-client/scripts/datapacks/models/dataset_model.gd.uid similarity index 100% rename from sessionzero-client/scripts/models/dataset_model.gd.uid rename to sessionzero-client/scripts/datapacks/models/dataset_model.gd.uid diff --git a/sessionzero-client/scripts/datapacks/models/session_template.gd b/sessionzero-client/scripts/datapacks/models/session_template.gd new file mode 100644 index 0000000..88022e5 --- /dev/null +++ b/sessionzero-client/scripts/datapacks/models/session_template.gd @@ -0,0 +1,13 @@ +# session_template.gd +class_name SessionTemplate +extends SzObject + +# Core definitions for session fields (e.g., initial currency, setting notes) +@export var top_level_field_definitions: Dictionary = {} +@export var group_definitions: Array[TemplateGroup] = [] + +@export var character_template_ref: DependencySource +@export var data_dependencies: Dictionary[String, DependencySource] = {} + +func _init() -> void: + sz_type = "session_template" diff --git a/sessionzero-client/scripts/datapacks/models/session_template.gd.uid b/sessionzero-client/scripts/datapacks/models/session_template.gd.uid new file mode 100644 index 0000000..3a221fb --- /dev/null +++ b/sessionzero-client/scripts/datapacks/models/session_template.gd.uid @@ -0,0 +1 @@ +uid://dnjuh1ypyoie8 diff --git a/sessionzero-client/scripts/models/szobject.gd b/sessionzero-client/scripts/datapacks/models/szobject.gd similarity index 100% rename from sessionzero-client/scripts/models/szobject.gd rename to sessionzero-client/scripts/datapacks/models/szobject.gd diff --git a/sessionzero-client/scripts/models/szobject.gd.uid b/sessionzero-client/scripts/datapacks/models/szobject.gd.uid similarity index 100% rename from sessionzero-client/scripts/models/szobject.gd.uid rename to sessionzero-client/scripts/datapacks/models/szobject.gd.uid diff --git a/sessionzero-client/scripts/datapacks/models/template_model.gd b/sessionzero-client/scripts/datapacks/models/template_model.gd new file mode 100644 index 0000000..7bcff2f --- /dev/null +++ b/sessionzero-client/scripts/datapacks/models/template_model.gd @@ -0,0 +1,7 @@ +# template_model.gd +class_name TemplateModel +extends SzObject + +@export var top_level_field_definitions: Dictionary = {} +@export var group_definitions: Array[TemplateGroup] = [] +@export var data_dependencies: Dictionary[String, String] = {} diff --git a/sessionzero-client/scripts/datapacks/models/template_model.gd.uid b/sessionzero-client/scripts/datapacks/models/template_model.gd.uid new file mode 100644 index 0000000..3387a9c --- /dev/null +++ b/sessionzero-client/scripts/datapacks/models/template_model.gd.uid @@ -0,0 +1 @@ +uid://bd60mhmrr4olw diff --git a/sessionzero-client/scripts/models/schema/data_field_value.gd b/sessionzero-client/scripts/datapacks/other/data_field_value.gd similarity index 83% rename from sessionzero-client/scripts/models/schema/data_field_value.gd rename to sessionzero-client/scripts/datapacks/other/data_field_value.gd index e6528fa..c9edcda 100644 --- a/sessionzero-client/scripts/models/schema/data_field_value.gd +++ b/sessionzero-client/scripts/datapacks/other/data_field_value.gd @@ -7,12 +7,16 @@ enum DataFieldType { NUMBER, BOOL, FORMULA, - LIST + LIST, + REFERENCE_SINGLE, + REFERENCE_LIST } @export var field_type: DataFieldType @export var value: Variant +# --- TYPE STRING MAPPING --- + static func _type_to_string(t: DataFieldType) -> String: match t: DataFieldType.TEXT: return "Text" @@ -21,6 +25,8 @@ static func _type_to_string(t: DataFieldType) -> String: DataFieldType.BOOL: return "Boolean" DataFieldType.FORMULA: return "Formula" DataFieldType.LIST: return "List" + DataFieldType.REFERENCE_SINGLE: return "RefSingle" + DataFieldType.REFERENCE_LIST: return "RefList" _: return str(int(t)) static func _string_to_type(s: String) -> DataFieldType: @@ -31,8 +37,23 @@ static func _string_to_type(s: String) -> DataFieldType: "Boolean": return DataFieldType.BOOL "Formula": return DataFieldType.FORMULA "List": return DataFieldType.LIST + "RefSingle": return DataFieldType.REFERENCE_SINGLE + "RefList": return DataFieldType.REFERENCE_LIST _: return DataFieldType.TEXT +# --- COMPLEX VALUE ACCESS --- + +func get_concrete_value() -> Variant: + match field_type: + DataFieldType.FORMULA: + return value as FormulaValue + DataFieldType.LIST, DataFieldType.REFERENCE_LIST: + return value as ListValue + _: + return value + +# --- SERIALIZATION HELPERS --- + static func _envelope_variant(v: Variant) -> Dictionary: return { "_kind": "gd-variant-b64", @@ -63,6 +84,8 @@ static func _deserialize_list_value(a: Array) -> Array: out.append(item) return out +# --- SERIALIZE/DESERIALIZE VALUE --- + static func serialize_value(p_field_type: DataFieldType, v: Variant) -> Variant: match p_field_type: DataFieldType.TEXT, DataFieldType.MULTILINE_TEXT: @@ -77,10 +100,6 @@ static func serialize_value(p_field_type: DataFieldType, v: Variant) -> Variant: return int(s) DataFieldType.BOOL: return bool(v) - DataFieldType.FORMULA: - return str(v) - DataFieldType.LIST: - return _serialize_list_value(v) _: if v is Dictionary or v is Array or v is String or v is int or v is float or v is bool or v == null: return v @@ -94,15 +113,13 @@ static func deserialize_value(p_field_type: DataFieldType, raw: Variant) -> Vari return raw if (raw is float or raw is int) else float(str(raw)) DataFieldType.BOOL: return bool(raw) - DataFieldType.FORMULA: - return str(raw) - DataFieldType.LIST: - return _deserialize_list_value(raw if raw is Array else []) _: if raw is Dictionary and raw.get("_kind") == "gd-variant-b64": return _deenvelope_variant(raw) return raw +# --- DICT CONVERSION --- + func to_dict() -> Dictionary: return { "field_type": _type_to_string(field_type), diff --git a/sessionzero-client/scripts/models/schema/data_field_value.gd.uid b/sessionzero-client/scripts/datapacks/other/data_field_value.gd.uid similarity index 100% rename from sessionzero-client/scripts/models/schema/data_field_value.gd.uid rename to sessionzero-client/scripts/datapacks/other/data_field_value.gd.uid diff --git a/sessionzero-client/scripts/models/schema/dataset_entry.gd b/sessionzero-client/scripts/datapacks/other/dataset_entry.gd similarity index 100% rename from sessionzero-client/scripts/models/schema/dataset_entry.gd rename to sessionzero-client/scripts/datapacks/other/dataset_entry.gd diff --git a/sessionzero-client/scripts/models/schema/dataset_entry.gd.uid b/sessionzero-client/scripts/datapacks/other/dataset_entry.gd.uid similarity index 100% rename from sessionzero-client/scripts/models/schema/dataset_entry.gd.uid rename to sessionzero-client/scripts/datapacks/other/dataset_entry.gd.uid diff --git a/sessionzero-client/scripts/models/schema/dataset_group.gd b/sessionzero-client/scripts/datapacks/other/dataset_group.gd similarity index 100% rename from sessionzero-client/scripts/models/schema/dataset_group.gd rename to sessionzero-client/scripts/datapacks/other/dataset_group.gd diff --git a/sessionzero-client/scripts/models/schema/dataset_group.gd.uid b/sessionzero-client/scripts/datapacks/other/dataset_group.gd.uid similarity index 100% rename from sessionzero-client/scripts/models/schema/dataset_group.gd.uid rename to sessionzero-client/scripts/datapacks/other/dataset_group.gd.uid diff --git a/sessionzero-client/scripts/datapacks/other/dependency_source.gd b/sessionzero-client/scripts/datapacks/other/dependency_source.gd new file mode 100644 index 0000000..d3694d4 --- /dev/null +++ b/sessionzero-client/scripts/datapacks/other/dependency_source.gd @@ -0,0 +1,20 @@ +# dependency_source.gd +class_name DependencySource +extends Resource + +enum DependencyMode { + SPECIFIC_DATASET, # Reference a known Dataset/Template by its ID and GUID + DATASET_TYPE_WILDCARD, # Reference ANY Dataset/Template that matches a specific type string +} + +@export var mode: DependencyMode = DependencyMode.SPECIFIC_DATASET +@export var display_name: String = "" + +# --- Used for SPECIFIC_DATASET mode --- +# The GUID of the Datapack the dependency lives in (optional, can be empty for local pack) +@export var target_datapack_guid: String = "" +@export var target_sz_object_id: String = "" + +# --- Used for DATASET_TYPE_WILDCARD mode --- +# The type string to match (e.g., "items"). +@export var target_type_string: String = "" diff --git a/sessionzero-client/scripts/datapacks/other/dependency_source.gd.uid b/sessionzero-client/scripts/datapacks/other/dependency_source.gd.uid new file mode 100644 index 0000000..e2bef7c --- /dev/null +++ b/sessionzero-client/scripts/datapacks/other/dependency_source.gd.uid @@ -0,0 +1 @@ +uid://cly4hb2wrx8fn diff --git a/sessionzero-client/scripts/datapacks/other/formula_resolver.gd b/sessionzero-client/scripts/datapacks/other/formula_resolver.gd new file mode 100644 index 0000000..001b890 --- /dev/null +++ b/sessionzero-client/scripts/datapacks/other/formula_resolver.gd @@ -0,0 +1,52 @@ +# formula_resolver.gd +class_name FormulaResolver +extends RefCounted + +var _expression: Expression = Expression.new() + +## Internal helper to replace named variables with their numerical values. +## NOTE: This simple approach sorts keys by length descending (Max_HP before HP) +## to prevent incorrect substitution of substrings. +func _substitute_variables(raw_expression: String, context: Dictionary) -> String: + var result = raw_expression + + # Sort keys by length descending to prevent shorter names (like 'HP') from matching + # before longer names that contain them (like 'Max_HP'). + var keys = context.keys() + keys.sort_custom(func(a, b): return len(a) > len(b)) + + for key in keys: + if raw_expression.find(key) != -1: + var value = str(context[key]) + # Replace the variable name with its numerical value string + result = result.replace(key, value) + + return result + +## Resolves the final value of the formula given a set of variable values. +## Handles variable substitution, parsing, and execution in one step. +func resolve_formula(raw_expression: String, context: Dictionary) -> float: + # 1. Substitute variables with their numerical values + var numerical_expression = _substitute_variables(raw_expression, context) + + # 2. Parse the new numerical expression + # The expression must be re-parsed every time, as its text content changes. + var error: Error = _expression.parse(numerical_expression, PackedStringArray([])) + + if error != OK: + push_error("FormulaResolver: Substitution/Parse failed. Expression: '%s'. Numerical: '%s'. Error: %s" % [raw_expression, numerical_expression, _expression.get_error_text()]) + return 0.0 + + # 3. Execute the expression (inputs: empty array, base_instance: null, show_error: true) + var result = _expression.execute([], null, true) + + if _expression.has_execute_failed(): + push_error("FormulaResolver: Execution failed for numerical expression: %s" % numerical_expression) + return 0.0 + + # 4. Check type and return + if typeof(result) != TYPE_FLOAT and typeof(result) != TYPE_INT: + push_error("FormulaResolver: Expression result was not a number. Got type: %s" % typeof(result)) + return 0.0 + + return float(result) diff --git a/sessionzero-client/scripts/datapacks/other/formula_resolver.gd.uid b/sessionzero-client/scripts/datapacks/other/formula_resolver.gd.uid new file mode 100644 index 0000000..32b0007 --- /dev/null +++ b/sessionzero-client/scripts/datapacks/other/formula_resolver.gd.uid @@ -0,0 +1 @@ +uid://ils0nw5fyh7j diff --git a/sessionzero-client/scripts/datapacks/other/formula_value.gd b/sessionzero-client/scripts/datapacks/other/formula_value.gd new file mode 100644 index 0000000..0aad5f3 --- /dev/null +++ b/sessionzero-client/scripts/datapacks/other/formula_value.gd @@ -0,0 +1,10 @@ +# formula_value.gd +class_name FormulaValue +extends Resource + +# The raw expression string (e.g., "Attack_Modifier + Weapon_Damage_Dice") +@export var expression: String = "" +@export var variables: Dictionary = {} + +# need a separate system (a FormulaEvaluator) later to parse and execute this. +# For now, this resource simply stores the definition. diff --git a/sessionzero-client/scripts/datapacks/other/formula_value.gd.uid b/sessionzero-client/scripts/datapacks/other/formula_value.gd.uid new file mode 100644 index 0000000..b7d0c73 --- /dev/null +++ b/sessionzero-client/scripts/datapacks/other/formula_value.gd.uid @@ -0,0 +1 @@ +uid://cyij11ojm1ecy diff --git a/sessionzero-client/scripts/datapacks/other/list_value.gd b/sessionzero-client/scripts/datapacks/other/list_value.gd new file mode 100644 index 0000000..60210bd --- /dev/null +++ b/sessionzero-client/scripts/datapacks/other/list_value.gd @@ -0,0 +1,5 @@ +# list_value.gd +class_name ListValue +extends Resource + +@export var items: Array[DataFieldValue] = [] diff --git a/sessionzero-client/scripts/datapacks/other/list_value.gd.uid b/sessionzero-client/scripts/datapacks/other/list_value.gd.uid new file mode 100644 index 0000000..1cdd1bf --- /dev/null +++ b/sessionzero-client/scripts/datapacks/other/list_value.gd.uid @@ -0,0 +1 @@ +uid://cpdx3w11rbt5j diff --git a/sessionzero-client/scripts/datapacks/other/template_field.gd b/sessionzero-client/scripts/datapacks/other/template_field.gd new file mode 100644 index 0000000..efc1d6d --- /dev/null +++ b/sessionzero-client/scripts/datapacks/other/template_field.gd @@ -0,0 +1,22 @@ +# template_field_definition.gd +class_name TemplateField +extends Resource + +enum DataFieldType { + TEXT, + MULTILINE_TEXT, + NUMBER, + BOOL, + FORMULA, + LIST, + REFERENCE_SINGLE, + REFERENCE_LIST +} + +@export var field_id: String +@export var display_name: String +@export var field_type: DataFieldType = DataFieldType.TEXT +@export var default_value: Variant + +@export var dependency_source_name: String = "" +@export var dependency_source_key: String = "" diff --git a/sessionzero-client/scripts/datapacks/other/template_field.gd.uid b/sessionzero-client/scripts/datapacks/other/template_field.gd.uid new file mode 100644 index 0000000..07b0579 --- /dev/null +++ b/sessionzero-client/scripts/datapacks/other/template_field.gd.uid @@ -0,0 +1 @@ +uid://b4d6mtffupbk3 diff --git a/sessionzero-client/scripts/datapacks/other/template_group.gd b/sessionzero-client/scripts/datapacks/other/template_group.gd new file mode 100644 index 0000000..99b0f3c --- /dev/null +++ b/sessionzero-client/scripts/datapacks/other/template_group.gd @@ -0,0 +1,8 @@ +# template_group.gd +class_name TemplateGroup +extends Resource + +@export var name: String +@export var group_id: String +@export var field_definitions: Dictionary = {} +@export var is_list: bool = false diff --git a/sessionzero-client/scripts/datapacks/other/template_group.gd.uid b/sessionzero-client/scripts/datapacks/other/template_group.gd.uid new file mode 100644 index 0000000..dbbe638 --- /dev/null +++ b/sessionzero-client/scripts/datapacks/other/template_group.gd.uid @@ -0,0 +1 @@ +uid://h3ptq7tj3tad diff --git a/sessionzero-client/scripts/helpers/datapack_creator.gd b/sessionzero-client/scripts/helpers/datapack_creator.gd index 40c000b..f9b40cd 100644 --- a/sessionzero-client/scripts/helpers/datapack_creator.gd +++ b/sessionzero-client/scripts/helpers/datapack_creator.gd @@ -2,22 +2,16 @@ class_name DatapackCreator extends RefCounted -# --- UPDATED CONSTANTS --- const BASE_DATAPACKS_PATH = "user://datapacks/" -const CONTENT_SUBDIR = "objects" -const METADATA_FILENAME = "config.szpack" # Renamed from METADATA_FILENAME -const DATAPACK_RESOURCE_FILENAME = "datapack.tres" # Renamed from DATAPACK_RESOURCE_FILENAME -const ASSETS_SUBDIR = "assets" # Renamed from MEDIA_SUBDIR +const METADATA_FILENAME = "config.szpack" +const DATAPACK_RESOURCE_FILENAME = "datapack.res" +const ASSETS_SUBDIR = "assets" + +const SINGLE_FILE_SAVE_FLAGS = ResourceSaver.FLAG_COMPRESS | ResourceSaver.FLAG_CHANGE_PATH -# --- INTERNAL STATE --- -var _saved_resources: Dictionary = {} var _pack_base_path: String = "" -# --- PUBLIC METHOD --- - -## Saves a DatapackModel and all its content resources to the file system. -## 'pack_name' is used to create the final folder structure: user://datapacks/[pack_name]/ func save_datapack(datapack: DatapackModel, pack_name: String) -> bool: var full_pack_name = pack_name.validate_node_name() if full_pack_name.is_empty(): @@ -26,25 +20,22 @@ func save_datapack(datapack: DatapackModel, pack_name: String) -> bool: _pack_base_path = BASE_DATAPACKS_PATH + full_pack_name + "/" - var content_path = _pack_base_path + CONTENT_SUBDIR var assets_path = _pack_base_path + ASSETS_SUBDIR var success_dirs = true - success_dirs &= _make_dir_recursive_safe(_pack_base_path) - success_dirs &= _make_dir_recursive_safe(content_path) - success_dirs &= _make_dir_recursive_safe(assets_path) + success_dirs = success_dirs and _make_dir_recursive_safe(_pack_base_path) + success_dirs = success_dirs and _make_dir_recursive_safe(assets_path) if not success_dirs: push_error("DatapackCreator: Failed to create necessary directories.") return false - - _saved_resources.clear() - if not _save_content_objects(datapack.content_objects, content_path + "/"): - return false - var main_resource_path = _pack_base_path + DATAPACK_RESOURCE_FILENAME - if not _save_single_resource(datapack, main_resource_path): + + var error = ResourceSaver.save(datapack, main_resource_path, SINGLE_FILE_SAVE_FLAGS) + + if error != OK: + push_error("DatapackCreator: Failed to save datapack resource at path '%s'. Error: %s" % [main_resource_path, error]) return false if not _write_metadata_file(datapack, _pack_base_path + METADATA_FILENAME): @@ -54,48 +45,14 @@ func save_datapack(datapack: DatapackModel, pack_name: String) -> bool: return true -# --- PRIVATE HELPER METHODS --- - func _make_dir_recursive_safe(path: String) -> bool: var error = DirAccess.make_dir_recursive_absolute(path) - if error != OK and error != ERR_ALREADY_EXISTS: + if error != OK and error != ERR_ALREADY_EXISTS: push_error("DirAccess: Failed to create directory: %s (Error: %s)" % [path, error]) return false return true -func _save_content_objects(objects: Array, save_path: String) -> bool: - var success = true - for obj in objects: - if not obj is SzObject: - push_error("DatapackCreator: Content array contains non-SzObject: %s" % obj) - success = false - continue - - # Filename format: [class_name]_[id].tres - var filename = "%s_%s.tres" % [obj.get_class().to_lower(), obj.id.to_lower()] - var full_path = save_path + filename - - if not _save_single_resource(obj, full_path): - success = false - - return success - - -func _save_single_resource(resource: Resource, path: String) -> bool: - if _saved_resources.has(resource): - return true - - var error = ResourceSaver.save(resource, path, ResourceSaver.FLAG_COMPRESS | ResourceSaver.FLAG_CHANGE_PATH) - - if error != OK: - push_error("DatapackCreator: Failed to save resource at path '%s'. Error: %s" % [path, error]) - return false - - _saved_resources[resource] = path - return true - - func _write_metadata_file(datapack: DatapackModel, path: String) -> bool: var config = ConfigFile.new() var section = "Datapack" diff --git a/sessionzero-client/scripts/helpers/datapack_loader.gd b/sessionzero-client/scripts/helpers/datapack_loader.gd index 732e621..fcf5397 100644 --- a/sessionzero-client/scripts/helpers/datapack_loader.gd +++ b/sessionzero-client/scripts/helpers/datapack_loader.gd @@ -4,7 +4,8 @@ extends RefCounted const BASE_DATAPACKS_PATH = "user://datapacks/" const METADATA_FILENAME = "config.szpack" -const DATAPACK_RESOURCE_FILENAME = "datapack.tres" +# CHANGE HERE +const DATAPACK_RESOURCE_FILENAME = "datapack.res" ## Loads a DatapackModel resource and all its referenced content from the user://datapacks/ folder. ## Returns the loaded DatapackModel object or null on failure. @@ -32,6 +33,7 @@ func load_datapack(pack_name: String) -> DatapackModel: push_error("DatapackLoader: Main Resource file not found at path: %s" % resource_path) return null + # Godot automatically detects the binary format and loads the resource. var datapack: DatapackModel = ResourceLoader.load(resource_path) if not datapack: @@ -42,14 +44,3 @@ func load_datapack(pack_name: String) -> DatapackModel: print("Successfully loaded Datapack: %s (GUID: %s)" % [datapack.name, datapack.guid.to_string()]) return datapack - -# ---------------------------------------------------------------------- -# Example Usage: -# var loader = DatapackLoader.new() -# var core_pack = loader.load_datapack("core_rules") -# -# if core_pack: -#\t var item = core_pack.get_object_by_id("sword-long") -#\t if item: -#\t\t print("Found item: " + item.name) -# ---------------------------------------------------------------------- diff --git a/sessionzero-client/scripts/test.gd b/sessionzero-client/scripts/test.gd deleted file mode 100644 index d6c72d2..0000000 --- a/sessionzero-client/scripts/test.gd +++ /dev/null @@ -1,12 +0,0 @@ -extends Node - -func _enter_tree() -> void: - var dp: DatapackModel = DatapackModel.new() - var dpd: DatapackDependency = DatapackDependency.new() - dpd.id = Guid.new_guid() - dpd.name = "test" - dpd.version = "1.0.0" - dp.dependencies.append(dpd) - - var json_string := JSON.stringify(dp.to_dict(), " ") - print(json_string)