2024-02-12 19:47:28 +01:00
# CORE FRAMEWORK SOURCE FILE
# Copyright (c) 2024 The StarOpenSource Project & Contributors
2024-03-03 18:53:09 +01:00
# Licensed under the GNU Affero General Public License v3
2024-02-12 19:47:28 +01:00
#
# This program is free software: you can redistribute it and/or modify
2024-03-03 18:53:09 +01:00
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
2024-02-12 19:47:28 +01:00
#
# 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
2024-03-03 18:53:09 +01:00
# GNU Affero General Public License for more details.
2024-02-12 19:47:28 +01:00
#
2024-03-03 18:53:09 +01:00
# You should have received a copy of the GNU Affero General Public License
2024-02-12 19:47:28 +01:00
# along with this program. If not, see <https://www.gnu.org/licenses/>.
## Initializes and manages the framework.
##
## The [b]CORE Object[/b] is responsible for initializing, managing and
## serving the CORE Framework to the developer.
2024-02-04 21:36:30 +01:00
extends Node
class_name Core
# Constants
2024-03-28 12:54:12 +01:00
## The version number
const version_version : int = 1
## The version type
2024-02-10 21:56:04 +01:00
const version_type : CoreTypes . VersionType = CoreTypes . VersionType . BETA
2024-03-28 12:54:12 +01:00
## The version type number. Resets on every new version and version type.
2024-03-27 15:15:46 +01:00
const version_typerelease : int = 4
2024-02-04 21:36:30 +01:00
# Modules
2024-03-29 02:40:56 +01:00
const modules : Array [ String ] = [ " logger " , " misc " , " sms " , " logui " , " edl " , " storage " ]
2024-02-12 19:47:28 +01:00
## Use this to access CORE's logging implementation.
2024-02-04 21:36:30 +01:00
var logger : CoreBaseModule
2024-02-12 19:47:28 +01:00
## Use this to access various useful functions.
2024-02-04 21:36:30 +01:00
var misc : CoreBaseModule
2024-02-12 19:47:28 +01:00
## Use this to access the scene management system.
2024-02-09 21:18:14 +01:00
var sms : CoreBaseModule
2024-02-12 19:47:28 +01:00
## Use this to access the graphical log. Serves no importance to you (probably).
2024-02-04 21:36:30 +01:00
var logui : CoreBaseModule
2024-02-12 19:47:28 +01:00
## Use this to access CORE's builtin HTTP request maker.
2024-02-09 22:27:21 +01:00
var edl : CoreBaseModule
2024-03-18 03:23:27 +01:00
## Use this to access configuration and settings files easily.
var storage : CoreBaseModule
2024-02-04 21:36:30 +01:00
# Variables
2024-02-12 19:47:28 +01:00
## Contains CORE's load path
2024-02-04 21:36:30 +01:00
var basepath : String
2024-02-12 19:47:28 +01:00
## Holds the configuration[br]
## [br]
## [b]NEVER access this yourself. To change the configuration file, use [method Core.reload_configuration] instead.[/b]
2024-02-05 18:03:16 +01:00
var config : CoreConfiguration
2024-02-12 19:47:28 +01:00
## Contains all loaded custom modules.
2024-02-05 18:03:16 +01:00
var custom_modules : Dictionary = { }
2024-02-12 19:47:28 +01:00
## Contains the custom modules node.
2024-02-09 15:30:20 +01:00
var custom_modules_node : Node
2024-02-04 21:36:30 +01:00
# Preinitialization
func _init ( new_config : CoreConfiguration = CoreConfiguration . new ( ) ) - > void :
name = " CORE "
2024-03-03 19:08:20 +01:00
if ! check_godot_version ( ) : return
2024-02-04 21:36:30 +01:00
if ! determine_basepath ( ) : queue_free ( )
2024-02-09 15:30:20 +01:00
custom_modules_node = Node . new ( )
2024-02-04 21:36:30 +01:00
reload_configuration ( new_config )
initialize_modules ( )
apply_configuration ( )
# Initialization
func _ready ( ) - > void :
inject_modules ( )
2024-02-09 22:59:43 +01:00
custom_modules_node . name = " Custom Modules "
2024-02-09 15:30:20 +01:00
add_child ( custom_modules_node )
2024-02-04 21:36:30 +01:00
2024-03-29 02:40:56 +01:00
# 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 ( )
2024-02-04 21:36:30 +01:00
# Initialize modules
2024-02-12 19:47:28 +01:00
## Initializes all modules during the first initialization phase.[br]
## [br]
## [b]NEVER call this yourself! You will break everything and risk a crash![/b]
2024-02-04 21:36:30 +01:00
func initialize_modules ( ) - > void :
2024-03-29 02:40:56 +01:00
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 ( )
2024-02-04 21:36:30 +01:00
# Inject modules into the SceneTree
2024-02-12 19:47:28 +01:00
## Injects CORE's builtin modules into the SceneTree.[br]
## [br]
## [b]NEVER call this yourself! You will break everything and risk a crash![/b]
2024-03-29 02:40:56 +01:00
func inject_modules ( ) - > void : for module in modules : add_child ( get ( module ) )
2024-02-04 21:36:30 +01:00
2024-03-22 21:12:11 +01:00
# Wait for all modules to be fully initialized
## Wait for all builtin modules to be fully initialized.[br]
## [br]
## This ensures that all of CORE's builtin modules are fully initialized and ready.
## [b]Not calling this function during startup may lead to runtime issues.[/b]
func complete_init ( no_success : bool = false ) - > void :
var modsinit_builtin : Array [ String ] = [ " workaround " ]
var modsinit_custom : Array [ String ] = [ " workaround " ]
while modsinit_builtin . size ( ) != 0 and modsinit_custom . size ( ) != 0 :
# Clear arrays
modsinit_builtin = [ ]
modsinit_custom = [ ]
# Check builtin modules
2024-03-29 02:40:56 +01:00
for module in modules : if ! get ( module ) . initialized : modsinit_builtin . append ( module )
2024-03-22 21:12:11 +01:00
# Check custom modules
for module_name in custom_modules :
if ! custom_modules [ module_name ] . initialized : modsinit_custom . append ( module_name )
# Print and sleep
if modsinit_builtin . size ( ) != 0 or modsinit_custom . size ( ) != 0 :
print ( " Waiting for modules to finish initialization: " )
2024-03-29 02:40:56 +01:00
if modsinit_builtin . size ( ) != 0 : print ( " Builtin: " + str ( modsinit_builtin ) )
if modsinit_custom . size ( ) != 0 : print ( " Custom: " + str ( modsinit_custom ) )
2024-03-22 21:12:11 +01:00
await get_tree ( ) . create_timer ( 1 ) . timeout
# Initialization complete
await get_tree ( ) . process_frame
if ! no_success : logger . infof ( " Core " , " Initialized CORE successfully " )
2024-02-05 18:03:16 +01:00
# Registers a custom module
2024-02-12 19:47:28 +01:00
## Registers a new custom module.
2024-02-10 18:49:25 +01:00
func register_custom_module ( module_name : String , module_class : CoreBaseModule ) - > bool :
2024-03-21 15:47:39 +01:00
logger . verbf ( " Core " , " Registering new custom module \" " + module_name + " \" " )
2024-02-10 18:49:25 +01:00
if ! config . custom_modules :
2024-03-21 15:47:39 +01:00
logger . errorf ( " Core " , " Registering module failed: Custom module support is disabled. " )
2024-02-10 18:49:25 +01:00
return false
2024-02-05 18:03:16 +01:00
if custom_modules . has ( module_name ) :
2024-03-21 15:47:39 +01:00
logger . errorf ( " Core " , " Registering module failed: A custom module with the name \" " + module_name + " \" already exists. " )
2024-02-10 18:49:25 +01:00
return false
2024-02-09 15:30:20 +01:00
module_class . name = module_name
2024-03-22 21:41:27 +01:00
logger . diagf ( " Core " , " Updating variables " )
module_class . core = self
2024-03-21 15:47:39 +01:00
logger . diagf ( " Core " , " Adding module to SceneTree " )
2024-02-09 15:30:20 +01:00
custom_modules_node . add_child ( module_class )
2024-03-21 15:47:39 +01:00
logger . diagf ( " Core " , " Merging module with custom_modules " )
2024-02-05 18:03:16 +01:00
custom_modules . merge ( { module_name : module_class } )
2024-03-21 15:47:39 +01:00
logger . diagf ( " Core " , " Initializing custom module " )
2024-02-05 18:03:16 +01:00
module_class . _initialize ( )
2024-03-21 15:47:39 +01:00
logger . diagf ( " Core " , " Updating custom module configuration " )
2024-02-05 18:03:16 +01:00
module_class . _pull_config ( )
2024-02-10 18:49:25 +01:00
return true
2024-02-05 18:03:16 +01:00
# Unregisters a custom module
2024-02-12 19:47:28 +01:00
## Unregisters a custom module, making it no longer function.
2024-02-05 18:03:16 +01:00
func unregister_custom_module ( module_name : String ) - > void :
2024-03-21 15:47:39 +01:00
logger . verbf ( " Core " , " Unregistering custom module \" " + module_name + " \" " )
2024-02-05 18:03:16 +01:00
if ! custom_modules . has ( module_name ) :
2024-03-21 15:47:39 +01:00
logger . errorf ( " Core " , " Unregistering module failed: A custom module with the name \" " + module_name + " \" does not exist. " )
2024-02-05 18:03:16 +01:00
return
2024-03-29 02:40:56 +01:00
var module : Node = get_custom_module ( module_name )
await module . _cleanup ( )
custom_modules_node . remove_child ( module )
2024-02-05 18:03:16 +01:00
custom_modules . erase ( module_name )
2024-03-29 02:40:56 +01:00
module . queue_free ( )
2024-02-05 18:03:16 +01:00
# Returns a custom module
2024-02-12 19:47:28 +01:00
## Returns a loaded custom module for access.
2024-02-05 18:03:16 +01:00
func get_custom_module ( module_name : String ) - > CoreBaseModule :
2024-03-21 15:47:39 +01:00
logger . diagf ( " Core " , " Getting custom module \" " + module_name + " \" " )
2024-02-05 18:03:16 +01:00
if ! custom_modules . has ( module_name ) :
2024-03-21 15:47:39 +01:00
logger . errorf ( " Core " , " Getting module failed: A custom module with the name \" " + module_name + " \" does not exist. " )
2024-03-22 21:41:27 +01:00
return null
2024-02-05 18:03:16 +01:00
return custom_modules [ module_name ]
2024-02-04 21:36:30 +01:00
# (Re-)Load configuration
2024-02-12 19:47:28 +01:00
## Loads a (new) configuration file and applies it to all modules.
2024-02-04 21:36:30 +01:00
func reload_configuration ( new_config : CoreConfiguration = CoreConfiguration . new ( ) ) - > void :
var initialized = config != null
2024-03-21 15:47:39 +01:00
if initialized : logger . verbf ( " Core " , " Reloading CORE ' s configuration " )
2024-03-29 02:40:56 +01:00
if config != null : config . queue_free ( )
2024-02-04 21:36:30 +01:00
config = new_config
if is_devmode ( ) : # Override configuration in development mode
config . logger_level = CoreTypes . LoggerLevel . VERB
2024-03-21 15:47:39 +01:00
if initialized : logger . verbf ( " Core " , " Overrode configuration (development mode) " )
2024-02-04 21:36:30 +01:00
if initialized : apply_configuration ( )
# Call _pull_config() functions
2024-02-12 19:47:28 +01:00
## Applies the newly applied configuration.[br]
## [br]
## [b]NEVER call this yourself unless you know what you are doing![/b]
2024-02-04 21:36:30 +01:00
func apply_configuration ( ) - > void :
2024-03-21 15:47:39 +01:00
logger . verbf ( " Core " , " Applying configuration " )
if is_devmode ( ) : logger . warnf ( " Core " , " The CORE Framework is in development mode. Here be dragons! " )
if config . headless : logger . warnf ( " Core " , " CORE is in headless mode. Certain modules will not work as expected. " )
2024-02-10 18:49:25 +01:00
edl . _pull_config ( )
if ! config . custom_modules :
2024-03-21 15:47:39 +01:00
logger . verbf ( " Core " , " Removing all custom modules (custom modules support is disabled) " )
2024-02-10 18:49:25 +01:00
for module in custom_modules : unregister_custom_module ( module )
2024-03-29 02:40:56 +01:00
for module in modules : get ( module ) . _pull_config ( )
2024-02-10 18:49:25 +01:00
if config . custom_modules :
for module in custom_modules :
2024-03-21 15:47:39 +01:00
logger . diagf ( " Core " , " Updating configuration for custom module \" " + module . name + " \" " )
2024-02-10 18:49:25 +01:00
module . _pull_config ( )
2024-02-04 21:36:30 +01:00
# Return development mode status
2024-02-12 19:47:28 +01:00
## Returns if the CORE Framework is in development mode.
2024-02-04 21:36:30 +01:00
func is_devmode ( ) - > bool :
2024-02-10 18:49:25 +01:00
return config . debugging and basepath == " res:// " and OS . is_debug_build ( )
2024-02-04 21:36:30 +01:00
# Replaces variables with human-friendly strings
2024-02-12 19:47:28 +01:00
## Replaces placeholders with human-friendly strings You can use the following placeholders:[br]
2024-03-03 19:08:40 +01:00
## - [code]%release%[/code]: Returns the release number.[br]
## - [code]%release_type%[/code]: Returns the typerelease number[br]
## - [code]%release_semantic%[/code]: Returns the result of [method Core.get_version_semantic], example [i]5.2.3[/i][br]
## - [code]%type%[/code]: Returns the release type as a word, for example [i]Release Candidate[/i][br]
## - [code]%type_technical%[/code]: Returns the release type as one or two lowercase letters, for example [i]rc[/i][br]
## - [code]%devmode%[/code]: Returns the development mode status[br]
## - [code]%headless%[/code]: Returns the headless mode status
2024-02-04 21:36:30 +01:00
func get_formatted_string ( string : String ) - > String :
# Version strings
2024-03-28 12:54:12 +01:00
string = string . replace ( " % version % " , str ( version_version ) )
string = string . replace ( " % version_type % " , str ( version_typerelease ) )
2024-02-04 21:36:30 +01:00
var semantic_version : Array [ int ] = get_version_semantic ( )
2024-03-28 12:54:12 +01:00
string = string . replace ( " % version_semantic % " , str ( semantic_version [ 0 ] ) + " . " + str ( semantic_version [ 1 ] ) + " . " + str ( semantic_version [ 2 ] ) )
2024-02-04 21:36:30 +01:00
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 " )
2024-03-21 15:47:39 +01:00
_ : await logger . crashf ( " Core " , " Invalid version type " + str ( version_type ) , true )
2024-02-04 21:36:30 +01:00
# Development mode
if is_devmode ( ) : string = string . replace ( " %d evmode % " , " Enabled " )
else : string = string . replace ( " %d evmode % " , " Disabled " )
# Headless mode
if config . headless : string = string . replace ( " %he adless % " , " Enabled " )
else : string = string . replace ( " %he adless % " , " Disabled " )
2024-02-10 18:49:25 +01:00
# Custom module support
if config . custom_modules : string = string . replace ( " %c ustommodules % " , " Enabled " )
else : string = string . replace ( " %c ustommodules % " , " Disabled " )
2024-02-04 21:36:30 +01:00
return string
# Return CORE's version in the semantic versioning scheme
2024-02-12 19:47:28 +01:00
## Returns CORE's versioning scheme into the semantic versioning scheme.[br]
## The first integer contains the release number, the second integer contains the release type ([code]0[/code] for alpha, [code]1[/code] for beta, [code]2[/code] for rc and [code]3[/code] for release and the last integer contains the typerelease number.
2024-02-04 21:36:30 +01:00
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
2024-03-28 12:54:12 +01:00
return [ version_version , version_type_int , version_typerelease ]
2024-02-12 19:56:17 +01:00
# Determines CORE's installation/base path
## Determines CORE's installation/base path[br]
## [br]
## [b]Calling this function is likely to be safe, but shouldn't be done nonetheless![/b]
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
# Checks Godot's version
## Checks compatibility with the running version.
func check_godot_version ( ) - > bool :
var version : Dictionary = Engine . get_version_info ( )
match ( version [ " major " ] ) :
4 : pass
_ :
2024-03-03 19:08:20 +01:00
printerr ( " The CORE Framework does not support Godot versions older or newer than 4.x.x " )
return false
2024-02-12 19:56:17 +01:00
match ( version [ " minor " ] ) :
2024-03-03 19:08:20 +01:00
0 : printerr ( " The CORE Framework does not support Godot versions older than 4.2.x. Please update to Godot 4.2.x to ensure full compatibility. " )
1 : printerr ( " The CORE Framework does not support Godot versions older than 4.2.x. Please update to Godot 4.2.x to ensure full compatibility. " )
2024-02-12 19:56:17 +01:00
2 : pass
2024-03-03 19:08:20 +01:00
_ :
printerr ( " The CORE Framework does not support Godot versions newer than 4.2.x. Please downgrade to Godot 4.2.x. " )
return false
if version [ " status " ] != " stable " :
printerr ( " The CORE Framework does not support unstable Godot versions. Please switch to Godot stable 4.2.x. " )
return false
2024-02-12 19:56:17 +01:00
return true