CORE/cml.gd

127 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