487 lines
22 KiB
GDScript
487 lines
22 KiB
GDScript
# CORE FRAMEWORK SOURCE FILE
|
|
# Copyright (c) 2024 The StarOpenSource Project & Contributors
|
|
# Licensed under the GNU Affero General Public License v3
|
|
#
|
|
# This program is free software: you can redistribute it and/or modify
|
|
# 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.
|
|
#
|
|
# 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 Affero General Public License for more details.
|
|
#
|
|
# You should have received a copy of the GNU Affero General Public License
|
|
# 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.
|
|
extends Node
|
|
class_name Core
|
|
|
|
# Versioning
|
|
## 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.RELEASE
|
|
## The version type number. Resets on every new version and version type.
|
|
const version_typerelease: int = 1
|
|
## The fork indicator. Update this if you intend on soft or hard forking this framework.
|
|
const version_fork: String = ""
|
|
|
|
# Modules
|
|
## Used internally for loading, managing and unloading modules.
|
|
const modules: Array[String] = [ "logger", "misc", "sms", "logui", "erm", "storage", "validation" ]
|
|
## CORE's configuration object.[br]
|
|
## [b]NEVER access this yourself! To change the configuration use [method reload_configuration] instead.[/b]
|
|
var config: CoreConfiguration
|
|
## The framework scheduler.[br]
|
|
## Performs various maintenance tasks every minute.
|
|
## [b]Danger: [i]Don't modify this.[/i][/b]
|
|
var scheduler: Timer
|
|
## The 'Logger' module
|
|
var logger: CoreBaseModule
|
|
## The 'Miscellaneous' module
|
|
var misc: CoreBaseModule
|
|
## The 'Scene Management System' module
|
|
var sms: CoreBaseModule
|
|
## The 'Log UI' module. Not important for you, it just displays the log graphically :3
|
|
var logui: CoreBaseModule
|
|
## The 'Easy Request Maker' module (formerly 'Easy DownLoader')
|
|
var erm: CoreBaseModule
|
|
## The 'Storage' module
|
|
var storage: CoreBaseModule
|
|
## The 'Data Validation' module
|
|
var validation: CoreBaseModule
|
|
|
|
# /etc/
|
|
## 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.
|
|
var cleanup_hooks: Dictionary = {}
|
|
## Internal, don't modify.
|
|
# Contains a list of all loaded custom modules.
|
|
var custom_modules: Dictionary = {}
|
|
## Internal, don't modify.
|
|
# Contains the node holding all custom modules as children.
|
|
var custom_modules_node: Node
|
|
## Internal, don't modify.
|
|
# The CORE Object's logger instance.
|
|
var loggeri: CoreLoggerInstance
|
|
## Internal, don't modify.
|
|
# Makes CORE inaccessible if true.
|
|
var disabled: bool = false
|
|
## Internal, don't modify.
|
|
# Displays the ✨ special ✨ welcome message if true
|
|
var welcomed: bool = false
|
|
|
|
## Contains the amount of time it took to preinitialize the framework, measured in milliseconds.[br]
|
|
## Captured in [method _init].[br]
|
|
## [b]Danger: [i]Don't modify this.[/i][/b]
|
|
var initduration_preinitialization: int = 0
|
|
## Contains the amount of time it took to initialize the framework, measured in milliseconds.[br]
|
|
## Captured in [method _ready].[br]
|
|
## [b]Danger: [i]Don't modify this.[/i][/b]
|
|
var initduration_initialization: int = 0
|
|
## Contains the amount of time it took to completely initialize the framework, measured in milliseconds.[br]
|
|
## Captured in [method complete_init].[br]
|
|
## [b]Danger: [i]Don't modify this.[/i][/b]
|
|
var initduration_complete_initialization: int = 0
|
|
|
|
# +++ initialization +++
|
|
# Preinitialization
|
|
func _init(new_config: CoreConfiguration = CoreConfiguration.new()) -> void:
|
|
var inittime: int = Time.get_ticks_msec()
|
|
name = "CORE"
|
|
if !check_godot_version(): return
|
|
if !determine_basepath():
|
|
get_tree().quit(70)
|
|
while true: await get_tree().create_timer(9999).timeout
|
|
custom_modules_node = Node.new()
|
|
reload_configuration(new_config)
|
|
initialize_modules()
|
|
apply_configuration()
|
|
initialize_scheduler()
|
|
initduration_preinitialization = Time.get_ticks_msec() - inittime
|
|
|
|
# Initialization
|
|
func _ready() -> void:
|
|
var inittime: int = Time.get_ticks_msec()
|
|
inject_modules()
|
|
custom_modules_node.name = "Custom Modules"
|
|
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
|
|
initduration_initialization = Time.get_ticks_msec() - inittime
|
|
|
|
# Initializes all built-in modules during the preinitialization phase.
|
|
## Internal, don't call.
|
|
func initialize_modules() -> void:
|
|
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).logger = logger.get_instance(basepath.replace("res://", "") + "src/" + module + ".gd", get(module))
|
|
get(module)._initialize()
|
|
|
|
# Injects CORE's builtin modules into the SceneTree.
|
|
## Internal, don't call.
|
|
func inject_modules() -> void: for module in modules: add_child(get(module))
|
|
|
|
# Initializes the framework scheduler.
|
|
## Internal, don't call.
|
|
func initialize_scheduler() -> void:
|
|
scheduler = Timer.new()
|
|
scheduler.name = "Scheduler"
|
|
scheduler.autostart = true
|
|
scheduler.one_shot = false
|
|
scheduler.paused = false
|
|
scheduler.wait_time = 60
|
|
scheduler.process_mode = Node.PROCESS_MODE_ALWAYS
|
|
scheduler.connect("timeout", func() -> void:
|
|
loggeri.verb("Running scheduler tasks")
|
|
for module in modules: await get(module)._schedule()
|
|
for module in custom_modules_node.get_children(): await module._schedule()
|
|
)
|
|
|
|
## Waits for all modules to fully initialize.[br]
|
|
## [br]
|
|
## This ensures that all modules are fully initialized and ready for usage.[br]
|
|
## [i][b]Not calling this function during startup may lead to runtime issues.[/b][/i]
|
|
func complete_init() -> void:
|
|
var inittime: int = Time.get_ticks_msec()
|
|
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
|
|
for module in modules: if !get(module).initialized: modsinit_builtin.append(module)
|
|
|
|
# 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:")
|
|
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
|
|
|
|
# Initialization complete
|
|
await get_tree().process_frame
|
|
if !welcomed:
|
|
welcomed = true
|
|
var version_welcome: String = await get_formatted_string("v%version%-%version_type_technical%%version_typerelease%%version_fork%")
|
|
if version_welcome.length() > 15:
|
|
await logger.crash("Invalid version size of 15")
|
|
elif version_welcome.length() < 15:
|
|
version_welcome = " ".repeat(15-version_welcome.length()) + version_welcome
|
|
logger._log(CoreTypes.LoggerLevel.SPECIAL, basepath.replace("res://", "") + "src/core.gd", """_________________________________ __________ %version% ______
|
|
__ ____/_ __ \\__ __ \\__ ____/ ___ ____/____________ _______ ___________ _________________ /__
|
|
_ / _ / / /_ /_/ /_ __/ __ /_ __ ___/ __ `/_ __ `__ \\ _ \\_ | /| / / __ \\_ ___/_ //_/
|
|
/ /___ / /_/ /_ _, _/_ /___ _ __/ _ / / /_/ /_ / / / / / __/_ |/ |/ // /_/ / / _ ,<
|
|
\\____/ \\____/ /_/ |_| /_____/ /_/ /_/ \\__,_/ /_/ /_/ /_/\\___/____/|__/ \\____//_/ /_/|_|
|
|
Copyright (c) 2023-2024 The StarOpenSource Project & Contributors.
|
|
Licensed under the GNU Affero General Public License v3 WITHOUT ANY WARRANTY.
|
|
You should have recieved a copy of the GNU Affero General Public License
|
|
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
|
|
Consider contributing to the CORE Framework to make it even better... and remember: #TransRightsAreHumanRights
|
|
Thank you for using the CORE Framework to develop your application or game!""".replace("%version%", version_welcome))
|
|
if is_devmode(): loggeri.warn("The CORE Framework is running in development mode.\nThis may cause bugs and issues inside the framework. Here be dragons!")
|
|
if logger.verbose_mode: loggeri.warn("Godot is running in verbose stdout mode.\nDue to a bug in the engine that prevents displaying truecolor ANSI escape\nsequences CORE changed the color of all diagnostic log messages.\nTo prevent this, set 'logger_detect_verbose_mode' in the configuration to 'false'.")
|
|
|
|
initduration_complete_initialization = Time.get_ticks_msec() - inittime
|
|
loggeri.info("Framework initialization took " + str(initduration_preinitialization + initduration_initialization + initduration_complete_initialization) + "ms (pre " + str(initduration_preinitialization) + "ms, init " + str(initduration_initialization) + "ms, complete " + str(initduration_complete_initialization) + "ms)")
|
|
|
|
# +++ configuration +++
|
|
## Loads a (new) configuration object and applies it to all modules.
|
|
func reload_configuration(new_config: CoreConfiguration = CoreConfiguration.new()) -> void:
|
|
var initialized = config != null
|
|
if initialized: loggeri.verb("Reloading CORE's configuration")
|
|
if is_instance_valid(config): config.free()
|
|
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)")
|
|
if initialized: apply_configuration()
|
|
|
|
# Applies a new configuration.
|
|
## Internal, don't call.
|
|
func apply_configuration() -> void:
|
|
if loggeri != null: loggeri.verb("Applying configuration")
|
|
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.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:
|
|
custom_modules[module]._pull_config()
|
|
|
|
# Workaround
|
|
if config.logger_detect_verbose_mode: logger.verbose_mode = OS.is_stdout_verbose()
|
|
|
|
# +++ custom module support +++
|
|
## Registers a new custom module.
|
|
func register_custom_module(module_name: String, module_origin: String, module_class: CoreBaseModule) -> bool:
|
|
loggeri.verb("Registering new custom module \"" + module_name + "\"")
|
|
if !config.custom_modules:
|
|
loggeri.error("Registering module failed: Custom module support is disabled.")
|
|
return false
|
|
if custom_modules.has(module_name):
|
|
loggeri.error(stringify_variables("Registering module failed: A custom module with the name %name% already exists.", { "name": module_name }))
|
|
return false
|
|
loggeri.diag("Updating variables")
|
|
module_class.name = module_name
|
|
module_class.core = self
|
|
module_class.logger = logger.get_instance(module_origin, module_class)
|
|
module_class.logger.framework = true
|
|
loggeri.diag("Adding module to SceneTree")
|
|
custom_modules_node.add_child(module_class)
|
|
loggeri.diag("Merging module with custom_modules")
|
|
custom_modules.merge({ module_name: module_class })
|
|
loggeri.diag("Initializing custom module")
|
|
module_class._initialize()
|
|
loggeri.diag("Updating custom module configuration")
|
|
module_class._pull_config()
|
|
return true
|
|
|
|
## Unregisters a custom module, making it no longer available.
|
|
func unregister_custom_module(module_name: String) -> void:
|
|
loggeri.verb("Unregistering custom module \"" + module_name + "\"")
|
|
if !custom_modules.has(module_name):
|
|
loggeri.error(stringify_variables("Unregistering module failed: A custom module with the name %name% does not exist.", { "name": module_name }))
|
|
return
|
|
var module: Node = custom_modules[module_name]
|
|
await module._cleanup()
|
|
module.logger.queue_free()
|
|
custom_modules_node.remove_child(module)
|
|
custom_modules.erase(module_name)
|
|
module.queue_free()
|
|
|
|
## Returns a registered custom module.[br]
|
|
## Please note that you can't get CORE's built-in modules with this function.
|
|
func get_custom_module(module_name: String) -> CoreBaseModule:
|
|
loggeri.diag("Getting custom module \"" + module_name + "\"")
|
|
if !custom_modules.has(module_name):
|
|
loggeri.error(stringify_variables("Getting module failed: A custom module with the name %name% does not exist.", { "name": module_name }))
|
|
return null
|
|
return custom_modules[module_name]
|
|
|
|
# +++ 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.[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(stringify_variables("Cleanup hook %id% is invalid", { "id": hook }))
|
|
else:
|
|
loggeri.diag("Calling cleanup hook #" + str(hook))
|
|
await cleanup_hooks[hook].call()
|
|
await get_tree().process_frame
|
|
loggeri.verb("Unregistering custom modules")
|
|
for module in custom_modules_node.get_children(): await unregister_custom_module(module.name)
|
|
await get_tree().process_frame
|
|
loggeri.verb("Removing custom module support")
|
|
remove_child(custom_modules_node)
|
|
custom_modules_node.queue_free()
|
|
await get_tree().process_frame
|
|
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).queue_free()
|
|
await get_tree().process_frame
|
|
print("Freeing configuration")
|
|
config.free()
|
|
print("Freeing")
|
|
queue_free()
|
|
|
|
# Generates a new cleanup hook id
|
|
## Internal, don't call.
|
|
func _generate_hook_id() -> int:
|
|
var id = randi()
|
|
if cleanup_hooks.has(id):
|
|
loggeri.warn(stringify_variables("New cleanup hook id %id% is already taken", { "id": id }))
|
|
return _generate_hook_id()
|
|
elif id == -1:
|
|
loggeri.warn(stringify_variables("Invalid cleanup hook id %id%", { "id": id }))
|
|
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(stringify_variables("Could not remove cleanup hook (id): Hook %id% does not exist", { "id": id }))
|
|
return false
|
|
loggeri.verb(stringify_variables("Removed cleanup hook %id%", { "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(stringify_variables("Could not remove cleanup hook (ref): find_key did not return an integer (returned %id%)", { "id": id }))
|
|
cleanup_hooks.erase(id)
|
|
loggeri.verb(stringify_variables("Removed cleanup hook %id% (ref)", { "id": id }))
|
|
return true
|
|
|
|
# +++ etc +++
|
|
## Returns if the framework is in development mode.
|
|
func is_devmode() -> bool:
|
|
return config.development
|
|
|
|
## Replaces placeholders with human-friendly strings.[br]
|
|
## You can use the following placeholders:[br]
|
|
## - [code]%version%[/code]: Returns the version number.[br]
|
|
## - [code]%version_type%[/code]: Returns the version type number[br]
|
|
## - [code]%version_semantic%[/code]: Returns the result of [method Core.get_version_semantic], example [i]5.2.3[/i][br]
|
|
## - [code]%version_type%[/code]: Returns the version type as a word, for example [i]Release Candidate[/i][br]
|
|
## - [code]%version_type_technical%[/code]: Returns the version 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[br]
|
|
## - [code]%custommodules%[/code]: Returns if custom module support is enabled
|
|
func get_formatted_string(string: String) -> String:
|
|
# Version strings
|
|
string = string.replace("%version%", str(version_version))
|
|
string = string.replace("%version_typerelease%", str(version_typerelease))
|
|
string = string.replace("%version_fork%", "" if version_fork == "" else "-" + version_fork)
|
|
var semantic_version: Array[int] = get_version_semantic()
|
|
string = string.replace("%version_semantic%", str(semantic_version[0]) + "." + str(semantic_version[1]) + "." + str(semantic_version[2]))
|
|
match(version_type):
|
|
CoreTypes.VersionType.RELEASE:
|
|
string = string.replace("%version_type%", "Release")
|
|
string = string.replace("%version_type_technical%", "r")
|
|
CoreTypes.VersionType.RELEASECANDIDATE:
|
|
string = string.replace("%version_type%", "Release Candidate")
|
|
string = string.replace("%version_type_technical%", "rc")
|
|
CoreTypes.VersionType.BETA:
|
|
string = string.replace("%version_type%", "Beta")
|
|
string = string.replace("%version_type_technical%", "b")
|
|
CoreTypes.VersionType.ALPHA:
|
|
string = string.replace("%version_type%", "Alpha")
|
|
string = string.replace("%version_type_technical%", "a")
|
|
_: await loggeri.crash("Invalid version type " + str(version_type))
|
|
# 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")
|
|
# Custom module support
|
|
if config.custom_modules: string = string.replace("%custommodules%", "Enabled")
|
|
else: string = string.replace("%custommodules%", "Disabled")
|
|
return string
|
|
|
|
## Returns CORE's versioning scheme into the semantic versioning scheme.[br]
|
|
## The first integer contains the version number, the second integer contains the version 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 version type number.
|
|
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_version, version_type_int, version_typerelease]
|
|
|
|
# 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:
|
|
printerr("CORE is not located at 'res://CORE/' or 'res://addons/CORE', aborting initialization.")
|
|
return false
|
|
return true
|
|
|
|
## Checks compatibility with the running version.
|
|
func check_godot_version() -> bool:
|
|
var version: Dictionary = Engine.get_version_info()
|
|
match(version["major"]):
|
|
4: pass
|
|
_:
|
|
printerr("The CORE Framework does not support Godot versions older or newer than 4.x.x.")
|
|
return false
|
|
match(version["minor"]):
|
|
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.")
|
|
2: pass
|
|
_:
|
|
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 4.2.x.stable to ensure full compatibility.")
|
|
return false
|
|
return true
|
|
|
|
## Makes sure for all log messages to be flushed and that CORE is correctly cleaned up.[br]
|
|
## Using [method SceneTree.quit] directly may cause various issues.[br]
|
|
## [b]Note: [i]Using the [code]await[/code] keyword is required for this function.[/i][/b]
|
|
func quit_safely(exitcode: int = 0) -> void:
|
|
loggeri.info("Shutting down (code " + str(exitcode) + ")")
|
|
if config.hide_window_on_shutdown:
|
|
loggeri.verb("Hiding window")
|
|
Engine.max_fps = -1 # a higher framerate seems to make the shutdown process muuuuch faster
|
|
DisplayServer.window_set_vsync_mode(DisplayServer.VSYNC_ENABLED) # we don't want to cook the cpu tho
|
|
DisplayServer.window_set_exclusive(0, false)
|
|
DisplayServer.window_set_mode(DisplayServer.WINDOW_MODE_MINIMIZED)
|
|
DisplayServer.window_set_min_size(Vector2.ZERO)
|
|
DisplayServer.window_set_size(Vector2i.ZERO)
|
|
DisplayServer.window_set_max_size(Vector2.ZERO)
|
|
DisplayServer.window_set_position(Vector2i(9999999, 9999999))
|
|
DisplayServer.window_set_flag(DisplayServer.WINDOW_FLAG_BORDERLESS, true)
|
|
DisplayServer.window_set_flag(DisplayServer.WINDOW_FLAG_ALWAYS_ON_TOP, false)
|
|
DisplayServer.window_set_flag(DisplayServer.WINDOW_FLAG_MOUSE_PASSTHROUGH, false)
|
|
DisplayServer.window_set_flag(DisplayServer.WINDOW_FLAG_NO_FOCUS, true)
|
|
DisplayServer.window_set_flag(DisplayServer.WINDOW_FLAG_RESIZE_DISABLED, true)
|
|
await get_tree().create_timer(0.25).timeout
|
|
await cleanup()
|
|
get_tree().quit(exitcode)
|
|
|
|
## Internal, don't call
|
|
# Makes misc.stringify_variables() calls shorter
|
|
func stringify_variables(template: String, arguments: Dictionary, no_quotes: bool = false) -> String:
|
|
return misc.stringify_variables(template, arguments, no_quotes, true)
|
|
|
|
## Internal, don't call.
|
|
# Just ignore this.
|
|
func _notification(what) -> void:
|
|
match(what):
|
|
NOTIFICATION_WM_CLOSE_REQUEST:
|
|
if config.automatic_shutdown:
|
|
loggeri.diag("Got close request, shutting down")
|
|
await quit_safely(0)
|