From ab5b3ade52ccd1b56c524f3df435bd1363c81be6 Mon Sep 17 00:00:00 2001 From: JeremyStarTM Date: Mon, 7 Aug 2023 14:49:12 +0200 Subject: [PATCH] Backup before I nuke the project - Rewrote mkdevprj/rmdevprj in Makefile - Add MOAM modloader - Add core.exception() - Add protection mode - Rewrote module initialization - Add experimental markdown parser - Many more very small changes --- Makefile | 29 +++-- config.gd.example | 14 +++ core.gd | 154 ++++++++++++++++++++++--- coreloader.gd | 24 +++- docs/references/config.gd.md | 2 +- docs/references/index.md | 1 + docs/references/moam.gd.md | 61 ++++++++++ docs/references/wmgr.gd.md | 2 +- events.gd | 29 +++-- mkdown.gd | 29 +++++ moam.gd | 123 ++++++++++++++++++++ resmgr.gd | 22 ++-- smgr.gd | 216 ++++++++++++++++++----------------- splash.gd | 34 +++--- wmgr.gd | 54 ++++++--- 15 files changed, 606 insertions(+), 188 deletions(-) create mode 100644 docs/references/moam.gd.md create mode 100644 mkdown.gd create mode 100644 moam.gd diff --git a/Makefile b/Makefile index e745a0b..c1701bd 100644 --- a/Makefile +++ b/Makefile @@ -34,22 +34,35 @@ docs-clean: @rm -rf "$(DOCSOUT)" mkdevprj: @echo ":: Creating development project" - @mkdir -p $(TESTPROJECT) + @mkdir -p $(TESTPROJECT)/mods/testmod/ @echo -e "config_version=5\n[application]\nconfig/name=\"CORE development project\"\nrun/main_scene=\"res://CORE/coreinit.tscn\"\nconfig/use_custom_user_dir=true\nconfig/custom_user_dir_name=\"core_devproject\"\nconfig/features=PackedStringArray(\"4.1\", \"GL Compatibility\")\nboot_splash/bg_color=Color(0, 0, 0, 1)\nboot_splash/show_image=false\nconfig/icon=\"res://CORE/soscore.png\"\n[autoload]\nLogger=\"*res://CORE/logger.gd\"\nPreprocessor=\"*res://CORE/preprocessor.gd\"\n[display]\nwindow/size/viewport_width=960\nwindow/size/viewport_height=540\n[rendering]\nrenderer/rendering_method=\"gl_compatibility\"\nrenderer/rendering_method.mobile=\"gl_compatibility\"" > "$(TESTPROJECT)/project.godot" - @echo -e "extends Node\n\n@onready\nvar core = get_node(\"/root/core\")\n\nfunc _ready() -> void:\n core.welcome()" > "$(TESTPROJECT)/init.gd" - @echo -e "extends Node\nvar core_startscript = \"res://init.gd\"\nvar wmgr_size_x = 960\nvar wmgr_size_y = 540\nvar wmgr_title = \"CORE development project\"\nvar wmgr_mode = 0\nvar splash_enabled = false\nvar splash_image = \"res://CORE/soscore.png\"\nvar splash_image_size = 256\nvar splash_color = \"000000\"" > config.gd + @echo -e "extends Node\n\n@onready\nvar core: Node = get_node(\"/root/core\")\n\nfunc _ready() -> void:\n core.welcome()" > "$(TESTPROJECT)/init.gd" + @echo -e "extends Node\nvar core_startscript = \"res://init.gd\"\nvar wmgr_size_x = 960\nvar wmgr_size_y = 540\nvar wmgr_title = \"CORE development project\"\nvar wmgr_mode = 0\nvar splash_enabled = false\nvar splash_image = \"res://CORE/soscore.png\"\nvar splash_image_size = 256\nvar splash_color = \"000000\"\nvar moam_loadpath = \"res://mods/\"\nvar moam_wanted_name = \"coredevprj\"\nvar moam_wanted_version = 1\nvar moam_wanted_api = 1" > config.gd + @echo -e "{\n\"wanted\": {\n\"min_version\": 1,\n\"max_version\": 1,\n\"min_api\": 1,\n\"max_api\": 1\n},\n\"mod\": {\n\"name\": \"CORE Test Mod\",\n\"version\": 1,\n\"version_friendly\": "1.0",\n\"authors\": [\n\"The CORE Team\"\n],\n\"license\": \"GNU General Public License v3\",\n\"entrypoint\": \"testmod.gd\"\n}\n}" > "$(TESTPROJECT)/mods/testmod/coredevprj.coremod" + @echo -e "extends Node\n@onready\nvar core: Node = get_node(\"/root/core\")\nfunc _start() -> String:\n await get_tree().create_timer(0).timeout\n Logger.info(\"testmod\",\"_start() has been called.\")\n return \"\"\nfunc _stop() -> void:\n await get_tree().create_timer(0).timeout\n Logger.info(\"testmod\",\"_stop() has been called.\")" > "$(TESTPROJECT)/mods/testmod/testmod.gd" @ln -s $(CORE) $(TESTPROJECT)/CORE @echo ":: Development project created." @echo " You can now launch Godot and open '$(TESTPROJECT)'." @echo " Don't forget to save and execute 'make rmdevprj' when you're finished." rmdevprj: @echo ":: Removing development project" - @echo ":: Killing all open programs" - @bash -c "lsof | grep '$(TESTPROJECT)'||exit 0 | awk '{print $2}'||exit 0 | xargs kill||exit 0;exit 0" &> /dev/null + @echo ":: !!!!!!!!!!!!!!!!!!!!!!!!!!!!" + @echo ":: ! IMPORTANT WARNING !" + @echo ":: !!!!!!!!!!!!!!!!!!!!!!!!!!!!" + @echo ":: ! Please remove the CORE !" + @echo ":: ! development project from !" + @echo ":: ! your project list or you !" + @echo ":: ! will experience bugs if !" + @echo ":: ! you edit the dev project !" + @echo ":: ! again in the future! !" + @echo ":: !!!!!!!!!!!!!!!!!!!!!!!!!!!!" + @echo ":: If you have done that, press [ENTER]." + @read -rs @echo ":: Removing directory" @rm -rf $(TESTPROJECT) @rm -rf config.gd - @echo ":: Removing .import files" - @bash -c "rm -rf *.import||exit 0;exit 0" @echo ":: Removing 'uid://' strings" - @bash -c "for file in *.tscn; do cat \"\$$file\"|sed -E 's/\ uid=\"[^\"]*\"//g'|tee \$$file;done &> /dev/null" + @#bash -c "for file in *.tscn; do cat \"\$$file\"|sed -E 's/\ uid=\"[^\"]*\"//g'|tee \$$file;done &> /dev/null" + @echo ":: Please open all .tscn files and remove all 'uid=uid://' strings" + @echo ":: If you have done that, press [ENTER]." + @read -rs diff --git a/config.gd.example b/config.gd.example index e072a0a..d2c358c 100644 --- a/config.gd.example +++ b/config.gd.example @@ -39,3 +39,17 @@ var splash_image = "res://CORE/soscore.png" var splash_image_size = 256 ## Background color var splash_color = "000000" + +# Mother Of All Mods (CORE modloader) +## The directory where all modifications should be located at +## Please note that you should use a clean directory or you will +## encounter errors and strange behaviour. +var moam_loadpath = "user://mods/" +## The name of your project (use lowercasing and snake casing!) +## Used for the mod info file, as it is named like this: ".coremod" +var moam_wanted_name = "a_core_using_project" +## The wanted version, for example your game version +var moam_wanted_version = 1 +## The wanted api version, if you have a modification api for your app/game +## If you don't have a mod api, comment it out or set it to moam_wanted_version +var moam_wanted_api = 1 diff --git a/core.gd b/core.gd index 81ee5a2..9ec10c0 100644 --- a/core.gd +++ b/core.gd @@ -6,7 +6,8 @@ # Licensed under GNU GPLv3 extends Node -const version = "git-develop" +const version = "source 0" +var protection_mode = false var locked = false var readycount = 0 var readylized = false # Fun Fact: "ready" is a signal from "Node" and I (JeremyStarTM) just added "lized" from "initialized" to it to avoid a error thrown by Godot @@ -16,38 +17,157 @@ var smgr = null var resmgr = null var events = null var splash = null +var moam = null +var mkdown = null +enum Errors {UNKNOWN} +enum Responsibilities {UNKNOWN} -func attach(type:String,component,do_setup:bool = true) -> void: +func attach(type:String,module,do_setup:bool = true) -> void: + if protection_mode: return if locked: return Logger.diag("core","Attaching " + type + " to CORE") - var comp = component + var mod = module if do_setup: - comp = Control.new() - comp.name = type - comp.set_script(component) + mod = Control.new() + mod.name = type + mod.set_script(module) match(type): - "config": config = comp - "wmgr": wmgr = comp - "smgr": smgr = comp - "resmgr": resmgr = comp - "events": events = comp - "splash": splash = comp + "config": config = mod + "wmgr": wmgr = mod + "smgr": smgr = mod + "resmgr": resmgr = mod + "events": events = mod + "splash": splash = mod + "moam": moam = mod + "mkdown": mkdown = mod _: - Logger.error("core","Failed attaching " + type + " to CORE: Invalid component") - comp.free() + exception("core","Attaching \"" + type + "\" failed as it is not a valid module") + mod.free() return - add_child(comp) + add_child(mod) Logger.diag("core","Attached " + type + " successfully") func setready() -> void: + if protection_mode: return readycount = readycount+1 - if readycount == 5: + if readycount == 7: readylized = true func lock() -> void: + if protection_mode: return locked = true Logger.diag("core","CORE is now locked. No new attachments can be added.") func welcome() -> void: - Logger.info("core","CORE " + version + " welcomes you!It seems like everything is working :)") + if protection_mode: return + Logger.info("core","CORE (" + version + ") welcomes you!It seems like everything is working :)") + +func exception(script:String,error:String): + protection_mode = true + var exc_message = """ +[color=red]################################ +EXCEPTION! EXCEPTION! EXCEPTION! + +The CORE Framework experienced a critical error in runtime. +Please report this crash to the CORE Framework repository (with +the full log file). You can submit a crash report at: +https://git.staropensource.de/StarOpenSource/core/issues/new + +CORE INFORMATION +version = "%version%" +readylized = %readylized% +readycount = %readycount% +locked = %locked% +preprocessor_enabled = %preprocessor_enabled% +preprocessor_tests = %preprocessor_tests% +preprocessor_tests_executed = %preprocessor_tests_executed% +preprocessor_tests_success = %preprocessor_tests_success% +logger_enabled = %logger_enabled% +logger_diag = %logger_diag% + +OPERATING SYSTEM +timezone = %timezone% +time_utc = "%time_utc%" +time_unix = %time_unix% +date = "%date%" +locale = "%locale%" +lang = "%lang%" +name = "%name%" +distro = "%distro%" +version = "%version_os%" +memory = %memory% +memory_peak = %memory_peak% +memory_usage = %memory_usage% +processor_count = %processor_count% +processor_name = "%processor_name%" +video_adapter = %video_adapter% +rendering_api = "%rendering_api%" + +APPLICATION +cmdline = %cmdline% +permissions = %permissions% +debug_build = %debug_build% + +ERROR INFORMATION +script = "%script%" +error = "%error%" + +STACKTRACE +%stacktrace% + +EXCEPTION! EXCEPTION! EXCEPTION! +################################ +""" + + exc_message = exc_message.replace("%version%",version) + exc_message = exc_message.replace("%readylized%",str(readylized)) + exc_message = exc_message.replace("%readycount%",str(readycount)) + exc_message = exc_message.replace("%locked%",str(locked)) + exc_message = exc_message.replace("%preprocessor_enabled%",str(Preprocessor.enabled)) + exc_message = exc_message.replace("%preprocessor_tests%",str(Preprocessor.tests_enabled)) + exc_message = exc_message.replace("%preprocessor_tests_executed%",str(Preprocessor.tests_executed)) + exc_message = exc_message.replace("%preprocessor_tests_success%",str(Preprocessor.tests_success)) + exc_message = exc_message.replace("%logger_enabled%",str(Logger.enable)) + exc_message = exc_message.replace("%logger_diag%",str(Logger.enable_diag)) + + exc_message = exc_message.replace("%timezone%",str(Time.get_time_zone_from_system())) + exc_message = exc_message.replace("%time_utc%",Time.get_time_string_from_system(true)) + exc_message = exc_message.replace("%time_unix%",str(Time.get_unix_time_from_datetime_string(Time.get_time_string_from_system(true)))) + exc_message = exc_message.replace("%date%",Time.get_date_string_from_system(true)) + exc_message = exc_message.replace("%locale%",OS.get_locale()) + exc_message = exc_message.replace("%lang%",OS.get_locale_language()) + exc_message = exc_message.replace("%name%",OS.get_name()) + exc_message = exc_message.replace("%distro%",OS.get_distribution_name()) + exc_message = exc_message.replace("%version_os%",OS.get_version()) + exc_message = exc_message.replace("%memory%",str(OS.get_memory_info())) + exc_message = exc_message.replace("%memory_peak%",str(OS.get_static_memory_peak_usage())) + exc_message = exc_message.replace("%memory_usage%",str(OS.get_static_memory_usage())) + exc_message = exc_message.replace("%processor_count%",str(OS.get_processor_count())) + exc_message = exc_message.replace("%processor_name%",OS.get_processor_name()) + exc_message = exc_message.replace("%video_adapter%",str(OS.get_video_adapter_driver_info())) + exc_message = exc_message.replace("%rendering_api%",RenderingServer.get_video_adapter_api_version()) + + exc_message = exc_message.replace("%cmdline%",str(OS.get_cmdline_args())) + exc_message = exc_message.replace("%permissions%",str(OS.get_granted_permissions())) + exc_message = exc_message.replace("%debug_build%",str(OS.is_debug_build())) + + exc_message = exc_message.replace("%script%",script) + exc_message = exc_message.replace("%error%",error) + exc_message = exc_message.replace("%stacktrace%",str(get_stack())) + + print_rich(exc_message) + await get_tree().create_timer(0.25).timeout + get_tree().quit(255) + +func error(module:String,respo:Responsibilities,error:Errors,error_info:Dictionary = {}) -> Dictionary: + var error_text = "" + match(respo): + Responsibilities["UNKNOWN"]: + pass + match(error): + Errors["UNKNOWN"]: + error_text = "" + _: + exception("core","Failed generating error: Invalid error \"" + str(error) + "\"") + return {} diff --git a/coreloader.gd b/coreloader.gd index 0fb2c78..6aec8d9 100644 --- a/coreloader.gd +++ b/coreloader.gd @@ -18,24 +18,36 @@ func _ready() -> void: var scr_resmgr = ResourceLoader.load("res://CORE/resmgr.gd") var scr_events = ResourceLoader.load("res://CORE/events.gd") var scr_splash = ResourceLoader.load("res://CORE/splash.tscn").instantiate() + var scr_moam = ResourceLoader.load("res://CORE/moam.gd") + var scr_mkdown = ResourceLoader.load("res://CORE/mkdown.gd") Logger.diag("coreloader","Constructing CORE") var core = Control.new() core.name = "core" core.script = scr_core Logger.diag("coreloader","Attaching CORE to /root/") get_tree().root.add_child(core) - Logger.diag("coreloader","Attaching configuration file to CORE") + Logger.diag("coreloader","Attaching modules") core.attach("config",scr_config) - Logger.diag("coreloader","Attaching wmgr to CORE") core.attach("wmgr",scr_wmgr) - Logger.diag("coreloader","Attaching smgr to CORE") core.attach("smgr",scr_smgr) - Logger.diag("coreloader","Attaching resmgr to CORE") core.attach("resmgr",scr_resmgr) - Logger.diag("coreloader","Attaching events to CORE") core.attach("events",scr_events) - Logger.diag("coreloader","Attaching splash to CORE") core.attach("splash",scr_splash,false) + core.attach("moam",scr_moam) + core.attach("mkdown",scr_mkdown) + if core.protection_mode: return + Logger.diag("coreloader","Loading module configurations") + core.wmgr.load_configuration() + core.splash.load_configuration() + core.moam.load_configuration() + Logger.diag("coreloader","Initializing modules") + core.wmgr.initialize() + core.smgr.initialize() + core.resmgr.initialize() + core.events.initialize() + core.splash.initialize() + core.moam.initialize() + core.mkdown.initialize() Logger.diag("coreloader","Locking CORE") core.lock() Logger.diag("coreloader","Waiting for CORE to fully initialize") diff --git a/docs/references/config.gd.md b/docs/references/config.gd.md index 7bbf1a6..36bb2ea 100644 --- a/docs/references/config.gd.md +++ b/docs/references/config.gd.md @@ -127,4 +127,4 @@ var splash_image_size = 256 ## The splash screen's background color. Do not include ## a "#" at the start. var splash_color = "000000" -``` \ No newline at end of file +``` diff --git a/docs/references/index.md b/docs/references/index.md index 7e72fe0..898ff06 100644 --- a/docs/references/index.md +++ b/docs/references/index.md @@ -14,3 +14,4 @@ Here you can find all code references.
## [Scene Manager (core.smgr)](/references/smgr.gd/) ## [Splash Screen (core.splash)](/references/splash.gd/) ## [Window Manager (core.wmgr)](/references/wmgr.gd/) +## [Mother Of All Mods [Mod loader] (core.moam)](/references/moam.gd/) diff --git a/docs/references/moam.gd.md b/docs/references/moam.gd.md new file mode 100644 index 0000000..58ff4fe --- /dev/null +++ b/docs/references/moam.gd.md @@ -0,0 +1,61 @@ +--- +hide: + - navigation +--- +# Mother Of All Mods [Modloader] (core.moam) +Loads and executes modifications. + +## Methods +### load_mods() +- returns `Array` +- description `Loads all modifications and returns all encountered errors as a a collection of strings` +### load_mod() +- returns `String` (if **not** `""` some error occured) +- description `Loads and executes a modification` + - argument `mod_name` + - type `String` + - mandatory `yes` + - description `The modification name to use` +### unload_mods() +- returns `void` +- description `Unloads all loaded modifications` +### unload_mod() +- returns `void` +- description `Unloads a modification` + - argument `mod_name` + - type `String` + - mandatory `yes` + - description `The modification name to use` +### get_list() +- returns `Array` +- description `Returns a list of all loaded modification names` +### get_info() +- returns `Dictionary` (if a error occured the dict will contain a `error` key) +- description `Returns information about a modification` + - argument `mod_name` + - type `String` + - mandatory `yes` + - description `The modification name to use` + +## Internal variables +Do not touch or you will cause breakage. +### modlist +- type `Array` +- default value `[]` +- description `All loaded modification (names)` +### config_loadpath +- type `String` +- default value `null` +- description `The mods directory all modifications are loaded from` +### config_wanted_name +- type `String` +- default value `null` +- description `Used for the modinfo filename. If set to "testapp" it will search for "testapp.coremod" files` +### config_wanted_version +- type `String` +- default value `null` +- description `The application or game version. Modifications will not load if they don't support the specified version` +### config_wanted_api +- type `String` +- default value `null` +- description `The application or game api version. Modifications will not load if they don't support the specified api version.` diff --git a/docs/references/wmgr.gd.md b/docs/references/wmgr.gd.md index d2c8bb5..a537db2 100644 --- a/docs/references/wmgr.gd.md +++ b/docs/references/wmgr.gd.md @@ -122,4 +122,4 @@ Do not execute or you will cause breakage. - argument `delta` - type `float` - mandatory `yes` - - description `You know what it is.` \ No newline at end of file + - description `You know what it is.` diff --git a/events.gd b/events.gd index 6defdfd..93d411e 100644 --- a/events.gd +++ b/events.gd @@ -23,22 +23,13 @@ signal logger_info signal logger_warn signal logger_error -func _ready() -> void: +func initialize() -> void: + if core.protection_mode: return Logger.connect("logevent",Callable(self,"logger_update")) core.setready() -func logger_update(type:String,script:String,message:String,_logmessage:String) -> void: - emit_signal("logger_all",type,script,message) - if type == "DIAG": - emit_signal("logger_diag",script,message) - elif type == "INFO": - emit_signal("logger_info",script,message) - elif type == "WARN": - emit_signal("logger_warn",script,message) - elif type == "ERROR": - emit_signal("logger_error",script,message) - func _process(_delta:float) -> void: + if core.protection_mode: return var window_title_var = core.wmgr.get_title() var window_size_x = core.wmgr.get_size().x var window_size_y = core.wmgr.get_size().y @@ -67,3 +58,17 @@ func _process(_delta:float) -> void: elif window_pos_y != window_pos_y_prev: emit_signal("window_pos","Y",Vector2i(window_pos_x,window_pos_y),Vector2i(window_pos_x_prev,window_pos_y_prev)) window_pos_y_prev = window_pos_y + +func logger_update(type:String,script:String,message:String,_logmessage:String) -> void: + if core.protection_mode: return + emit_signal("logger_all",type,script,message) + if type == "DIAG": + emit_signal("logger_diag",script,message) + elif type == "INFO": + emit_signal("logger_info",script,message) + elif type == "WARN": + emit_signal("logger_warn",script,message) + elif type == "ERROR": + emit_signal("logger_error",script,message) + else: + core.exception("events","The log type \"" + type + "\" is not known to events.gd") diff --git a/mkdown.gd b/mkdown.gd new file mode 100644 index 0000000..1fb4dc6 --- /dev/null +++ b/mkdown.gd @@ -0,0 +1,29 @@ +extends Node + +@onready +var core = get_node("/root/core") +var fsizes = {"text":"16","h1":"29","h2":"24","h3":"19","h4":"14","h5":"9","h6":"4"} + +func parse(content:String) -> String: + var parse_text = "" + for line in content.split("\n",false): + var line_mod = line + if line_mod.begins_with("######"): + line_mod = line_mod.replace("######","[font_size=" + fsizes["h6"] + "]") + "[/font_size]" + elif line_mod.begins_with("#####"): + line_mod = line_mod.replace("#####","[font_size=" + fsizes["h5"] + "]") + "[/font_size]" + elif line_mod.begins_with("####"): + line_mod = line_mod.replace("####","[font_size=" + fsizes["h4"] + "]") + "[/font_size]" + elif line_mod.begins_with("###"): + line_mod = line_mod.replace("###","[font_size=" + fsizes["h3"] + "]") + "[/font_size]" + elif line_mod.begins_with("##"): + line_mod = line_mod.replace("##","[font_size=" + fsizes["h2"] + "]") + "[/font_size]" + elif line_mod.begins_with("#"): + line_mod = line_mod.replace("#","[font_size=" + fsizes["h1"] + "]") + "[/font_size]" + else: + line_mod = "[font_size=" + fsizes["text"] + "]" + line_mod + "[/font_size]" + parse_text = parse_text + line_mod + "\n" + return parse_text + +func initialize() -> void: + core.setready() diff --git a/moam.gd b/moam.gd new file mode 100644 index 0000000..e5a681a --- /dev/null +++ b/moam.gd @@ -0,0 +1,123 @@ +# 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() diff --git a/resmgr.gd b/resmgr.gd index 83bd1f3..82eb54a 100644 --- a/resmgr.gd +++ b/resmgr.gd @@ -10,28 +10,36 @@ extends Node var core = get_node(NodePath("/root/core")) var resources = {} +func initialize() -> void: + if core.protection_mode: return + core.setready() + func loadres(resname:String,respath:String,replace:bool = false) -> void: + if core.protection_mode: return if resources.has(resname): - if replace: - resources.erase(resname) - else: - Logger.error("resmgr","Loading \"" + resname + "\" failed: Resource is loaded already") - return + if replace: + resources.erase(resname) + else: + Logger.error("resmgr","Loading \"" + resname + "\" failed: Resource is loaded already") + return resources.merge({resname:ResourceLoader.load(respath)}) func unloadres(resname:String) -> void: + if core.protection_mode: return if !resources.has(resname): Logger.error("resmgr","Unloading \"" + resname + "\" failed: Resource is not present") return resources.erase(resname) func loadbatch(batch:Dictionary,replace:bool = false) -> void: + if core.protection_mode: return if batch == {}: Logger.error("resmgr","Loading a batch failed: Batch is empty") for i in batch: loadres(i,batch[i],replace) func unloadbatch(batch:Array) -> void: + if core.protection_mode: return if batch == []: Logger.error("resmgr","Unloading a batch failed: Batch is empty") for i in batch: @@ -39,9 +47,7 @@ func unloadbatch(batch:Array) -> void: # No set return type here as it can return literally everything. func getres(resname:String): + if core.protection_mode: return null if !resources.has(resname): return null return resources[resname] - -func _ready() -> void: - core.setready() diff --git a/smgr.gd b/smgr.gd index 24db525..83170c9 100644 --- a/smgr.gd +++ b/smgr.gd @@ -18,109 +18,8 @@ var ccr_cutscene_inuse = false var ccr_debug = null var ccr_debug_names = [] -func add_game(resname:String) -> bool: - Logger.diag("smgr","Adding game scene") - if ccr_game_inuse: - Logger.error("smgr","Failed adding game scene: Only one game scene is allowed") - return false - var res = core.resmgr.getres(resname) - if res == null or res.get_class() != "PackedScene": - Logger.error("smgr","Resource is not type of PackedScene") - return false - ccr_game.add_child(res.instantiate()) - ccr_game_inuse = true - Logger.diag("smgr","Added game scene") - return true - -func remove_game() -> bool: - Logger.diag("smgr","Removing game scene") - if !ccr_game_inuse: - Logger.error("smgr","Failed removing game scene: No game scene is active") - return false - ccr_game.remove_child(ccr_game.get_child(0)) - ccr_game_inuse = false - Logger.diag("smgr","Removed game scene") - return true - -func add_menu(resname:String) -> bool: - Logger.diag("smgr","Adding menu scene \"" + resname + "\"") - if ccr_menu_names.has(resname): - Logger.error("smgr","Failed adding menu scene: Menu scene with name \"" + resname + "\" already exists") - return false - var res = core.resmgr.getres(resname) - if res == null or res.get_class() != "PackedScene": - Logger.error("smgr","Resource is not type of PackedScene") - return false - ccr_menu.add_child(res.instantiate()) - ccr_menu_names.append(resname) - Logger.diag("smgr","Added menu scene \"" + resname + "\"") - return true - -func remove_menu(scene_name:String) -> bool: - Logger.diag("smgr","Removing menu scene \"" + scene_name + "\"") - if !ccr_menu_names.has(scene_name): - Logger.error("smgr","Failed removing menu scene: Menu scene with name \"" + scene_name + "\" does not exist") - return false - for i in ccr_menu.get_children(): - if i.name == scene_name: - ccr_menu.remove_child(i) - break - ccr_menu_names.erase(scene_name) - Logger.diag("smgr","Removed menu scene \"" + scene_name + "\"") - return true - -func add_cutscene(resname:String) -> bool: - Logger.diag("smgr","Adding cutscene scene") - if ccr_cutscene_inuse: - Logger.error("smgr","Failed adding cutscene scene: Only one cutscene scene is allowed") - return false - var res = core.resmgr.getres(resname) - if res == null or res.get_class() != "PackedScene": - Logger.error("smgr","Resource is not type of PackedScene") - return false - ccr_cutscene.add_child(res.instantiate()) - ccr_cutscene_inuse = true - Logger.diag("smgr","Added cutscene scene") - return true - -func remove_cutscene() -> bool: - Logger.diag("smgr","Removing cutscene scene") - if !ccr_cutscene_inuse: - Logger.error("smgr","Failed removing cutscene scene: No cutscene scene is active") - return false - ccr_cutscene.remove_child(ccr_cutscene.get_child(0)) - ccr_cutscene_inuse = false - Logger.diag("smgr","Removed cutscene scene") - return true - -func add_debug(resname:String) -> bool: - Logger.diag("smgr","Adding debug scene \"" + resname + "\"") - if ccr_debug_names.has(resname): - Logger.error("smgr","Failed adding debug scene: Debug scene with name \"" + resname + "\" already exists") - return false - var res = core.resmgr.getres(resname) - if res == null or res.get_class() != "PackedScene": - Logger.error("smgr","Resource is not type of PackedScene") - return false - ccr_debug.add_child(res.instantiate()) - ccr_debug_names.append(resname) - Logger.diag("smgr","Added debug scene \"" + resname + "\"") - return true - -func remove_debug(scene_name:String) -> bool: - Logger.diag("smgr","Removing debug scene \"" + scene_name + "\"") - if !ccr_menu_names.has(scene_name): - Logger.error("smgr","Failed removing debug scene: Debug scene with name \"" + scene_name + "\" does not exist") - return false - for i in ccr_debug.get_children(): - if i.name == scene_name: - ccr_debug.remove_child(i) - break - ccr_debug_names.erase(scene_name) - Logger.diag("smgr","Removed debug scene \"" + scene_name + "\"") - return true - -func _ready() -> void: +func initialize() -> void: + if core.protection_mode: return Logger.diag("smgr","Constructing CORE Scene Root (CCR)") ccr = Control.new() ccr.name = "ccr" @@ -140,7 +39,118 @@ func _ready() -> void: add_overlays() core.setready() +func add_game(resname:String) -> bool: + if core.protection_mode: return false + Logger.diag("smgr","Adding game scene") + if ccr_game_inuse: + Logger.error("smgr","Failed adding game scene: Only one game scene is allowed") + return false + var res = core.resmgr.getres(resname) + if res == null or res.get_class() != "PackedScene": + Logger.error("smgr","Resource is not type of PackedScene") + return false + ccr_game.add_child(res.instantiate()) + ccr_game_inuse = true + Logger.diag("smgr","Added game scene") + return true + +func remove_game() -> bool: + if core.protection_mode: return false + Logger.diag("smgr","Removing game scene") + if !ccr_game_inuse: + Logger.error("smgr","Failed removing game scene: No game scene is active") + return false + ccr_game.remove_child(ccr_game.get_child(0)) + ccr_game_inuse = false + Logger.diag("smgr","Removed game scene") + return true + +func add_menu(resname:String) -> bool: + if core.protection_mode: return false + Logger.diag("smgr","Adding menu scene \"" + resname + "\"") + if ccr_menu_names.has(resname): + Logger.error("smgr","Failed adding menu scene: Menu scene with name \"" + resname + "\" already exists") + return false + var res = core.resmgr.getres(resname) + if res == null or res.get_class() != "PackedScene": + Logger.error("smgr","Resource is not type of PackedScene") + return false + ccr_menu.add_child(res.instantiate()) + ccr_menu_names.append(resname) + Logger.diag("smgr","Added menu scene \"" + resname + "\"") + return true + +func remove_menu(scene_name:String) -> bool: + if core.protection_mode: return false + Logger.diag("smgr","Removing menu scene \"" + scene_name + "\"") + if !ccr_menu_names.has(scene_name): + Logger.error("smgr","Failed removing menu scene: Menu scene with name \"" + scene_name + "\" does not exist") + return false + for i in ccr_menu.get_children(): + if i.name == scene_name: + ccr_menu.remove_child(i) + break + ccr_menu_names.erase(scene_name) + Logger.diag("smgr","Removed menu scene \"" + scene_name + "\"") + return true + +func add_cutscene(resname:String) -> bool: + if core.protection_mode: return false + Logger.diag("smgr","Adding cutscene scene") + if ccr_cutscene_inuse: + Logger.error("smgr","Failed adding cutscene scene: Only one cutscene scene is allowed") + return false + var res = core.resmgr.getres(resname) + if res == null or res.get_class() != "PackedScene": + Logger.error("smgr","Resource is not type of PackedScene") + return false + ccr_cutscene.add_child(res.instantiate()) + ccr_cutscene_inuse = true + Logger.diag("smgr","Added cutscene scene") + return true + +func remove_cutscene() -> bool: + if core.protection_mode: return false + Logger.diag("smgr","Removing cutscene scene") + if !ccr_cutscene_inuse: + Logger.error("smgr","Failed removing cutscene scene: No cutscene scene is active") + return false + ccr_cutscene.remove_child(ccr_cutscene.get_child(0)) + ccr_cutscene_inuse = false + Logger.diag("smgr","Removed cutscene scene") + return true + +func add_debug(resname:String) -> bool: + if core.protection_mode: return false + Logger.diag("smgr","Adding debug scene \"" + resname + "\"") + if ccr_debug_names.has(resname): + Logger.error("smgr","Failed adding debug scene: Debug scene with name \"" + resname + "\" already exists") + return false + var res = core.resmgr.getres(resname) + if res == null or res.get_class() != "PackedScene": + Logger.error("smgr","Resource is not type of PackedScene") + return false + ccr_debug.add_child(res.instantiate()) + ccr_debug_names.append(resname) + Logger.diag("smgr","Added debug scene \"" + resname + "\"") + return true + +func remove_debug(scene_name:String) -> bool: + if core.protection_mode: return false + Logger.diag("smgr","Removing debug scene \"" + scene_name + "\"") + if !ccr_menu_names.has(scene_name): + Logger.error("smgr","Failed removing debug scene: Debug scene with name \"" + scene_name + "\" does not exist") + return false + for i in ccr_debug.get_children(): + if i.name == scene_name: + ccr_debug.remove_child(i) + break + ccr_debug_names.erase(scene_name) + Logger.diag("smgr","Removed debug scene \"" + scene_name + "\"") + return true + func add_overlays() -> void: + if core.protection_mode: return Logger.diag("smgr","Adding game overlay") ccr_game = Control.new() ccr_game.name = "GameOverlay" diff --git a/splash.gd b/splash.gd index 3411ba1..3c8d66f 100644 --- a/splash.gd +++ b/splash.gd @@ -8,33 +8,41 @@ extends Node @onready var core = get_node("/root/core") -@onready -var enabled = core.config.splash_enabled -@onready -var image = core.config.splash_image -@onready -var image_size = core.config.splash_image_size -@onready -var color = core.config.splash_color -func _ready() -> void: +var config_enabled = null +var config_image = null +var config_image_size = null +var config_color = null + +func load_configuration() -> void: + if core.protection_mode: return + config_enabled = core.config.splash_enabled + config_image = core.config.splash_image + config_image_size = core.config.splash_image_size + config_color = core.config.splash_color + +func initialize() -> void: + if core.protection_mode: return apply_config() - if enabled: + if config_enabled: display() else: $Background.visible = false core.setready() func apply_config() -> void: - $Background.color = color - $Background/Image.texture = ResourceLoader.load(image) - $Background/Image.size = Vector2i(image_size,image_size) + if core.protection_mode: return + $Background.color = config_color + $Background/Image.texture = ResourceLoader.load(config_image) + $Background/Image.size = Vector2i(config_image_size,config_image_size) func display() -> void: + if core.protection_mode: return Logger.info("splash","Displaying splash screen") get_tree().root.move_child($"/root/ccr",0) $Background.visible = true func dissolve() -> void: + if core.protection_mode: return Logger.info("splash","Dissolving splash screen") $Background.visible = false diff --git a/wmgr.gd b/wmgr.gd index 8ec5c5b..267469a 100644 --- a/wmgr.gd +++ b/wmgr.gd @@ -8,34 +8,51 @@ extends Node @onready var core = get_node(NodePath("/root/core")) -@onready -var window_size_x = core.config.wmgr_size_x -@onready -var window_size_y = core.config.wmgr_size_y -@onready -var window_title = core.config.wmgr_title -@onready -var window_mode = core.config.wmgr_mode + +var config_window_size_x = null +var config_window_size_y = null +var config_window_title = null +var config_window_mode = null var delta = 0 +func load_configuration() -> void: + if core.protection_mode: return + config_window_size_x = core.config.wmgr_size_x + config_window_size_y = core.config.wmgr_size_y + config_window_title = core.config.wmgr_title + config_window_mode = core.config.wmgr_mode + +func initialize() -> void: + if core.protection_mode: return + Logger.diag("wmgr","Configuring window") + DisplayServer.window_set_size(Vector2i(config_window_size_x,config_window_size_y)) + DisplayServer.window_set_min_size(Vector2i(960,540)) + DisplayServer.window_set_title(config_window_title) + DisplayServer.window_set_mode(config_window_mode) + core.setready() + func _process(deltap) -> void: delta = deltap func set_size(x:int,y:int) -> void: + if core.protection_mode: return Logger.diag("wmgr","Applying new window size (" + str(x) + "x" + str(y) + ")") DisplayServer.window_set_size(Vector2i(x,y)) func set_position(x:int,y:int) -> void: + if core.protection_mode: return Logger.diag("wmgr","Applying new window position (X" + str(x) + " Y" + str(y) + ")") DisplayServer.window_set_position(Vector2i(x,y)) func set_title(title:String) -> void: + if core.protection_mode: return Logger.diag("wmgr","Applying new window title (" + title + ")") DisplayServer.window_set_title(title) - window_title = title + config_window_title = title func set_mode(mode:String) -> bool: + if core.protection_mode: return false Logger.diag("wmgr","Applying new window mode (" + mode + ")") match(mode): "WINDOWED": DisplayServer.window_set_mode(DisplayServer.WINDOW_MODE_WINDOWED) @@ -49,15 +66,19 @@ func set_mode(mode:String) -> bool: return true func get_size() -> Vector2i: + if core.protection_mode: return Vector2i(0,0) return DisplayServer.window_get_size() func get_position() -> Vector2i: + if core.protection_mode: return Vector2i(0,0) return DisplayServer.window_get_position() func get_title() -> String: - return window_title + if core.protection_mode: return "" + return config_window_title func get_mode() -> String: + if core.protection_mode: return "" match(DisplayServer.window_get_mode()): 0: return "WINDOWED" 1: return "MINIMIZED" @@ -65,10 +86,11 @@ func get_mode() -> String: 3: return "FULLSCREEN" 4: return "EXCLUSIVE_FULLSCREEN" _: - Logger.error("wmgr","Failed to get window mode: The window mode id \"" + str(DisplayServer.window_get_mode()) + "\" is not known to CORE.") + core.exception("wmgr","The window mode id \"" + str(DisplayServer.window_get_mode()) + "\" is not known to wmgr.gd") return "INVALID_MODE" func get_fps(flat:bool = false) -> float: + if core.protection_mode: return 0.0 if str(delta) == "0": return INF else: @@ -78,15 +100,9 @@ func get_fps(flat:bool = false) -> float: return 1/delta func get_rendertime() -> float: + if core.protection_mode: return 0.0 return Performance.get_monitor(Performance.TIME_PROCESS) func get_delta() -> float: + if core.protection_mode: return 0.0 return delta - -func _ready() -> void: - Logger.diag("wmgr","Configuring window") - DisplayServer.window_set_size(Vector2i(window_size_x,window_size_y)) - DisplayServer.window_set_min_size(Vector2i(960,540)) - DisplayServer.window_set_title(window_title) - DisplayServer.window_set_mode(window_mode) - core.setready()