From 2041631462b7d2306e02dd2d95ff3e5159261244 Mon Sep 17 00:00:00 2001 From: minjaesong Date: Fri, 29 Sep 2023 16:26:35 +0900 Subject: [PATCH] savegame migrator wip --- .../modulebasegame/SavegameMigrator.kt | 111 ++++++++++++++++++ .../terrarum/modulebasegame/TerrarumIngame.kt | 26 ++-- 2 files changed, 126 insertions(+), 11 deletions(-) create mode 100644 src/net/torvald/terrarum/modulebasegame/SavegameMigrator.kt diff --git a/src/net/torvald/terrarum/modulebasegame/SavegameMigrator.kt b/src/net/torvald/terrarum/modulebasegame/SavegameMigrator.kt new file mode 100644 index 000000000..019a64a32 --- /dev/null +++ b/src/net/torvald/terrarum/modulebasegame/SavegameMigrator.kt @@ -0,0 +1,111 @@ +package net.torvald.terrarum.modulebasegame + +import net.torvald.terrarum.gameactors.Actor +import net.torvald.terrarum.modulebasegame.gameactors.IngamePlayer +import net.torvald.util.SortedArrayList +import kotlin.reflect.full.declaredFunctions +import kotlin.reflect.full.findAnnotation + +/** + * @param versionAnnotation If the version of the savegame meets the condition, the filter will be run + * - `all`: applies to all versions + * - `0.3.1`: applies to any past versions up to 0.3.1 + * - `0.3.1+`: applies to any future versions including 0.3.1 + * - `0.3.1!`: applies to verson 0.3.1 only + * - `0.3.1-0.3.4`: applies to version 0.3.1 to 0.3.4 + * - `0.3.1-0.3.4;0.4.3-0.4.5`: applies to (0.3.1 to 0.3.4) and (0.4.3-0.4.5) + * - `0.3.1-0.3.4;0.5.0+`: applies to (0.3.1 to 0.3.4) and (0.5.0 and onward) + * + * Following syntaxes are considered as undefined behaviours: + * - `0.3.1-0.3.4;0.5.0` + */ +annotation class AppliedVersion(val versionAnnotation: String) + +/** + * Created by minjaesong on 2023-09-29. + */ +internal object SavegameMigrator { + + private fun Char.isNotAsciiDigit() = this.code !in 0x30..0x39 + + private fun toVersionIndex(a_dot_b_dot_c: String): Long { + val abc = a_dot_b_dot_c.split('.').map { it.toLong() } + if (abc.size != 3) throw IllegalArgumentException("Parse error: $a_dot_b_dot_c") + + return abc[0].shl(48) or abc[1].shl(24) or abc[2] + } + + private fun annotationMatches(fileVersion: Long, annotation: AppliedVersion?): Boolean { + if (annotation == null) return false + if (annotation.versionAnnotation.equals("all")) return true + + var matched = false + + annotation.versionAnnotation.split(";").forEach { query -> + // is range? + if (query.contains('-')) { + val from0 = query.substringBefore('-') + val to0 = query.substringAfter('-') + if (from0.last().isNotAsciiDigit() || to0.last().isNotAsciiDigit()) + throw IllegalArgumentException("Illegal query '$query'") + + val from = toVersionIndex(from0) + val to = toVersionIndex(to0) + + if (from > to) throw IllegalArgumentException("Illegal query '$query'") + + matched = matched or (from <= fileVersion && fileVersion <= to) + } + else if (query.endsWith('+') || query.endsWith('!')) { + val operator = query.last() + val specVersion = toVersionIndex(query.substringBefore(operator)) + + matched = matched or when (operator) { + '+' -> (fileVersion >= specVersion) + '!' -> (fileVersion == specVersion) + else -> throw IllegalArgumentException("Unknown operator '$operator' for query '$query'") + } + } + else { + matched = matched or (fileVersion <= toVersionIndex(query)) + } + } + + return matched + } + + operator fun invoke(worldVersion: Long, playerVersion: Long, actors0: SortedArrayList) { + if (worldVersion == playerVersion) { + this::class.declaredFunctions.filter { + annotationMatches(worldVersion, it.findAnnotation()) + }.forEach { func -> + actors0.forEach { actor -> func.call(actor) } + } + } + else { + val nonPlayers = ArrayList() + val players = ArrayList() + for (actor in actors0) { + if (actor is IngamePlayer) players.add(actor) + else nonPlayers.add(actor) + } + + this::class.declaredFunctions.filter { + annotationMatches(worldVersion, it.findAnnotation()) + }.forEach { func -> + nonPlayers.forEach { actor -> func.call(actor) } + } + + this::class.declaredFunctions.filter { + annotationMatches(playerVersion, it.findAnnotation()) + }.forEach { func -> + players.forEach { player -> func.call(player) } + } + } + } + + @AppliedVersion("all") + fun mergeUnlitBlocks(actor: Actor) { + TODO() + } +} \ No newline at end of file diff --git a/src/net/torvald/terrarum/modulebasegame/TerrarumIngame.kt b/src/net/torvald/terrarum/modulebasegame/TerrarumIngame.kt index abd037123..c4a969e09 100644 --- a/src/net/torvald/terrarum/modulebasegame/TerrarumIngame.kt +++ b/src/net/torvald/terrarum/modulebasegame/TerrarumIngame.kt @@ -306,17 +306,19 @@ open class TerrarumIngame(batch: FlippingSpriteBatch) : IngameInstance(batch) { } data class Codices( - val disk: VirtualDisk, // WORLD disk - val world: GameWorld, -// val meta: WriteMeta.WorldMeta, -// val block: BlockCodex, -// val item: ItemCodex, -// val wire: WireCodex, -// val material: MaterialCodex, -// val faction: FactionCodex, -// val apocryphas: Map, - val actors: List, - val player: IngamePlayer + val disk: VirtualDisk, // WORLD disk + val world: GameWorld, +// val meta: WriteMeta.WorldMeta, +// val block: BlockCodex, +// val item: ItemCodex, +// val wire: WireCodex, +// val material: MaterialCodex, +// val faction: FactionCodex, +// val apocryphas: Map, + val actors: List, + val player: IngamePlayer, + val worldGenver: Long, + val playerGenver: Long ) @@ -403,6 +405,8 @@ open class TerrarumIngame(batch: FlippingSpriteBatch) : IngameInstance(batch) { actorNowPlaying = codices.player actorGamer = codices.player + SavegameMigrator.invoke(codices.worldGenver, codices.playerGenver, actorContainerActive) + printdbg(this, "postInitForLoadFromSave exit") }