extends Node onready var ConsoleService = $"/root/ConsoleService" var package_dict = {} var package_load_order = [] # Locate all packages that can be loaded. func discover_packages(): var dir = Directory.new() dir.open("res://packages") dir.list_dir_begin() while true: var package_name = dir.get_next() if package_name == "": break if not dir.current_is_dir() or package_name == "." or package_name == "..": continue var package_script = load("res://packages/"+package_name+"/main.gd"); if !package_script: continue var package = { "name": package_script.package.name if package_script.package.has("name") else package_name, "version": package_script.package.version if package_script.package.has("version") else "1.0.0", "dependencies": package_script.package.dependencies if package_script.package.has("dependencies") else [], "_script": package_script } package_dict[package.name] = package dir.list_dir_end() _compute_dependency_graph() func load_packages(): for package_name in package_load_order: var package = package_dict[package_name] package._instance = package._script.new() if !package._instance: ConsoleService.log("[PackageService] Failed to load package '"+package.name+"'") continue func init_packages(): for package_name in package_load_order: var package = package_dict[package_name] if not package.has("_instance"): ConsoleService.log("[PackageService] Package '"+package.name+"' has not been loaded. Please load before initializing.") continue $"/root/Main/Packages".add_child(package._instance) if package._instance.has_method('init'): package._instance.init() func _compute_dependency_graph(): if package_dict.size() == 0: ConsoleService.error("[PackageService] packages have not been preloaded (Run preload_instances first)") return var resolved = []; # Properly ordered package list. for package in package_dict.values(): _resolve_dependency(package, resolved, []) package_load_order = resolved func _resolve_dependency(package: Dictionary, resolved: Array, unresolved: Array): if package.name in resolved: return # Unresolved is used to prevent recursive lookups. unresolved.append(package.name) for dependency_name in package.dependencies: if dependency_name in resolved: continue if dependency_name in unresolved: ConsoleService.error("[PackageService] Circular dependency between '"+package.name+"' <-> '"+dependency_name+"'") assert(false) if not package_dict.has(dependency_name): ConsoleService.error("[PackageService] package '"+package.name+"' requires package '"+dependency_name+"' to be installed, but it is not") assert(false) var dependency = package_dict[dependency_name] _resolve_dependency(dependency, resolved, unresolved) resolved.append(package.name) unresolved.erase(package.name)