working savegame migrator

This commit is contained in:
minjaesong
2023-09-29 21:26:24 +09:00
parent 2041631462
commit 908a6ed953
5 changed files with 123 additions and 44 deletions

View File

@@ -194,4 +194,5 @@ class ItemCodex {
} }
fun hasItem(itemID: ItemID): Boolean = dynamicItemInventory.containsKey(itemID) fun hasItem(itemID: ItemID): Boolean = dynamicItemInventory.containsKey(itemID)
fun isEmpty(): Boolean = itemCodex.isEmpty()
} }

View File

@@ -1,7 +1,12 @@
package net.torvald.terrarum.modulebasegame package net.torvald.terrarum.modulebasegame
import net.torvald.terrarum.App.printdbg
import net.torvald.terrarum.ItemCodex
import net.torvald.terrarum.gameactors.Actor import net.torvald.terrarum.gameactors.Actor
import net.torvald.terrarum.gameitems.isDynamic
import net.torvald.terrarum.modulebasegame.gameactors.FixtureInventory
import net.torvald.terrarum.modulebasegame.gameactors.IngamePlayer import net.torvald.terrarum.modulebasegame.gameactors.IngamePlayer
import net.torvald.terrarum.modulebasegame.gameactors.Pocketed
import net.torvald.util.SortedArrayList import net.torvald.util.SortedArrayList
import kotlin.reflect.full.declaredFunctions import kotlin.reflect.full.declaredFunctions
import kotlin.reflect.full.findAnnotation import kotlin.reflect.full.findAnnotation
@@ -54,7 +59,7 @@ internal object SavegameMigrator {
if (from > to) throw IllegalArgumentException("Illegal query '$query'") if (from > to) throw IllegalArgumentException("Illegal query '$query'")
matched = matched or (from <= fileVersion && fileVersion <= to) matched = matched or (fileVersion in from..to)
} }
else if (query.endsWith('+') || query.endsWith('!')) { else if (query.endsWith('+') || query.endsWith('!')) {
val operator = query.last() val operator = query.last()
@@ -79,7 +84,8 @@ internal object SavegameMigrator {
this::class.declaredFunctions.filter { this::class.declaredFunctions.filter {
annotationMatches(worldVersion, it.findAnnotation()) annotationMatches(worldVersion, it.findAnnotation())
}.forEach { func -> }.forEach { func ->
actors0.forEach { actor -> func.call(actor) } printdbg(this, func.toString())
actors0.forEach { actor -> func.call(this, actor) }
} }
} }
else { else {
@@ -93,19 +99,59 @@ internal object SavegameMigrator {
this::class.declaredFunctions.filter { this::class.declaredFunctions.filter {
annotationMatches(worldVersion, it.findAnnotation()) annotationMatches(worldVersion, it.findAnnotation())
}.forEach { func -> }.forEach { func ->
nonPlayers.forEach { actor -> func.call(actor) } nonPlayers.forEach { actor -> func.call(this, actor) }
} }
this::class.declaredFunctions.filter { this::class.declaredFunctions.filter {
annotationMatches(playerVersion, it.findAnnotation()) annotationMatches(playerVersion, it.findAnnotation())
}.forEach { func -> }.forEach { func ->
players.forEach { player -> func.call(player) } players.forEach { player -> func.call(this, player) }
} }
} }
} }
/***********************************/
/* INSERT MIGRATION FUNCTIONS HERE */
/***********************************/
@AppliedVersion("all") @AppliedVersion("all")
fun mergeUnlitBlocks(actor: Actor) { fun mergeUnlitBlocks(actor: Actor) {
TODO() if (ItemCodex.isEmpty()) throw Error("ItemCodex is empty")
if (actor is Pocketed) {
val oldItemEquipped = actor.inventory.itemEquipped.copyOf()
val oldQuickSlot = actor.inventory.quickSlot.copyOf()
val oldItems = actor.inventory.clear()
oldItems.forEach { (itm, qty) ->
actor.inventory.add(itm, qty)
}
oldItemEquipped.forEachIndexed { index, id0 ->
if (id0?.isDynamic() == true)
actor.inventory.itemEquipped[index] = id0
else {
val id = FixtureInventory.filterItem(ItemCodex[id0])
actor.inventory.itemEquipped[index] = id?.dynamicID
}
}
oldQuickSlot.forEachIndexed { index, id0 ->
if (id0?.isDynamic() == true)
actor.inventory.quickSlot[index] = id0
else {
val id = FixtureInventory.filterItem(ItemCodex[id0])
actor.inventory.quickSlot[index] = id?.dynamicID
}
}
}
} }
} }

View File

@@ -122,10 +122,11 @@ class ActorInventory() : FixtureInventory() {
} }
} }
override fun clear() { override fun clear(): List<InventoryPair> {
super.clear() val r = super.clear()
itemEquipped.fill(null) itemEquipped.fill(null)
quickSlot.fill(null) quickSlot.fill(null)
return r
} }
} }

