feat: Support for Escoria and Game migrations (#473)
Co-authored-by: Dennis Ploeger <develop@dieploegers.de>
This commit is contained in:
@@ -1,6 +1,6 @@
|
|||||||
[gd_scene load_steps=2 format=2]
|
[gd_scene load_steps=2 format=2]
|
||||||
|
|
||||||
[ext_resource path="res://addons/escoria-core/game/core-scripts/esc/_test/test_esc_compiler.gd" type="Script" id=1]
|
[ext_resource path="res://addons/escoria-core/_test/test_esc_compiler.gd" type="Script" id=1]
|
||||||
|
|
||||||
[node name="Testsuite" type="Control"]
|
[node name="Testsuite" type="Control"]
|
||||||
anchor_right = 1.0
|
anchor_right = 1.0
|
||||||
20
addons/escoria-core/_test/test_migrations.gd
Normal file
20
addons/escoria-core/_test/test_migrations.gd
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
extends Control
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
func _on_CheckESCMigrationManager_pressed() -> bool:
|
||||||
|
var savegame: ESCSaveGame = ESCSaveGame.new()
|
||||||
|
|
||||||
|
savegame.globals["test"] = "testa"
|
||||||
|
|
||||||
|
var migration_manager: ESCMigrationManager = ESCMigrationManager.new()
|
||||||
|
savegame = migration_manager.migrate(
|
||||||
|
savegame,
|
||||||
|
"1.0.0",
|
||||||
|
"2.0.0",
|
||||||
|
"res://addons/escoria-core/_test/testversions"
|
||||||
|
)
|
||||||
|
|
||||||
|
assert(savegame.globals["test"] == "testc")
|
||||||
|
|
||||||
|
return true
|
||||||
25
addons/escoria-core/_test/test_migrations.tscn
Normal file
25
addons/escoria-core/_test/test_migrations.tscn
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
[gd_scene load_steps=2 format=2]
|
||||||
|
|
||||||
|
[ext_resource path="res://addons/escoria-core/_test/test_migrations.gd" type="Script" id=1]
|
||||||
|
|
||||||
|
[node name="Control" type="Control"]
|
||||||
|
anchor_right = 1.0
|
||||||
|
anchor_bottom = 1.0
|
||||||
|
script = ExtResource( 1 )
|
||||||
|
__meta__ = {
|
||||||
|
"_edit_use_anchors_": false
|
||||||
|
}
|
||||||
|
|
||||||
|
[node name="VBoxContainer" type="VBoxContainer" parent="."]
|
||||||
|
anchor_right = 1.0
|
||||||
|
anchor_bottom = 1.0
|
||||||
|
__meta__ = {
|
||||||
|
"_edit_use_anchors_": false
|
||||||
|
}
|
||||||
|
|
||||||
|
[node name="CheckESCMigrationManager" type="CheckButton" parent="VBoxContainer"]
|
||||||
|
margin_right = 1280.0
|
||||||
|
margin_bottom = 40.0
|
||||||
|
text = "Check ESCMigrationManager"
|
||||||
|
|
||||||
|
[connection signal="pressed" from="VBoxContainer/CheckESCMigrationManager" to="." method="_on_CheckESCMigrationManager_pressed"]
|
||||||
5
addons/escoria-core/_test/testversions/1.0.1.gd
Normal file
5
addons/escoria-core/_test/testversions/1.0.1.gd
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
extends ESCMigration
|
||||||
|
|
||||||
|
func migrate():
|
||||||
|
self._savegame.globals["test"] = "testb"
|
||||||
|
|
||||||
5
addons/escoria-core/_test/testversions/1.1.0.gd
Normal file
5
addons/escoria-core/_test/testversions/1.1.0.gd
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
extends ESCMigration
|
||||||
|
|
||||||
|
func migrate():
|
||||||
|
self._savegame.globals["test"] = "testc"
|
||||||
|
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
# Base class for all migration version scripts. Extending scripts should be
|
||||||
|
# named like the version they migrate the savegame to. (e.g. 1.0.0.gd, 1.0.1.gd)
|
||||||
|
extends Object
|
||||||
|
class_name ESCMigration
|
||||||
|
|
||||||
|
|
||||||
|
var _savegame: ESCSaveGame
|
||||||
|
|
||||||
|
|
||||||
|
# Set the savegame
|
||||||
|
#
|
||||||
|
# #### Parameters
|
||||||
|
# - savegame: Savegame to modify
|
||||||
|
func set_savegame(savegame: ESCSaveGame):
|
||||||
|
_savegame = savegame
|
||||||
|
|
||||||
|
|
||||||
|
# Get the savegame
|
||||||
|
# **Returns** Savegame
|
||||||
|
func get_savegame():
|
||||||
|
return _savegame
|
||||||
|
|
||||||
|
|
||||||
|
# Override this function in the version script with
|
||||||
|
# the things that need to be applied to the savegame
|
||||||
|
func migrate():
|
||||||
|
pass
|
||||||
@@ -0,0 +1,172 @@
|
|||||||
|
# Class that handles migrations between different game or escoria versions
|
||||||
|
extends Object
|
||||||
|
class_name ESCMigrationManager
|
||||||
|
|
||||||
|
|
||||||
|
# Regular expression that matches a simple semver version string
|
||||||
|
const VERSION_REGEX = "^(?<major>\\d+)\\.(?<minor>\\d+)\\.(?<patch>\\d+)$"
|
||||||
|
|
||||||
|
|
||||||
|
# Compiled regex
|
||||||
|
var version_regex: RegEx
|
||||||
|
|
||||||
|
|
||||||
|
func _init() -> void:
|
||||||
|
version_regex = RegEx.new()
|
||||||
|
version_regex.compile(VERSION_REGEX)
|
||||||
|
|
||||||
|
|
||||||
|
# Migrates the specified savegame from a specified version to another version
|
||||||
|
# based on a directory of migration scripts.
|
||||||
|
#
|
||||||
|
# The migration manager searches for scripts from after the given version up
|
||||||
|
# to the target version in this directory, loads them and applies the version.
|
||||||
|
#
|
||||||
|
# Each migration will return a modified version of the given savegame
|
||||||
|
func migrate(
|
||||||
|
savegame: ESCSaveGame,
|
||||||
|
from: String,
|
||||||
|
to: String,
|
||||||
|
versions_directory: String
|
||||||
|
) -> ESCSaveGame:
|
||||||
|
escoria.logger.info("Migrating from version %s to %s" % [
|
||||||
|
from,
|
||||||
|
to
|
||||||
|
])
|
||||||
|
|
||||||
|
var from_info = version_regex.search(from)
|
||||||
|
var to_info = version_regex.search(to)
|
||||||
|
|
||||||
|
var wrong_version: bool = false
|
||||||
|
if from_info.get_string("major") > to_info.get_string("major"):
|
||||||
|
wrong_version = true
|
||||||
|
elif from_info.get_string("major") == to_info.get_string("major") and\
|
||||||
|
from_info.get_string("minor") > to_info.get_string("minor"):
|
||||||
|
wrong_version = true
|
||||||
|
elif from_info.get_string("major") == to_info.get_string("major") and\
|
||||||
|
from_info.get_string("minor") == to_info.get_string("minor") and\
|
||||||
|
from_info.get_string("patch") > to_info.get_string("patch"):
|
||||||
|
wrong_version = true
|
||||||
|
|
||||||
|
if wrong_version:
|
||||||
|
escoria.logger.report_errors(
|
||||||
|
"esc_migration_manager:migrate",
|
||||||
|
[
|
||||||
|
"Can not migrate savegame from version %s to version %s" % [
|
||||||
|
from,
|
||||||
|
to
|
||||||
|
]
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
var versions = _find_versions(versions_directory, from, to)
|
||||||
|
versions.sort_custom(self, "_compare_version")
|
||||||
|
if versions[0].get_file().get_basename() == from:
|
||||||
|
versions.pop_front()
|
||||||
|
|
||||||
|
for version in versions:
|
||||||
|
var migration_script = load(version).new()
|
||||||
|
if not migration_script is ESCMigration:
|
||||||
|
escoria.logger.report_errors(
|
||||||
|
"esc_migration_manager:migrate",
|
||||||
|
[
|
||||||
|
"File %s is not a valid migration script" % version
|
||||||
|
]
|
||||||
|
)
|
||||||
|
escoria.logger.debug("Migrating using %s" % version)
|
||||||
|
(migration_script as ESCMigration).set_savegame(savegame)
|
||||||
|
(migration_script as ESCMigration).migrate()
|
||||||
|
savegame = (migration_script as ESCMigration).get_savegame()
|
||||||
|
|
||||||
|
return savegame
|
||||||
|
|
||||||
|
|
||||||
|
# Find all fitting version scripts between the given versions in a directory
|
||||||
|
# and all its subdirectories
|
||||||
|
#
|
||||||
|
# #### Parameters
|
||||||
|
# - directory: Directory to search in
|
||||||
|
# - from: Start version to check
|
||||||
|
# - to: End version to check
|
||||||
|
# **Returns** A list of version scripts
|
||||||
|
func _find_versions(directory: String, from: String, to: String) -> Array:
|
||||||
|
escoria.logger.trace("Searching directory %s" % directory)
|
||||||
|
var versions = []
|
||||||
|
var dir = Directory.new()
|
||||||
|
dir.open(directory)
|
||||||
|
dir.list_dir_begin(true, true)
|
||||||
|
var file_name = dir.get_next()
|
||||||
|
while file_name != "":
|
||||||
|
var version = file_name.get_basename()
|
||||||
|
var regex_result = version_regex.search(version)
|
||||||
|
if dir.current_is_dir():
|
||||||
|
versions += _find_versions(
|
||||||
|
directory.plus_file(file_name),
|
||||||
|
from,
|
||||||
|
to
|
||||||
|
)
|
||||||
|
elif regex_result and _version_between(version, from, to):
|
||||||
|
escoria.logger.trace("Found fitting migration script %s" % version)
|
||||||
|
versions.append(
|
||||||
|
directory.plus_file(file_name)
|
||||||
|
)
|
||||||
|
file_name = dir.get_next()
|
||||||
|
return versions
|
||||||
|
|
||||||
|
|
||||||
|
# Check, whether the given version is >= from and <= to
|
||||||
|
#
|
||||||
|
# #### Parameters
|
||||||
|
# - version: Version to check
|
||||||
|
# - from: Start version
|
||||||
|
# - to: End version
|
||||||
|
# **Returns** Whether the version matches
|
||||||
|
func _version_between(version: String, from: String, to: String) -> bool:
|
||||||
|
var version_info = version_regex.search(version)
|
||||||
|
var from_info = version_regex.search(from)
|
||||||
|
var to_info = version_regex.search(to)
|
||||||
|
|
||||||
|
if from_info.get_string("major") < version_info.get_string("major") and \
|
||||||
|
version_info.get_string("major") < to_info.get_string("major"):
|
||||||
|
return true
|
||||||
|
elif from_info.get_string("major") == version_info.get_string("major") and \
|
||||||
|
from_info.get_string("minor") < version_info.get_string("minor"):
|
||||||
|
return true
|
||||||
|
elif from_info.get_string("major") == version_info.get_string("major") and \
|
||||||
|
from_info.get_string("minor") == \
|
||||||
|
version_info.get_string("minor") and \
|
||||||
|
from_info.get_string("patch") < version_info.get_string("patch"):
|
||||||
|
return true
|
||||||
|
elif to_info.get_string("major") == version_info.get_string("major") and \
|
||||||
|
to_info.get_string("minor") > version_info.get_string("minor"):
|
||||||
|
return true
|
||||||
|
elif to_info.get_string("major") == version_info.get_string("major") and \
|
||||||
|
to_info.get_string("minor") == version_info.get_string("minor") and\
|
||||||
|
to_info.get_string("patch") > version_info.get_string("patch"):
|
||||||
|
return true
|
||||||
|
|
||||||
|
return false
|
||||||
|
|
||||||
|
|
||||||
|
# Compare to version strings
|
||||||
|
#
|
||||||
|
# #### Parameters
|
||||||
|
# - version_a: First version to compare
|
||||||
|
# - version_b: Second version to compare
|
||||||
|
# **Returns** true when version_b should be sorted after version_a
|
||||||
|
func _compare_version(version_a: String, version_b: String) -> bool:
|
||||||
|
var a_info = version_regex.search(version_a.get_file().get_basename())
|
||||||
|
var b_info = version_regex.search(version_b.get_file().get_basename())
|
||||||
|
|
||||||
|
if a_info.get_string("major") < b_info.get_string("major"):
|
||||||
|
return true
|
||||||
|
elif a_info.get_string("major") == b_info.get_string("major") and \
|
||||||
|
a_info.get_string("minor") < b_info.get_string("minor"):
|
||||||
|
return true
|
||||||
|
elif a_info.get_string("major") == b_info.get_string("major") and \
|
||||||
|
a_info.get_string("minor") == b_info.get_string("minor") and \
|
||||||
|
a_info.get_string("patch") < b_info.get_string("patch"):
|
||||||
|
return true
|
||||||
|
|
||||||
|
return false
|
||||||
|
|
||||||
@@ -174,7 +174,39 @@ func load_game(id: int):
|
|||||||
"esc_save_manager.gd:load_game()",
|
"esc_save_manager.gd:load_game()",
|
||||||
["Loading savegame %s" % str(id)])
|
["Loading savegame %s" % str(id)])
|
||||||
|
|
||||||
var save_game: Resource = ResourceLoader.load(save_file_path)
|
var save_game: ESCSaveGame = ResourceLoader.load(save_file_path)
|
||||||
|
|
||||||
|
var plugin_config = ConfigFile.new()
|
||||||
|
plugin_config.load("res://addons/escoria-core/plugin.cfg")
|
||||||
|
var escoria_version = plugin_config.get_value("plugin", "version")
|
||||||
|
|
||||||
|
# Migrate savegame through escoria versions
|
||||||
|
|
||||||
|
if escoria_version != save_game.escoria_version:
|
||||||
|
var migration_manager: ESCMigrationManager = ESCMigrationManager.new()
|
||||||
|
save_game = migration_manager.migrate(
|
||||||
|
save_game,
|
||||||
|
save_game.escoria_version,
|
||||||
|
escoria_version,
|
||||||
|
"res://addons/escoria-core/game/core-scripts/migrations/versions"
|
||||||
|
)
|
||||||
|
|
||||||
|
# Migrate savegame through game versions
|
||||||
|
|
||||||
|
if ProjectSettings.get_setting("escoria/main/game_version") != \
|
||||||
|
save_game.game_version and \
|
||||||
|
ProjectSettings.get_setting(
|
||||||
|
"escoria/main/game_migration_path"
|
||||||
|
) != "":
|
||||||
|
var migration_manager: ESCMigrationManager = ESCMigrationManager.new()
|
||||||
|
save_game = migration_manager.migrate(
|
||||||
|
save_game,
|
||||||
|
save_game.game_version,
|
||||||
|
ProjectSettings.get_setting("escoria/main/game_version"),
|
||||||
|
ProjectSettings.get_setting(
|
||||||
|
"escoria/main/game_migration_path"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
escoria.event_manager.interrupt_running_event()
|
escoria.event_manager.interrupt_running_event()
|
||||||
|
|
||||||
|
|||||||
@@ -178,6 +178,15 @@ func set_escoria_main_settings():
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
escoria.register_setting(
|
||||||
|
"escoria/main/game_migration_path",
|
||||||
|
"",
|
||||||
|
{
|
||||||
|
"type": TYPE_STRING,
|
||||||
|
"hint": PROPERTY_HINT_DIR
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
# Prepare the settings in the Escoria debug category
|
# Prepare the settings in the Escoria debug category
|
||||||
func set_escoria_debug_settings():
|
func set_escoria_debug_settings():
|
||||||
|
|||||||
@@ -264,6 +264,16 @@ _global_script_classes=[ {
|
|||||||
"language": "GDScript",
|
"language": "GDScript",
|
||||||
"path": "res://addons/escoria-core/game/core-scripts/log/esc_logger.gd"
|
"path": "res://addons/escoria-core/game/core-scripts/log/esc_logger.gd"
|
||||||
}, {
|
}, {
|
||||||
|
"base": "Object",
|
||||||
|
"class": "ESCMigration",
|
||||||
|
"language": "GDScript",
|
||||||
|
"path": "res://addons/escoria-core/game/core-scripts/migrations/esc_migration.gd"
|
||||||
|
}, {
|
||||||
|
"base": "Object",
|
||||||
|
"class": "ESCMigrationManager",
|
||||||
|
"language": "GDScript",
|
||||||
|
"path": "res://addons/escoria-core/game/core-scripts/migrations/esc_migration_manager.gd"
|
||||||
|
}, {
|
||||||
"base": "Node",
|
"base": "Node",
|
||||||
"class": "ESCMovable",
|
"class": "ESCMovable",
|
||||||
"language": "GDScript",
|
"language": "GDScript",
|
||||||
@@ -596,6 +606,8 @@ _global_script_class_icons={
|
|||||||
"ESCItem": "res://addons/escoria-core/design/esc_item.svg",
|
"ESCItem": "res://addons/escoria-core/design/esc_item.svg",
|
||||||
"ESCLocation": "res://addons/escoria-core/design/esc_location.svg",
|
"ESCLocation": "res://addons/escoria-core/design/esc_location.svg",
|
||||||
"ESCLogger": "",
|
"ESCLogger": "",
|
||||||
|
"ESCMigration": "",
|
||||||
|
"ESCMigrationManager": "",
|
||||||
"ESCMovable": "",
|
"ESCMovable": "",
|
||||||
"ESCMusicPlayer": "",
|
"ESCMusicPlayer": "",
|
||||||
"ESCObject": "",
|
"ESCObject": "",
|
||||||
@@ -702,7 +714,7 @@ sound/sfx_volume=1
|
|||||||
sound/speech_volume=1
|
sound/speech_volume=1
|
||||||
sound/master_volume=1
|
sound/master_volume=1
|
||||||
main/command_directories=[ "res://addons/escoria-core/game/core-scripts/esc/commands" ]
|
main/command_directories=[ "res://addons/escoria-core/game/core-scripts/esc/commands" ]
|
||||||
debug/log_level="DEBUG"
|
debug/log_level="TRACE"
|
||||||
platform/skip_cache=false
|
platform/skip_cache=false
|
||||||
platform/skip_cache.mobile=true
|
platform/skip_cache.mobile=true
|
||||||
ui/items_autoregister_path="res://game/items/inventory"
|
ui/items_autoregister_path="res://game/items/inventory"
|
||||||
@@ -732,6 +744,7 @@ debug/crash_message="We're sorry, but the game crashed. Please send us the follo
|
|||||||
%s"
|
%s"
|
||||||
ui/default_dialog_scene="res://addons/escoria-core/ui_library/dialogs/floating_dialog_player.tscn"
|
ui/default_dialog_scene="res://addons/escoria-core/ui_library/dialogs/floating_dialog_player.tscn"
|
||||||
esc/command_paths=[ "res://addons/escoria-core/game/core-scripts/esc/commands" ]
|
esc/command_paths=[ "res://addons/escoria-core/game/core-scripts/esc/commands" ]
|
||||||
|
main/game_migration_path=""
|
||||||
|
|
||||||
[input]
|
[input]
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user