Vest/Services/PackageService.gd

82 lines
2.8 KiB
GDScript

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)