############################################################################## ### CORE FRAMEWORK SOURCE FILE ### ### Copyright (c) 2024 The StarOpenSource Project & Contributors ### ### Licensed under the GNU General Public License v3 ### ### ### ### This program is free software: you can redistribute it and/or modify ### ### it under the terms of the GNU General Public License as published by ### ### the Free Software Foundation, either version 3 of the License, or ### ### (at your option) any later version. ### ### ### ### This program is distributed in the hope that it will be useful, ### ### but WITHOUT ANY WARRANTY; without even the implied warranty of ### ### MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ### ### GNU General Public License for more details. ### ### ### ### You should have received a copy of the GNU General Public License ### ### along with this program. If not, see . ### ############################################################################## ### src/core.gd (CORE Object) ### ### ### ### This source file is responsible for initializing CORE's modules, ### ### handling communication between them and much more. ### ############################################################################## extends Node class_name Core # Constants const version_release: int = 1 const version_type: CoreTypes.VersionType = CoreTypes.VersionType.ALPHA const version_typerelease: int = 0 # Modules var logger: CoreBaseModule var misc: CoreBaseModule var sms: CoreBaseModule var logui: CoreBaseModule # Variables var basepath: String var config: CoreConfiguration var custom_modules: Dictionary = {} var custom_modules_node: Node # Preinitialization func _init(new_config: CoreConfiguration = CoreConfiguration.new()) -> void: name = "CORE" if !determine_basepath(): queue_free() custom_modules_node = Node.new() reload_configuration(new_config) initialize_modules() apply_configuration() # Initialization func _ready() -> void: inject_modules() add_child(custom_modules_node) logger.info("Initialized CORE successfully") if is_devmode(): logger.warn("The CORE Framework is in development mode. Here be dragons!") if config.headless: logger.warn("CORE is in headless mode. Certain modules will not work as expected.") # Initialize modules func initialize_modules() -> void: # Create Nodes logger = CoreBaseModule.new() misc = CoreBaseModule.new() sms = CoreBaseModule.new() logui = CoreBaseModule.new() # Set names logger.name = "Logger" misc.name = "Misc" sms.name = "SceneManagementSystem" logui.name = "LogUI" # 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")) # Set reference to self logger.core = self misc.core = self sms.core = self logui.core = self # Call _initialize() (workaround as modules cannot access "core" during _init()) logger._initialize() misc._initialize() sms._initialize() logui._initialize() # Inject modules into the SceneTree func inject_modules() -> void: add_child(logger) add_child(misc) add_child(sms) add_child(logui) # Registers a custom module func register_custom_module(module_name: String, module_class: CoreBaseModule) -> void: logger.verb("Registering new custom module \"" + module_name + "\"") if custom_modules.has(module_name): logger.error("Registering module failed: A custom module with the name \"" + module_name + "\" already exists.") return module_class.name = module_name logger.diag("Adding module to SceneTree") custom_modules_node.add_child(module_class) logger.diag("Merging module with custom_modules") custom_modules.merge({ module_name: module_class }) logger.diag("Initializing custom module") module_class._initialize() logger.diag("Updating custom module configuration") module_class._pull_config() # Unregisters a custom module func unregister_custom_module(module_name: String) -> void: logger.verb("Unregistering custom module \"" + module_name + "\"") if !custom_modules.has(module_name): logger.error("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)) custom_modules.erase(module_name) # Returns a custom module func get_custom_module(module_name: String) -> CoreBaseModule: logger.diag("Getting custom module \"" + module_name + "\"") if !custom_modules.has(module_name): logger.error("Getting module failed: A custom module with the name \"" + module_name + "\" does not exist.") return return custom_modules[module_name] # (Re-)Load configuration func reload_configuration(new_config: CoreConfiguration = CoreConfiguration.new()) -> void: var initialized = config != null if initialized: logger.verb("Reloading CORE's configuration") config = new_config if is_devmode(): # Override configuration in development mode config.logger_level = CoreTypes.LoggerLevel.VERB if initialized: logger.verb("Overrode configuration (development mode)") if initialized: apply_configuration() # Call _pull_config() functions func apply_configuration() -> void: logger.verb("Applying configuration") logger._pull_config() misc._pull_config() sms._pull_config() logui._pull_config() for module in custom_modules: logger.diag("Updating configuration for custom module \"" + module.name + "\"") module._pull_config() # Determines CORE's installation/base path func determine_basepath() -> bool: if FileAccess.file_exists("res://.corebasepath"): basepath = "res://" elif FileAccess.file_exists("res://CORE/.corebasepath"): basepath = "res://CORE/" elif FileAccess.file_exists("res://addons/CORE/.corebasepath"): basepath = "res://addons/CORE/" else: assert(false, "CORE is not located at 'res://CORE/', aborting initialization") return false return true # Return development mode status func is_devmode() -> bool: return config.debug_allow and basepath == "res://" and OS.is_debug_build() # Replaces variables with human-friendly strings func get_formatted_string(string: String) -> String: # Version strings string = string.replace("%release%", str(version_release)) string = string.replace("%release_type%", str(version_typerelease)) var semantic_version: Array[int] = get_version_semantic() string = string.replace("%release_semantic%", str(semantic_version[0]) + "." + str(semantic_version[1]) + "." + str(semantic_version[2])) match(version_type): CoreTypes.VersionType.RELEASE: string = string.replace("%type%", "Release") string = string.replace("%type_technical%", "r") CoreTypes.VersionType.RELEASECANDIDATE: string = string.replace("%type%", "Release Candidate") string = string.replace("%type_technical%", "rc") CoreTypes.VersionType.BETA: string = string.replace("%type%", "Beta") string = string.replace("%type_technical%", "b") CoreTypes.VersionType.ALPHA: string = string.replace("%type%", "Alpha") string = string.replace("%type_technical%", "a") _: await logger.crash("Invalid version type " + str(version_type), true) # Development mode if is_devmode(): string = string.replace("%devmode%", "Enabled") else: string = string.replace("%devmode%", "Disabled") # Headless mode if config.headless: string = string.replace("%headless%", "Enabled") else: string = string.replace("%headless%", "Disabled") return string # Return CORE's version in the semantic versioning scheme func get_version_semantic() -> Array[int]: var version_type_int: int match(version_type): CoreTypes.VersionType.RELEASE: version_type_int = 3 CoreTypes.VersionType.RELEASECANDIDATE: version_type_int = 2 CoreTypes.VersionType.BETA: version_type_int = 1 CoreTypes.VersionType.ALPHA: version_type_int = 0 return [version_release, version_type_int, version_typerelease]