# moam.gd # Mother Of All Mods (the CORE modloader) # # This file is part of StarOpenSource CORE (SOSCORE) # Made by the StarOpenSource Project and Contributers # Licensed under GNU GPLv3 extends Node @onready var core = get_node("/root/core") var config_loadpath = null var config_wanted_name = null var config_wanted_version = null var config_wanted_api = null var modlist = [] var hooks = {} func load_mods() -> Array: if core.protection_mode: return [] var errors = [] for directory in DirAccess.get_directories_at(config_loadpath): var return_value = await load_mod(directory) if return_value != "": errors.append(return_value) return errors func load_mod(mod_name:String) -> String: Logger.info("moam","Loading modification \"" + mod_name + "\"") if modlist.has(mod_name): Logger.error("moam","Modification \"" + mod_name + "\" is already loaded") if !FileAccess.file_exists(config_loadpath + "/" + mod_name + "/" + config_wanted_name + ".coremod"): Logger.error("moam","Modification located at " + config_loadpath + "/" + mod_name + " does not contain a " + config_wanted_name + ".coremod file") return "Modification located at " + config_loadpath + "/" + mod_name + " does not contain a " + config_wanted_name + ".coremod file" var modinfo_raw = FileAccess.open(config_loadpath + "/" + mod_name + "/" + config_wanted_name + ".coremod",FileAccess.READ) var modinfo = modinfo_raw.get_as_text() modinfo_raw.close() modinfo = JSON.parse_string(modinfo) if modinfo == null: Logger.error("moam","Failed modinfo parsing for modification located at " + config_loadpath + "/" + mod_name) return "Failed modinfo parsing for modification located at " + config_loadpath + "/" + mod_name if !modinfo.has_all(["wanted","mod"]): Logger.error("moam","The modinfo of modification located at " + config_loadpath + "/" + mod_name + " does not contain wanted, mod or both") return "The modinfo of modification located at " + config_loadpath + "/" + mod_name + " does not contain wanted, mod or both" if !modinfo["wanted"].has_all(["min_version","max_version","min_api","max_api"]): Logger.error("moam","The modinfo of modification located at " + config_loadpath + "/" + mod_name + " does not contain wanted.min_version, wanted.max_version, wanted.min_api or wanted.max_api or some combination of them.") return "The modinfo of modification located at " + config_loadpath + "/" + mod_name + " does not contain wanted.min_version, wanted.max_version, wanted.min_api or wanted.max_api or some combination of them." if !modinfo["mod"].has_all(["name","version","authors","license","entrypoint"]): Logger.error("moam","The modinfo of modification located at " + config_loadpath + "/" + mod_name + " does not contain mod.name, mod.version, mod.authors, mod.license or mod.entrypoint or some combination of them.") return "The modinfo of modification located at " + config_loadpath + "/" + mod_name + " does not contain mod.name, mod.version, mod.authors, mod.license or mod.entrypoint or some combination of them." if not modinfo["wanted"]["min_version"] <= config_wanted_version or not modinfo["wanted"]["max_version"] >= config_wanted_version: Logger.error("moam","The modification \"" + modinfo["mod"]["name"] + " does not match wanted version " + str(config_wanted_version)) return "The modification \"" + modinfo["mod"]["name"] + " does not match wanted version " + str(config_wanted_version) if not modinfo["wanted"]["min_api"] <= config_wanted_api or not modinfo["wanted"]["max_api"] >= config_wanted_api: Logger.error("moam","The modification \"" + modinfo["mod"]["name"] + " does not match wanted api " + str(config_wanted_api)) return "The modification \"" + modinfo["mod"]["name"] + " does not match wanted api " + str(config_wanted_api) if !FileAccess.file_exists(config_loadpath + "/" + mod_name + "/" + modinfo["mod"]["entrypoint"]): Logger.error("moam","The entrypoint for the modification \"" + modinfo["mod"]["name"] + "\" located at \"" + config_loadpath + "/" + mod_name + "/" + modinfo["mod"]["entrypoint"] + "\" does not exist") return "The entrypoint for the modification \"" + modinfo["mod"]["name"] + "\" located at \"" + config_loadpath + "/" + mod_name + "/" + modinfo["mod"]["entrypoint"] + "\" does not exist" var entrypoint_script = ResourceLoader.load(config_loadpath + "/" + mod_name + "/" + modinfo["mod"]["entrypoint"]) var entrypoint = Control.new() entrypoint.name = mod_name entrypoint.set_script(entrypoint_script) get_tree().root.add_child(entrypoint) var mod_err = await get_node("/root/" + mod_name)._start() if mod_err == "": modlist.append(mod_name) return "" else: get_tree().root.remove_child(entrypoint) return "Modification \"" + mod_name + "\" could not be loaded as it returned this error: " + mod_err func unload_mods() -> void: for mod in modlist: await unload_mod(mod) func unload_mod(mod_name:String) -> void: Logger.info("moam","Unloading modification \"" + mod_name + "\"") if !modlist.has(mod_name): Logger.error("moam","Modification \"" + mod_name + "\" is not loaded") if get_tree().root.get_node_or_null(mod_name) == null: core.exception("moam","Could not locate mod entrypoint script for mod \"" + mod_name + "\" during unload") return await get_node("/root/" + mod_name)._stop() get_tree().root.remove_child(get_node("/root/" + mod_name)) modlist.erase(mod_name) func register_hook(mod_name:String,hook_name:String,hook_action:int,method:Callable) -> bool: if !modlist.has(mod_name): core.exception("moam","Failed registering hook \"" + hook_name + "\" for mod \"" + mod_name + "\" as it does not exist or is not loaded") return false if hooks.has(hook_name): Logger.error("moam","Failed registering hook \"" + hook_name + "\" for mod \"" + mod_Name + "\" as the hook already exists") return true func get_list() -> Array: return modlist func get_info(mod_name:String) -> Dictionary: if !modlist.has(mod_name): return {"error":"Modification \"" + mod_name + "\" is not loaded"} var modinfo_raw = FileAccess.open(config_loadpath + "/" + mod_name + "/" + config_wanted_name + ".coremod",FileAccess.READ) var modinfo = JSON.parse_string(modinfo_raw.get_as_text()) modinfo_raw.close() if modinfo == null: return {"error":"Failed parsing modinfo for modification \"" + modinfo + "\""} return modinfo func load_configuration() -> void: if core.protection_mode: return config_loadpath = core.config.moam_loadpath config_wanted_name = core.config.moam_wanted_name config_wanted_version = core.config.moam_wanted_version if core.config.get("moam_wanted_api") == null: config_wanted_api = config_wanted_version else: config_wanted_api = core.config.moam_wanted_api func initialize() -> void: if core.protection_mode: return core.setready()