Reviewed-on: StarOpenSource/core#1 Rewrote CORE and improved the startup process and startup time significantly. The documentation has been beefed up too and is now much better. Existing projects may need major refactoring however. Co-authored-by: JeremyStarTM <jeremystartm@staropensource.de> Co-committed-by: JeremyStarTM <jeremystartm@staropensource.de>
126 lines
6.9 KiB
GDScript
126 lines
6.9 KiB
GDScript
######################################
|
|
# 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
|