net.silkmc.silk.persistence.PersistentCompound.kt Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of silk-persistence Show documentation
Show all versions of silk-persistence Show documentation
Silk is a Minecraft API for Kotlin
package net.silkmc.silk.persistence
import net.minecraft.nbt.CompoundTag
/**
* Holds data which can be accessed fast because it is stored in memory. Additionally, the data
* will be stored persistently to the disk, if the game decides to do so.
*/
abstract class PersistentCompound {
@PublishedApi
internal abstract var data: CompoundTag?
@PublishedApi
internal val values = HashMap, Any>()
/**
* Puts the given value into the persistent storage.
*
* Values **have to** be serializable. Annotate them with
* [kotlinx.serialization.Serializable] to enable support for fast
* serialization.
*
* An exception to the above are values of the type [NbtElement].
* You can use these to skip serialization and deserialization. It is not
* as convenient to work with them, but they are faster.
*/
operator fun set(key: CompoundKey, value: T) {
if (data == null) return
values[key] = value
}
/**
* Tries to get the value for the given [key] and convert it
* to the given type [T].
*
* The access is fast, as it caches deserialized values. But keep in mind
* that if a value has been deserialized by the game itself it has to
* be deserialized by Silk once (lazily) before it can be cached into memory.
*
* Note: this function will only return null if the value is not present, it
* will still throw an exception if cast or conversion to the given type [T] failed
*/
inline operator fun get(key: CompoundKey): T? {
if (data == null) return null
return (values[key] as T?)
?: data!!.get(key.name)
?.let { key.convertNbtElementToValue(it) }
?.also { values[key] = it }
}
/**
* Removes the current value associated with the given [key].
*
* This function does not return the removed value, if you need that
* use [getAndRemove].
*/
fun remove(key: CompoundKey<*>) {
if (data == null) return
data!!.remove(key.name)
values -= key
}
/**
* Removes the current value associated with the given [key]. Additionally,
* this function returns the value that was removed, **if** one was removed.
* Otherwise, the return value will be null.
*
* Note: Calling this function may result in deserialization of the value which
* was deleted from the internal [NbtCompound], if the removed value was not
* loaded into memory.
*/
inline fun getAndRemove(key: CompoundKey): T? {
if (data == null) return null
return ((values.remove(key) as T?) ?: data!!.get(key.name)?.let { key.convertNbtElementToValue(it) })
.also { data!!.remove(key.name) }
}
/**
* Clears all persistent data from this compound.
*
* **Be aware that this deletes data of other mods as well!**
*/
fun clear() {
if (data == null) return
values.clear()
data = CompoundTag()
}
/**
* Removes the current value associated with the given [key].
*
* As this function is an operator function, it does not return the
* removed value.
*
* @see remove
*/
inline operator fun minusAssign(key: CompoundKey) {
remove(key)
}
/**
* Executes the [get] function. If the result of the [get] function is null
* the [defaultValue] will be evaluated and put into the compound.
*
* This function won't return null, it either returns the value in already present
* in the compound, or it will return the evaluated default value, after having
* stored it as well.
*
* @see get
*/
inline fun getOrPut(key: CompoundKey, defaultValue: () -> T): T {
if (data == null) return defaultValue()
return this[key] ?: defaultValue().also { this[key] = it }
}
// the following to functions are there for calling from java
@PublishedApi
internal fun loadFromCompound(nbtCompound: CompoundTag) = loadFromCompound(nbtCompound, false)
@PublishedApi
internal fun putInCompound(nbtCompound: CompoundTag) = putInCompound(nbtCompound, false)
internal abstract fun loadFromCompound(nbtCompound: CompoundTag, loadRaw: Boolean)
internal abstract fun putInCompound(nbtCompound: CompoundTag, writeRaw: Boolean)
}
/**
* A [PersistentCompound] which does nothing.
* Needed for empty holders such as [net.minecraft.world.chunk.EmptyChunk] for example.
*/
object EmptyPersistentCompound : PersistentCompound() {
override var data: CompoundTag? = null
override fun loadFromCompound(nbtCompound: CompoundTag, loadRaw: Boolean) = Unit
override fun putInCompound(nbtCompound: CompoundTag, writeRaw: Boolean) = Unit
}
/**
* The [PersistentCompound] implementation used by all normal
* [CompoundProvider]s.
*/
internal class PersistentCompoundImpl : PersistentCompound() {
companion object {
const val CUSTOM_DATA_KEY = "fabrikmcData"
}
override var data: CompoundTag? = CompoundTag()
override fun loadFromCompound(nbtCompound: CompoundTag, loadRaw: Boolean) {
data = if (loadRaw) nbtCompound else nbtCompound.getCompound(CUSTOM_DATA_KEY)
}
override fun putInCompound(nbtCompound: CompoundTag, writeRaw: Boolean) {
val currentData = data!!
for ((untypedKey, value) in values) {
@Suppress("UNCHECKED_CAST")
val typedKey = untypedKey as CompoundKey
currentData.put(typedKey.name, typedKey.convertValueToNbtElement(value))
}
if (!currentData.isEmpty) {
if (writeRaw)
nbtCompound.merge(currentData)
else
nbtCompound.put(CUSTOM_DATA_KEY, currentData)
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy