integration of blob and actorvalue

This commit is contained in:
minjaesong
2025-02-09 14:58:06 +09:00
parent fd0732cd39
commit 497782b428
2 changed files with 118 additions and 34 deletions

View File

@@ -27,6 +27,7 @@ open class KVHashMap {
var hashMap: HashMap<String, Any>
/**
* Add key-value pair to the configuration table.
* If key does not exist on the table, new key will be generated.
@@ -37,18 +38,7 @@ open class KVHashMap {
* @param value
*/
open operator fun set(key: String, value: Any) {
hashMap.put(key.toLowerCase(), value)
}
fun setBlob(key: String, value: ByteArray) {
val filename0 = hashMap.getOrPut(key.toLowerCase()) { "blob://${UUID.randomUUID()}" } as String
if (filename0.startsWith("blob://")) {
Terrarum.getSharedSaveFiledesc(filename0.removePrefix("blob://")).let {
if (!it.exists()) it.createNewFile()
it.writeBytes(value) // will overwrite whatever's there
}
}
else throw TypeCastException("ActorValue is not blob")
hashMap[key.toLowerCase()] = value
}
/**
@@ -89,19 +79,6 @@ open class KVHashMap {
return value as Boolean
}
fun getAsBlob(key: String): ByteArray? {
val uri = getAsString(key) ?: return null
if (uri.startsWith("blob://")) {
val filename = uri.removePrefix("blob://")
val file = Terrarum.getSharedSaveFiledesc(filename)
if (file.exists())
return file.readBytes()
else
throw IOException("Blob not found")
}
else throw TypeCastException("ActorValue is not blob")
}
fun hasKey(key: String) = hashMap.containsKey(key)
val keySet: Set<Any>
@@ -111,13 +88,6 @@ open class KVHashMap {
if (hashMap[key] != null) {
val value = hashMap[key]!!
hashMap.remove(key, value)
(value as? String)?.let {
if (it.startsWith("blob://")) {
val filename = it.removePrefix("blob://")
Terrarum.getSharedSaveFiledesc(filename).delete()
}
}
}
}

View File

@@ -1,6 +1,14 @@
package net.torvald.terrarum.gameactors
import net.torvald.terrarum.App
import net.torvald.terrarum.INGAME
import net.torvald.terrarum.KVHashMap
import net.torvald.terrarum.Terrarum
import net.torvald.terrarum.modulebasegame.gameactors.IngamePlayer
import net.torvald.terrarum.savegame.*
import java.io.IOException
import java.util.*
import kotlin.collections.HashMap
/**
* For the dictionary of recognised ActorValues, see the source code of [net.torvald.terrarum.gameactors.AVKey]
@@ -12,6 +20,8 @@ class ActorValue : KVHashMap {
@Transient lateinit var actor: Actor
internal set
@Transient private val BLOB = "blob://" // typical filename will be like "blob://-2147483648"
constructor()
constructor(actor: Actor) : this() {
@@ -24,18 +34,122 @@ class ActorValue : KVHashMap {
}
override fun set(key: String, value: Any) {
super.set(key, value)
// check if the key exists and is a blob
if (getAsString(key.toLowerCase())?.startsWith(BLOB) == true) {
throw IllegalStateException("Cannot write plain values to the blob object")
}
else
super.set(key, value)
actor.onActorValueChange(key, value) // fire the event handler
}
fun setBlob(key: String, ref: Long, value: ByteArray64) {
checkActorBlobRef(ref)
// if setBlob is invoked on non-existing key, adds random empty blob to the key
// filename0 will be whatever value stored on the actorvalue, cast to String
val preexisted = hashMap.containsKey(key.toLowerCase())
val filename0 = hashMap.getOrPut(key.toLowerCase()) { "$BLOB$ref" } as String
if (filename0.startsWith(BLOB)) {
writeBlobBin(ref, value)
}
else {
// rollback key addition, if applicable
if (preexisted) hashMap.remove(key.toLowerCase())
throw TypeCastException("ActorValue is not blob")
}
}
fun getAsBlob(key: String): ByteArray64? {
val uri = getAsString(key) ?: return null
if (uri.startsWith(BLOB)) {
return readBlobBin(uri.removePrefix(BLOB).toLong())
}
else throw TypeCastException("ActorValue is not blob")
}
override fun remove(key: String) {
if (hashMap[key] != null) {
hashMap.remove(key, hashMap[key]!!)
val value = hashMap[key]!!
hashMap.remove(key, value)
// remove blob
(value as? String)?.let {
if (it.startsWith(BLOB)) {
deleteBlobBin(it.removePrefix(BLOB).toLong())
}
}
actor.onActorValueChange(key, null)
}
}
fun clone(newActor: Actor): ActorValue {
return ActorValue(newActor, hashMap)
// TODO clone blobs
}
private fun readBlobBin(ref: Long): ByteArray64? {
checkActorBlobRef(ref)
if (INGAME.actorNowPlaying == actor) {
val disk = INGAME.playerDisk
return disk.getFile(ref)?.bytes
}
else if (actor is IngamePlayer) {
val diskSkimmer = DiskSkimmer(Terrarum.getPlayerSaveFiledesc((actor as IngamePlayer).uuid.toString()))
return diskSkimmer.getFile(ref)?.bytes
}
else throw IllegalStateException("Actor is not a player")
}
private fun writeBlobBin(ref: Long, contents: ByteArray64) {
checkActorBlobRef(ref)
val time = App.getTIME_T()
val file = EntryFile(contents)
if (INGAME.actorNowPlaying == actor) {
val disk = INGAME.playerDisk
disk.getFile(ref).let {
// create new file if one not exists, then pass it
if (it == null) {
VDUtil.addFile(disk, DiskEntry(ref, 0, time, time, file))
file
}
else it
}.bytes = contents
disk.getEntry(ref)!!.modificationDate = time
}
else if (actor is IngamePlayer) {
val diskSkimmer = DiskSkimmer(Terrarum.getPlayerSaveFiledesc((actor as IngamePlayer).uuid.toString()))
val oldCreationDate = diskSkimmer.getEntry(ref)?.creationDate
diskSkimmer.appendEntry(DiskEntry(ref, 0, oldCreationDate ?: time, time, file))
}
else throw IllegalStateException("Actor is not a player")
}
private fun deleteBlobBin(ref: Long) {
checkActorBlobRef(ref)
if (INGAME.actorNowPlaying == actor) {
val disk = INGAME.playerDisk
VDUtil.deleteFile(disk, ref)
}
else if (actor is IngamePlayer) {
val diskSkimmer = DiskSkimmer(Terrarum.getPlayerSaveFiledesc((actor as IngamePlayer).uuid.toString()))
diskSkimmer.deleteEntry(ref)
}
else throw IllegalStateException("Actor is not a player")
}
private fun checkActorBlobRef(ref: Long) {
if (ref > -2147483648L) throw IllegalArgumentException("File Ref $ref is not a valid ActorValue blob")
}
}