213 lines
6.7 KiB
GDScript
213 lines
6.7 KiB
GDScript
extends Node
|
|
## Handles the loading and saving of settings data.
|
|
|
|
## Emitted when loading the settings data.
|
|
signal settings_retrieved
|
|
## Emitted when applying specific settings to in game objects.
|
|
signal applied_in_game_setting(section: String, setting: String, value)
|
|
|
|
## PATH to the settings save file.
|
|
var DATA_FOLDER: String = OS.get_user_data_dir()
|
|
## Name of the file that the settings data is saved in.
|
|
const FILE_NAME: String = "settings"
|
|
## Extension for the save file.
|
|
const FILE_EXTENSION: String = ".cfg"
|
|
## The PATH to the save file on the computer.
|
|
var PATH: String = DATA_FOLDER + "/" + FILE_NAME + FILE_EXTENSION
|
|
|
|
## Dictionary that stores all settings data.
|
|
var settingsData_: Dictionary
|
|
## A reference table of all sections.
|
|
var SECTION_REFERENCE_TABLE_: Dictionary
|
|
## A reference table of all element panels.
|
|
var ELEMENT_PANEL_REFERENCE_TABLE_: Dictionary
|
|
|
|
## The number of elements that have been changed since the settings have been saved.
|
|
var changedElementsCount: int = 0
|
|
|
|
## Flag for checking if a save file exists.
|
|
var noSaveFile: bool
|
|
## Flag for checking if an invalid value was found in the save file.
|
|
var invalidSaveFile: bool = false
|
|
|
|
|
|
func _ready() -> void:
|
|
# Verify the directory
|
|
DirAccess.make_dir_absolute(DATA_FOLDER)
|
|
|
|
# Check if a save file exists
|
|
if FileAccess.file_exists(PATH):
|
|
# Proceed normally by retrieving data from the save file
|
|
call_deferred("get_data")
|
|
else:
|
|
# Enable the no save file flag
|
|
noSaveFile = true
|
|
push_warning("No save file found")
|
|
|
|
|
|
## Called to save the settings data to the save file.
|
|
func save_data() -> void:
|
|
# Create a new config instance
|
|
var config := ConfigFile.new()
|
|
|
|
# Add the data from the settings data dictionary
|
|
for section in settingsData_:
|
|
for element in settingsData_[section]:
|
|
config.set_value(section, element, settingsData_[section][element])
|
|
|
|
# Save the data to the specified directory
|
|
var err = config.save(PATH)
|
|
|
|
# Check for errors
|
|
if err != OK:
|
|
push_error("Failed to save data_: ", err)
|
|
return
|
|
|
|
# Disable the no save file flag if it was enabled
|
|
if noSaveFile:
|
|
noSaveFile = false
|
|
|
|
# Disable the invalid save file flag if it was enabled
|
|
if invalidSaveFile:
|
|
invalidSaveFile = false
|
|
|
|
|
|
## Called to retrieve data from the save file.
|
|
func get_data() -> void:
|
|
# Create a new config instance
|
|
var config := ConfigFile.new()
|
|
# Load the save data
|
|
var err := config.load(PATH)
|
|
# Temporary data dictionary
|
|
var data_: Dictionary = {}
|
|
|
|
# Check for errors
|
|
if err != OK:
|
|
push_error("Failed to load settings data_: ", err)
|
|
return
|
|
|
|
# Add the retrieved data to the settings data dictionary
|
|
for section in config.get_sections():
|
|
data_[section] = {}
|
|
for key in config.get_section_keys(section):
|
|
data_[section][key] = config.get_value(section, key)
|
|
|
|
# Verify the validity of the loaded data
|
|
verify_settings_data_(data_)
|
|
|
|
# Copy the retrieved data into the settings data dictionary
|
|
settingsData_ = data_.duplicate(true)
|
|
|
|
|
|
## Checks if the save file has any invalid entries and removes them or adds missing sections.
|
|
func verify_settings_data_(data_: Dictionary) -> void:
|
|
# List of valid entries to compare to
|
|
var validEntries_: Dictionary = SECTION_REFERENCE_TABLE_.duplicate()
|
|
# Merge the element panel references into the valid entries
|
|
validEntries_.merge(ELEMENT_PANEL_REFERENCE_TABLE_)
|
|
# List of invalid entries to be removed
|
|
var invalidEntries_: Dictionary = {}
|
|
|
|
# Itterate through the loaded settings data
|
|
for section in data_:
|
|
if is_valid_section(invalidEntries_, section):
|
|
verify_elements(data_, invalidEntries_, section)
|
|
|
|
# Check if there are any sections missing from the retrieved data
|
|
check_for_missing_sections(data_, validEntries_)
|
|
|
|
# Check if there are any invalid entries
|
|
if invalidEntries_.size() > 0:
|
|
# Set the invalid save file flag to true
|
|
invalidSaveFile = true
|
|
remove_invalid_entries(data_, invalidEntries_, validEntries_)
|
|
|
|
|
|
## Used by the verify_settings_data() function to verify the retrieved sections.
|
|
func is_valid_section(invalidEntries_: Dictionary, SECTION: String) -> bool:
|
|
# Check if the section is in either of the reference tables
|
|
if (
|
|
SECTION_REFERENCE_TABLE_.has(SECTION)
|
|
or ELEMENT_PANEL_REFERENCE_TABLE_.has(SECTION)
|
|
):
|
|
return true
|
|
|
|
# Add the invalid section to the invalid entries list
|
|
invalidEntries_[SECTION] = []
|
|
push_warning("Invalid section '", SECTION, "' found.")
|
|
return false
|
|
|
|
|
|
## Used by the verify_settings_data() function to verify the elements inside of the retrieved sections.
|
|
func verify_elements(
|
|
data_: Dictionary,
|
|
invalidEntries_: Dictionary,
|
|
SECTION: String
|
|
) -> void:
|
|
# Array of all elements under the section
|
|
var VALID_SECTION_ELEMENTS: Array = get_valid_elements(SECTION)
|
|
|
|
# Itterate through all the elements in the section
|
|
for element in data_[SECTION]:
|
|
# Check for invalid elements
|
|
if not VALID_SECTION_ELEMENTS.has(element):
|
|
# Check if the element is in a valid section
|
|
if not invalidEntries_.has(SECTION):
|
|
# Add the section to the invalid entries list
|
|
invalidEntries_[SECTION] = []
|
|
|
|
# Add the invalid element to the invalid entries list
|
|
invalidEntries_[SECTION].append(element)
|
|
push_warning(
|
|
"Invalid element '"
|
|
+ element
|
|
+ "' found in section '"
|
|
+ SECTION
|
|
+ "'."
|
|
)
|
|
|
|
|
|
## Used by the verify_elements() function to retrieve the valid elements for the retrieved section.
|
|
func get_valid_elements(SECTION: String) -> Array:
|
|
# Check if the section is a settings section
|
|
if SECTION_REFERENCE_TABLE_.has(SECTION):
|
|
return SECTION_REFERENCE_TABLE_[SECTION].ELEMENT_REFERENCE_TABLE_.keys()
|
|
|
|
# Check if the section is an element panel
|
|
if ELEMENT_PANEL_REFERENCE_TABLE_.has(SECTION):
|
|
return ELEMENT_PANEL_REFERENCE_TABLE_[SECTION].ELEMENT_REFERENCE_TABLE_.keys()
|
|
|
|
return []
|
|
|
|
|
|
## Used by the verify_settings_data() function to check if any expected sections are missing.
|
|
func check_for_missing_sections(data_: Dictionary, validEntries_: Dictionary) -> void:
|
|
# Itterate through all valid sections
|
|
for section in validEntries_:
|
|
# Check if the section is missing from the loaded data_
|
|
if not data_.has(section):
|
|
# Add an empty entry for the section
|
|
data_[section] = {}
|
|
# Set the invalid save file flag to true
|
|
invalidSaveFile = true
|
|
push_warning("Settings section is missing: ", section)
|
|
|
|
|
|
## Used by the verify_settings_data() function to remove invalid entires from the retrieved data.
|
|
func remove_invalid_entries(
|
|
data_: Dictionary,
|
|
invalidEntries_: Dictionary,
|
|
validEntries_: Dictionary
|
|
) -> void:
|
|
# Itterate through the sections in the invalid entries list
|
|
for section in invalidEntries_:
|
|
# Check if the section is valid
|
|
if validEntries_.has(section):
|
|
# Itterate through the invalid elements
|
|
for element in invalidEntries_[section]:
|
|
# Remove the invalid element
|
|
data_[section].erase(element)
|
|
else:
|
|
# Remove the invalid section
|
|
data_.erase(section)
|