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
This commit is contained in:
JeremyStar™ 2023-08-07 14:49:12 +02:00
parent f06b031a5e
commit ab5b3ade52
15 changed files with 606 additions and 188 deletions

View file

@ -34,22 +34,35 @@ docs-clean:
@rm -rf "$(DOCSOUT)" @rm -rf "$(DOCSOUT)"
mkdevprj: mkdevprj:
@echo ":: Creating development project" @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 "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\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\"" > config.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 @ln -s $(CORE) $(TESTPROJECT)/CORE
@echo ":: Development project created." @echo ":: Development project created."
@echo " You can now launch Godot and open '$(TESTPROJECT)'." @echo " You can now launch Godot and open '$(TESTPROJECT)'."
@echo " Don't forget to save and execute 'make rmdevprj' when you're finished." @echo " Don't forget to save and execute 'make rmdevprj' when you're finished."
rmdevprj: rmdevprj:
@echo ":: Removing development project" @echo ":: Removing development project"
@echo ":: Killing all open programs" @echo ":: !!!!!!!!!!!!!!!!!!!!!!!!!!!!"
@bash -c "lsof | grep '$(TESTPROJECT)'||exit 0 | awk '{print $2}'||exit 0 | xargs kill||exit 0;exit 0" &> /dev/null @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" @echo ":: Removing directory"
@rm -rf $(TESTPROJECT) @rm -rf $(TESTPROJECT)
@rm -rf config.gd @rm -rf config.gd
@echo ":: Removing .import files"
@bash -c "rm -rf *.import||exit 0;exit 0"
@echo ":: Removing 'uid://' strings" @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

View file

@ -39,3 +39,17 @@ var splash_image = "res://CORE/soscore.png"
var splash_image_size = 256 var splash_image_size = 256
## Background color ## Background color
var splash_color = "000000" 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: "<moam_wanted_name>.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

154
core.gd
View file

@ -6,7 +6,8 @@
# Licensed under GNU GPLv3 # Licensed under GNU GPLv3
extends Node extends Node
const version = "git-develop" const version = "source 0"
var protection_mode = false
var locked = false var locked = false
var readycount = 0 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 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 resmgr = null
var events = null var events = null
var splash = 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: if locked:
return return
Logger.diag("core","Attaching " + type + " to CORE") Logger.diag("core","Attaching " + type + " to CORE")
var comp = component var mod = module
if do_setup: if do_setup:
comp = Control.new() mod = Control.new()
comp.name = type mod.name = type
comp.set_script(component) mod.set_script(module)
match(type): match(type):
"config": config = comp "config": config = mod
"wmgr": wmgr = comp "wmgr": wmgr = mod
"smgr": smgr = comp "smgr": smgr = mod
"resmgr": resmgr = comp "resmgr": resmgr = mod
"events": events = comp "events": events = mod
"splash": splash = comp "splash": splash = mod
"moam": moam = mod
"mkdown": mkdown = mod
_: _:
Logger.error("core","Failed attaching " + type + " to CORE: Invalid component") exception("core","Attaching \"" + type + "\" failed as it is not a valid module")
comp.free() mod.free()
return return
add_child(comp) add_child(mod)
Logger.diag("core","Attached " + type + " successfully") Logger.diag("core","Attached " + type + " successfully")
func setready() -> void: func setready() -> void:
if protection_mode: return
readycount = readycount+1 readycount = readycount+1
if readycount == 5: if readycount == 7:
readylized = true readylized = true
func lock() -> void: func lock() -> void:
if protection_mode: return
locked = true locked = true
Logger.diag("core","CORE is now locked. No new attachments can be added.") Logger.diag("core","CORE is now locked. No new attachments can be added.")
func welcome() -> void: func welcome() -> void:
Logger.info("core","CORE " + version + " welcomes you!<nl>It seems like everything is working :)") if protection_mode: return
Logger.info("core","CORE (" + version + ") welcomes you!<nl>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 {}

View file

@ -18,24 +18,36 @@ func _ready() -> void:
var scr_resmgr = ResourceLoader.load("res://CORE/resmgr.gd") var scr_resmgr = ResourceLoader.load("res://CORE/resmgr.gd")
var scr_events = ResourceLoader.load("res://CORE/events.gd") var scr_events = ResourceLoader.load("res://CORE/events.gd")
var scr_splash = ResourceLoader.load("res://CORE/splash.tscn").instantiate() 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") Logger.diag("coreloader","Constructing CORE")
var core = Control.new() var core = Control.new()
core.name = "core" core.name = "core"
core.script = scr_core core.script = scr_core
Logger.diag("coreloader","Attaching CORE to /root/") Logger.diag("coreloader","Attaching CORE to /root/")
get_tree().root.add_child(core) get_tree().root.add_child(core)
Logger.diag("coreloader","Attaching configuration file to CORE") Logger.diag("coreloader","Attaching modules")
core.attach("config",scr_config) core.attach("config",scr_config)
Logger.diag("coreloader","Attaching wmgr to CORE")
core.attach("wmgr",scr_wmgr) core.attach("wmgr",scr_wmgr)
Logger.diag("coreloader","Attaching smgr to CORE")
core.attach("smgr",scr_smgr) core.attach("smgr",scr_smgr)
Logger.diag("coreloader","Attaching resmgr to CORE")
core.attach("resmgr",scr_resmgr) core.attach("resmgr",scr_resmgr)
Logger.diag("coreloader","Attaching events to CORE")
core.attach("events",scr_events) core.attach("events",scr_events)
Logger.diag("coreloader","Attaching splash to CORE")
core.attach("splash",scr_splash,false) 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") Logger.diag("coreloader","Locking CORE")
core.lock() core.lock()
Logger.diag("coreloader","Waiting for CORE to fully initialize") Logger.diag("coreloader","Waiting for CORE to fully initialize")

View file

@ -127,4 +127,4 @@ var splash_image_size = 256
## The splash screen's background color. Do not include ## The splash screen's background color. Do not include
## a "#" at the start. ## a "#" at the start.
var splash_color = "000000" var splash_color = "000000"
``` ```

View file

@ -14,3 +14,4 @@ Here you can find all code references.<br/>
## [Scene Manager (core.smgr)](/references/smgr.gd/) ## [Scene Manager (core.smgr)](/references/smgr.gd/)
## [Splash Screen (core.splash)](/references/splash.gd/) ## [Splash Screen (core.splash)](/references/splash.gd/)
## [Window Manager (core.wmgr)](/references/wmgr.gd/) ## [Window Manager (core.wmgr)](/references/wmgr.gd/)
## [Mother Of All Mods [Mod loader] (core.moam)](/references/moam.gd/)

View file

@ -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.`

View file

@ -122,4 +122,4 @@ Do not execute or you will cause breakage.
- argument `delta` - argument `delta`
- type `float` - type `float`
- mandatory `yes` - mandatory `yes`
- description `You know what it is.` - description `You know what it is.`

View file

@ -23,22 +23,13 @@ signal logger_info
signal logger_warn signal logger_warn
signal logger_error signal logger_error
func _ready() -> void: func initialize() -> void:
if core.protection_mode: return
Logger.connect("logevent",Callable(self,"logger_update")) Logger.connect("logevent",Callable(self,"logger_update"))
core.setready() 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: func _process(_delta:float) -> void:
if core.protection_mode: return
var window_title_var = core.wmgr.get_title() var window_title_var = core.wmgr.get_title()
var window_size_x = core.wmgr.get_size().x var window_size_x = core.wmgr.get_size().x
var window_size_y = core.wmgr.get_size().y 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: 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)) 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 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")

29
mkdown.gd Normal file
View file

@ -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()

123
moam.gd Normal file
View file

@ -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()

View file

@ -10,28 +10,36 @@ extends Node
var core = get_node(NodePath("/root/core")) var core = get_node(NodePath("/root/core"))
var resources = {} var resources = {}
func initialize() -> void:
if core.protection_mode: return
core.setready()
func loadres(resname:String,respath:String,replace:bool = false) -> void: func loadres(resname:String,respath:String,replace:bool = false) -> void:
if core.protection_mode: return
if resources.has(resname): if resources.has(resname):
if replace: if replace:
resources.erase(resname) resources.erase(resname)
else: else:
Logger.error("resmgr","Loading \"" + resname + "\" failed: Resource is loaded already") Logger.error("resmgr","Loading \"" + resname + "\" failed: Resource is loaded already")
return return
resources.merge({resname:ResourceLoader.load(respath)}) resources.merge({resname:ResourceLoader.load(respath)})
func unloadres(resname:String) -> void: func unloadres(resname:String) -> void:
if core.protection_mode: return
if !resources.has(resname): if !resources.has(resname):
Logger.error("resmgr","Unloading \"" + resname + "\" failed: Resource is not present") Logger.error("resmgr","Unloading \"" + resname + "\" failed: Resource is not present")
return return
resources.erase(resname) resources.erase(resname)
func loadbatch(batch:Dictionary,replace:bool = false) -> void: func loadbatch(batch:Dictionary,replace:bool = false) -> void:
if core.protection_mode: return
if batch == {}: if batch == {}:
Logger.error("resmgr","Loading a batch failed: Batch is empty") Logger.error("resmgr","Loading a batch failed: Batch is empty")
for i in batch: for i in batch:
loadres(i,batch[i],replace) loadres(i,batch[i],replace)
func unloadbatch(batch:Array) -> void: func unloadbatch(batch:Array) -> void:
if core.protection_mode: return
if batch == []: if batch == []:
Logger.error("resmgr","Unloading a batch failed: Batch is empty") Logger.error("resmgr","Unloading a batch failed: Batch is empty")
for i in batch: for i in batch:
@ -39,9 +47,7 @@ func unloadbatch(batch:Array) -> void:
# No set return type here as it can return literally everything. # No set return type here as it can return literally everything.
func getres(resname:String): func getres(resname:String):
if core.protection_mode: return null
if !resources.has(resname): if !resources.has(resname):
return null return null
return resources[resname] return resources[resname]
func _ready() -> void:
core.setready()

216
smgr.gd
View file

@ -18,109 +18,8 @@ var ccr_cutscene_inuse = false
var ccr_debug = null var ccr_debug = null
var ccr_debug_names = [] var ccr_debug_names = []
func add_game(resname:String) -> bool: func initialize() -> void:
Logger.diag("smgr","Adding game scene") if core.protection_mode: return
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:
Logger.diag("smgr","Constructing CORE Scene Root (CCR)") Logger.diag("smgr","Constructing CORE Scene Root (CCR)")
ccr = Control.new() ccr = Control.new()
ccr.name = "ccr" ccr.name = "ccr"
@ -140,7 +39,118 @@ func _ready() -> void:
add_overlays() add_overlays()
core.setready() 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: func add_overlays() -> void:
if core.protection_mode: return
Logger.diag("smgr","Adding game overlay") Logger.diag("smgr","Adding game overlay")
ccr_game = Control.new() ccr_game = Control.new()
ccr_game.name = "GameOverlay" ccr_game.name = "GameOverlay"

View file

@ -8,33 +8,41 @@ extends Node
@onready @onready
var core = get_node("/root/core") 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() apply_config()
if enabled: if config_enabled:
display() display()
else: else:
$Background.visible = false $Background.visible = false
core.setready() core.setready()
func apply_config() -> void: func apply_config() -> void:
$Background.color = color if core.protection_mode: return
$Background/Image.texture = ResourceLoader.load(image) $Background.color = config_color
$Background/Image.size = Vector2i(image_size,image_size) $Background/Image.texture = ResourceLoader.load(config_image)
$Background/Image.size = Vector2i(config_image_size,config_image_size)
func display() -> void: func display() -> void:
if core.protection_mode: return
Logger.info("splash","Displaying splash screen") Logger.info("splash","Displaying splash screen")
get_tree().root.move_child($"/root/ccr",0) get_tree().root.move_child($"/root/ccr",0)
$Background.visible = true $Background.visible = true
func dissolve() -> void: func dissolve() -> void:
if core.protection_mode: return
Logger.info("splash","Dissolving splash screen") Logger.info("splash","Dissolving splash screen")
$Background.visible = false $Background.visible = false

54
wmgr.gd
View file

@ -8,34 +8,51 @@ extends Node
@onready @onready
var core = get_node(NodePath("/root/core")) var core = get_node(NodePath("/root/core"))
@onready
var window_size_x = core.config.wmgr_size_x var config_window_size_x = null
@onready var config_window_size_y = null
var window_size_y = core.config.wmgr_size_y var config_window_title = null
@onready var config_window_mode = null
var window_title = core.config.wmgr_title
@onready
var window_mode = core.config.wmgr_mode
var delta = 0 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: func _process(deltap) -> void:
delta = deltap delta = deltap
func set_size(x:int,y:int) -> void: 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) + ")") Logger.diag("wmgr","Applying new window size (" + str(x) + "x" + str(y) + ")")
DisplayServer.window_set_size(Vector2i(x,y)) DisplayServer.window_set_size(Vector2i(x,y))
func set_position(x:int,y:int) -> void: 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) + ")") Logger.diag("wmgr","Applying new window position (X" + str(x) + " Y" + str(y) + ")")
DisplayServer.window_set_position(Vector2i(x,y)) DisplayServer.window_set_position(Vector2i(x,y))
func set_title(title:String) -> void: func set_title(title:String) -> void:
if core.protection_mode: return
Logger.diag("wmgr","Applying new window title (" + title + ")") Logger.diag("wmgr","Applying new window title (" + title + ")")
DisplayServer.window_set_title(title) DisplayServer.window_set_title(title)
window_title = title config_window_title = title
func set_mode(mode:String) -> bool: func set_mode(mode:String) -> bool:
if core.protection_mode: return false
Logger.diag("wmgr","Applying new window mode (" + mode + ")") Logger.diag("wmgr","Applying new window mode (" + mode + ")")
match(mode): match(mode):
"WINDOWED": DisplayServer.window_set_mode(DisplayServer.WINDOW_MODE_WINDOWED) "WINDOWED": DisplayServer.window_set_mode(DisplayServer.WINDOW_MODE_WINDOWED)
@ -49,15 +66,19 @@ func set_mode(mode:String) -> bool:
return true return true
func get_size() -> Vector2i: func get_size() -> Vector2i:
if core.protection_mode: return Vector2i(0,0)
return DisplayServer.window_get_size() return DisplayServer.window_get_size()
func get_position() -> Vector2i: func get_position() -> Vector2i:
if core.protection_mode: return Vector2i(0,0)
return DisplayServer.window_get_position() return DisplayServer.window_get_position()
func get_title() -> String: func get_title() -> String:
return window_title if core.protection_mode: return ""
return config_window_title
func get_mode() -> String: func get_mode() -> String:
if core.protection_mode: return ""
match(DisplayServer.window_get_mode()): match(DisplayServer.window_get_mode()):
0: return "WINDOWED" 0: return "WINDOWED"
1: return "MINIMIZED" 1: return "MINIMIZED"
@ -65,10 +86,11 @@ func get_mode() -> String:
3: return "FULLSCREEN" 3: return "FULLSCREEN"
4: return "EXCLUSIVE_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" return "INVALID_MODE"
func get_fps(flat:bool = false) -> float: func get_fps(flat:bool = false) -> float:
if core.protection_mode: return 0.0
if str(delta) == "0": if str(delta) == "0":
return INF return INF
else: else:
@ -78,15 +100,9 @@ func get_fps(flat:bool = false) -> float:
return 1/delta return 1/delta
func get_rendertime() -> float: func get_rendertime() -> float:
if core.protection_mode: return 0.0
return Performance.get_monitor(Performance.TIME_PROCESS) return Performance.get_monitor(Performance.TIME_PROCESS)
func get_delta() -> float: func get_delta() -> float:
if core.protection_mode: return 0.0
return delta 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()