99 lines
3.1 KiB
GDScript
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
|