Update to f5e50b1f6b3d5e9f5d4c74fd32ac536e46143804
This commit is contained in:
parent
c165939267
commit
99efd65300
7 changed files with 283 additions and 108 deletions
|
@ -45,6 +45,18 @@ class_name CoreConfiguration
|
||||||
@export var logui_background_color: Color
|
@export var logui_background_color: Color
|
||||||
## What font size the graphical log should have.
|
## What font size the graphical log should have.
|
||||||
@export var logui_font_size: int
|
@export var logui_font_size: int
|
||||||
|
|
||||||
|
@export_category("Miscellaneous")
|
||||||
|
## Shows or hides the type when calling [code]stringify_variables[/code].
|
||||||
|
@export var misc_stringify_show_type: bool
|
||||||
|
## Determines how [code]stringify_variables[/code] should display [class Color] variables.[br]
|
||||||
|
## Will display colors from [code]0[/code] to [code]255[/code] if [code]true[/code] or from [code]-1.0[/code] to [code]1.0[/code] if [code]false[/code].
|
||||||
|
@export var misc_stringify_color_range8: bool
|
||||||
|
## Determines if [class Array]s should be processed by [code]stringify_variables[/code].
|
||||||
|
@export var misc_stringify_array: bool
|
||||||
|
## Determines if [class Dictionary]s should be processed by [code]stringify_variables[/code].
|
||||||
|
@export var misc_stringify_dictionary: bool
|
||||||
|
|
||||||
@export_category("Easy Request Maker")
|
@export_category("Easy Request Maker")
|
||||||
## Determines how unsecure requests should be handled.
|
## Determines how unsecure requests should be handled.
|
||||||
@export var erm_unsecure_requests: CoreTypes.BlockadeLevel
|
@export var erm_unsecure_requests: CoreTypes.BlockadeLevel
|
||||||
|
@ -63,9 +75,17 @@ func _init() -> void:
|
||||||
logger_format = "%color%[%time%] [%level% %origin%] %message%"
|
logger_format = "%color%[%time%] [%level% %origin%] %message%"
|
||||||
logger_newlines_override = true
|
logger_newlines_override = true
|
||||||
logger_newlines_sizelimit = 40
|
logger_newlines_sizelimit = 40
|
||||||
|
|
||||||
# Log UI
|
# Log UI
|
||||||
logui_enabled = true
|
logui_enabled = true
|
||||||
logui_background_color = Color.BLACK # To disable the background, use Color.TRANSPARENT
|
logui_background_color = Color.BLACK # To disable the background, use Color.TRANSPARENT
|
||||||
logui_font_size = 14
|
logui_font_size = 14
|
||||||
|
|
||||||
|
# Misc
|
||||||
|
misc_stringify_show_type = true
|
||||||
|
misc_stringify_color_range8 = true
|
||||||
|
misc_stringify_array = true
|
||||||
|
misc_stringify_dictionary = true
|
||||||
|
|
||||||
# Easy Request Maker
|
# Easy Request Maker
|
||||||
erm_unsecure_requests = CoreTypes.BlockadeLevel.BLOCK
|
erm_unsecure_requests = CoreTypes.BlockadeLevel.BLOCK
|
||||||
|
|
98
src/core.gd
98
src/core.gd
|
@ -57,18 +57,16 @@ var storage: CoreBaseModule
|
||||||
## Stores the path to CORE's installation directory.[br]
|
## Stores the path to CORE's installation directory.[br]
|
||||||
## [b]Danger: [i]Don't modify this.[/i][/b]
|
## [b]Danger: [i]Don't modify this.[/i][/b]
|
||||||
var basepath: String
|
var basepath: String
|
||||||
## Contains a list of all loaded custom modules.[br]
|
# Contains a list of all registered cleanup hooks.
|
||||||
## [b]Danger: [i]Don't modify this.[/i][/b]
|
var cleanup_hooks: Dictionary = {}
|
||||||
|
# Contains a list of all loaded custom modules.
|
||||||
var custom_modules: Dictionary = {}
|
var custom_modules: Dictionary = {}
|
||||||
## Contains the node holding all custom modules as children.[br]
|
# Contains the node holding all custom modules as children.
|
||||||
## [b]Danger: [i]Don't modify this.[/i][/b]
|
|
||||||
var custom_modules_node: Node
|
var custom_modules_node: Node
|
||||||
## The CORE Object's logger instance.
|
# The CORE Object's logger instance.
|
||||||
## [b]Danger: [i]Don't modify this.[/i][/b]
|
|
||||||
var loggeri: CoreLoggerInstance
|
var loggeri: CoreLoggerInstance
|
||||||
|
|
||||||
# +++ initialization +++
|
# +++ initialization +++
|
||||||
## Handles the preinitialization part. Does stuff like checking the engine version, loading the config and loading all modules into memory.
|
|
||||||
func _init(new_config: CoreConfiguration = CoreConfiguration.new()) -> void:
|
func _init(new_config: CoreConfiguration = CoreConfiguration.new()) -> void:
|
||||||
name = "CORE"
|
name = "CORE"
|
||||||
if !check_godot_version(): return
|
if !check_godot_version(): return
|
||||||
|
@ -79,8 +77,6 @@ func _init(new_config: CoreConfiguration = CoreConfiguration.new()) -> void:
|
||||||
apply_configuration()
|
apply_configuration()
|
||||||
initialize_scheduler()
|
initialize_scheduler()
|
||||||
|
|
||||||
## Handles the initialization part. Injects the builtin modules into the SceneTree and makes sure custom modules can be loaded properly.[br]
|
|
||||||
## [b]Danger: [i]Don't call this.[/i][/b]
|
|
||||||
func _ready() -> void:
|
func _ready() -> void:
|
||||||
inject_modules()
|
inject_modules()
|
||||||
custom_modules_node.name = "Custom Modules"
|
custom_modules_node.name = "Custom Modules"
|
||||||
|
@ -89,8 +85,7 @@ func _ready() -> void:
|
||||||
add_child(scheduler)
|
add_child(scheduler)
|
||||||
get_tree().auto_accept_quit = false
|
get_tree().auto_accept_quit = false
|
||||||
|
|
||||||
## Initializes all built-in modules during the preinitialization phase.[br]
|
# Initializes all built-in modules during the preinitialization phase.
|
||||||
## [b]Danger: [i]Don't call this.[/i][/b]
|
|
||||||
func initialize_modules() -> void:
|
func initialize_modules() -> void:
|
||||||
for module in modules:
|
for module in modules:
|
||||||
set(module, CoreBaseModule.new())
|
set(module, CoreBaseModule.new())
|
||||||
|
@ -100,12 +95,10 @@ func initialize_modules() -> void:
|
||||||
get(module).loggeri = logger.get_instance(basepath.replace("res://", "") + "src/" + module + ".gd", get(module))
|
get(module).loggeri = logger.get_instance(basepath.replace("res://", "") + "src/" + module + ".gd", get(module))
|
||||||
get(module)._initialize()
|
get(module)._initialize()
|
||||||
|
|
||||||
## Injects CORE's builtin modules into the SceneTree.[br]
|
# Injects CORE's builtin modules into the SceneTree.
|
||||||
## [b]Danger: [i]Don't call this.[/i][/b]
|
|
||||||
func inject_modules() -> void: for module in modules: add_child(get(module))
|
func inject_modules() -> void: for module in modules: add_child(get(module))
|
||||||
|
|
||||||
## Initializes the framework scheduler.
|
# Initializes the framework scheduler.
|
||||||
## [b]Danger: [i]Don't call this.[/i][/b]
|
|
||||||
func initialize_scheduler() -> void:
|
func initialize_scheduler() -> void:
|
||||||
scheduler = Timer.new()
|
scheduler = Timer.new()
|
||||||
scheduler.name = "Scheduler"
|
scheduler.name = "Scheduler"
|
||||||
|
@ -163,8 +156,7 @@ func reload_configuration(new_config: CoreConfiguration = CoreConfiguration.new(
|
||||||
if initialized: loggeri.verb("Overrode configuration (development mode)")
|
if initialized: loggeri.verb("Overrode configuration (development mode)")
|
||||||
if initialized: apply_configuration()
|
if initialized: apply_configuration()
|
||||||
|
|
||||||
## Applies the a configuration.[br]
|
# Applies a new configuration.
|
||||||
## [b]Danger: [i]Don't call this.[/i][/b]
|
|
||||||
func apply_configuration() -> void:
|
func apply_configuration() -> void:
|
||||||
if loggeri != null: loggeri.verb("Applying configuration")
|
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 is_devmode() and loggeri != null: loggeri.warn("The CORE Framework is in development mode. Here be dragons!")
|
||||||
|
@ -210,7 +202,7 @@ func unregister_custom_module(module_name: String) -> void:
|
||||||
if !custom_modules.has(module_name):
|
if !custom_modules.has(module_name):
|
||||||
loggeri.error("Unregistering module failed: A custom module with the name \"" + module_name + "\" does not exist.")
|
loggeri.error("Unregistering module failed: A custom module with the name \"" + module_name + "\" does not exist.")
|
||||||
return
|
return
|
||||||
var module: Node = get_custom_module(module_name)
|
var module: Node = custom_modules[module_name]
|
||||||
await module._cleanup()
|
await module._cleanup()
|
||||||
module.loggeri.queue_free()
|
module.loggeri.queue_free()
|
||||||
custom_modules_node.remove_child(module)
|
custom_modules_node.remove_child(module)
|
||||||
|
@ -226,23 +218,80 @@ func get_custom_module(module_name: String) -> CoreBaseModule:
|
||||||
return null
|
return null
|
||||||
return custom_modules[module_name]
|
return custom_modules[module_name]
|
||||||
|
|
||||||
# +++ etc ++
|
# +++ cleanup ++
|
||||||
## Makes sure that CORE does not leak memory on shutdown/unload.[br]
|
## 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:
|
func cleanup() -> void:
|
||||||
loggeri.info("Cleaning up")
|
loggeri.info("Cleaning up")
|
||||||
for module in custom_modules_node.get_children(): unregister_custom_module(module.name)
|
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)
|
remove_child(custom_modules_node)
|
||||||
custom_modules_node.queue_free()
|
custom_modules_node.queue_free()
|
||||||
|
loggeri.verb("Unloading built-in modules")
|
||||||
var modules_reverse: Array[String] = modules.duplicate()
|
var modules_reverse: Array[String] = modules.duplicate()
|
||||||
modules_reverse.reverse()
|
modules_reverse.reverse()
|
||||||
for module in modules_reverse:
|
for module in modules_reverse:
|
||||||
await get(module)._cleanup()
|
await get(module)._cleanup()
|
||||||
get(module).loggeri.queue_free()
|
|
||||||
get(module).queue_free()
|
get(module).queue_free()
|
||||||
|
print("Freeing configuration")
|
||||||
config.queue_free()
|
config.queue_free()
|
||||||
|
await get_tree().process_frame
|
||||||
|
print("Freeing")
|
||||||
queue_free()
|
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.
|
## Returns if the framework is in development mode.
|
||||||
func is_devmode() -> bool:
|
func is_devmode() -> bool:
|
||||||
return config.development
|
return config.development
|
||||||
|
@ -299,8 +348,7 @@ func get_version_semantic() -> Array[int]:
|
||||||
CoreTypes.VersionType.ALPHA: version_type_int = 0
|
CoreTypes.VersionType.ALPHA: version_type_int = 0
|
||||||
return [version_version, version_type_int, version_typerelease]
|
return [version_version, version_type_int, version_typerelease]
|
||||||
|
|
||||||
## Determines CORE's installation/base path.[br]
|
# Determines CORE's installation/base path.
|
||||||
## [b]Danger: [i]Don't call this.[/i][/b]
|
|
||||||
func determine_basepath() -> bool:
|
func determine_basepath() -> bool:
|
||||||
if FileAccess.file_exists("res://.corebasepath"):
|
if FileAccess.file_exists("res://.corebasepath"):
|
||||||
basepath = "res://"
|
basepath = "res://"
|
||||||
|
@ -313,7 +361,6 @@ func determine_basepath() -> bool:
|
||||||
return false
|
return false
|
||||||
return true
|
return true
|
||||||
|
|
||||||
# Checks Godot's version
|
|
||||||
## Checks compatibility with the running version.
|
## Checks compatibility with the running version.
|
||||||
func check_godot_version() -> bool:
|
func check_godot_version() -> bool:
|
||||||
var version: Dictionary = Engine.get_version_info()
|
var version: Dictionary = Engine.get_version_info()
|
||||||
|
@ -343,6 +390,7 @@ func quit_safely(exitcode: int = 0) -> void:
|
||||||
await cleanup()
|
await cleanup()
|
||||||
get_tree().quit(exitcode)
|
get_tree().quit(exitcode)
|
||||||
|
|
||||||
|
# Just ignore this.
|
||||||
func _notification(what) -> void:
|
func _notification(what) -> void:
|
||||||
match(what):
|
match(what):
|
||||||
NOTIFICATION_WM_CLOSE_REQUEST:
|
NOTIFICATION_WM_CLOSE_REQUEST:
|
||||||
|
|
14
src/erm.gd
14
src/erm.gd
|
@ -18,14 +18,11 @@
|
||||||
## Allows for awaited, batched and oneline requests.
|
## Allows for awaited, batched and oneline requests.
|
||||||
extends CoreBaseModule
|
extends CoreBaseModule
|
||||||
|
|
||||||
## Contains a list of all queued downloads.[br]
|
# Contains a list of all queued downloads.
|
||||||
## [b]Danger: [i]Don't modify this[/i][/b].
|
|
||||||
var list_queue: Dictionary = {}
|
var list_queue: Dictionary = {}
|
||||||
## Contains a list of all active downloads.[br]
|
# Contains a list of all active downloads.
|
||||||
## [b]Danger: [i]Don't modify this[/i][/b].
|
|
||||||
var list_active: Dictionary = {}
|
var list_active: Dictionary = {}
|
||||||
## Contains a liust of all completed downloads.[br]
|
# Contains a liust of all completed downloads.
|
||||||
## [b]Danger: [i]Don't modify this[/i][/b].
|
|
||||||
var list_complete: Dictionary = {}
|
var list_complete: Dictionary = {}
|
||||||
|
|
||||||
## Determines how unsecure requests should be handled.
|
## Determines how unsecure requests should be handled.
|
||||||
|
@ -107,7 +104,7 @@ func batch_awaited_request(urls: PackedStringArray, parse_utf8: bool, method: HT
|
||||||
list_complete.erase(id)
|
list_complete.erase(id)
|
||||||
return dldata
|
return dldata
|
||||||
|
|
||||||
## Internal function, do not call
|
# Does the work, but in a thread.
|
||||||
func _batch_awaited_request(url: String, parse_utf8: bool, method: HTTPClient.Method = HTTPClient.Method.METHOD_GET, headers: PackedStringArray = PackedStringArray([]), data: String = "") -> int:
|
func _batch_awaited_request(url: String, parse_utf8: bool, method: HTTPClient.Method = HTTPClient.Method.METHOD_GET, headers: PackedStringArray = PackedStringArray([]), data: String = "") -> int:
|
||||||
var id: int = create_request(url, method, headers, data)
|
var id: int = create_request(url, method, headers, data)
|
||||||
start_request(id, parse_utf8)
|
start_request(id, parse_utf8)
|
||||||
|
@ -116,8 +113,7 @@ func _batch_awaited_request(url: String, parse_utf8: bool, method: HTTPClient.Me
|
||||||
return id
|
return id
|
||||||
|
|
||||||
# +++ internal +++
|
# +++ internal +++
|
||||||
## Returns a new download id.[br]
|
# Returns a new download id.
|
||||||
## [b]Danger: [i]Don't call this.[/i][/b]
|
|
||||||
func generate_id() -> int:
|
func generate_id() -> int:
|
||||||
var id = randi()
|
var id = randi()
|
||||||
if list_queue.has(id) or list_active.has(id):
|
if list_queue.has(id) or list_active.has(id):
|
||||||
|
|
|
@ -23,9 +23,7 @@ extends CoreBaseModule
|
||||||
|
|
||||||
signal log_event
|
signal log_event
|
||||||
|
|
||||||
## Keeps track of all logger instances.
|
# Keeps track of all logger instances. Unused instances will be cleaned periodically by CORE's scheduler.
|
||||||
## Unused instances will be cleaned periodically.
|
|
||||||
## [b]Danger: [i]Don't modify this.[/i][/b]
|
|
||||||
var instances: Array[CoreLoggerInstance] = []
|
var instances: Array[CoreLoggerInstance] = []
|
||||||
|
|
||||||
## Used to determine if running in verbose/command line mode.[br]
|
## Used to determine if running in verbose/command line mode.[br]
|
||||||
|
@ -46,15 +44,20 @@ var config_newlines_sizelimit: int
|
||||||
|
|
||||||
# +++ module +++
|
# +++ module +++
|
||||||
func _cleanup() -> void:
|
func _cleanup() -> void:
|
||||||
_schedule()
|
for instance in instances:
|
||||||
await get_tree().process_frame
|
if is_instance_valid(instance):
|
||||||
|
loggeri.diag("Removing instance '" + instance.name + "'")
|
||||||
|
instance.queue_free()
|
||||||
|
|
||||||
func _schedule() -> void:
|
func _schedule() -> void:
|
||||||
|
var instances_remove_enty: Array[CoreLoggerInstance] = []
|
||||||
for instance in instances:
|
for instance in instances:
|
||||||
if !is_instance_valid(instance): continue
|
if !is_instance_valid(instance): continue
|
||||||
if !is_instance_valid(instance.parent):
|
if !is_instance_valid(instance.parent):
|
||||||
loggeri.diag("Removing instance '" + instance.name + "'")
|
loggeri.diag("Removing instance '" + instance.name + "'")
|
||||||
instance.queue_free()
|
instance.queue_free()
|
||||||
|
instances_remove_enty.append(instance)
|
||||||
|
for instance in instances_remove_enty:
|
||||||
instances.remove_at(instances.find(instance))
|
instances.remove_at(instances.find(instance))
|
||||||
|
|
||||||
func _pull_config() -> void:
|
func _pull_config() -> void:
|
||||||
|
@ -65,8 +68,7 @@ func _pull_config() -> void:
|
||||||
config_newlines_sizelimit = core.config.logger_newlines_sizelimit
|
config_newlines_sizelimit = core.config.logger_newlines_sizelimit
|
||||||
|
|
||||||
# +++ logging +++
|
# +++ logging +++
|
||||||
## The main logging function that does the heavy lifting.[br]
|
# The main logging function that does the heavy lifting.
|
||||||
## [b]Danger: [i]Don't call this.[/i][/b]
|
|
||||||
func _log(level: CoreTypes.LoggerLevel, origin: String, message: String) -> void:
|
func _log(level: CoreTypes.LoggerLevel, origin: String, message: String) -> void:
|
||||||
if !is_level_allowed(level):
|
if !is_level_allowed(level):
|
||||||
emit_signal("log_event", false, level, origin, message, "")
|
emit_signal("log_event", false, level, origin, message, "")
|
||||||
|
|
121
src/misc.gd
121
src/misc.gd
|
@ -21,6 +21,19 @@
|
||||||
## and generally make your life as a developer easier.
|
## and generally make your life as a developer easier.
|
||||||
extends CoreBaseModule
|
extends CoreBaseModule
|
||||||
|
|
||||||
|
# Configuration
|
||||||
|
var config_stringify_show_type: bool
|
||||||
|
var config_stringify_color_range8: bool
|
||||||
|
var config_stringify_array: bool
|
||||||
|
var config_stringify_dictionary: bool
|
||||||
|
|
||||||
|
# +++ module +++
|
||||||
|
func _pull_config() -> void:
|
||||||
|
config_stringify_show_type = core.config.misc_stringify_show_type
|
||||||
|
config_stringify_color_range8 = core.config.misc_stringify_color_range8
|
||||||
|
config_stringify_array = core.config.misc_stringify_array
|
||||||
|
config_stringify_dictionary = core.config.misc_stringify_dictionary
|
||||||
|
|
||||||
# +++ data type conversion +++
|
# +++ data type conversion +++
|
||||||
## Converts a number of bytes into mebibytes.[br]
|
## Converts a number of bytes into mebibytes.[br]
|
||||||
## [br]
|
## [br]
|
||||||
|
@ -125,6 +138,114 @@ func stringarray_to_array(array: Array[String]) -> Array:
|
||||||
func get_center(parent_size: Vector2, child_size: Vector2) -> Vector2:
|
func get_center(parent_size: Vector2, child_size: Vector2) -> Vector2:
|
||||||
return Vector2(parent_size.x/2-child_size.x/2, parent_size.y/2-child_size.y/2)
|
return Vector2(parent_size.x/2-child_size.x/2, parent_size.y/2-child_size.y/2)
|
||||||
|
|
||||||
|
## Makes variables as look correct inside strings.[br]
|
||||||
|
## Short examples:[br]
|
||||||
|
## [code]true[/code] -> [code]'true'[/code][br]
|
||||||
|
## [code]Vector2(69.064, PI)[/code] -> [code]'x=69.064 y=3.14159265358979'[/code][br]
|
||||||
|
## [code]"This is a test string"[/code] -> [code]'"This is a test string"'[/code][br]
|
||||||
|
## Full example:[br]
|
||||||
|
## [codeblock]
|
||||||
|
## Code:
|
||||||
|
## logger.diag(stringify_variables("Triggered %trigger% (pos=%position%, successful=%success%)", { "trigger": "shoot", "position": Vector2(5156.149, 581.69), "success": true }))
|
||||||
|
##
|
||||||
|
## Output:
|
||||||
|
## [16:44:35] [DIAG Test.gd] Triggered '"shoot"' (pos='x=5156.149 y=581.69', successful='true')
|
||||||
|
## [/codeblock]
|
||||||
|
func stringify_variables(template: String, variables: Dictionary, no_quotes: bool = false) -> String:
|
||||||
|
# To decrease allocations
|
||||||
|
var value
|
||||||
|
var type: String = ""
|
||||||
|
var replacement: String = ""
|
||||||
|
for placeholder in variables:
|
||||||
|
# Check key type
|
||||||
|
if typeof(placeholder) != TYPE_STRING:
|
||||||
|
logger.error("Invalid placeholder type '\"" + type_string(typeof(placeholder)) + "\", skipping")
|
||||||
|
continue
|
||||||
|
|
||||||
|
# Check for correct type
|
||||||
|
value = variables[placeholder]
|
||||||
|
|
||||||
|
match(typeof(value)):
|
||||||
|
# Primitives
|
||||||
|
Variant.Type.TYPE_NIL: replacement = "null"
|
||||||
|
Variant.Type.TYPE_BOOL: replacement = str(value)
|
||||||
|
Variant.Type.TYPE_INT: replacement = str(value)
|
||||||
|
Variant.Type.TYPE_FLOAT: replacement = str(value)
|
||||||
|
Variant.Type.TYPE_STRING: replacement = "\"" + value + "\""
|
||||||
|
Variant.Type.TYPE_STRING_NAME: replacement = "\"" + value + "\""
|
||||||
|
# Non-primitives
|
||||||
|
Variant.Type.TYPE_OBJECT: replacement = str(value)
|
||||||
|
Variant.Type.TYPE_COLOR:
|
||||||
|
if config_stringify_color_range8: replacement = "r=" + _sa(value.r8) + " g=" + _sa(value.g8) + " b=" + _sa(value.b8) + " a=" + _sa(value.a8)
|
||||||
|
else: replacement = "r=" + _sa(value.r) + " g=" + _sa(value.g) + " b=" + _sa(value.b) + " a=" + _sa(value.a)
|
||||||
|
Variant.Type.TYPE_RID: replacement = "id=" + _sa(value.get_id()) + " valid=" + _sa(value.is_valid())
|
||||||
|
Variant.Type.TYPE_ARRAY:
|
||||||
|
if config_stringify_array:
|
||||||
|
if value.size() == 0:
|
||||||
|
replacement = "[]"
|
||||||
|
else:
|
||||||
|
replacement = "[ "
|
||||||
|
for item in value:
|
||||||
|
if replacement == "[ ": replacement += _sa(item)
|
||||||
|
else: replacement += ", " + _sa(item)
|
||||||
|
replacement += " ]"
|
||||||
|
else: replacement = str(value)
|
||||||
|
Variant.Type.TYPE_DICTIONARY:
|
||||||
|
if config_stringify_dictionary:
|
||||||
|
if value.size() == 0: replacement = "{}"
|
||||||
|
else:
|
||||||
|
replacement = "{ "
|
||||||
|
for key in value:
|
||||||
|
if replacement == "{ ": replacement += _sa(key) + ": " + _sa(value[key])
|
||||||
|
else: replacement += ", " + _sa(key)
|
||||||
|
replacement += " }"
|
||||||
|
else: replacement = str(value)
|
||||||
|
# TODO: Packed Arrays
|
||||||
|
# Nodes & scripting
|
||||||
|
Variant.Type.TYPE_NODE_PATH: replacement = str(value)
|
||||||
|
Variant.Type.TYPE_CALLABLE: replacement = "valid=" + _sa(value.is_valid()) + " standard=" + _sa(value.is_standard()) + " object=" + _sa(value.get_object() ) + " method=" + value.get_method() + " args=" + _sa(value.get_bound_arguments())
|
||||||
|
Variant.Type.TYPE_SIGNAL: replacement = "name=" + _sa(value.get_name()) + " object=" + _sa(value.get_object())
|
||||||
|
# 2D
|
||||||
|
Variant.Type.TYPE_VECTOR2: replacement = "x=" + _sa(value.x) + " y=" + _sa(value.y)
|
||||||
|
Variant.Type.TYPE_VECTOR2I: replacement = "x=" + _sa(value.x) + " y=" + _sa(value.y)
|
||||||
|
Variant.Type.TYPE_RECT2: replacement = "size=" + _sa(value.size) + " pos=" + _sa(value.position) + " end=" + _sa(value.end)
|
||||||
|
Variant.Type.TYPE_RECT2I: replacement = "size=" + _sa(value.size) + " pos=" + _sa(value.position) + " end=" + _sa(value.end)
|
||||||
|
Variant.Type.TYPE_TRANSFORM2D: replacement = "x=" + _sa(value.x) + " y=" + _sa(value.y) + " origin=" + _sa(value.origin)
|
||||||
|
# 3D
|
||||||
|
Variant.Type.TYPE_VECTOR3: replacement = "x=" + _sa(value.x) + " y=" + _sa(value.y) + " z=" + _sa(value.z)
|
||||||
|
Variant.Type.TYPE_VECTOR3I: replacement = "x=" + _sa(value.x) + " y=" + _sa(value.y) + " z=" + _sa(value.z)
|
||||||
|
Variant.Type.TYPE_PLANE: replacement = "x=" + _sa(value.x) + " y=" + _sa(value.y) + " z=" + _sa(value.z) + " d=" + _sa(value.d) + " normal=" + _sa(value.normal)
|
||||||
|
Variant.Type.TYPE_QUATERNION: replacement = "x=" + _sa(value.x) + " y=" + _sa(value.y) + " z=" + _sa(value.z) + " w=" + _sa(value.w)
|
||||||
|
Variant.Type.TYPE_AABB: replacement = "size=" + _sa(value.size) + " pos=" + _sa(value.position) + " end=" + _sa(value.end)
|
||||||
|
Variant.Type.TYPE_TRANSFORM3D: replacement = "basis=" + _sa(value.basis) + " origin=" + _sa(value.origin)
|
||||||
|
Variant.Type.TYPE_BASIS: replacement = "x=" + _sa(value.x) + " y=" + _sa(value.y) + " z=" + _sa(value.z)
|
||||||
|
Variant.Type.TYPE_PROJECTION: replacement = "x=" + _sa(value.x) + " y=" + _sa(value.y) + " z=" + _sa(value.z) + " w=" + _sa(value.w)
|
||||||
|
# 4D
|
||||||
|
Variant.Type.TYPE_VECTOR4: replacement = "x=" + _sa(value.x) + " y=" + _sa(value.y) + " z=" + _sa(value.z) + " w=" + _sa(value.w)
|
||||||
|
Variant.Type.TYPE_VECTOR4I: replacement = "x=" + _sa(value.x) + " y=" + _sa(value.y) + " z=" + _sa(value.z) + " w=" + _sa(value.w)
|
||||||
|
# etc
|
||||||
|
Variant.Type.TYPE_MAX: replacement = str(value)
|
||||||
|
_:
|
||||||
|
replacement = str(value)
|
||||||
|
type = "(unknown) "
|
||||||
|
|
||||||
|
# Replace
|
||||||
|
if config_stringify_show_type and type == "":
|
||||||
|
match(typeof(value)):
|
||||||
|
Variant.Type.TYPE_NIL: type = ""
|
||||||
|
Variant.Type.TYPE_MAX: type = "(invalid) "
|
||||||
|
Variant.Type.TYPE_ARRAY:
|
||||||
|
if value.get_typed_builtin() != TYPE_NIL:
|
||||||
|
type = "(" + type_string(typeof(value)).to_lower() + "[" + type_string(typeof(value.get_typed_builtin())).to_lower() + "]) "
|
||||||
|
_: type = "(" + type_string(typeof(value)).to_lower() + ") " # something is causing almost everything to be displayed as a boolean, which is not true. i don't know what's the problem here however.
|
||||||
|
var quote: String = "'" if !no_quotes else ""
|
||||||
|
template = template.replace("%" + placeholder + "%", quote + type + replacement + quote)
|
||||||
|
return template
|
||||||
|
|
||||||
|
# Makes calls shorter
|
||||||
|
func _sa(value) -> String:
|
||||||
|
return stringify_variables("%var%", { "var": value }, true)
|
||||||
|
|
||||||
## Moved to [method Core.quit_safely].
|
## Moved to [method Core.quit_safely].
|
||||||
## @deprecated
|
## @deprecated
|
||||||
func quit_safely(exitcode: int = 0) -> void: await core.quit_safely(exitcode)
|
func quit_safely(exitcode: int = 0) -> void: await core.quit_safely(exitcode)
|
||||||
|
|
112
src/sms.gd
112
src/sms.gd
|
@ -22,24 +22,14 @@ extends CoreBaseModule
|
||||||
|
|
||||||
## Used internally for adding, managing and removing scene collections.
|
## Used internally for adding, managing and removing scene collections.
|
||||||
const scene_nodes: Array[String] = [ "debug", "cutscene", "menu", "main", "background" ]
|
const scene_nodes: Array[String] = [ "debug", "cutscene", "menu", "main", "background" ]
|
||||||
## The 'debug' scene collection.[br]
|
# Scene collections
|
||||||
## [b]Danger: [i]Don't modify this.[/i][/b]
|
|
||||||
var scenes_debug: Node = Node.new()
|
var scenes_debug: Node = Node.new()
|
||||||
## The 'cutscene' scene collection.[br]
|
|
||||||
## [b]Danger: [i]Don't modify this.[/i][/b]
|
|
||||||
var scenes_cutscene: Node = Node.new()
|
var scenes_cutscene: Node = Node.new()
|
||||||
## The 'menu' scene collection.[br]
|
|
||||||
## [b]Danger: [i]Don't modify this.[/i][/b]
|
|
||||||
var scenes_menu: Node = Node.new()
|
var scenes_menu: Node = Node.new()
|
||||||
## The 'main' scene collection.[br]
|
|
||||||
## [b]Danger: [i]Don't modify this.[/i][/b]
|
|
||||||
var scenes_main: Node = Node.new()
|
var scenes_main: Node = Node.new()
|
||||||
## The 'background' scene collection.[br]
|
|
||||||
## [b]Danger: [i]Don't modify this.[/i][/b]
|
|
||||||
var scenes_background: Node = Node.new()
|
var scenes_background: Node = Node.new()
|
||||||
|
|
||||||
## A list of all loaded scenes[br]
|
# A list of all loaded scenes.
|
||||||
## [b]Danger: [i]Don't modify this.[/i][/b]
|
|
||||||
var scenes: Dictionary = {}
|
var scenes: Dictionary = {}
|
||||||
|
|
||||||
# +++ module +++
|
# +++ module +++
|
||||||
|
@ -64,109 +54,109 @@ func _pull_config() -> void:
|
||||||
|
|
||||||
# +++ scene management +++
|
# +++ scene management +++
|
||||||
## Adds a scene to some scene collection.
|
## Adds a scene to some scene collection.
|
||||||
func add_scene(sname: String, sclass: Node, type: CoreTypes.SceneType) -> bool:
|
func add_scene(scene_name: String, scene_class: Node, scene_type: CoreTypes.SceneType) -> bool:
|
||||||
if core.config.headless: return false
|
if core.config.headless: return false
|
||||||
loggeri.verb("Adding scene \"" + sname + "\" of type " + str(type))
|
loggeri.verb("Adding scene \"" + scene_name + "\" of type " + str(scene_type))
|
||||||
if exists(sname) != CoreTypes.SceneType.NONE:
|
if exists(scene_name) != CoreTypes.SceneType.NONE:
|
||||||
loggeri.error("Scene with name \"" + sname + "\" already exists")
|
loggeri.error("Scene with name \"" + scene_name + "\" already exists")
|
||||||
return false
|
return false
|
||||||
if typeof(sclass) != TYPE_OBJECT or !sclass.is_class("Node"):
|
if typeof(scene_class) != TYPE_OBJECT or !scene_class.is_class("Node"):
|
||||||
loggeri.error("Scene class \"" + sname + "\" is not of type Node")
|
loggeri.error("Scene class \"" + scene_name + "\" is not of type Node")
|
||||||
return false
|
return false
|
||||||
sclass.name = sname
|
scene_class.name = scene_name
|
||||||
match(type):
|
match(scene_type):
|
||||||
CoreTypes.SceneType.DEBUG: scenes_debug.add_child(sclass)
|
CoreTypes.SceneType.DEBUG: scenes_debug.add_child(scene_class)
|
||||||
CoreTypes.SceneType.CUTSCENE: scenes_cutscene.add_child(sclass)
|
CoreTypes.SceneType.CUTSCENE: scenes_cutscene.add_child(scene_class)
|
||||||
CoreTypes.SceneType.MENU: scenes_menu.add_child(sclass)
|
CoreTypes.SceneType.MENU: scenes_menu.add_child(scene_class)
|
||||||
CoreTypes.SceneType.MAIN: scenes_main.add_child(sclass)
|
CoreTypes.SceneType.MAIN: scenes_main.add_child(scene_class)
|
||||||
CoreTypes.SceneType.BACKGROUND: scenes_background.add_child(sclass)
|
CoreTypes.SceneType.BACKGROUND: scenes_background.add_child(scene_class)
|
||||||
CoreTypes.SceneType.NONE:
|
CoreTypes.SceneType.NONE:
|
||||||
loggeri.error("CoreTypes.SceneType.NONE is not a valid scene type")
|
loggeri.error("CoreTypes.SceneType.NONE is not a valid scene type")
|
||||||
return false
|
return false
|
||||||
_: await loggeri.crash("Invalid SceneType " + str(type))
|
_: await loggeri.crash("Invalid SceneType " + str(scene_type))
|
||||||
scenes.merge({ sname: { "type": type, "class": sclass } })
|
scenes.merge({ scene_name: { "type": scene_type, "class": scene_class } })
|
||||||
return true
|
return true
|
||||||
|
|
||||||
## Removes a scene from some scene collection.[br]
|
## Removes a scene from some scene collection.[br]
|
||||||
## [b]Danger: [i]Don't set [code]force_remove[/code] to [code]true[/code], thanks![/i][/b]
|
## [b]Danger: [i]Don't set [code]force_remove[/code] to [code]true[/code], thanks![/i][/b]
|
||||||
func remove_scene(sname: String, force_remove: bool = false) -> bool:
|
func remove_scene(scene_name: String, force_remove: bool = false) -> bool:
|
||||||
if core.config.headless and !force_remove: return false
|
if core.config.headless and !force_remove: return false
|
||||||
if force_remove: await loggeri.crash("force_remove = true is not allowed")
|
if force_remove: await loggeri.crash("force_remove = true is not allowed")
|
||||||
loggeri.verb("Removing scene \"" + sname + "\"")
|
loggeri.verb("Removing scene \"" + scene_name + "\"")
|
||||||
match(exists(sname)):
|
match(exists(scene_name)):
|
||||||
CoreTypes.SceneType.DEBUG:
|
CoreTypes.SceneType.DEBUG:
|
||||||
scenes_debug.remove_child(scenes[sname]["class"])
|
scenes_debug.remove_child(scenes[scene_name]["class"])
|
||||||
scenes[sname]["class"].queue_free()
|
scenes[scene_name]["class"].queue_free()
|
||||||
CoreTypes.SceneType.CUTSCENE:
|
CoreTypes.SceneType.CUTSCENE:
|
||||||
scenes_cutscene.remove_child(scenes[sname]["class"])
|
scenes_cutscene.remove_child(scenes[scene_name]["class"])
|
||||||
scenes[sname]["class"].queue_free()
|
scenes[scene_name]["class"].queue_free()
|
||||||
CoreTypes.SceneType.MENU:
|
CoreTypes.SceneType.MENU:
|
||||||
scenes_menu.remove_child(scenes[sname]["class"])
|
scenes_menu.remove_child(scenes[scene_name]["class"])
|
||||||
scenes[sname]["class"].queue_free()
|
scenes[scene_name]["class"].queue_free()
|
||||||
CoreTypes.SceneType.MAIN:
|
CoreTypes.SceneType.MAIN:
|
||||||
scenes_main.remove_child(scenes[sname]["class"])
|
scenes_main.remove_child(scenes[scene_name]["class"])
|
||||||
scenes[sname]["class"].queue_free()
|
scenes[scene_name]["class"].queue_free()
|
||||||
CoreTypes.SceneType.BACKGROUND:
|
CoreTypes.SceneType.BACKGROUND:
|
||||||
scenes_background.remove_child(scenes[sname]["class"])
|
scenes_background.remove_child(scenes[scene_name]["class"])
|
||||||
scenes[sname]["class"].queue_free()
|
scenes[scene_name]["class"].queue_free()
|
||||||
CoreTypes.SceneType.NONE:
|
CoreTypes.SceneType.NONE:
|
||||||
loggeri.error("Scene \"" + sname + "\" does not exist")
|
loggeri.error("Scene \"" + scene_name + "\" does not exist")
|
||||||
return false
|
return false
|
||||||
_: await loggeri.crash("Invalid SceneType " + str(exists(sname)))
|
_: await loggeri.crash("Invalid SceneType " + str(exists(scene_name)))
|
||||||
scenes.erase(sname)
|
scenes.erase(scene_name)
|
||||||
return true
|
return true
|
||||||
|
|
||||||
# +++ getters +++
|
# +++ getters +++
|
||||||
## Returns a scene from some scene collection.[br]
|
## Returns a scene from some scene collection.[br]
|
||||||
## [br]
|
## [br]
|
||||||
## Returns [code]null[/code] if no scene with that name was found.
|
## Returns [code]null[/code] if no scene with that name was found.
|
||||||
func get_scene(sname: String) -> Node:
|
func get_scene(scene_name: String) -> Node:
|
||||||
if core.config.headless: return null
|
if core.config.headless: return null
|
||||||
match(exists(sname)):
|
match(exists(scene_name)):
|
||||||
CoreTypes.SceneType.DEBUG: return scenes[sname]["class"]
|
CoreTypes.SceneType.DEBUG: return scenes[scene_name]["class"]
|
||||||
CoreTypes.SceneType.CUTSCENE: return scenes[sname]["class"]
|
CoreTypes.SceneType.CUTSCENE: return scenes[scene_name]["class"]
|
||||||
CoreTypes.SceneType.MENU: return scenes[sname]["class"]
|
CoreTypes.SceneType.MENU: return scenes[scene_name]["class"]
|
||||||
CoreTypes.SceneType.MAIN: return scenes[sname]["class"]
|
CoreTypes.SceneType.MAIN: return scenes[scene_name]["class"]
|
||||||
CoreTypes.SceneType.BACKGROUND: return scenes[sname]["class"]
|
CoreTypes.SceneType.BACKGROUND: return scenes[scene_name]["class"]
|
||||||
CoreTypes.SceneType.NONE: loggeri.error("Scene \"" + sname + "\" does not exist")
|
CoreTypes.SceneType.NONE: loggeri.error("Scene \"" + scene_name + "\" does not exist")
|
||||||
_: await loggeri.crash("Invalid SceneType " + str(exists(sname)))
|
_: await loggeri.crash("Invalid SceneType " + str(exists(scene_name)))
|
||||||
return null
|
return null
|
||||||
|
|
||||||
## Returns a scene collection node.[br]
|
## Returns a scene collection node.[br]
|
||||||
## Useful if you want to change a child's index.[br]
|
## Useful if you want to change a child's index.[br]
|
||||||
## [b]Danger: [i]Don't change any properties of the scene collection or free it, otherwise you may cause breakage.[/i][/b]
|
## [b]Danger: [i]Don't change any properties of the scene collection or free it, otherwise you may cause breakage.[/i][/b]
|
||||||
func get_scene_collection(type: CoreTypes.SceneType) -> Node:
|
func get_scene_collection(scene_type: CoreTypes.SceneType) -> Node:
|
||||||
if core.config.headless: return null
|
if core.config.headless: return null
|
||||||
match(type):
|
match(scene_type):
|
||||||
CoreTypes.SceneType.DEBUG: return scenes_debug
|
CoreTypes.SceneType.DEBUG: return scenes_debug
|
||||||
CoreTypes.SceneType.CUTSCENE: return scenes_cutscene
|
CoreTypes.SceneType.CUTSCENE: return scenes_cutscene
|
||||||
CoreTypes.SceneType.MENU: return scenes_menu
|
CoreTypes.SceneType.MENU: return scenes_menu
|
||||||
CoreTypes.SceneType.MAIN: return scenes_main
|
CoreTypes.SceneType.MAIN: return scenes_main
|
||||||
CoreTypes.SceneType.BACKGROUND: return scenes_background
|
CoreTypes.SceneType.BACKGROUND: return scenes_background
|
||||||
CoreTypes.SceneType.NONE: loggeri.error("No scene collection was found for CoreTypes.SceneType.NONE")
|
CoreTypes.SceneType.NONE: loggeri.error("No scene collection was found for CoreTypes.SceneType.NONE")
|
||||||
_: await loggeri.crash("Invalid SceneType " + str(type))
|
_: await loggeri.crash("Invalid SceneType " + str(scene_type))
|
||||||
return null
|
return null
|
||||||
|
|
||||||
## Returns a list of all loaded scenes in some scene collection.
|
## Returns a list of all loaded scenes in some scene collection.
|
||||||
func get_scene_collection_list(type: CoreTypes.SceneType) -> Array[Node]:
|
func get_scene_collection_list(scene_type: CoreTypes.SceneType) -> Array[Node]:
|
||||||
var list: Array[Node] = []
|
var list: Array[Node] = []
|
||||||
for scene in scenes:
|
for scene in scenes:
|
||||||
if scenes[scene]["type"] == type:
|
if scenes[scene]["type"] == scene_type:
|
||||||
list.append(scenes[scene]["class"])
|
list.append(scenes[scene]["class"])
|
||||||
return list
|
return list
|
||||||
|
|
||||||
## Returns the number of loaded scenes in some scene collection.
|
## Returns the number of loaded scenes in some scene collection.
|
||||||
func get_scene_collection_count(type: CoreTypes.SceneType) -> int:
|
func get_scene_collection_count(scene_type: CoreTypes.SceneType) -> int:
|
||||||
var amount: int = 0
|
var amount: int = 0
|
||||||
for scene in scenes:
|
for scene in scenes:
|
||||||
if scenes[scene]["type"] == type:
|
if scenes[scene]["type"] == scene_type:
|
||||||
amount += 1
|
amount += 1
|
||||||
return amount
|
return amount
|
||||||
|
|
||||||
## Returns the scene collection a scene is loaded in.[br]
|
## Returns the scene collection a scene is loaded in.[br]
|
||||||
## [br]
|
## [br]
|
||||||
## [enum CoreTypes.SceneType][code].NONE[/code] if no scene with that name was found.
|
## [enum CoreTypes.SceneType][code].NONE[/code] if no scene with that name was found.
|
||||||
func exists(sname: String) -> CoreTypes.SceneType:
|
func exists(scene_name: String) -> CoreTypes.SceneType:
|
||||||
for scene in scenes:
|
for scene in scenes:
|
||||||
if scene == sname: return scenes[scene]["type"]
|
if scene == scene_name: return scenes[scene]["type"]
|
||||||
return CoreTypes.SceneType.NONE
|
return CoreTypes.SceneType.NONE
|
||||||
|
|
|
@ -23,15 +23,13 @@ extends CoreBaseModule
|
||||||
## Indicates if a storage file is currently open.[br]
|
## Indicates if a storage file is currently open.[br]
|
||||||
## [b]Danger: [i]Don't modify this.[/i][/b]
|
## [b]Danger: [i]Don't modify this.[/i][/b]
|
||||||
var is_open: bool = false
|
var is_open: bool = false
|
||||||
## The parsed data inside the storage file.[br]
|
# The parsed data inside the storage file.
|
||||||
## [b]Danger: [i]Don't modify this.[/i][/b]
|
|
||||||
var storage: Dictionary = {}
|
var storage: Dictionary = {}
|
||||||
## The location of the storage file.[br]
|
# The location of the storage file.
|
||||||
## [b]Danger: [i]Don't modify this.[/i][/b]
|
|
||||||
var storage_location: String = ""
|
var storage_location: String = ""
|
||||||
|
|
||||||
# +++ file management +++
|
# +++ file management +++
|
||||||
## Opens a storage file into memory.
|
## Opens a storage file and loads it into memory.
|
||||||
func open_storage(location: String, create_new: bool = true, sanity_check: bool = true, fail_on_sanity_check: bool = false) -> bool:
|
func open_storage(location: String, create_new: bool = true, sanity_check: bool = true, fail_on_sanity_check: bool = false) -> bool:
|
||||||
if is_open:
|
if is_open:
|
||||||
loggeri.error("Failed to open storage: A storage file is already open")
|
loggeri.error("Failed to open storage: A storage file is already open")
|
||||||
|
|
Loading…
Reference in a new issue