signalis-eb/godot/src/persistence.gd

99 lines
3.1 KiB
GDScript

class_name Persistence extends Node
@export var path = "user://saves/"
@export var group_name = "persist"
const SaveMethod = "on_save"
const OnBeforeLoadMethod = "on_before_load"
const LoadMethod = "on_load"
static func get_instance_data(node: Node):
return {
path = node.get_path(),
scene = node.scene_file_path,
data = node.call(SaveMethod)
}
func get_file(filename: String) -> Result:
if not filename.is_valid_filename():
return Result.err('invalid filename "%s"' % filename)
else:
var file_path = path.path_join(filename)
return Result.ok(file_path)
func save(filename: String) -> Result:
DirAccess.make_dir_recursive_absolute(path.get_base_dir())
var file_result = get_file(filename)
if file_result.is_err():
push_error(file_result.unwrap_err())
return file_result
var file_path = file_result.unwrap()
var file = FileAccess.open(file_path, FileAccess.WRITE)
var nodes = get_tree().get_nodes_in_group(group_name)
for node in nodes:
if node.scene_file_path.is_empty():
push_warning('"%s" is not instanced, skipping' % node.name)
continue
if not node.has_method("on_save"):
push_warning('"%s" does not have a "%s" method, skipping' % [node.name, SaveMethod])
continue
var data = get_instance_data(node)
var serial = BinarySerializer.serialize_var(data)
var bytes = var_to_bytes(serial)
var length = bytes.size()
file.store_32(length)
file.store_buffer(var_to_bytes(serial))
return Result.Unit
func _get_or_add(instance_data: Dictionary) -> Variant:
var node_path: NodePath = instance_data['path']
var scene_path = instance_data['scene']
var node = get_node_or_null(node_path)
if node.is_queued_for_deletion():
await node.tree_exited
node = null
if node == null:
node = load(scene_path).instantiate()
var parent = node_path.slice(0, node_path.get_name_count() - 1)
get_node(parent).call_deferred('add_child', node)
return node
func load(filename: String) -> Result:
var file_result = get_file(filename)
if file_result.is_err():
push_error(file_result.unwrap_err())
return file_result
var file_path = file_result.unwrap()
if not FileAccess.file_exists(file_path):
var err = 'ENOENT: "%s" does not exist' % path
push_error(err)
return Result.err(err)
get_tree().call_group(group_name, OnBeforeLoadMethod)
var file = FileAccess.open(file_path, FileAccess.READ)
var length = file.get_length()
while file.get_position() < length:
var byte_len = file.get_32()
var bytes = file.get_buffer(byte_len)
var result = bytes_to_var(bytes)
var save_data = BinarySerializer.deserialize_var(result)
var instance = await _get_or_add(save_data)
if not instance.has_method(LoadMethod):
push_warning('"%s" does not have a "%s" method' % [instance.name, LoadMethod])
instance.queue_free()
continue
instance.call(LoadMethod, save_data.data)
return Result.Unit