###################################### # THE CORE FRAMEWORK # # MADE BY THE STAROPENSOURCE PROJECT # # AND CONTRIBUTERS (THANK YOU!) # # # # COPYRIGHT 2023 THE STAROPENSOURCE # # PROJECT AND CONTRIBUTERS # # # # LICENSED UNDER THE GNU GENERAL # # PUBLIC LICENSE VERSION 3 (ONLY) # ###################################### extends Node # CORE modules var core: Node = null var logger: Node = null # Configuration var config_loadpath = null var config_wanted_name = null var config_wanted_version = null var config_wanted_api = null # Modification list var modlist = [] # Hook list var hooks = {} func load_mods() -> Array: if core.protection_mode: return [] var errors: Array = [] for directory in DirAccess.get_directories_at(config_loadpath): var return_value: String = await load_mod(directory) if return_value != "": errors.append(return_value) return errors func load_mod(mod_name:String) -> String: logger.info("CORE/cml.gd","Loading modification \"" + mod_name + "\"") if modlist.has(mod_name): logger.error("CORE/cml.gd","Modification \"" + mod_name + "\" is already loaded") if !FileAccess.file_exists(config_loadpath + "/" + mod_name + "/" + config_wanted_name + ".coremod"): logger.error("CORE/cml.gd","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 = FileAccess.open(config_loadpath + "/" + mod_name + "/" + config_wanted_name + ".coremod",FileAccess.READ) var modinfo: Dictionary = JSON.parse_string(modinfo_raw.get_as_text()) modinfo_raw.close() if modinfo == null: logger.error("CORE/cml.gd","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("CORE/cml.gd","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("CORE/cml.gd","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("CORE/cml.gd","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("CORE/cml.gd","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("CORE/cml.gd","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("CORE/cml.gd","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: Script = ResourceLoader.load(config_loadpath + "/" + mod_name + "/" + modinfo["mod"]["entrypoint"]) var entrypoint: Node = Node.new() entrypoint.name = mod_name entrypoint.set_script(entrypoint_script) get_tree().root.add_child(entrypoint) var mod_err: String = 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("CORE/cml.gd","Unloading modification \"" + mod_name + "\"") if !modlist.has(mod_name): logger.error("CORE/cml.gd","Modification \"" + mod_name + "\" is not loaded") if get_tree().root.get_node_or_null(mod_name) == null: core.exception("CORE/cml.gd","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("CORE/cml.gd","Failed registering hook \"" + hook_name + "\" for mod \"" + mod_name + "\" as it does not exist or is not loaded") return false core.exception("CORE/cml.gd","Function not implemented") 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 = FileAccess.open(config_loadpath + "/" + mod_name + "/" + config_wanted_name + ".coremod",FileAccess.READ) var modinfo: Dictionary = JSON.parse_string(modinfo_raw.get_as_text()) modinfo_raw.close() if modinfo == null: return {"error":"Failed parsing modinfo for modification \"" + mod_name + "\""} return modinfo func load_configuration() -> void: if core.protection_mode: return config_loadpath = core.config.cml_loadpath config_wanted_name = core.config.cml_wanted_name config_wanted_version = core.config.cml_wanted_version if core.config.get("cml_wanted_api") == null: config_wanted_api = config_wanted_version else: config_wanted_api = core.config.cml_wanted_api