Revisit enumvaluecodec
This commit is contained in:
parent
b8837d4312
commit
3e4d6dcc11
@ -77,7 +77,7 @@ class SynchronizedRedstoneControl(
|
||||
val state = isBlockedByRedstone
|
||||
valueChanges.invoke(state, old)
|
||||
}
|
||||
}, name = if (fieldNamePrefix != null) "${fieldNamePrefix}_redstoneSetting" else null, writeByIndices = true)
|
||||
}, name = if (fieldNamePrefix != null) "${fieldNamePrefix}_redstoneSetting" else null)
|
||||
|
||||
override var redstoneSignal: Int by synchronizer.int(0, setter = { value, access, setByRemote ->
|
||||
if (access.read() == value) return@int
|
||||
|
@ -1,7 +1,14 @@
|
||||
package ru.dbotthepony.mc.otm.core.util
|
||||
|
||||
import com.google.common.collect.ImmutableList
|
||||
import com.mojang.datafixers.util.Pair
|
||||
import com.mojang.serialization.Codec
|
||||
import com.mojang.serialization.DataResult
|
||||
import com.mojang.serialization.DynamicOps
|
||||
import net.minecraft.nbt.NbtAccounter
|
||||
import net.minecraft.util.StringRepresentable
|
||||
import net.minecraft.world.item.ItemStack
|
||||
import ru.dbotthepony.mc.otm.core.immutableMap
|
||||
import ru.dbotthepony.mc.otm.core.math.readDecimal
|
||||
import ru.dbotthepony.mc.otm.core.math.writeDecimal
|
||||
import java.io.DataInput
|
||||
@ -14,6 +21,7 @@ import java.util.*
|
||||
import java.util.function.Predicate
|
||||
import kotlin.NoSuchElementException
|
||||
import kotlin.math.absoluteValue
|
||||
import kotlin.reflect.KClass
|
||||
|
||||
/**
|
||||
* Represents value which can be encoded onto or decoded from stream.
|
||||
@ -86,25 +94,20 @@ val VarIntValueCodec = StreamCodec(DataInputStream::readVarIntLE, DataOutputStre
|
||||
val VarLongValueCodec = StreamCodec(DataInputStream::readVarLongLE, DataOutputStream::writeVarLongLE)
|
||||
val BinaryStringCodec = StreamCodec(DataInputStream::readBinaryString, DataOutputStream::writeBinaryString)
|
||||
|
||||
class EnumValueCodec<V : Enum<V>>(clazz: Class<out V>, val writeByIndices: Boolean = false) : IStreamCodec<V> {
|
||||
class EnumValueCodec<V : Enum<V>> private constructor(clazz: Class<out V>) : IStreamCodec<V>, Codec<V> {
|
||||
val clazz = searchClass(clazz)
|
||||
private val values = searchClass(clazz).enumConstants!!
|
||||
val values: ImmutableList<V> = ImmutableList.copyOf(this.clazz.enumConstants!!)
|
||||
val valuesMap = immutableMap {
|
||||
for (v in values) put(v.name, v)
|
||||
}
|
||||
|
||||
override fun read(stream: DataInputStream, sizeLimit: NbtAccounter): V {
|
||||
if (writeByIndices) {
|
||||
val id = stream.readVarIntLE(sizeLimit)
|
||||
return values.getOrNull(id) ?: throw NoSuchElementException("No such enum with index $id")
|
||||
}
|
||||
|
||||
val id = stream.readBinaryString(sizeLimit)
|
||||
return values.firstOrNull { id == it.name } ?: throw NoSuchElementException("No such enum $id")
|
||||
}
|
||||
|
||||
override fun write(stream: DataOutputStream, value: V) {
|
||||
if (writeByIndices)
|
||||
stream.writeVarIntLE(value.ordinal)
|
||||
else
|
||||
stream.writeBinaryString(value.name)
|
||||
}
|
||||
|
||||
override fun copy(value: V): V {
|
||||
@ -115,7 +118,41 @@ class EnumValueCodec<V : Enum<V>>(clazz: Class<out V>, val writeByIndices: Boole
|
||||
return a === b
|
||||
}
|
||||
|
||||
override fun <T : Any> encode(input: V, ops: DynamicOps<T>, prefix: T): DataResult<T> {
|
||||
if (ops.compressMaps()) {
|
||||
return DataResult.success(ops.createInt(input.ordinal))
|
||||
}
|
||||
|
||||
return DataResult.success(ops.createString(input.name))
|
||||
}
|
||||
|
||||
override fun <T : Any> decode(ops: DynamicOps<T>, input: T): DataResult<Pair<V, T>> {
|
||||
if (ops.compressMaps()) {
|
||||
return ops.getNumberValue(input)
|
||||
.flatMap { values.getOrNull(it.toInt())?.let { DataResult.success(Pair(it, ops.empty())) } ?: DataResult.error("No such enum with ordinal index $it") }
|
||||
}
|
||||
|
||||
return ops.getStringValue(input)
|
||||
.flatMap { valuesMap[it]?.let { DataResult.success(Pair(it, ops.empty())) } ?: DataResult.error("No such enum value $it") }
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val codecs = WeakHashMap<Class<*>, EnumValueCodec<*>>()
|
||||
|
||||
fun <T : Enum<T>> of(clazz: Class<out T>): EnumValueCodec<T> {
|
||||
synchronized(codecs) {
|
||||
val search = searchClass(clazz)
|
||||
|
||||
if (search !== clazz) {
|
||||
val result = codecs.computeIfAbsent(search) { EnumValueCodec(it as Class<T>) } as EnumValueCodec<T>
|
||||
codecs.putIfAbsent(clazz, result)
|
||||
return result
|
||||
}
|
||||
|
||||
return codecs.computeIfAbsent(clazz) { EnumValueCodec(it as Class<T>) } as EnumValueCodec<T>
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* FIXME: enums with abstract methods which get compiled to subclasses, whose DO NOT expose "parent's" enum constants array
|
||||
*
|
||||
@ -137,6 +174,9 @@ class EnumValueCodec<V : Enum<V>>(clazz: Class<out V>, val writeByIndices: Boole
|
||||
}
|
||||
}
|
||||
|
||||
fun <E : Enum<E>> Class<E>.codec() = EnumValueCodec.of(this)
|
||||
fun <E : Enum<E>> KClass<E>.codec() = EnumValueCodec.of(this.java)
|
||||
|
||||
fun OutputStream.writeInt(value: Int) {
|
||||
if (this is DataOutput) {
|
||||
writeInt(value)
|
||||
|
@ -1,30 +0,0 @@
|
||||
package ru.dbotthepony.mc.otm.data
|
||||
|
||||
import com.google.common.collect.ImmutableMap
|
||||
import com.mojang.datafixers.util.Pair
|
||||
import com.mojang.serialization.Codec
|
||||
import com.mojang.serialization.DataResult
|
||||
import com.mojang.serialization.DynamicOps
|
||||
import kotlin.reflect.KClass
|
||||
|
||||
class EnumCodec<E : Enum<E>>(val type: Class<E>) : Codec<E> {
|
||||
private val values: Map<String, E> by lazy {
|
||||
val builder = ImmutableMap.Builder<String, E>()
|
||||
for (value in type.enumConstants) builder.put(value.name, value)
|
||||
builder.build()
|
||||
}
|
||||
|
||||
override fun <T : Any> encode(input: E, ops: DynamicOps<T>, prefix: T): DataResult<T> {
|
||||
require(prefix == ops.empty()) { "Non-empty prefix: $prefix" }
|
||||
return DataResult.success(ops.createString(input.name))
|
||||
}
|
||||
|
||||
override fun <T : Any> decode(ops: DynamicOps<T>, input: T): DataResult<Pair<E, T>> {
|
||||
return ops.getStringValue(input).flatMap {
|
||||
DataResult.success(values[it] ?: return@flatMap DataResult.error("No such enum $it (valid ones are: ${values.keys.joinToString(", ")})"))
|
||||
}.map { Pair.of(it, ops.empty()) }
|
||||
}
|
||||
}
|
||||
|
||||
fun <E : Enum<E>> Class<E>.codec() = EnumCodec(this)
|
||||
fun <E : Enum<E>> KClass<E>.codec() = EnumCodec(this.java)
|
@ -10,8 +10,8 @@ inline fun <reified E : Enum<E>> EnumInputWithFeedback(menu: MatteryMenu, state:
|
||||
inline fun <reified E : Enum<E>> EnumInputWithFeedback(menu: MatteryMenu, state: GetterSetter<E>) = EnumInputWithFeedback(menu, E::class.java, state)
|
||||
|
||||
class EnumInputWithFeedback<E : Enum<E>>(menu: MatteryMenu, clazz: Class<E>) : AbstractPlayerInputWithFeedback<E>() {
|
||||
val codec = EnumValueCodec(clazz)
|
||||
private val default = codec.clazz.enumConstants!![0]
|
||||
val codec = EnumValueCodec.of(clazz)
|
||||
private val default = codec.values.first()
|
||||
|
||||
override val input = menu.PlayerInput(codec, false) { consumer?.invoke(it) }
|
||||
override val value by menu.mSynchronizer.ComputedField(getter = { supplier?.invoke() ?: default }, codec)
|
||||
|
@ -15,6 +15,7 @@ import ru.dbotthepony.mc.otm.core.addSorted
|
||||
import ru.dbotthepony.mc.otm.core.util.BooleanValueCodec
|
||||
import ru.dbotthepony.mc.otm.core.util.EnumValueCodec
|
||||
import ru.dbotthepony.mc.otm.core.util.ItemSorter
|
||||
import ru.dbotthepony.mc.otm.core.util.codec
|
||||
import ru.dbotthepony.mc.otm.graph.matter.IMatterGraphListener
|
||||
import ru.dbotthepony.mc.otm.graph.matter.MatterNetworkGraph
|
||||
import ru.dbotthepony.mc.otm.menu.MatteryMenu
|
||||
@ -153,7 +154,7 @@ class MatterPanelMenu @JvmOverloads constructor(
|
||||
|
||||
val sorting: ItemSorter by mSynchronizer.ComputedField(
|
||||
getter = { tile?.getPlayerSettings(ply)?.sorter ?: ItemSorter.DEFAULT },
|
||||
codec = EnumValueCodec(ItemSorter::class.java),
|
||||
codec = ItemSorter::class.codec(),
|
||||
observer = {
|
||||
patterns.sortWith(actualComparator)
|
||||
tasks.sortWith(actualTaskComparator)
|
||||
@ -168,7 +169,7 @@ class MatterPanelMenu @JvmOverloads constructor(
|
||||
})
|
||||
|
||||
val changeIsAscending = booleanInput(allowSpectators = true) { tile?.getPlayerSettings(ply)?.ascending = it }
|
||||
val changeSorting = PlayerInput(EnumValueCodec(ItemSorter::class.java), allowSpectators = true) { tile?.getPlayerSettings(ply)?.sorter = it }
|
||||
val changeSorting = PlayerInput(ItemSorter::class.codec(), allowSpectators = true) { tile?.getPlayerSettings(ply)?.sorter = it }
|
||||
|
||||
val sortingGS = GetterSetter.of(::sorting, changeSorting::input)
|
||||
val isAscendingGS = GetterSetter.of(::isAscending, changeIsAscending::input)
|
||||
|
@ -202,14 +202,14 @@ class FieldSynchronizer(private val callback: Runnable, private val alwaysCallCa
|
||||
@JvmOverloads fun item(getter: Supplier<ItemStack>, name: String? = nextFieldName()) = ComputedField(getter::get, ItemStackValueCodec, name ?: nextFieldName())
|
||||
@JvmOverloads fun string(getter: Supplier<String>, name: String? = nextFieldName()) = ComputedField(getter::get, BinaryStringCodec, name ?: nextFieldName())
|
||||
|
||||
@JvmOverloads fun <T : Enum<T>> enum(type: Class<T>, getter: () -> T, name: String? = nextFieldName()) = ComputedField(getter, EnumValueCodec(type), name ?: nextFieldName())
|
||||
inline fun <reified T : Enum<T>> enum(noinline getter: () -> T, name: String? = nextFieldName()) = ComputedField(getter, EnumValueCodec(T::class.java), name ?: nextFieldName())
|
||||
@JvmOverloads fun <T : Enum<T>> enum(type: Class<T>, getter: () -> T, name: String? = nextFieldName()) = ComputedField(getter, EnumValueCodec.of(type), name ?: nextFieldName())
|
||||
inline fun <reified T : Enum<T>> enum(noinline getter: () -> T, name: String? = nextFieldName()) = ComputedField(getter, EnumValueCodec.of(T::class.java), name ?: nextFieldName())
|
||||
|
||||
@JvmOverloads fun <T : Enum<T>> enum(type: Class<T>, getter: KProperty0<T>, name: String? = nextFieldName()) = ComputedField(getter, EnumValueCodec(type), name ?: nextFieldName())
|
||||
inline fun <reified T : Enum<T>> enum(getter: KProperty0<T>, name: String? = nextFieldName()) = ComputedField(getter, EnumValueCodec(T::class.java), name ?: nextFieldName())
|
||||
@JvmOverloads fun <T : Enum<T>> enum(type: Class<T>, getter: KProperty0<T>, name: String? = nextFieldName()) = ComputedField(getter, EnumValueCodec.of(type), name ?: nextFieldName())
|
||||
inline fun <reified T : Enum<T>> enum(getter: KProperty0<T>, name: String? = nextFieldName()) = ComputedField(getter, EnumValueCodec.of(T::class.java), name ?: nextFieldName())
|
||||
|
||||
@JvmOverloads fun <T : Enum<T>> enum(type: Class<T>, getter: Supplier<T>, name: String? = nextFieldName()) = ComputedField(getter::get, EnumValueCodec(type), name ?: nextFieldName())
|
||||
inline fun <reified T : Enum<T>> enum(getter: Supplier<T>, name: String? = nextFieldName()) = ComputedField(getter::get, EnumValueCodec(T::class.java), name ?: nextFieldName())
|
||||
@JvmOverloads fun <T : Enum<T>> enum(type: Class<T>, getter: Supplier<T>, name: String? = nextFieldName()) = ComputedField(getter::get, EnumValueCodec.of(type), name ?: nextFieldName())
|
||||
inline fun <reified T : Enum<T>> enum(getter: Supplier<T>, name: String? = nextFieldName()) = ComputedField(getter::get, EnumValueCodec.of(T::class.java), name ?: nextFieldName())
|
||||
|
||||
@JvmOverloads
|
||||
fun byte(
|
||||
@ -347,10 +347,9 @@ class FieldSynchronizer(private val callback: Runnable, private val alwaysCallCa
|
||||
value: T = type.enumConstants[0],
|
||||
getter: FieldGetter<T>? = null,
|
||||
setter: FieldSetter<T>? = null,
|
||||
writeByIndices: Boolean = false,
|
||||
name: String? = nextFieldName(),
|
||||
): Field<T> {
|
||||
return Field(value, EnumValueCodec(type, writeByIndices = writeByIndices), getter, setter, name = name ?: nextFieldName())
|
||||
return Field(value, EnumValueCodec.of(type), getter, setter, name = name ?: nextFieldName())
|
||||
}
|
||||
|
||||
@JvmOverloads
|
||||
@ -358,10 +357,9 @@ class FieldSynchronizer(private val callback: Runnable, private val alwaysCallCa
|
||||
value: T,
|
||||
getter: FieldGetter<T>? = null,
|
||||
setter: FieldSetter<T>? = null,
|
||||
writeByIndices: Boolean = false,
|
||||
name: String? = nextFieldName(),
|
||||
): Field<T> {
|
||||
return Field(value, EnumValueCodec(value::class.java, writeByIndices = writeByIndices), getter, setter, name = name ?: nextFieldName())
|
||||
return Field(value, EnumValueCodec.of(value::class.java), getter, setter, name = name ?: nextFieldName())
|
||||
}
|
||||
|
||||
@JvmOverloads
|
||||
@ -584,6 +582,9 @@ class FieldSynchronizer(private val callback: Runnable, private val alwaysCallCa
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Networked variable with backing field holding immutable value
|
||||
*/
|
||||
inner class Field<V>(
|
||||
private var field: V,
|
||||
private val codec: IStreamCodec<V>,
|
||||
@ -684,6 +685,9 @@ class FieldSynchronizer(private val callback: Runnable, private val alwaysCallCa
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Networked value with backing getter which is constantly polled
|
||||
*/
|
||||
inner class ComputedField<V>(
|
||||
private val getter: () -> V,
|
||||
private val codec: IStreamCodec<V>,
|
||||
@ -733,6 +737,9 @@ class FieldSynchronizer(private val callback: Runnable, private val alwaysCallCa
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Networked variable with backing field holding mutable value, which is constantly observed for changes
|
||||
*/
|
||||
inner class ObservedField<V> : AbstractField<V>, IMutableField<V> {
|
||||
private val codec: IStreamCodec<V>
|
||||
private val getter: () -> V
|
||||
|
Loading…
Reference in New Issue
Block a user