diff --git a/docs/docs/reference/corebasemodule.md b/docs/docs/reference/basemodule.md similarity index 100% rename from docs/docs/reference/corebasemodule.md rename to docs/docs/reference/basemodule.md diff --git a/docs/docs/reference/coreconfiguration.md b/docs/docs/reference/config.md similarity index 74% rename from docs/docs/reference/coreconfiguration.md rename to docs/docs/reference/config.md index 349f08e..f18d615 100644 --- a/docs/docs/reference/coreconfiguration.md +++ b/docs/docs/reference/config.md @@ -17,12 +17,21 @@ Puts the framework into development mode. \ Unlocks experimental features. ### *bool* custom_modules = *false* Allows or disallows custom modules. +### *bool* automatic_shutdown = *true* +If `quit_safely` (and by extension `Core.cleanup`) should be called when pressing the X. ## Logger ### *CoreTypes.LoggerLevel* logger_level = *CoreTypes.LoggerLevel.INFO* The minimum log level you want to be displayed. ### *bool* logger_colored = *true* Determines if the logger's output should be colored. +### *bool* logger_detect_verbose_mode = *false* +:::warning +Updating this during runtime does nothing. +::: +Determines if the logger should check if running in verbose mode (see [`OS.is_stdout_verbose`](https://docs.godotengine.org/en/4.2/classes/class_os.html#class-os-method-is-stdout-verbose)). \ +Comes with a huge performance penalty on startup, delaying startup by about one to two seconds. \ +Update `verbose_mode` accordingly yourself if you've disabled this, or diagnostic log messages might appear messed up. \ ### *String* logger_format = *"%color%[%time%] [%level% %source%:%line%] %message%"* The template for all log messages Available placeholders are: `%time%`, `%time_ms%`, `%level%`, `%color%`, `%message%`, `%source%`, `%source_raw%`, `%function%` and `%line%` diff --git a/docs/docs/reference/core.md b/docs/docs/reference/core.md index 77ffb4e..26ed5db 100644 --- a/docs/docs/reference/core.md +++ b/docs/docs/reference/core.md @@ -18,7 +18,7 @@ CORE's version type number. Resets on every new version and version type. ## Modules Use these to access CORE's modules. -- [`config`](/reference/coreconfiguration) (**NEVER access this yourself. To change the configuration, use `reload_configuration()` instead**) +- [`config`](/reference/config) (**NEVER access this yourself. To change the configuration, use `reload_configuration()` instead**) - `scheduler` (runs clean up tasks periodically) - [`logger`](/reference/logger) - [`misc`](/reference/misc) diff --git a/docs/docs/reference/logger.md b/docs/docs/reference/logger.md index 7b4849a..d296683 100644 --- a/docs/docs/reference/logger.md +++ b/docs/docs/reference/logger.md @@ -9,10 +9,9 @@ Allows for colored output, better newlines, multiple logger levels and a large v ## Signals ### log_event -- Signature: *bool* allowed, *CoreTypes.LoggerLevel* level, *Dictionary* origin, *String* message, *String* format \ +- Signature: *bool* allowed, *CoreTypes.LoggerLevel* level, *String* origin, *String* message, *String* format \ Emitted on any log call, permitted or not. \ -**origin** contains the keys `source`, `source_clean`, `function` and `line`. \ **format** is set to `""` when **allowed** is set `false`. ## Variables @@ -22,6 +21,9 @@ Do not call this. ::: Keeps track of all logger instances. Unused instances will be cleaned periodically. +### *bool* verbose_mode = *false* +Used to determine if running in verbose/command line mode. +Makes diagnostic log messages display correctly (workaround for Godot's ANSI true color limitation). ## Functions ### *void* _log(*CoreTypes.LoggerLevel* level, *String* origin, *String* message) diff --git a/src/classes/config.gd b/src/classes/config.gd index 132b8ae..259550c 100644 --- a/src/classes/config.gd +++ b/src/classes/config.gd @@ -17,11 +17,19 @@ class_name CoreConfiguration @export var development: bool ## Allows or disallows custom modules. @export var custom_modules: bool +## If [method Core.quit_safely] (and by extension [method Core.cleanup]) should be called when pressing the X. +@export var automatic_shutdown: bool + @export_category("Logger") ## The minimum log level you want to be displayed. @export var logger_level: CoreTypes.LoggerLevel ## Determines if the logger's output should be colored. @export var logger_colored: bool +## Determines if the logger should check if running in verbose mode (see [method OS.is_stdout_verbose]).[br] +## Comes with a huge performance penalty on startup, delaying startup by about one to two seconds.[br] +## Update [code]verbose_mode[/code] accordingly yourself if you've disabled this, or diagnostic log messages might appear messed up.[br] +## [b]Warning: [i]Updating this during runtime does nothing.[/i][/b] +@export var logger_detect_verbose_mode: bool ## The template for all log messages.[br] ## Available placeholders are: [code]%time%[/code], [code]%time_ms%[/code], [code]%level%[/code], [code]%color%[/code], [code]%message%[/code], [code]%source%[/code], [code]%source_raw%[/code], [code]%function%[/code] and [code]%line%[/code] @export var logger_format: String @@ -29,6 +37,7 @@ class_name CoreConfiguration @export var logger_newlines_override: bool ## The maximum amount of characters excluding the message before newlines are no longer idented. Set to [code]-1[/code] to disable this behaviour. @export var logger_newlines_sizelimit: int + @export_category("Log UI") ## Enables or disables Log UI. @export var logui_enabled: bool @@ -46,10 +55,12 @@ func _init() -> void: headless = false development = false custom_modules = false + automatic_shutdown = true # Logger logger_level = CoreTypes.LoggerLevel.INFO logger_colored = true + logger_detect_verbose_mode = false logger_format = "%color%[%time%] [%level% %origin%] %message%" logger_newlines_override = true logger_newlines_sizelimit = 40 diff --git a/src/core.gd b/src/core.gd index 36df126..efcda1b 100644 --- a/src/core.gd +++ b/src/core.gd @@ -26,9 +26,9 @@ class_name Core ## The version number const version_version: int = 1 ## The version type. See [enum CoreTypes.VersionType] for more information. -const version_type: CoreTypes.VersionType = CoreTypes.VersionType.RELEASECANDIDATE +const version_type: CoreTypes.VersionType = CoreTypes.VersionType.RELEASE ## The version type number. Resets on every new version and version type. -const version_typerelease: int = 1 +const version_typerelease: int = 0 # Modules ## Used internally for loading, managing and unloading modules. @@ -87,6 +87,7 @@ func _ready() -> void: add_child(custom_modules_node) loggeri = logger.get_instance(basepath.replace("res://", "") + "src/core.gd", self) add_child(scheduler) + get_tree().auto_accept_quit = false ## Initializes all built-in modules during the preinitialization phase.[br] ## [b]Danger: [i]Don't call this.[/i][/b] @@ -142,7 +143,7 @@ func complete_init(no_success_message: 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_builtin.size() != 0: print(" Built-in: " + str(modsinit_builtin)) if modsinit_custom.size() != 0: print(" Custom: " + str(modsinit_custom)) await get_tree().create_timer(1).timeout @@ -156,7 +157,7 @@ func reload_configuration(new_config: CoreConfiguration = CoreConfiguration.new( var initialized = config != null if initialized: loggeri.verb("Reloading CORE's configuration") if config != null: config.queue_free() - config = new_config + config = new_config.duplicate() if is_devmode(): # Override configuration in development mode config.logger_level = CoreTypes.LoggerLevel.DIAG if initialized: loggeri.verb("Overrode configuration (development mode)") @@ -166,16 +167,14 @@ func reload_configuration(new_config: CoreConfiguration = CoreConfiguration.new( ## [b]Danger: [i]Don't call this.[/i][/b] func apply_configuration() -> void: if loggeri != null: loggeri.verb("Applying configuration") - if is_devmode(): if loggeri != null: loggeri.warn("The CORE Framework is in development mode. Here be dragons!") - if config.headless: loggeri.warn("CORE is in headless mode. Certain modules will not work as expected.") + if is_devmode() and loggeri != null: loggeri.warn("The CORE Framework is in development mode. Here be dragons!") if !config.custom_modules: - if loggeri != null: loggeri.verb("Removing all custom modules (custom modules support is disabled)") + if loggeri != null: loggeri.diag("Removing all custom modules (custom modules support is disabled)") for module in custom_modules: unregister_custom_module(module) for module in modules: get(module)._pull_config() if config.custom_modules: for module in custom_modules: - if loggeri != null: loggeri.diag("Updating configuration for custom module \"" + module.name + "\"") - module._pull_config() + custom_modules[module]._pull_config() # +++ custom module support +++ ## Registers a new custom module. @@ -229,16 +228,16 @@ func get_custom_module(module_name: String) -> CoreBaseModule: ## Unloads all custom modules, built-in modules, frees any of CORE's classes and lastly itself. func cleanup() -> void: loggeri.info("Cleaning up") - config.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() 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() - for module in custom_modules_node.get_children(): unregister_custom_module(module.name) - remove_child(custom_modules_node) - custom_modules_node.queue_free() + config.queue_free() queue_free() ## Returns if the framework is in development mode. @@ -340,3 +339,9 @@ func quit_safely(exitcode: int = 0) -> void: await get_tree().create_timer(0.25).timeout await cleanup() get_tree().quit(exitcode) + +func _notification(what) -> void: + match(what): + NOTIFICATION_WM_CLOSE_REQUEST: + if config.automatic_shutdown: + await quit_safely(0) diff --git a/src/logger.gd b/src/logger.gd index f8035e0..11f9244 100644 --- a/src/logger.gd +++ b/src/logger.gd @@ -28,6 +28,10 @@ signal log_event ## [b]Danger: [i]Don't modify this.[/i][/b] var instances: Array[CoreLoggerInstance] = [] +## Used to determine if running in verbose/command line mode.[br] +## Makes diagnostic log messages display correctly (workaround for Godot's ANSI true color limitation). +var verbose_mode: bool = false + ## The minimum log level you want to be displayed. var config_level: CoreTypes.LoggerLevel ## Determines if the logger's output should be colored. @@ -41,6 +45,14 @@ var config_newlines_override: bool var config_newlines_sizelimit: int # +++ module +++ +func _initialize() -> void: + if core.config.logger_detect_verbose_mode and OS.is_stdout_verbose(): + verbose_mode = true + +func _cleanup() -> void: + _schedule() + await get_tree().process_frame + func _schedule() -> void: for instance in instances: if !is_instance_valid(instance): continue @@ -73,7 +85,8 @@ func _log(level: CoreTypes.LoggerLevel, origin: String, message: String) -> void CoreTypes.LoggerLevel.DIAG: format = format.replace("%level%", "DIAG") format_newline = format_newline.replace("%level%", "DIAG") - format = format.replace("%color%", "[color=dark_gray]") + if verbose_mode: format = format.replace("%color%", "[color=gray]") + else: format = format.replace("%color%", "[color=dark_gray]") CoreTypes.LoggerLevel.VERB: format = format.replace("%level%", "VERB") format_newline = format_newline.replace("%level%", "VERB")