CORE/src/storage.gd

184 lines
7.8 KiB
GDScript3
Raw Permalink Normal View History

2024-03-18 03:23:27 +01:00
# 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/>.
extends CoreBaseModule
2024-04-08 20:14:38 +02:00
## Easy config management.
##
## Allows you to read and write configuration files with ease, without any headaches.
## Indicates if a storage file is currently open.[br]
## [b]Danger: [i]Don't modify this.[/i][/b]
2024-03-18 03:23:27 +01:00
var is_open: bool = false
# The parsed data inside the storage file.
2024-03-18 03:23:27 +01:00
var storage: Dictionary = {}
# The location of the storage file.
2024-03-18 03:23:27 +01:00
var storage_location: String = ""
2024-04-08 20:14:38 +02:00
# +++ file management +++
## Opens a storage file and loads it into memory.
2024-03-18 03:23:27 +01:00
func open_storage(location: String, create_new: bool = true, sanity_check: bool = true, fail_on_sanity_check: bool = false) -> bool:
if is_open:
logger.error("Failed to open storage: A storage file is already open")
2024-03-18 03:23:27 +01:00
return false
logger.verb(core.stringify_variables("Opening storage file at %location%", { "location": location }))
2024-03-18 03:23:27 +01:00
var file: FileAccess
if !FileAccess.file_exists(location):
if create_new:
file = FileAccess.open(location, FileAccess.WRITE)
if file == null:
await logger.crash(core.stringify_variables("Could not open storage file at %location%: Failed with error %error%", { "location": location, "error": error_string(FileAccess.get_open_error()) }))
2024-03-18 03:23:27 +01:00
return false
file.store_string("{}")
file.close()
else:
logger.error("Failed to open storage: create_new is set to false")
2024-03-18 03:23:27 +01:00
return false
file = FileAccess.open(location, FileAccess.READ)
var storage_temp: Variant = file.get_as_text()
file.close()
storage_temp = JSON.parse_string(storage_temp)
if typeof(storage_temp) != TYPE_DICTIONARY:
logger.error(core.stringify_variables("Failed to open storage: Parsed storage file is of type %type%", { "type": type_string(typeof(storage_temp)) }))
2024-03-18 03:23:27 +01:00
return false
if sanity_check:
var check_result: Array[String] = perform_sanity_check(storage_temp)
if check_result.size() != 0:
if fail_on_sanity_check:
logger.error("Sanity check failed (stopping):")
2024-03-18 03:23:27 +01:00
for error in check_result:
logger.error("-> " + error)
2024-03-18 03:23:27 +01:00
return false
else:
logger.warn("Sanity check failed (continuing anyway):")
2024-03-18 03:23:27 +01:00
for error in check_result:
logger.warn("-> " + error)
2024-03-18 03:23:27 +01:00
storage = storage_temp
storage_location = location
is_open = true
return true
2024-04-08 20:14:38 +02:00
## Closes the active storage file.
2024-03-18 03:23:27 +01:00
func close_storage() -> bool:
if !is_open:
logger.error("Failed to close storage: No storage file is open")
2024-03-18 03:23:27 +01:00
return false
logger.verb("Closing storage file")
2024-03-18 03:23:27 +01:00
storage = {}
is_open = false
return true
2024-04-08 20:14:38 +02:00
## Saves the active storage file to disk.
2024-03-18 03:23:27 +01:00
func save_storage() -> bool:
if !is_open:
logger.error("Failed to save storage: No storage file is open")
2024-03-18 03:23:27 +01:00
return false
var file: FileAccess = FileAccess.open(storage_location, FileAccess.WRITE)
if file == null:
await logger.crash(core.stringify_variables("Could not open storage file at %location%: Failed with error %error%", { "location": storage_location, "error": error_string(FileAccess.get_open_error()) }))
2024-03-18 03:23:27 +01:00
return false
logger.diag("Writing storage file to disk")
2024-03-18 03:23:27 +01:00
file.store_string(JSON.stringify(storage))
file.close()
return true
2024-04-08 20:14:38 +02:00
# +++ config manipulation +++
## Removes all keys from the active storage file. The nuclear option basically.
2024-03-18 03:23:27 +01:00
func nuke_storage(autosave: bool = true) -> bool:
if !is_open:
logger.error("Failed to nuke storage: No storage file is open")
2024-03-18 03:23:27 +01:00
return false
logger.warn("Nuking storage")
2024-03-18 03:23:27 +01:00
storage = {}
if autosave: save_storage()
return true
2024-04-08 20:14:38 +02:00
## Returns a storage key. Can also return a default value if unset.
2024-04-06 13:12:49 +02:00
func get_key(key: String, default: Variant = null) -> Variant:
2024-03-18 03:23:27 +01:00
if !is_open:
logger.error("Failed to get key: No storage file is open")
2024-03-18 03:23:27 +01:00
return NAN
logger.diag(core.stringify_variables("Returning storage key %key% (default=%default%)", { "key": key, "default": default }))
2024-03-18 03:23:27 +01:00
return storage.get(key, default)
2024-04-08 20:14:38 +02:00
## Updates a storage key with the specified value.
2024-03-18 03:23:27 +01:00
func set_key(key: String, value: Variant, overwrite: bool = true, autosave: bool = true) -> bool:
if !is_open:
logger.error("Failed to set key: No storage file is open")
2024-03-18 03:23:27 +01:00
return false
logger.diag(core.stringify_variables("Updating storage key %key% with value %value% (overwrite=%overwrite% autosave=%autosave%)", { "key": key, "value": value, "overwrite": overwrite, "autosave": autosave }))
2024-03-18 03:23:27 +01:00
storage.merge({key: value}, overwrite)
if autosave: save_storage()
return true
2024-04-08 20:14:38 +02:00
## Deletes a storage key.
2024-03-18 03:23:27 +01:00
func del_key(key: String, autosave: bool = true) -> bool:
if !is_open:
2024-03-31 18:01:57 +02:00
logger.errof("storage", "Failed to delete key: No storage file is open")
2024-03-18 03:23:27 +01:00
return false
logger.diag(core.stringify_variables("Deleting storage key %key% (autosave=%autosave%)", { "key": key, "autosave": autosave }))
2024-03-18 03:23:27 +01:00
storage.erase(key)
if autosave: save_storage()
return true
2024-04-08 20:14:38 +02:00
## Returns the [param storage] [class Dictionary], useful for more advanced operations.[br]
## Changes are not reflected onto the [param storage] though. To save changes through this module,
2024-04-08 20:14:38 +02:00
## pass your modified [class Dictionary to [method safe_dict].
2024-03-18 03:23:27 +01:00
func get_dict() -> Dictionary:
if !is_open:
logger.error("Failed to get dictionary: No storage file is open")
2024-03-18 03:23:27 +01:00
return {}
logger.verb("Returning storage dictionary")
2024-03-18 03:23:27 +01:00
return storage
2024-04-08 20:14:38 +02:00
# +++ raw manipulation +++
## Saves a arbitrary dictionary as a [param storage] [class Dictionary] with sanity checking ([code]sanity_check[/code] and [code]fail_on_sanity_check[/code]).
2024-03-18 03:23:27 +01:00
func save_dict(dict: Dictionary, sanity_check: bool = true, fail_on_sanity_check: bool = false, autosave: bool = true) -> bool:
if !is_open:
logger.error("Failed to save dictionary: No storage file is open")
2024-03-18 03:23:27 +01:00
return false
logger.verb("Saving custom dictionary as storage")
2024-03-18 03:23:27 +01:00
if sanity_check:
var check_result: Array[String] = perform_sanity_check(dict)
if check_result.size() != 0:
if fail_on_sanity_check:
logger.error("Sanity check failed (stopping):")
2024-03-18 03:23:27 +01:00
for error in check_result:
logger.error("-> " + error)
2024-03-18 03:23:27 +01:00
return false
else:
logger.warn("Sanity check failed (continuing anyway):")
2024-03-18 03:23:27 +01:00
for error in check_result:
logger.warn("-> " + error)
2024-03-18 03:23:27 +01:00
storage = dict
if autosave: save_storage()
return true
2024-04-08 20:14:38 +02:00
# +++ etc +++
## Performs sanity checks on a [class Dictionary] to determine if it can be saved and loaded using this module.
2024-03-18 03:23:27 +01:00
func perform_sanity_check(storage_check: Dictionary) -> Array[String]:
logger.verb("Performing a sanity check on some storage dictionary")
2024-03-18 03:23:27 +01:00
var errors: Array[String] = []
for key in storage_check:
if typeof(key) != TYPE_STRING:
errors.append(core.stringify_variables("Key %key% is not of type String (is of type %type%)", { "key": key, "type": type_string(typeof(key)) }))
2024-03-18 03:23:27 +01:00
continue
if typeof(storage_check[key]) != TYPE_NIL and typeof(storage_check[key]) != TYPE_STRING and typeof(storage_check[key]) != TYPE_INT and typeof(storage_check[key]) != TYPE_FLOAT and typeof(storage_check[key]) != TYPE_BOOL and typeof(storage_check[key]) != TYPE_ARRAY and typeof(storage_check[key]) != TYPE_DICTIONARY:
errors.append(core.stringify_variables("The value of %key% is not null, a String, an int, a float, boolean, an Array or a Dictionary (is of type %type%)", { "key": key, "type": type_string(typeof(key)) }))
logger.verb(core.stringify_variables("Completed sanity check with %errors% errors", { "errors": errors.size() }))
2024-03-18 03:23:27 +01:00
return errors