# datapack_manager.gd extends Node # --- CONFIGURATION --- const DATAPACKS_DIR = "user://datapacks/" # --- STATE --- var loaded_packs: Dictionary[String, DatapackModel] = {} func _ready(): var dir = DirAccess.open("user://") if dir: if not dir.dir_exists("datapacks"): dir.make_dir("datapacks") load_all_local_packs() ## Scans the local DATAPACKS_DIR, loads all valid datapack subdirectories, ## and registers them with the manager. This serves as the initial load and the refresh option. func load_all_local_packs(): print("DatapackManager: Starting scan for local datapacks in '%s'" % DATAPACKS_DIR) var loader = DatapackLoader.new() var dir = DirAccess.open(DATAPACKS_DIR) if dir == null: push_error("DatapackManager: Failed to open datapacks directory: %s" % DATAPACKS_DIR) return dir.list_dir_begin() var dir_name = dir.get_next() while dir_name != "": if dir_name == "." or dir_name == "..": dir_name = dir.get_next() continue if dir.current_is_dir(): print("DatapackManager: Found pack directory: %s" % dir_name) var datapack: DatapackModel = loader.load_datapack(dir_name) if datapack: register_pack(datapack) else: push_warning("DatapackManager: Failed to load pack in directory: %s. Skipping." % dir_name) dir_name = dir.get_next() dir.list_dir_end() print("DatapackManager: Finished scanning local datapacks.") 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 []