From 15d78eb0d76fc99a9322a2ccfea8c2fe6d3c8100 Mon Sep 17 00:00:00 2001 From: JeremyStarTM Date: Sun, 14 Apr 2024 23:48:53 +0200 Subject: [PATCH] Add cleanup hooking system (closes #24) --- docs/docs/reference/core.md | 12 +++++++ src/core.gd | 65 +++++++++++++++++++++++++++++++++++-- 2 files changed, 74 insertions(+), 3 deletions(-) diff --git a/docs/docs/reference/core.md b/docs/docs/reference/core.md index 8240494..01c0f56 100644 --- a/docs/docs/reference/core.md +++ b/docs/docs/reference/core.md @@ -36,6 +36,11 @@ Used internally for loading, managing and unloading modules. Do not modify this. ::: Stores the path to CORE's installation directory. +### *Dictionary* cleanup_hooks = *{}* +:::danger[Don't modify] +Do not modify this. +::: +Contains a list of all registered cleanup hooks. ### *Dictionary* custom_modules = *{}* ### *void* _ready() :::danger[Don't modify] @@ -96,6 +101,13 @@ Applies the a configuration. ### *void* cleanup() Makes sure that CORE does not leak memory on shutdown/unload. \ Unloads all custom modules, built-in modules, frees any of CORE's classes and lastly itself. +### *int* register_cleanup_hook(*Callable* callable) +Registers a new cleanup hook. \ +Returns the hook id. +### *bool* unregister_cleanup_hook_by_id(*int* id) +Unregisters a cleanup hook by it's id. +### *bool* unregister_cleanup_hook_by_ref(*Callable* callable) +Unregisters a cleanup hook by it's reference. ### *bool* is_devmode() Returns if the framework is in development mode. ### *String* get_format_string(*String* string) diff --git a/src/core.gd b/src/core.gd index 227cdd4..8728b9f 100644 --- a/src/core.gd +++ b/src/core.gd @@ -57,6 +57,9 @@ var storage: CoreBaseModule ## Stores the path to CORE's installation directory.[br] ## [b]Danger: [i]Don't modify this.[/i][/b] var basepath: String +## Contains a list of all registered cleanup hooks.[br] +## [b]Danger: [i]Don't modify this.[/i][/b] +var cleanup_hooks: Dictionary = {} ## Contains a list of all loaded custom modules.[br] ## [b]Danger: [i]Don't modify this.[/i][/b] var custom_modules: Dictionary = {} @@ -223,24 +226,80 @@ func get_custom_module(module_name: String) -> CoreBaseModule: return null return custom_modules[module_name] -# +++ etc ++ +# +++ cleanup ++ ## Makes sure that CORE does not leak memory on shutdown/unload.[br] -## Unloads all custom modules, built-in modules, frees any of CORE's classes and lastly itself. +## Unloads all custom modules, built-in modules, frees any of CORE's classes and lastly itself.[br] +## Only call this function if you're sure that your application or game no longer uses the CORE Framework. func cleanup() -> void: loggeri.info("Cleaning up") + loggeri.verb("Calling cleanup hooks") + for hook in cleanup_hooks: + if !cleanup_hooks[hook].is_valid(): + loggeri.error("Cleanup hook #" + str(hook) + " is invalid") + else: + loggeri.diag("Calling cleanup hook #" + str(hook)) + await cleanup_hooks[hook].call() + loggeri.verb("Unregistering custom modules") for module in custom_modules_node.get_children(): await unregister_custom_module(module.name) + loggeri.verb("Removing custom module support") remove_child(custom_modules_node) custom_modules_node.queue_free() + loggeri.verb("Unloading built-in modules") var modules_reverse: Array[String] = modules.duplicate() modules_reverse.reverse() for module in modules_reverse: await get(module)._cleanup() - get(module).loggeri.queue_free() get(module).queue_free() + print("Freeing configuration") config.queue_free() await get_tree().process_frame + print("Freeing") queue_free() +# Generates a new cleanup hook id +func _generate_hook_id() -> int: + var id = randi() + if cleanup_hooks.has(id): + loggeri.warn("New cleanup hook id #" + str(id) + " is already taken") + return _generate_hook_id() + elif id == -1: + loggeri.warn("Invalid cleanup hook id '-1'") + return _generate_hook_id() + return id + +## Registers a new cleanup hook.[br] +## Returns the hook id. +func register_cleanup_hook(callable: Callable) -> int: + if !callable.is_valid(): + loggeri.error("Could not add cleanup hook: Callable is not valid") + return -1 + + var id: int = _generate_hook_id() + loggeri.verb("Adding new cleanup hook #" + str(id)) + cleanup_hooks.merge({ id: callable }) + return id + +## Unregisters a cleanup hook by it's id. +func unregister_cleanup_hook_by_id(id: int) -> bool: + if !cleanup_hooks.has(id): + loggeri.error("Could not remove cleanup hook (id): Hook #" + str(id) + " does not exist") + return false + loggeri.verb("Removed cleanup hook #" + str(id) + " (id)") + cleanup_hooks.erase(id) + return true + +## Unregisters a cleanup hook by it's reference. +func unregister_cleanup_hook_by_ref(callable: Callable) -> bool: + var id: Variant = cleanup_hooks.find_key(callable) + if id == null: + loggeri.error("Could not remove cleanup hook (ref): Could not find a matching hook") + return false + if typeof(id) != TYPE_INT: await loggeri.crash("Could not remove cleanup hook (ref): find_key did not return an integer (returned '" + str(id) + "')") + loggeri.verb("Removed cleanup hook #" + str(id) + " (ref)") + cleanup_hooks.erase(id) + return true + +# +++ etc +++ ## Returns if the framework is in development mode. func is_devmode() -> bool: return config.development