# 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 . extends CoreBaseModule var is_open: bool = false var storage: Dictionary = {} var storage_location: String = "" func open_storage(location: String, create_new: bool = true, sanity_check: bool = true, fail_on_sanity_check: bool = false) -> bool: if is_open: logger.errorf("Storage", "Failed to open storage: A storage file is already open") return false logger.verbf("Storage", "Opening storage file at \"" + location + "\"") var file: FileAccess if !FileAccess.file_exists(location): if create_new: file = FileAccess.open(location, FileAccess.WRITE) if file == null: await logger.crashf("Storage", "Could not open storage file at \"" + location + "\": Failed with code " + str(FileAccess.get_open_error())) return false file.store_string("{}") file.close() else: logger.errorf("Storage", "Failed to open storage: create_new is set to false") 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.errorf("Storage", "Failed to open storage: Parsed storage file is of type " + str(typeof(storage_temp))) 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.errorf("Storage", "Sanity check failed (stopping):") for error in check_result: logger.errorf("Storage", "-> " + error) return false else: logger.warnf("Storage", "Sanity check failed (continuing anyway):") for error in check_result: logger.warnf("Storage", "-> " + error) storage = storage_temp storage_location = location is_open = true return true func close_storage() -> bool: if !is_open: logger.errorf("Storage", "Failed to close storage: No storage file is open") return false logger.verbf("Storage", "Closing storage file") storage = {} is_open = false return true func save_storage() -> bool: if !is_open: logger.errorf("Storage", "Failed to save storage: No storage file is open") return false var file: FileAccess = FileAccess.open(storage_location, FileAccess.WRITE) if file == null: await logger.crashf("Storage", "Could not open storage file at \"" + storage_location + "\": Failed with code " + str(FileAccess.get_open_error())) return false logger.diagf("Storage", "Writing storage file to disk") file.store_string(JSON.stringify(storage)) file.close() return true func nuke_storage(autosave: bool = true) -> bool: if !is_open: logger.errorf("Storage", "Failed to nuke storage: No storage file is open") return false logger.warnf("Storage", "Nuking storage") storage = {} if autosave: save_storage() return true func get_key(key: String, default: Variant) -> Variant: if !is_open: logger.errorf("Storage", "Failed to get key: No storage file is open") return NAN logger.diagf("Storage", "Returning storage key \"" + key + "\" (default='" + default + "')") return storage.get(key, default) func set_key(key: String, value: Variant, overwrite: bool = true, autosave: bool = true) -> bool: if !is_open: logger.errorf("Storage", "Failed to set key: No storage file is open") return false logger.diagf("Storage", "Updating storage key \"" + key + "\" with value '" + str(value) + "' (overwrite='" + str(overwrite) + "' autosave='" + str(autosave) + "'") storage.merge({key: value}, overwrite) if autosave: save_storage() return true func del_key(key: String, autosave: bool = true) -> bool: if !is_open: logger.errof("Storage", "Failed to delete key: No storage file is open") return false logger.diagf("Storage", "Deleting storage key \"" + key + "\" (autosave='" + str(autosave) + "')") storage.erase(key) if autosave: save_storage() return true func get_dict() -> Dictionary: if !is_open: logger.errorf("Storage", "Failed to get dictionary: No storage file is open") return {} logger.verbf("Storage", "Returning storage dictionary") return storage func save_dict(dict: Dictionary, sanity_check: bool = true, fail_on_sanity_check: bool = false, autosave: bool = true) -> bool: if !is_open: logger.errorf("Storage", "Failed to save dictionary: No storage file is open") return false logger.verbf("Storage", "Saving custom dictionary as storage") if sanity_check: var check_result: Array[String] = perform_sanity_check(dict) if check_result.size() != 0: if fail_on_sanity_check: logger.errorf("Storage", "Sanity check failed (stopping):") for error in check_result: logger.errorf("Storage", "-> " + error) return false else: logger.warnf("Storage", "Sanity check failed (continuing anyway):") for error in check_result: logger.warnf("Storage", "-> " + error) storage = dict if autosave: save_storage() return true func perform_sanity_check(storage_check: Dictionary) -> Array[String]: logger.verbf("Storage", "Performing a sanity check on some storage dictionary") var errors: Array[String] = [] for key in storage_check: if typeof(key) != TYPE_STRING: errors.append("Key \"" + str(key) + "\" is not of type String (type '" + type_string(typeof(key)) + "')") 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("The value of \"" + key + "\" is not null, a string, an integer, a float, boolean, array or dictionary (type '" + type_string(typeof(key)) + "')") logger.verbf("Storage", "Completed sanity check with " + str(errors.size()) + " errors") return errors