Revisit enumvaluecodec
This commit is contained in:
parent
b8837d4312
commit
3e4d6dcc11
@ -77,7 +77,7 @@ class SynchronizedRedstoneControl(
|
|||||||
val state = isBlockedByRedstone
|
val state = isBlockedByRedstone
|
||||||
valueChanges.invoke(state, old)
|
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 ->
|
override var redstoneSignal: Int by synchronizer.int(0, setter = { value, access, setByRemote ->
|
||||||
if (access.read() == value) return@int
|
if (access.read() == value) return@int
|
||||||
|
@ -1,7 +1,14 @@
|
|||||||
package ru.dbotthepony.mc.otm.core.util
|
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.nbt.NbtAccounter
|
||||||
|
import net.minecraft.util.StringRepresentable
|
||||||
import net.minecraft.world.item.ItemStack
|
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.readDecimal
|
||||||
import ru.dbotthepony.mc.otm.core.math.writeDecimal
|
import ru.dbotthepony.mc.otm.core.math.writeDecimal
|
||||||
import java.io.DataInput
|
import java.io.DataInput
|
||||||
@ -14,6 +21,7 @@ import java.util.*
|
|||||||
import java.util.function.Predicate
|
import java.util.function.Predicate
|
||||||
import kotlin.NoSuchElementException
|
import kotlin.NoSuchElementException
|
||||||
import kotlin.math.absoluteValue
|
import kotlin.math.absoluteValue
|
||||||
|
import kotlin.reflect.KClass
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents value which can be encoded onto or decoded from stream.
|
* 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 VarLongValueCodec = StreamCodec(DataInputStream::readVarLongLE, DataOutputStream::writeVarLongLE)
|
||||||
val BinaryStringCodec = StreamCodec(DataInputStream::readBinaryString, DataOutputStream::writeBinaryString)
|
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)
|
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 {
|
override fun read(stream: DataInputStream, sizeLimit: NbtAccounter): V {
|
||||||
if (writeByIndices) {
|
|
||||||
val id = stream.readVarIntLE(sizeLimit)
|
val id = stream.readVarIntLE(sizeLimit)
|
||||||
return values.getOrNull(id) ?: throw NoSuchElementException("No such enum with index $id")
|
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) {
|
override fun write(stream: DataOutputStream, value: V) {
|
||||||
if (writeByIndices)
|
|
||||||
stream.writeVarIntLE(value.ordinal)
|
stream.writeVarIntLE(value.ordinal)
|
||||||
else
|
|
||||||
stream.writeBinaryString(value.name)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun copy(value: V): V {
|
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
|
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 {
|
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
|
* 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) {
|
fun OutputStream.writeInt(value: Int) {
|
||||||
if (this is DataOutput) {
|
if (this is DataOutput) {
|
||||||
writeInt(value)
|
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)
|
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>() {
|
class EnumInputWithFeedback<E : Enum<E>>(menu: MatteryMenu, clazz: Class<E>) : AbstractPlayerInputWithFeedback<E>() {
|
||||||
val codec = EnumValueCodec(clazz)
|
val codec = EnumValueCodec.of(clazz)
|
||||||
private val default = codec.clazz.enumConstants!![0]
|
private val default = codec.values.first()
|
||||||
|
|
||||||
override val input = menu.PlayerInput(codec, false) { consumer?.invoke(it) }
|
override val input = menu.PlayerInput(codec, false) { consumer?.invoke(it) }
|
||||||
override val value by menu.mSynchronizer.ComputedField(getter = { supplier?.invoke() ?: default }, codec)
|
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.BooleanValueCodec
|
||||||
import ru.dbotthepony.mc.otm.core.util.EnumValueCodec
|
import ru.dbotthepony.mc.otm.core.util.EnumValueCodec
|
||||||
import ru.dbotthepony.mc.otm.core.util.ItemSorter
|
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.IMatterGraphListener
|
||||||
import ru.dbotthepony.mc.otm.graph.matter.MatterNetworkGraph
|
import ru.dbotthepony.mc.otm.graph.matter.MatterNetworkGraph
|
||||||
import ru.dbotthepony.mc.otm.menu.MatteryMenu
|
import ru.dbotthepony.mc.otm.menu.MatteryMenu
|
||||||
@ -153,7 +154,7 @@ class MatterPanelMenu @JvmOverloads constructor(
|
|||||||
|
|
||||||
val sorting: ItemSorter by mSynchronizer.ComputedField(
|
val sorting: ItemSorter by mSynchronizer.ComputedField(
|
||||||
getter = { tile?.getPlayerSettings(ply)?.sorter ?: ItemSorter.DEFAULT },
|
getter = { tile?.getPlayerSettings(ply)?.sorter ?: ItemSorter.DEFAULT },
|
||||||
codec = EnumValueCodec(ItemSorter::class.java),
|
codec = ItemSorter::class.codec(),
|
||||||
observer = {
|
observer = {
|
||||||
patterns.sortWith(actualComparator)
|
patterns.sortWith(actualComparator)
|
||||||
tasks.sortWith(actualTaskComparator)
|
tasks.sortWith(actualTaskComparator)
|
||||||
@ -168,7 +169,7 @@ class MatterPanelMenu @JvmOverloads constructor(
|
|||||||
})
|
})
|
||||||
|
|
||||||
val changeIsAscending = booleanInput(allowSpectators = true) { tile?.getPlayerSettings(ply)?.ascending = it }
|
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 sortingGS = GetterSetter.of(::sorting, changeSorting::input)
|
||||||
val isAscendingGS = GetterSetter.of(::isAscending, changeIsAscending::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 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 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())
|
@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(T::class.java), 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())
|
@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(T::class.java), 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())
|
@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(T::class.java), 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
|
@JvmOverloads
|
||||||
fun byte(
|
fun byte(
|
||||||
@ -347,10 +347,9 @@ class FieldSynchronizer(private val callback: Runnable, private val alwaysCallCa
|
|||||||
value: T = type.enumConstants[0],
|
value: T = type.enumConstants[0],
|
||||||
getter: FieldGetter<T>? = null,
|
getter: FieldGetter<T>? = null,
|
||||||
setter: FieldSetter<T>? = null,
|
setter: FieldSetter<T>? = null,
|
||||||
writeByIndices: Boolean = false,
|
|
||||||
name: String? = nextFieldName(),
|
name: String? = nextFieldName(),
|
||||||
): Field<T> {
|
): 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
|
@JvmOverloads
|
||||||
@ -358,10 +357,9 @@ class FieldSynchronizer(private val callback: Runnable, private val alwaysCallCa
|
|||||||
value: T,
|
value: T,
|
||||||
getter: FieldGetter<T>? = null,
|
getter: FieldGetter<T>? = null,
|
||||||
setter: FieldSetter<T>? = null,
|
setter: FieldSetter<T>? = null,
|
||||||
writeByIndices: Boolean = false,
|
|
||||||
name: String? = nextFieldName(),
|
name: String? = nextFieldName(),
|
||||||
): Field<T> {
|
): 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
|
@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>(
|
inner class Field<V>(
|
||||||
private var field: V,
|
private var field: V,
|
||||||
private val codec: IStreamCodec<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>(
|
inner class ComputedField<V>(
|
||||||
private val getter: () -> V,
|
private val getter: () -> V,
|
||||||
private val codec: IStreamCodec<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> {
|
inner class ObservedField<V> : AbstractField<V>, IMutableField<V> {
|
||||||
private val codec: IStreamCodec<V>
|
private val codec: IStreamCodec<V>
|
||||||
private val getter: () -> V
|
private val getter: () -> V
|
||||||
|
Loading…
Reference in New Issue
Block a user