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.
2024-04-08 21:19:24 +02:00
## 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
2024-04-15 00:07:25 +02:00
# The parsed data inside the storage file.
2024-03-18 03:23:27 +01:00
var storage : Dictionary = { }
2024-04-15 00:07:25 +02:00
# 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 +++
2024-04-15 00:07:25 +02:00
## 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 :
2024-04-25 20:20:34 +02:00
logger . error ( " Failed to open storage: A storage file is already open " )
2024-03-18 03:23:27 +01:00
return false
2024-04-25 20:20:34 +02:00
logger . verb ( core . stringify_variables ( " Opening storage file at %lo cation % " , { " 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 :
2024-04-25 20:20:34 +02:00
await logger . crash ( core . stringify_variables ( " Could not open storage file at %lo cation % : Failed with error %e rror % " , { " 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 :
2024-04-25 20:20:34 +02:00
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 :
2024-04-25 20:20:34 +02:00
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 :
2024-04-25 20:20:34 +02:00
logger . error ( " Sanity check failed (stopping): " )
2024-03-18 03:23:27 +01:00
for error in check_result :
2024-04-25 20:20:34 +02:00
logger . error ( " -> " + error )
2024-03-18 03:23:27 +01:00
return false
else :
2024-04-25 20:20:34 +02:00
logger . warn ( " Sanity check failed (continuing anyway): " )
2024-03-18 03:23:27 +01:00
for error in check_result :
2024-04-25 20:20:34 +02:00
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 :
2024-04-25 20:20:34 +02:00
logger . error ( " Failed to close storage: No storage file is open " )
2024-03-18 03:23:27 +01:00
return false
2024-04-25 20:20:34 +02:00
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 :
2024-04-25 20:20:34 +02:00
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 :
2024-04-25 20:20:34 +02:00
await logger . crash ( core . stringify_variables ( " Could not open storage file at %lo cation % : Failed with error %e rror % " , { " location " : storage_location , " error " : error_string ( FileAccess . get_open_error ( ) ) } ) )
2024-03-18 03:23:27 +01:00
return false
2024-04-25 20:20:34 +02:00
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 :
2024-04-25 20:20:34 +02:00
logger . error ( " Failed to nuke storage: No storage file is open " )
2024-03-18 03:23:27 +01:00
return false
2024-04-25 20:20:34 +02:00
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 :
2024-04-25 20:20:34 +02:00
logger . error ( " Failed to get key: No storage file is open " )
2024-03-18 03:23:27 +01:00
return NAN
2024-04-25 20:20:34 +02:00
logger . diag ( core . stringify_variables ( " Returning storage key % key % (default= %d efault % ) " , { " 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 :
2024-04-25 20:20:34 +02:00
logger . error ( " Failed to set key: No storage file is open " )
2024-03-18 03:23:27 +01:00
return false
2024-04-25 20:20:34 +02:00
logger . diag ( core . stringify_variables ( " Updating storage key % key % with value % value % (overwrite= %o verwrite % 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
2024-04-25 20:20:34 +02:00
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]
2024-04-08 21:19:24 +02:00
## 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 :
2024-04-25 20:20:34 +02:00
logger . error ( " Failed to get dictionary: No storage file is open " )
2024-03-18 03:23:27 +01:00
return { }
2024-04-25 20:20:34 +02:00
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 :
2024-04-25 20:20:34 +02:00
logger . error ( " Failed to save dictionary: No storage file is open " )
2024-03-18 03:23:27 +01:00
return false
2024-04-25 20:20:34 +02:00
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 :
2024-04-25 20:20:34 +02:00
logger . error ( " Sanity check failed (stopping): " )
2024-03-18 03:23:27 +01:00
for error in check_result :
2024-04-25 20:20:34 +02:00
logger . error ( " -> " + error )
2024-03-18 03:23:27 +01:00
return false
else :
2024-04-25 20:20:34 +02:00
logger . warn ( " Sanity check failed (continuing anyway): " )
2024-03-18 03:23:27 +01:00
for error in check_result :
2024-04-25 20:20:34 +02:00
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 ] :
2024-04-25 20:20:34 +02:00
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 :
2024-04-25 20:20:34 +02:00
errors . append ( core . stringify_variables ( " Key % key % i s 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 :
2024-04-25 20:20:34 +02:00
errors . append ( core . stringify_variables ( " The value of % key % i s 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 %e rrors % e rrors " , { " errors " : errors . size ( ) } ) )
2024-03-18 03:23:27 +01:00
return errors