xiandie/manager/archive_manager/archive_manager.gd
2025-05-15 04:43:55 +08:00

263 lines
7.7 KiB
GDScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

@tool
extends Node
signal archive_loaded
static var archive: AssembledArchive:
set(val):
archive = val
if archive:
print("use archive ", archive.resource_path)
# current archive
static var user_root_dir := "user://data/" # must end with "/"
static var archive_dir := "user://data/archives/"
static var archive_prefix := "save"
var archives := {}
var autosave_timer := Timer.new()
func _ready() -> void:
# 禁用默认退出行为,在 _notification 处理 NOTIFICATION_WM_CLOSE_REQUEST 时保存数据
get_tree().set_auto_accept_quit(false)
process_mode = Node.PROCESS_MODE_ALWAYS
if not _check_dirs_and_archives():
_handle_load_error("存档目录", "读写")
return
autosave_timer.timeout.connect(_try_auto_save)
autosave_timer.stop()
add_child(autosave_timer)
# config should be loaded first
load_config()
# 在 debug or editor 模式下,直接保证有 archive
if GlobalConfig.DEBUG or Engine.is_editor_hint():
if archives.size() == 0:
create_and_use_new_archive()
else:
# debug 模式下默认使用 0 号存档
GlobalConfigManager.config.current_selected_archive_id = 0
func _notification(what):
# handle window close request
if what == NOTIFICATION_WM_CLOSE_REQUEST:
save_all()
if has_node("/root/Main"):
print("Saved all success before Quit")
# 已保存所有数据 [ID:ui_saved_all]
SceneManager.pop_notification(tr("ui_saved_all"))
var tree = get_tree()
tree.create_timer(1.5).timeout.connect(tree.quit)
else:
get_tree().quit()
func _on_archive_id_changed():
var selected_id = GlobalConfigManager.config.current_selected_archive_id
if selected_id < 0:
return
# if archive and selected_id == archive.archive_id:
# print("_on_archive_id_changed same id=", selected_id)
# return
print("_on_archive_id_changed id=", selected_id)
if not archives.has(selected_id):
print("新建存档 ", selected_id)
create_and_use_new_archive(selected_id)
# 已创建新存档 [ID:ui_new_archive]
SceneManager.pop_notification(tr("ui_new_archive"))
else:
load_archive()
func check_autosave_options():
if (
GlobalConfigManager.config.auto_save_enabled
and archive
and GlobalConfigManager.config.auto_save_seconds > 1
):
# reset left time
autosave_timer.stop()
autosave_timer.one_shot = false
autosave_timer.wait_time = GlobalConfigManager.config.auto_save_seconds
autosave_timer.start()
else:
autosave_timer.stop()
if GlobalConfig.DEBUG:
print(
"check_autosave_option: ",
GlobalConfigManager.config.auto_save_enabled,
" wait_time=",
autosave_timer.wait_time
)
func _try_auto_save():
if GlobalConfig.DEBUG:
print("Auto save")
if archive and GlobalConfigManager.config.auto_save_seconds > 1:
save_all()
# 自动保存成功 [ID:ui_auto_saved]
SceneManager.pop_notification(tr("ui_auto_saved"))
func _check_dirs_and_archives() -> bool:
if !DirAccess.dir_exists_absolute(user_root_dir):
DirAccess.make_dir_recursive_absolute(user_root_dir)
print("Create user_root_dir:", user_root_dir)
# Check if the archive directory is accessible
if !DirAccess.dir_exists_absolute(archive_dir):
DirAccess.make_dir_recursive_absolute(archive_dir)
print("Create archive_dir:", archive_dir)
var archive_dir_access = DirAccess.open(archive_dir)
if !archive_dir_access:
_handle_load_error("存档目录", "读取")
# TODO pop up a dialog to inform the user
return false
var files = archive_dir_access.get_files()
files.sort()
# get archive number
for file in files:
if file.begins_with(archive_prefix) and file.ends_with(GlobalConfig.RES_FILE_FORMAT):
var id_str = file.get_basename().substr(archive_prefix.length()).strip_escapes()
# 非三位数的 id 会被忽略
if id_str.length() != 3:
continue
var id = int(id_str)
# 读取范围是 0-99
if id < 0 or id > 99:
continue
var path = archive_dir + file
if not archives.has(id):
archives[id] = ResourceLoader.load(
path, "AssembledArchive", ResourceLoader.CACHE_MODE_REPLACE_DEEP
)
return true
# id = -1 means create a new archive, otherwise create an archive with the given id
func create_and_use_new_archive(id := -1) -> void:
_check_dirs_and_archives()
var archive_path = _get_archive_path(id)
if id < 0:
# 如果 id 小于 0找到一个新的 id创建新存档
id = 0
# find a new id
archive_path = _get_archive_path(id)
while FileAccess.file_exists(archive_path):
id += 1
archive_path = _get_archive_path(id)
_create_and_save_new_archive_resoure(id)
else:
# 如果 id 大于等于 0创建指定 id 的存档
if FileAccess.file_exists(archive_path):
_create_and_save_new_archive_resoure(id, true)
else:
_create_and_save_new_archive_resoure(id)
archives[id] = archive
# this will auto trigger signal and load the new archive
GlobalConfigManager.config.current_selected_archive_id = id
func _create_and_save_new_archive_resoure(id, take_over_path = false) -> void:
var archive_path = _get_archive_path(id)
archive = AssembledArchive.new() as Resource
if take_over_path:
archive.take_over_path(archive_path)
else:
archive.resource_path = archive_path
archive.archive_id = id
archive.created_time = Time.get_datetime_string_from_system(false, true)
ResourceSaver.save(archive, archive_path)
# 超过 999 个存档会出问题;不过这个游戏不会有这么多存档
func _get_archive_path(id: int) -> String:
var id_str := ""
if id < 10:
id_str = "00" + str(id)
elif id < 100:
id_str = "0" + str(id)
else:
id_str = str(id)
return archive_dir + archive_prefix + id_str + GlobalConfig.RES_FILE_FORMAT
func save_all() -> void:
# save config
var config = GlobalConfigManager.config
if config:
ResourceSaver.save(config)
# player_global_position
var player = SceneManager.get_player() as MainPlayer
# 在此处保存 player 的位置信息
if archive and player:
archive.player_global_position_x = player.global_position.x
archive.player_direction = player.facing_direction
if archive:
ResourceSaver.save(archive)
# reset autosave timer
check_autosave_options()
func load_config() -> void:
if GlobalConfigManager.config:
return
var path = user_root_dir + "config" + GlobalConfig.RES_FILE_FORMAT
if FileAccess.file_exists(path):
var config = ResourceLoader.load(path)
GlobalConfigManager.config = config
else:
var config = GlobalConfig.new()
GlobalConfigManager.config = config
ResourceSaver.save(config, path)
GlobalConfigManager.config.resource_path = path
if Engine.is_editor_hint():
return
# connect signals
GlobalConfigManager.config.current_selected_archive_id_changed.connect(_on_archive_id_changed)
GlobalConfigManager.config.auto_save_seconds_changed.connect(check_autosave_options)
GlobalConfigManager.config.auto_save_enabled_changed.connect(check_autosave_options)
func load_archive() -> void:
_check_dirs_and_archives()
var selected_id = GlobalConfigManager.config.current_selected_archive_id
# if archive and selected_id == archive.archive_id:
# return
print("load_archive ", selected_id)
if not archives.has(selected_id):
_handle_load_error(str(selected_id) + " 号存档", "查找")
return
archive = archives[selected_id]
# emit signal
archive_loaded.emit()
check_autosave_options()
func _handle_load_error(target, action) -> void:
var msg = str(target) + " " + str(action) + " 失败,请检查文件访问权限"
SceneManager.pop_notification(msg)
printerr(msg)
# TODO handle error
func set_global_entry(property: StringName, value) -> void:
if not archive:
printerr("Archive is null, cannot set global entry")
return
archive.global_data_dict[property] = value
func get_global_value(property: StringName, default = null) -> Variant:
if not archive:
printerr("Archive is null, cannot get global value")
return default
var val = archive.global_data_dict.get(property)
if val == null:
return default
return val