Framework rework

Changes in this commit include:
- Add Core.cleanup() and CoreBaseModule._cleanup() functions
- All builtin modules now clean and free any nodes
- Reduce code duplication in src/core.gd by not writing the same code for all builtin modules but instead iterating through a array
This commit is contained in:
JeremyStar™ 2024-03-29 02:40:56 +01:00
parent 3bcc18df82
commit b8a43ce159
Signed by: JeremyStarTM
GPG key ID: E366BAEF67E4704D
7 changed files with 77 additions and 77 deletions

View file

@ -36,6 +36,9 @@ var initialized: bool = false
## CORE's replacement for [method Object._init] and [method Node._ready]
## It's [b]strongly[/b] recommended to initialize your module here.
func _initialize() -> void: initialized = true
## Called when CORE is about to cleanup
## Use this function to free any children
func _cleanup() -> void: pass
## Called by [method Core.apply_configuration].
## This should be used to update your module configuration.
func _pull_config() -> void: pass

View file

@ -31,6 +31,7 @@ const version_type: CoreTypes.VersionType = CoreTypes.VersionType.BETA
const version_typerelease: int = 4
# Modules
const modules: Array[String] = [ "logger", "misc", "sms", "logui", "edl", "storage" ]
## Use this to access CORE's logging implementation.
var logger: CoreBaseModule
## Use this to access various useful functions.
@ -72,58 +73,38 @@ func _ready() -> void:
custom_modules_node.name = "Custom Modules"
add_child(custom_modules_node)
# Cleanup
# Particularily useful during testing to cleanup stuff, or if you want to do some stupid stuff with CORE during runtime
func cleanup() -> void:
logger.infof("Core", "Cleaning up")
config.queue_free()
var modules_reverse: Array[String] = modules.duplicate()
modules_reverse.reverse()
for module in modules_reverse:
await get(module)._cleanup()
get(module).queue_free()
for module in custom_modules_node.get_children(): unregister_custom_module(module.name)
remove_child(custom_modules_node)
custom_modules_node.queue_free()
queue_free()
# Initialize modules
## Initializes all modules during the first initialization phase.[br]
## [br]
## [b]NEVER call this yourself! You will break everything and risk a crash![/b]
func initialize_modules() -> void:
# Create Nodes
logger = CoreBaseModule.new()
misc = CoreBaseModule.new()
sms = CoreBaseModule.new()
logui = CoreBaseModule.new()
edl = CoreBaseModule.new()
storage = CoreBaseModule.new()
# Set names
logger.name = "Logger"
misc.name = "Misc"
sms.name = "SceneManagementSystem"
logui.name = "LogUI"
edl.name = "EasyDownLoader"
storage.name = "Storage"
# Set scripts
logger.set_script(ResourceLoader.load(basepath + "src/logger.gd"))
misc.set_script(ResourceLoader.load(basepath + "src/misc.gd"))
sms.set_script(ResourceLoader.load(basepath + "src/sms.gd"))
logui.set_script(ResourceLoader.load(basepath + "src/logui.gd"))
edl.set_script(ResourceLoader.load(basepath + "src/edl.gd"))
storage.set_script(ResourceLoader.load(basepath + "src/storage.gd"))
# Set reference to self
logger.core = self
misc.core = self
sms.core = self
logui.core = self
edl.core = self
storage.core = self
# Call _initialize() (workaround as modules cannot access "core" during _init())
logger._initialize()
misc._initialize()
sms._initialize()
logui._initialize()
edl._initialize()
storage._initialize()
for module in modules:
set(module, CoreBaseModule.new())
get(module).name = module
get(module).set_script(load(basepath + "src/" + module + ".gd"))
get(module).core = self
get(module)._initialize()
# Inject modules into the SceneTree
## Injects CORE's builtin modules into the SceneTree.[br]
## [br]
## [b]NEVER call this yourself! You will break everything and risk a crash![/b]
func inject_modules() -> void:
add_child(logger)
add_child(misc)
add_child(sms)
add_child(logui)
add_child(edl)
add_child(storage)
func inject_modules() -> void: for module in modules: add_child(get(module))
# Wait for all modules to be fully initialized
## Wait for all builtin modules to be fully initialized.[br]
@ -140,12 +121,7 @@ func complete_init(no_success: bool = false) -> void:
modsinit_custom = []
# Check builtin modules
if !logger.initialized: modsinit_builtin.append("logger")
if !misc.initialized: modsinit_builtin.append("misc")
if !sms.initialized: modsinit_builtin.append("sms")
if !logui.initialized: modsinit_builtin.append("logui")
if !edl.initialized: modsinit_builtin.append("edl")
if !storage.initialized: modsinit_builtin.append("storage")
for module in modules: if !get(module).initialized: modsinit_builtin.append(module)
# Check custom modules
for module_name in custom_modules:
@ -154,10 +130,8 @@ func complete_init(no_success: bool = false) -> void:
# Print and sleep
if modsinit_builtin.size() != 0 or modsinit_custom.size() != 0:
print("Waiting for modules to finish initialization:")
if modsinit_builtin.size() != 0:
print(" Builtin: " + str(modsinit_builtin))
if modsinit_custom.size() != 0:
print(" Custom: " + str(modsinit_custom))
if modsinit_builtin.size() != 0: print(" Builtin: " + str(modsinit_builtin))
if modsinit_custom.size() != 0: print(" Custom: " + str(modsinit_custom))
await get_tree().create_timer(1).timeout
# Initialization complete
@ -194,8 +168,11 @@ func unregister_custom_module(module_name: String) -> void:
if !custom_modules.has(module_name):
logger.errorf("Core", "Unregistering module failed: A custom module with the name \"" + module_name + "\" does not exist.")
return
custom_modules_node.remove_child(get_custom_module(module_name))
var module: Node = get_custom_module(module_name)
await module._cleanup()
custom_modules_node.remove_child(module)
custom_modules.erase(module_name)
module.queue_free()
# Returns a custom module
## Returns a loaded custom module for access.
@ -211,6 +188,7 @@ func get_custom_module(module_name: String) -> CoreBaseModule:
func reload_configuration(new_config: CoreConfiguration = CoreConfiguration.new()) -> void:
var initialized = config != null
if initialized: logger.verbf("Core", "Reloading CORE's configuration")
if config != null: config.queue_free()
config = new_config
if is_devmode(): # Override configuration in development mode
config.logger_level = CoreTypes.LoggerLevel.VERB
@ -229,10 +207,7 @@ func apply_configuration() -> void:
if !config.custom_modules:
logger.verbf("Core", "Removing all custom modules (custom modules support is disabled)")
for module in custom_modules: unregister_custom_module(module)
logger._pull_config()
misc._pull_config()
sms._pull_config()
logui._pull_config()
for module in modules: get(module)._pull_config()
if config.custom_modules:
for module in custom_modules:
logger.diagf("Core", "Updating configuration for custom module \"" + module.name + "\"")

