Move savetables logic to utility class
This commit is contained in:
parent
e9b753667a
commit
0c06d6edb8
@ -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.math.RelativeSide
|
||||||
import ru.dbotthepony.mc.otm.core.nbt.set
|
import ru.dbotthepony.mc.otm.core.nbt.set
|
||||||
import ru.dbotthepony.mc.otm.core.util.EnumValueCodec
|
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.BlockEntitySyncPacket
|
||||||
import ru.dbotthepony.mc.otm.network.FieldSynchronizer
|
import ru.dbotthepony.mc.otm.network.FieldSynchronizer
|
||||||
import ru.dbotthepony.mc.otm.network.WorldNetworkChannel
|
import ru.dbotthepony.mc.otm.network.WorldNetworkChannel
|
||||||
@ -414,68 +415,10 @@ abstract class MatteryBlockEntity(p_155228_: BlockEntityType<*>, p_155229_: Bloc
|
|||||||
side.revive()
|
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)
|
protected val savetables = Savetables()
|
||||||
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)
|
|
||||||
|
|
||||||
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) {
|
protected inline fun <reified T : Tag, V : INBTSerializable<T?>> savetable(property: KProperty0<V>, name: String = property.name) {
|
||||||
savetable(T::class.java, property, name)
|
savetables.Stateful(property, name, T::class.java)
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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 })
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun saveAdditional(nbt: CompoundTag) {
|
override fun saveAdditional(nbt: CompoundTag) {
|
||||||
@ -491,13 +434,7 @@ abstract class MatteryBlockEntity(p_155228_: BlockEntityType<*>, p_155229_: Bloc
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (data in savetables0) {
|
savetables.serializeNBT(nbt)
|
||||||
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())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun load(nbt: CompoundTag) {
|
override fun load(nbt: CompoundTag) {
|
||||||
@ -513,25 +450,7 @@ abstract class MatteryBlockEntity(p_155228_: BlockEntityType<*>, p_155229_: Bloc
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (data in savetables0) {
|
savetables.deserializeNBT(nbt)
|
||||||
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}")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected fun tickOnce(func: Runnable) {
|
protected fun tickOnce(func: Runnable) {
|
||||||
|
@ -118,7 +118,7 @@ class MatterBottlerBlockEntity(p_155229_: BlockPos, p_155230_: BlockState) :
|
|||||||
exposeGlobally(MatteryCapability.MATTER_NODE, this)
|
exposeGlobally(MatteryCapability.MATTER_NODE, this)
|
||||||
exposeItemsGlobally(itemHandler)
|
exposeItemsGlobally(itemHandler)
|
||||||
|
|
||||||
savetableBoolean(::isBottling)
|
savetables.bool(::isBottling)
|
||||||
savetable(::energy, ENERGY_KEY)
|
savetable(::energy, ENERGY_KEY)
|
||||||
savetable(::matter, MATTER_STORAGE_KEY)
|
savetable(::matter, MATTER_STORAGE_KEY)
|
||||||
savetable(::container, INVENTORY_KEY)
|
savetable(::container, INVENTORY_KEY)
|
||||||
|
@ -48,7 +48,7 @@ class StoragePowerSupplierBlockEntity(blockPos: BlockPos, blockState: BlockState
|
|||||||
private set
|
private set
|
||||||
|
|
||||||
init {
|
init {
|
||||||
savetable(::powerPassed, POWER_PASSED_KEY, Decimal::serializeNBT, Decimal.Companion::deserializeNBT)
|
savetables.decimal(::powerPassed, POWER_PASSED_KEY)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun setLevel(p_155231_: Level) {
|
override fun setLevel(p_155231_: Level) {
|
||||||
|
@ -52,7 +52,7 @@ class PlatePressBlockEntity(
|
|||||||
init {
|
init {
|
||||||
savetable(::energy, ENERGY_KEY)
|
savetable(::energy, ENERGY_KEY)
|
||||||
savetable(::container, INVENTORY_KEY)
|
savetable(::container, INVENTORY_KEY)
|
||||||
savetableDouble(::experience)
|
savetables.double(::experience)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun createMenu(containerID: Int, inventory: Inventory, ply: Player): AbstractContainerMenu {
|
override fun createMenu(containerID: Int, inventory: Inventory, ply: Player): AbstractContainerMenu {
|
||||||
|
252
src/main/kotlin/ru/dbotthepony/mc/otm/core/util/Savetables.kt
Normal file
252
src/main/kotlin/ru/dbotthepony/mc/otm/core/util/Savetables.kt
Normal 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))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user