Move savetables logic to utility class

This commit is contained in:
DBotThePony 2023-02-18 00:18:09 +07:00
parent e9b753667a
commit 0c06d6edb8
Signed by: DBot
GPG Key ID: DCC23B5715498507
5 changed files with 260 additions and 89 deletions

View File

@ -48,6 +48,7 @@ import ru.dbotthepony.mc.otm.core.math.BlockRotation
import ru.dbotthepony.mc.otm.core.math.RelativeSide
import ru.dbotthepony.mc.otm.core.nbt.set
import ru.dbotthepony.mc.otm.core.util.EnumValueCodec
import ru.dbotthepony.mc.otm.core.util.Savetables
import ru.dbotthepony.mc.otm.network.BlockEntitySyncPacket
import ru.dbotthepony.mc.otm.network.FieldSynchronizer
import ru.dbotthepony.mc.otm.network.WorldNetworkChannel
@ -414,68 +415,10 @@ abstract class MatteryBlockEntity(p_155228_: BlockEntityType<*>, p_155229_: Bloc
side.revive()
}
private data class Savetable0<T : Tag, V : Any>(val type: Class<T>, val property: KProperty0<V>, val name: String, val serialize: (V) -> T, val deserialize: (V, T) -> Unit)
private data class Savetable1<T : Tag, V : Any>(val type: Class<T>, val property: KMutableProperty0<V>, val name: String, val serialize: (V) -> T, val deserialize: (T) -> V)
protected val savetables = Savetables()
private val savetables0 = ArrayList<Savetable0<*, *>>()
private val savetables1 = ArrayList<Savetable1<*, *>>()
/**
* Save and load object state
*/
protected fun <T : Tag, V : Any> savetable(type: Class<T>, property: KProperty0<V>, name: String = property.name, serialize: (V) -> T, deserialize: (V, T) -> Unit) {
check(!savetables0.any { it.name == name }) { "Already has save field with name $name" }
check(!savetables1.any { it.name == name }) { "Already has save field with name $name" }
savetables0.add(Savetable0(type, property, name, serialize, deserialize))
}
/**
* Save and load value
*/
protected fun <T : Tag, V : Any> savetable(type: Class<T>, property: KMutableProperty0<V>, name: String = property.name, serialize: (V) -> T, deserialize: (T) -> V) {
check(!savetables0.any { it.name == name }) { "Already has save field with name $name" }
check(!savetables1.any { it.name == name }) { "Already has save field with name $name" }
savetables1.add(Savetable1(type, property, name, serialize, deserialize))
}
/**
* Save and load object state
*/
protected fun <T : Tag, V : INBTSerializable<T?>> savetable(type: Class<T>, property: KProperty0<V>, name: String = property.name) {
savetable(type, property, name, { it.serializeNBT()!! }, { self, it -> self.deserializeNBT(it) })
}
/**
* Save and load object state
*/
protected inline fun <reified T : Tag, V : INBTSerializable<T?>> savetable(property: KProperty0<V>, name: String = property.name) {
savetable(T::class.java, property, name)
}
/**
* Save and load object state
*/
protected inline fun <reified T : Tag, V : Any> savetable(property: KProperty0<V>, name: String = property.name, noinline serialize: (V) -> T, noinline deserialize: (V, T) -> Unit) {
savetable(T::class.java, property, name, serialize, deserialize)
}
/**
* Save and load value
*/
protected inline fun <reified T : Tag, V : Any> savetable(property: KMutableProperty0<V>, name: String = property.name, noinline serialize: (V) -> T, noinline deserialize: (T) -> V) {
savetable(T::class.java, property, name, serialize, deserialize)
}
protected fun savetableFloat(property: KMutableProperty0<Float>, name: String = property.name) {
savetable(FloatTag::class.java, property, name, { FloatTag.valueOf(it) }, { tag -> tag.asFloat })
}
protected fun savetableDouble(property: KMutableProperty0<Double>, name: String = property.name) {
savetable(DoubleTag::class.java, property, name, { DoubleTag.valueOf(it) }, { tag -> tag.asDouble })
}
protected fun savetableBoolean(property: KMutableProperty0<Boolean>, name: String = property.name) {
savetable(ByteTag::class.java, property, name, { if (it) ByteTag.ONE else ByteTag.ZERO }, { tag -> tag.asInt > 0 })
savetables.Stateful(property, name, T::class.java)
}
override fun saveAdditional(nbt: CompoundTag) {
@ -491,13 +434,7 @@ abstract class MatteryBlockEntity(p_155228_: BlockEntityType<*>, p_155229_: Bloc
}
}
for (data in savetables0) {
nbt[data.name] = (data.serialize as (Any) -> Tag).invoke(data.property.get())
}
for (data in savetables1) {
nbt[data.name] = (data.serialize as (Any) -> Tag).invoke(data.property.get())
}
savetables.serializeNBT(nbt)
}
override fun load(nbt: CompoundTag) {
@ -513,25 +450,7 @@ abstract class MatteryBlockEntity(p_155228_: BlockEntityType<*>, p_155229_: Bloc
}
}
for (data in savetables0) {
val value = nbt[data.name]
if (value != null && data.type.isAssignableFrom(value.javaClass)) {
(data.deserialize as (Any, Tag) -> Unit).invoke(data.property.get(), value)
} else if (value != null) {
throw ClassCastException("Expected ${data.type.canonicalName}, got ${value.javaClass.canonicalName}")
}
}
for (data in savetables1) {
val value = nbt[data.name]
if (value != null && data.type.isAssignableFrom(value.javaClass)) {
(data.property as KMutableProperty0<Any>).set((data.deserialize as (Tag) -> Any).invoke(value))
} else if (value != null) {
throw ClassCastException("Expected ${data.type.canonicalName}, got ${value.javaClass.canonicalName}")
}
}
savetables.deserializeNBT(nbt)
}
protected fun tickOnce(func: Runnable) {

View File

@ -118,7 +118,7 @@ class MatterBottlerBlockEntity(p_155229_: BlockPos, p_155230_: BlockState) :
exposeGlobally(MatteryCapability.MATTER_NODE, this)
exposeItemsGlobally(itemHandler)
savetableBoolean(::isBottling)
savetables.bool(::isBottling)
savetable(::energy, ENERGY_KEY)
savetable(::matter, MATTER_STORAGE_KEY)
savetable(::container, INVENTORY_KEY)

View File

@ -48,7 +48,7 @@ class StoragePowerSupplierBlockEntity(blockPos: BlockPos, blockState: BlockState
private set
init {
savetable(::powerPassed, POWER_PASSED_KEY, Decimal::serializeNBT, Decimal.Companion::deserializeNBT)
savetables.decimal(::powerPassed, POWER_PASSED_KEY)
}
override fun setLevel(p_155231_: Level) {

View File

@ -52,7 +52,7 @@ class PlatePressBlockEntity(
init {
savetable(::energy, ENERGY_KEY)
savetable(::container, INVENTORY_KEY)
savetableDouble(::experience)
savetables.double(::experience)
}
override fun createMenu(containerID: Int, inventory: Inventory, ply: Player): AbstractContainerMenu {

View File

@ -0,0 +1,252 @@
package ru.dbotthepony.mc.otm.core.util
import net.minecraft.nbt.ByteTag
import net.minecraft.nbt.CompoundTag
import net.minecraft.nbt.DoubleTag
import net.minecraft.nbt.FloatTag
import net.minecraft.nbt.IntTag
import net.minecraft.nbt.NumericTag
import net.minecraft.nbt.Tag
import net.minecraftforge.common.util.INBTSerializable
import ru.dbotthepony.mc.otm.core.GetterSetter
import ru.dbotthepony.mc.otm.core.asGetterSetter
import ru.dbotthepony.mc.otm.core.math.Decimal
import ru.dbotthepony.mc.otm.core.nbt.set
import java.util.function.Supplier
import kotlin.reflect.KMutableProperty0
import kotlin.reflect.KProperty0
/**
* Utility class to manage list of composited properties in other classes to be (de)serialized in NBT tags
*/
class Savetables : INBTSerializable<CompoundTag> {
private val entries = ArrayList<Entry<*, *>>()
interface Entry<V : Any?, T : Tag?> : INBTSerializable<T?> {
val name: String
val type: Class<T>
fun validate()
}
inline fun <V : INBTSerializable<T>, reified T : Tag> stateful(getter: Supplier<V>, name: String): Stateful<V, T> {
return Stateful(getter, name, T::class.java)
.withSerializer { it.serializeNBT() }
.withDeserializer { v, t -> v.deserializeNBT(t) }
}
inline fun <V : INBTSerializable<T>, reified T : Tag> stateful(getter: KProperty0<V>, name: String = getter.name): Stateful<V, T> {
return Stateful(getter, name, T::class.java)
.withSerializer { it.serializeNBT() }
.withDeserializer { v, t -> v.deserializeNBT(t) }
}
fun decimal(prop: GetterSetter<Decimal>, name: String, default: Decimal = Decimal.ZERO): Stateless<Decimal, Tag> {
return Stateless(prop, name, Tag::class.java)
.withDeserializer { Decimal.deserializeNBT(it) }
.withSerializer { it.serializeNBT() }
.withDefault { default }
}
fun decimalNullable(prop: GetterSetter<Decimal?>, name: String, default: Decimal = Decimal.ZERO): Stateless<Decimal?, Tag> {
return Stateless(prop, name, Tag::class.java)
.withDeserializer { Decimal.deserializeNBT(it) }
.withSerializer { it?.serializeNBT() }
.withDefault { default }
}
fun decimal(prop: KMutableProperty0<Decimal>, name: String = prop.name, default: Decimal = Decimal.ZERO): Stateless<Decimal, Tag> {
return Stateless(prop, name, Tag::class.java)
.withDeserializer { Decimal.deserializeNBT(it) }
.withSerializer { it.serializeNBT() }
.withDefault { default }
}
fun decimalNullable(prop: KMutableProperty0<Decimal?>, name: String = prop.name, default: Decimal = Decimal.ZERO): Stateless<Decimal?, Tag> {
return Stateless(prop, name, Tag::class.java)
.withDeserializer { Decimal.deserializeNBT(it) }
.withSerializer { it?.serializeNBT() }
.withDefault { default }
}
fun float(prop: GetterSetter<Float>, name: String): Stateless<Float, NumericTag> {
return Stateless(prop, name, NumericTag::class.java)
.withSerializer { FloatTag.valueOf(it) }
.withDeserializer { it.asFloat }
}
fun float(prop: KMutableProperty0<Float>, name: String = prop.name): Stateless<Float, NumericTag> {
return Stateless(prop, name, NumericTag::class.java)
.withSerializer { FloatTag.valueOf(it) }
.withDeserializer { it.asFloat }
}
fun double(prop: GetterSetter<Double>, name: String): Stateless<Double, NumericTag> {
return Stateless(prop, name, NumericTag::class.java)
.withSerializer { DoubleTag.valueOf(it) }
.withDeserializer { it.asDouble }
}
fun double(prop: KMutableProperty0<Double>, name: String = prop.name): Stateless<Double, NumericTag> {
return Stateless(prop, name, NumericTag::class.java)
.withSerializer { DoubleTag.valueOf(it) }
.withDeserializer { it.asDouble }
}
fun int(prop: GetterSetter<Int>, name: String): Stateless<Int, NumericTag> {
return Stateless(prop, name, NumericTag::class.java)
.withSerializer { IntTag.valueOf(it) }
.withDeserializer { it.asInt }
}
fun int(prop: KMutableProperty0<Int>, name: String = prop.name): Stateless<Int, NumericTag> {
return Stateless(prop, name, NumericTag::class.java)
.withSerializer { IntTag.valueOf(it) }
.withDeserializer { it.asInt }
}
fun bool(prop: GetterSetter<Boolean>, name: String): Stateless<Boolean, NumericTag> {
return Stateless(prop, name, NumericTag::class.java)
.withSerializer { ByteTag.valueOf(it) }
.withDeserializer { it.asByte > 0 }
}
fun bool(prop: KMutableProperty0<Boolean>, name: String = prop.name): Stateless<Boolean, NumericTag> {
return Stateless(prop, name, NumericTag::class.java)
.withSerializer { ByteTag.valueOf(it) }
.withDeserializer { it.asByte > 0 }
}
override fun serializeNBT(): CompoundTag {
return CompoundTag().also(::serializeNBT)
}
private var validated = false
fun validate() {
if (validated) return
for (entry in entries)
entry.validate()
validated = true
}
override fun deserializeNBT(nbt: CompoundTag) {
validate()
for (entry in entries) {
val value = nbt[entry.name]
if (value != null && entry.type.isAssignableFrom(value.javaClass)) {
(entry as INBTSerializable<Tag>).deserializeNBT(value)
} else {
entry.deserializeNBT(null)
}
}
}
fun serializeNBT(nbt: CompoundTag) {
validate()
for (entry in entries) {
val value = entry.serializeNBT()
if (value != null)
nbt[entry.name] = value
}
}
inner class Stateful<V : Any?, T : Tag>(private val prop: Supplier<V>, override val name: String, override val type: Class<T>) : Entry<V, T> {
constructor(field: KProperty0<V>, name: String = field.name, type: Class<T>) : this(field::get, name, type)
init {
check(!entries.any { it.name == name }) { "Already has entry with name $name!" }
entries.add(this)
validated = false
}
private var serializer: ((V) -> T?)? = null
private var deserializer: ((V, T) -> Unit)? = null
private var resetter: ((V) -> Unit)? = null
fun withSerializer(serializer: (V) -> T?): Stateful<V, T> {
this.serializer = serializer
return this
}
fun withDeserializer(deserializer: (V, T) -> Unit): Stateful<V, T> {
this.deserializer = deserializer
return this
}
fun withResetter(resetter: (V) -> Unit): Stateful<V, T> {
this.resetter = resetter
return this
}
override fun serializeNBT(): T? {
return checkNotNull(serializer) { "No serializer specified for $name" }.invoke(prop.get())
}
override fun deserializeNBT(nbt: T?) {
if (nbt == null) {
resetter?.invoke(prop.get())
} else {
checkNotNull(deserializer) { "No deserializer specified for $name" }.invoke(prop.get(), nbt)
}
}
override fun validate() {
checkNotNull(serializer) { "No serializer specified for $name" }
checkNotNull(deserializer) { "No deserializer specified for $name" }
}
}
inner class Stateless<V : Any?, T : Tag>(private val prop: GetterSetter<V>, override val name: String, override val type: Class<T>) : Entry<V, T> {
constructor(field: KMutableProperty0<V>, name: String = field.name, type: Class<T>) : this(field.asGetterSetter(), name, type)
init {
check(!entries.any { it.name == name }) { "Already has entry with name $name!" }
entries.add(this)
validated = false
}
private var serializer: ((V) -> T?)? = null
private var deserializer: ((T) -> V)? = null
private var default: (() -> V)? = null
override fun validate() {
checkNotNull(serializer) { "No serializer specified for $name" }
checkNotNull(deserializer) { "No deserializer specified for $name" }
}
fun withSerializer(serializer: (V) -> T?): Stateless<V, T> {
this.serializer = serializer
return this
}
fun withDeserializer(deserializer: (T) -> V): Stateless<V, T> {
this.deserializer = deserializer
return this
}
fun withDefault(default: () -> V): Stateless<V, T> {
this.default = default
return this
}
override fun serializeNBT(): T? {
return checkNotNull(serializer) { "No serializer specified for $name" }.invoke(prop.get())
}
override fun deserializeNBT(nbt: T?) {
if (nbt == null) {
if (default != null) {
prop.accept(default!!.invoke())
}
} else {
prop.accept(checkNotNull(deserializer) { "No deserializer specified for $name" }.invoke(nbt))
}
}
}
}