View file

@ -24,6 +24,15 @@ var list_queue: Dictionary = {}
var list_active: Dictionary = {}
var list_complete: Dictionary = {}
func _cleanup() -> void:
clean_queue()
clean_completed()
for child in get_children():
if child.is_class("HTTPRequest"):
child.cancel_request()
await get_tree().process_frame
remove_child(child)
func generate_id() -> int:
var id = randi()
if list_queue.has(id) or list_active.has(id): return generate_id()
@ -75,11 +84,13 @@ func start_download(id: int, parse_utf8: bool) -> void:
download.name = "Request #" + str(id)
download.accept_gzip = true
download.use_threads = true
var lambda: Callable = func(result: int, http_code: int, headers: PackedStringArray, body: PackedByteArray) -> void:
var lambda: Callable = func(result: int, http_code: int, headers: PackedStringArray, body: PackedByteArray, httprequest: HTTPRequest) -> void:
logger.verbf("Edl", "Request " + str(id) + " completed\nResult: " + str(result) + "\nHTTP response code: " + str(http_code) + "\nHeaders: " + str(headers.size()) + "\nBody size: " + str(body.size()) + " Bytes // " + str(core.misc.byte2mib(body.size(), true)) + " MiB")
list_complete.merge({ id: { "result": result, "http_code": http_code, "headers": headers, "body": body, "body_utf8": body.get_string_from_utf8() if !parse_utf8 else "" } })
list_active.erase(id)
download.connect("request_completed", lambda)
remove_child(httprequest)
httprequest.queue_free()
download.connect("request_completed", lambda.bind(download))
add_child(download)
download.request(list_active[id]["url"], list_active[id]["headers"], list_active[id]["method"], list_active[id]["body"])

View file

