All Downloads are FREE. Search and download functionalities are using the official Maven repository.

net.silkmc.silk.persistence.PersistentCompound.kt Maven / Gradle / Ivy

There is a newer version: 1.10.7
Show newest version
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