127 lines
6.9 KiB
GDScript
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
|