@ -30,8 +30,8 @@ var font_bold: Font
func _initialize() -> void:
# Load fonts into memory
font_normal = ResourceLoader.load(core.basepath + "dist/FiraCode/Regular.ttf")
font_bold = ResourceLoader.load(core.basepath + "dist/FiraCode/Bold.ttf")
font_normal = load(core.basepath + "dist/FiraCode/Regular.ttf")
font_bold = load(core.basepath + "dist/FiraCode/Bold.ttf")
# Create LogUI
background = ColorRect.new()
background.name = "LOGUI"
@ -59,17 +59,12 @@ func _initialize() -> void:
# Mark as initialized
initialized = true
func _pull_config() -> void:
background.visible = !core.config.headless and core.config.logui_enabled
background.color = core.config.logui_background_color
logrtl.add_theme_font_size_override("normal_font_size", core.config.logui_font_size)
logrtl.add_theme_font_size_override("bold_font_size", core.config.logui_font_size)
func _ready() -> void:
# Add to SceneTree
core.sms.add_child(background)
core.sms.move_child(background, 0)
background.add_child(logrtl)
# Hide VScrollBar
var vsbar: VScrollBar = logrtl.get_child(0, true)
vsbar.set_deferred("size", Vector2i(1, 1))
@ -79,9 +74,22 @@ func _ready() -> void:
vsbar.add_theme_stylebox_override("grabber", StyleBoxEmpty.new())
vsbar.add_theme_stylebox_override("grabber_highlight", StyleBoxEmpty.new())
vsbar.add_theme_stylebox_override("grabber_pressed", StyleBoxEmpty.new())
# Connect log_event
logger.connect("log_event", func(allowed: bool, _level: CoreTypes.LoggerLevel, _origin: String, _message: String, format: String) -> void: if allowed: logrtl.text = logrtl.text + format + "\n")
func _cleanup() -> void:
background.remove_child(logrtl)
core.sms.remove_child(background)
logrtl.queue_free()
background.queue_free()
func _pull_config() -> void:
background.visible = !core.config.headless and core.config.logui_enabled
background.color = core.config.logui_background_color
logrtl.add_theme_font_size_override("normal_font_size", core.config.logui_font_size)
logrtl.add_theme_font_size_override("bold_font_size", core.config.logui_font_size)
func _process(_delta: float) -> void:
if !core.config.headless:
var window_size: Vector2i = DisplayServer.window_get_size()

View file

@ -25,6 +25,7 @@ func quit_safely(exitcode: int = 0) -> void:
logger.infof("Misc", "Shutting down (code " + str(exitcode) + ")")
logger.diagf("Misc", "Waiting for log messages to be flushed")
await get_tree().create_timer(0.25).timeout
await core.cleanup()
get_tree().quit(exitcode)
@warning_ignore("integer_division")

View file

@ -21,6 +21,7 @@
extends CoreBaseModule
# Objects
const scene_nodes: Array[String] = ["debug", "cutscene", "menu", "main", "background"]
var scenes_debug: Node = Node.new()
var scenes_cutscene: Node = Node.new()
var scenes_menu: Node = Node.new()
@ -31,20 +32,18 @@ var scenes_background: Node = Node.new()
var scenes: Dictionary = {}
func _initialize() -> void:
scenes_debug.name = "DEBUG"
scenes_cutscene.name = "CUTSCENE"
scenes_menu.name = "MENU"
scenes_main.name = "MAIN"
scenes_background.name = "BACKGROUND"
add_child(scenes_background)
add_child(scenes_main)
add_child(scenes_menu)
add_child(scenes_cutscene)
add_child(scenes_debug)
for scene in scene_nodes:
get("scenes_" + scene).name = scene.to_upper()
add_child(get("scenes_" + scene))
# Mark as initialized
initialized = true
func _cleanup() -> void:
for scene in scene_nodes:
remove_child(get("scenes_" + scene))
get("scenes_" + scene).queue_free()
func _pull_config() -> void:
if core.config.headless:
# Remove all scenes

View file

@ -6,6 +6,9 @@ func _initialize() -> void:
logger.info("tests/custom_module.gd", "Module has been initialized")
suite.callback = "_initialize"
func _cleanup() -> void:
logger.info("tests/custom_module.gd", "Cleaning module")
func _pull_config() -> void:
logger.info("tests/custom_module.gd", "The configuration has been updated")
suite.callback = "_pull_config"