View File

@@ -29,6 +29,45 @@ open class FixtureInventory() {
val CAPACITY_MODE_NO_ENCUMBER = 0 val CAPACITY_MODE_NO_ENCUMBER = 0
val CAPACITY_MODE_COUNT = 1 val CAPACITY_MODE_COUNT = 1
val CAPACITY_MODE_WEIGHT = 2 val CAPACITY_MODE_WEIGHT = 2
private fun filterItem0(item: GameItem): GameItem {
return if (item.originalID.isBlock()) {
val drop = BlockCodex[item.originalID].drop.ifBlank { item.originalID }
ItemCodex[drop] ?: throw NullPointerException("Item: ${item.originalID}, drop: ${BlockCodex[item.originalID].drop}")
}
else if (item.originalID.isWall()) {
val blockID = item.originalID.substringAfter('@')
val drop = BlockCodex[blockID].drop.ifBlank { blockID }
ItemCodex["wall@$drop"] ?: throw NullPointerException("Item: ${item.originalID}, drop: ${BlockCodex[item.originalID].drop}")
}
else {
item
}
}
fun filterItem(item0: GameItem?): GameItem? {
if (item0 == null) return null
var item = item0
var recursionCount = 0
var breakNow = false
var item1: GameItem
while (!breakNow) {
if (recursionCount > 16) throw IllegalStateException("Item filter recursion is too deep, check the filtering code")
item1 = filterItem0(item!!)
if (item1 == item) {
breakNow = true
}
item = item1
recursionCount++
}
return item
}
} }
/** /**
@@ -40,19 +79,6 @@ open class FixtureInventory() {
fun isEmpty() = totalCount == 0L fun isEmpty() = totalCount == 0L
fun isNotEmpty() = totalCount > 0 fun isNotEmpty() = totalCount > 0
private fun filterItem(item: GameItem): GameItem {
return if (item.originalID.isBlock()) {
ItemCodex[BlockCodex[item.originalID].drop]!!
}
else if (item.originalID.isWall()) {
val blockID = item.originalID.substringAfter('@')
ItemCodex["wall@${BlockCodex[blockID].drop}"]!!
}
else {
item
}
}
open fun add(itemID: ItemID, count: Long = 1) { open fun add(itemID: ItemID, count: Long = 1) {
if (ItemCodex[itemID] == null) if (ItemCodex[itemID] == null)
throw NullPointerException("Item not found: $itemID") throw NullPointerException("Item not found: $itemID")
@@ -61,23 +87,7 @@ open class FixtureInventory() {
} }
open fun add(item0: GameItem, count: Long = 1L) { open fun add(item0: GameItem, count: Long = 1L) {
var item = item0 val item = filterItem(item0)!!
var recursionCount = 0
var breakNow = false
var item1: GameItem
while (!breakNow) {
if (recursionCount > 16) throw IllegalStateException("Item filter recursion is too deep, check the filtering code")
item1 = filterItem(item)
if (item1 == item) {
breakNow = true
}
item = item1
recursionCount++
}
// println("[ActorInventory] add-by-elem $item, $count") // println("[ActorInventory] add-by-elem $item, $count")
@@ -166,6 +176,12 @@ open class FixtureInventory() {
updateEncumbrance() updateEncumbrance()
} }
open fun clear(): List<InventoryPair> {
val r = itemList.cloneToList()
itemList.clear()
return r
}
/** /**
* HashMap<GameItem, Amounts> * HashMap<GameItem, Amounts>
@@ -285,10 +301,6 @@ open class FixtureInventory() {
return -(low + 1) // key not found return -(low + 1) // key not found
} }
open fun clear() {
itemList.clear()
}
fun updateEncumbrance() { fun updateEncumbrance() {
totalWeight0 = itemList.sumOf { ItemCodex[it.itm]!!.mass * it.qty } totalWeight0 = itemList.sumOf { ItemCodex[it.itm]!!.mass * it.qty }
totalCount0 = itemList.sumOf { it.qty } totalCount0 = itemList.sumOf { it.qty }

View File

@@ -9,12 +9,14 @@ import net.torvald.terrarum.gameworld.GameWorld
import net.torvald.terrarum.langpack.Lang import net.torvald.terrarum.langpack.Lang
import net.torvald.terrarum.modulebasegame.ChunkLoadingLoadScreen import net.torvald.terrarum.modulebasegame.ChunkLoadingLoadScreen
import net.torvald.terrarum.modulebasegame.IngameRenderer import net.torvald.terrarum.modulebasegame.IngameRenderer
import net.torvald.terrarum.modulebasegame.SavegameMigrator
import net.torvald.terrarum.modulebasegame.TerrarumIngame import net.torvald.terrarum.modulebasegame.TerrarumIngame
import net.torvald.terrarum.modulebasegame.gameactors.IngamePlayer import net.torvald.terrarum.modulebasegame.gameactors.IngamePlayer
import net.torvald.terrarum.realestate.LandUtil import net.torvald.terrarum.realestate.LandUtil
import net.torvald.terrarum.savegame.* import net.torvald.terrarum.savegame.*
import net.torvald.terrarum.savegame.VDFileID.SAVEGAMEINFO import net.torvald.terrarum.savegame.VDFileID.SAVEGAMEINFO
import net.torvald.terrarum.serialise.Common import net.torvald.terrarum.serialise.Common
import net.torvald.terrarum.utils.JsonFetcher
import net.torvald.terrarum.worlddrawer.WorldCamera import net.torvald.terrarum.worlddrawer.WorldCamera
import java.io.File import java.io.File
import java.io.Reader import java.io.Reader
@@ -120,6 +122,8 @@ object LoadSavegame {
operator fun invoke(diskPair: DiskPair) = invoke(diskPair.player, diskPair.world) operator fun invoke(diskPair: DiskPair) = invoke(diskPair.player, diskPair.world)
private val getGenver = Regex("""(?<="genver":)[0-9]+""")
/** /**
* @param playerDisk DiskSkimmer representing the Player. * @param playerDisk DiskSkimmer representing the Player.
* @param worldDisk0 DiskSkimmer representing the World to be loaded. * @param worldDisk0 DiskSkimmer representing the World to be loaded.
@@ -128,14 +132,18 @@ object LoadSavegame {
operator fun invoke(playerDisk: DiskSkimmer, worldDisk0: DiskSkimmer? = null) { operator fun invoke(playerDisk: DiskSkimmer, worldDisk0: DiskSkimmer? = null) {
val newIngame = TerrarumIngame(App.batch) val newIngame = TerrarumIngame(App.batch)
playerDisk.rebuild() playerDisk.rebuild()
val player = ReadActor.invoke(playerDisk, ByteArray64Reader(playerDisk.getFile(SAVEGAMEINFO)!!.bytes, Common.CHARSET)) as IngamePlayer
val playerDiskSavegameInfo = ByteArray64Reader(playerDisk.getFile(SAVEGAMEINFO)!!.bytes, Common.CHARSET)
val player = ReadActor.invoke(playerDisk, playerDiskSavegameInfo) as IngamePlayer
printdbg(this, "Player localhash: ${player.localHashStr}, hasSprite: ${player.sprite != null}") printdbg(this, "Player localhash: ${player.localHashStr}, hasSprite: ${player.sprite != null}")
val currentWorldId = player.worldCurrentlyPlaying val currentWorldId = player.worldCurrentlyPlaying
val worldDisk = worldDisk0 ?: App.savegameWorlds[currentWorldId]!!.loadable() val worldDisk = worldDisk0 ?: App.savegameWorlds[currentWorldId]!!.loadable()
worldDisk.rebuild() worldDisk.rebuild()
val world = ReadWorld(ByteArray64Reader(worldDisk.getFile(SAVEGAMEINFO)!!.bytes, Common.CHARSET), worldDisk.diskFile) val worldDiskSavegameInfo = ByteArray64Reader(worldDisk.getFile(SAVEGAMEINFO)!!.bytes, Common.CHARSET)
val world = ReadWorld(worldDiskSavegameInfo, worldDisk.diskFile)
world.layerTerrain = BlockLayer(world.width, world.height) world.layerTerrain = BlockLayer(world.width, world.height)
world.layerWall = BlockLayer(world.width, world.height) world.layerWall = BlockLayer(world.width, world.height)
@@ -150,13 +158,24 @@ object LoadSavegame {
// worldDisk.dispose() // worldDisk.dispose()
// playerDisk.dispose() // playerDisk.dispose()
val loadJob = { it: LoadScreenBase -> val loadJob = { it: LoadScreenBase ->
val loadscreen = it as ChunkLoadingLoadScreen val loadscreen = it as ChunkLoadingLoadScreen
loadscreen.addMessage(Lang["MENU_IO_LOADING"]) loadscreen.addMessage(Lang["MENU_IO_LOADING"])
val worldGenver = CharArray(128).let {
worldDiskSavegameInfo.read(it, 0, 128)
getGenver.find(String(it))?.value?.toLong()!!
}
val playerGenver = CharArray(128).let {
playerDiskSavegameInfo.read(it, 0, 128)
getGenver.find(String(it))?.value?.toLong()!!
}
val actors = world.actors.distinct() val actors = world.actors.distinct()
val worldParam = TerrarumIngame.Codices(newIngame.worldDisk, world, actors, player) val worldParam = TerrarumIngame.Codices(newIngame.worldDisk, world, actors, player, worldGenver, playerGenver)
newIngame.gameLoadInfoPayload = worldParam newIngame.gameLoadInfoPayload = worldParam