Even more ComputeAction/Constant codecs

This commit is contained in:
DBotThePony 2023-07-19 17:14:45 +07:00
parent 47feee8425
commit 41b5b1a57b
Signed by: DBot
GPG Key ID: DCC23B5715498507
3 changed files with 266 additions and 66 deletions

View File

@ -0,0 +1,112 @@
package ru.dbotthepony.mc.otm.data
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 ru.dbotthepony.mc.otm.core.stream
import java.util.function.Predicate
import java.util.stream.Stream
class CodecList<S : Any>(codecs: Stream<Codec<S>>) : Codec<S> {
constructor(codecs: Collection<Codec<S>>) : this(codecs.stream())
constructor(vararg codecs: Codec<S>) : this(codecs.stream() as Stream<Codec<S>>)
private val codecs = codecs.collect(ImmutableList.toImmutableList())
init {
require(this.codecs.isNotEmpty()) { "No codecs provided" }
}
override fun <T : Any> encode(input: S, ops: DynamicOps<T>, prefix: T): DataResult<T> {
val results = ArrayList<DataResult<T>>(codecs.size)
for (codec in codecs) {
val result = codec.encode(input, ops, prefix)
if (result.result().isPresent) {
return result
} else {
results.add(result)
}
}
return DataResult.error {
"None of codecs encoded the input:\n " + results.joinToString(";\n ") { it.error().get().message() }
}
}
override fun <T : Any> decode(ops: DynamicOps<T>, input: T): DataResult<Pair<S, T>> {
val results = ArrayList<DataResult<Pair<S, T>>>(codecs.size)
for (codec in codecs) {
val result = codec.decode(ops, input)
if (result.result().isPresent) {
return result
} else {
results.add(result)
}
}
return DataResult.error {
"None of codecs decoded the input:\n " + results.joinToString(";\n ") { it.error().get().message() }
}
}
}
class PredicatedCodecList<S : Any>(codecs: Stream<kotlin.Pair<Codec<S>, Predicate<S>>>) : Codec<S> {
constructor(codecs: Collection<kotlin.Pair<Codec<S>, Predicate<S>>>) : this(codecs.stream())
constructor(vararg codecs: kotlin.Pair<Codec<S>, Predicate<S>>) : this(codecs.stream() as Stream<kotlin.Pair<Codec<S>, Predicate<S>>>)
private val codecs = codecs.collect(ImmutableList.toImmutableList())
init {
require(this.codecs.isNotEmpty()) { "No codecs provided" }
}
override fun <T : Any> encode(input: S, ops: DynamicOps<T>, prefix: T): DataResult<T> {
val results = ArrayList<DataResult<T>>(codecs.size)
var i = -1
for ((codec, predicate) in codecs) {
i++
if (predicate.test(input)) {
val result = codec.encode(input, ops, prefix)
if (result.result().isPresent) {
return result
} else {
results.add(result)
}
} else {
val i2 = i
results.add(DataResult.error { "Codec #$i2 predicate tested false" })
}
}
return DataResult.error {
"None of codecs encoded the input:\n " + results.joinToString(";\n ") { it.error().get().message() }
}
}
override fun <T : Any> decode(ops: DynamicOps<T>, input: T): DataResult<Pair<S, T>> {
val results = ArrayList<DataResult<Pair<S, T>>>(codecs.size)
for ((codec) in codecs) {
val result = codec.decode(ops, input)
if (result.result().isPresent) {
return result
} else {
results.add(result)
}
}
return DataResult.error {
"None of codecs decoded the input:\n " + results.joinToString(";\n ") { it.error().get().message() }
}
}
}

View File

@ -12,12 +12,20 @@ object DecimalCodec : Codec<Decimal> {
} }
override fun <T : Any> decode(ops: DynamicOps<T>, input: T): DataResult<Pair<Decimal, T>> { override fun <T : Any> decode(ops: DynamicOps<T>, input: T): DataResult<Pair<Decimal, T>> {
return ops.getStringValue(input).flatMap { val result = ops.getStringValue(input).flatMap {
try { try {
DataResult.success(Pair(Decimal(it), ops.empty())) DataResult.success(Pair(Decimal(it), ops.empty()))
} catch (err: NumberFormatException) { } catch (err: NumberFormatException) {
DataResult.error { "Not a valid number for converting into Decimal: $it" } DataResult.error { "Not a valid number for converting into Decimal: $it" }
} }
} }
if (result.result().isPresent) {
return result
}
return ops.getNumberValue(input).flatMap {
DataResult.success(Pair(Decimal(it.toString()), ops.empty()))
}
} }
} }

View File

@ -13,7 +13,9 @@ import net.minecraft.world.item.Item
import net.minecraftforge.registries.ForgeRegistries import net.minecraftforge.registries.ForgeRegistries
import ru.dbotthepony.mc.otm.core.math.Decimal import ru.dbotthepony.mc.otm.core.math.Decimal
import ru.dbotthepony.mc.otm.data.DecimalCodec import ru.dbotthepony.mc.otm.data.DecimalCodec
import ru.dbotthepony.mc.otm.data.PredicatedCodecList
import java.util.Optional import java.util.Optional
import java.util.function.Predicate
class ComputeAction( class ComputeAction(
id: Either<ResourceLocation, TagKey<Item>>, id: Either<ResourceLocation, TagKey<Item>>,
@ -21,74 +23,158 @@ class ComputeAction(
priority: Optional<Int> = Optional.empty(), priority: Optional<Int> = Optional.empty(),
) : AbstractRegistryAction(id, priority = priority) { ) : AbstractRegistryAction(id, priority = priority) {
companion object : Type<ComputeAction> { companion object : Type<ComputeAction> {
private val constantCodecConcise: Codec<Constant> by lazy { private val constantCodecFull: kotlin.Pair<Codec<Constant>, Predicate<Constant>> by lazy {
RecordCodecBuilder.create { RecordCodecBuilder.create<Constant> {
it.group(
DecimalCodec.fieldOf("matter").forGetter(Constant::matter),
Codec.DOUBLE.fieldOf("complexity").forGetter(Constant::complexity),
IMatterFunction.registry.codec.fieldOf("function").forGetter(Constant::matterFunction),
).apply(it, ::Constant)
}
}
private val constantCodecVeryConcise: Codec<Constant> by lazy {
RecordCodecBuilder.create {
it.group(
DecimalCodec.fieldOf("value").forGetter(Constant::matter),
IMatterFunction.registry.codec.fieldOf("function").forGetter(Constant::matterFunction),
).apply(it, ::Constant)
}
}
private val constantCodecVeryConcise2: Codec<Constant> by lazy {
RecordCodecBuilder.create {
it.group(
Codec.DOUBLE.fieldOf("value").forGetter(Constant::complexity),
IMatterFunction.registry.codec.fieldOf("function").forGetter(Constant::matterFunction),
).apply(it, ::Constant)
}
}
private val constantCodecVerbose: Codec<Constant> by lazy {
RecordCodecBuilder.create {
it.group( it.group(
DecimalCodec.fieldOf("matter").forGetter(Constant::matter), DecimalCodec.fieldOf("matter").forGetter(Constant::matter),
Codec.DOUBLE.fieldOf("complexity").forGetter(Constant::complexity), Codec.DOUBLE.fieldOf("complexity").forGetter(Constant::complexity),
IMatterFunction.registry.codec.fieldOf("matterFunction").forGetter(Constant::matterFunction), IMatterFunction.registry.codec.fieldOf("matterFunction").forGetter(Constant::matterFunction),
IMatterFunction.registry.codec.fieldOf("complexityFunction").forGetter(Constant::complexityFunction), IMatterFunction.registry.codec.fieldOf("complexityFunction").forGetter(Constant::complexityFunction),
).apply(it, ::Constant) ).apply(it, ::Constant)
} to Predicate { true }
}
private val constantCodecValues: kotlin.Pair<Codec<Constant>, Predicate<Constant>> by lazy {
RecordCodecBuilder.create<Constant> {
it.group(
DecimalCodec.fieldOf("matter").forGetter(Constant::matter),
Codec.DOUBLE.fieldOf("complexity").forGetter(Constant::complexity),
IMatterFunction.registry.codec.fieldOf("function").forGetter(Constant::matterFunction),
).apply(it) { a, b, c -> Constant(a, b, c, c) }
} to Predicate { it.matterFunction == it.complexityFunction }
}
private val constantCodecFunctions: kotlin.Pair<Codec<Constant>, Predicate<Constant>> by lazy {
RecordCodecBuilder.create<Constant> {
it.group(
DecimalCodec.fieldOf("value").forGetter(Constant::matter),
IMatterFunction.registry.codec.fieldOf("matterFunction").forGetter(Constant::matterFunction),
IMatterFunction.registry.codec.fieldOf("complexityFunction").forGetter(Constant::complexityFunction),
).apply(it) { a, b, c -> Constant(a, a.toDouble(), b, c) }
} to Predicate { it.matter == Decimal(it.complexity.toString()) }
}
private val constantCodecConcise: kotlin.Pair<Codec<Constant>, Predicate<Constant>> by lazy {
RecordCodecBuilder.create<Constant> {
it.group(
DecimalCodec.fieldOf("value").forGetter(Constant::matter),
IMatterFunction.registry.codec.fieldOf("function").forGetter(Constant::matterFunction),
).apply(it) { a, b -> Constant(a, a.toDouble(), b, b) }
} to Predicate { it.matter == Decimal(it.complexity.toString()) && it.matterFunction == it.complexityFunction }
}
private val constantCodecComplexity: kotlin.Pair<Codec<Constant>, Predicate<Constant>> by lazy {
RecordCodecBuilder.create<Constant> {
it.group(
Codec.DOUBLE.fieldOf("complexity").forGetter(Constant::complexity),
IMatterFunction.registry.codec.fieldOf("function").forGetter(Constant::complexityFunction),
).apply(it) { a, b -> Constant(Decimal.ZERO, a.toDouble(), IMatterFunction.NOOP, b) }
} to Predicate { it.matterFunction == IMatterFunction.NOOP }
}
private val constantCodecComplexity2: kotlin.Pair<Codec<Constant>, Predicate<Constant>> by lazy {
RecordCodecBuilder.create<Constant> {
it.group(
Codec.DOUBLE.fieldOf("complexity").forGetter(Constant::complexity),
IMatterFunction.registry.codec.fieldOf("complexityFunction").forGetter(Constant::complexityFunction),
).apply(it) { a, b -> Constant(Decimal.ZERO, a.toDouble(), IMatterFunction.NOOP, b) }
} to Predicate { it.matterFunction == IMatterFunction.NOOP }
}
private val constantCodecMatter: kotlin.Pair<Codec<Constant>, Predicate<Constant>> by lazy {
RecordCodecBuilder.create<Constant> {
it.group(
DecimalCodec.fieldOf("matter").forGetter(Constant::matter),
IMatterFunction.registry.codec.fieldOf("function").forGetter(Constant::matterFunction),
).apply(it) { a, b -> Constant(a, 0.0, b, IMatterFunction.NOOP) }
} to Predicate { it.complexityFunction == IMatterFunction.NOOP }
}
private val constantCodecMatter2: kotlin.Pair<Codec<Constant>, Predicate<Constant>> by lazy {
RecordCodecBuilder.create<Constant> {
it.group(
DecimalCodec.fieldOf("matter").forGetter(Constant::matter),
IMatterFunction.registry.codec.fieldOf("matterFunction").forGetter(Constant::matterFunction),
).apply(it) { a, b -> Constant(a, 0.0, b, IMatterFunction.NOOP) }
} to Predicate { it.complexityFunction == IMatterFunction.NOOP }
}
private data class StringConstantCodec(val fn: IMatterFunction, val symbol: Char) : Codec<Constant>, Predicate<Constant> {
val pair = this to this
override fun <T : Any> encode(input: Constant, ops: DynamicOps<T>, prefix: T): DataResult<T> {
return DataResult.success(ops.createString(symbol + input.matter.toString()))
}
override fun <T : Any> decode(ops: DynamicOps<T>, input: T): DataResult<Pair<Constant, T>> {
return ops.getStringValue(input).flatMap {
if (it[0] == symbol) {
try {
DataResult.success(Pair(Constant(Decimal(it.substring(1).trim()), it.substring(1).trim().toDouble(), fn, fn), ops.empty()))
} catch (err: NumberFormatException) {
DataResult.error { "Not a number: ${it.substring(1).trim()} (input string: $it)" }
}
} else {
DataResult.error { "Input string does not match expected operand: expected $symbol, got ${it[0]} (for input $it)" }
}
}
}
override fun test(t: Constant): Boolean {
return t.matterFunction == fn && t.complexityFunction == fn && t.matter == Decimal(t.complexity.toString())
}
}
private object PlainStringConstantCodec : Codec<Constant>, Predicate<Constant> {
override fun <T : Any> encode(input: Constant, ops: DynamicOps<T>, prefix: T): DataResult<T> {
return DataResult.success(ops.createString(input.matter.toString()))
}
override fun <T : Any> decode(ops: DynamicOps<T>, input: T): DataResult<Pair<Constant, T>> {
val result = ops.getNumberValue(input).flatMap {
try {
DataResult.success(Pair(Constant(Decimal(it.toString()), it.toDouble(), IMatterFunction.PLUS, IMatterFunction.PLUS), ops.empty()))
} catch (err: NumberFormatException) {
DataResult.error { "Not a number: $it" }
}
}
if (result.result().isPresent)
return result
return ops.getStringValue(input).flatMap {
try {
DataResult.success(Pair(Constant(Decimal(it), it.toDouble(), IMatterFunction.PLUS, IMatterFunction.PLUS), ops.empty()))
} catch (err: NumberFormatException) {
DataResult.error { "Not a number: $it" }
}
}
}
override fun test(t: Constant): Boolean {
return t.matterFunction == IMatterFunction.PLUS && t.complexityFunction == IMatterFunction.PLUS && t.matter == Decimal(t.complexity.toString())
} }
} }
private val constantCodec: Codec<Constant> by lazy { private val constantCodec: Codec<Constant> by lazy {
Codec.either(constantCodecConcise, constantCodecVerbose) PredicatedCodecList(
.xmap({ it.map({ it }, { it }) }, { if (it.matterFunction == it.complexityFunction) Either.left(it) else Either.right(it) }) PlainStringConstantCodec to PlainStringConstantCodec,
} StringConstantCodec(IMatterFunction.DIV, '/').pair,
StringConstantCodec(IMatterFunction.MUL, '*').pair,
private object ActualConstantCodec : Codec<Constant> { StringConstantCodec(IMatterFunction.AT_LEAST, '>').pair,
override fun <T : Any> encode(input: Constant, ops: DynamicOps<T>, prefix: T): DataResult<T> { StringConstantCodec(IMatterFunction.AT_MOST, '<').pair,
if (input.complexity == input.matter.toDouble()) { StringConstantCodec(IMatterFunction.MINUS, '-').pair,
return constantCodecVeryConcise.encode(input, ops, prefix) StringConstantCodec(IMatterFunction.MOD, '%').pair,
} else { StringConstantCodec(IMatterFunction.REPLACE, '^').pair,
return constantCodec.encode(input, ops, prefix) constantCodecMatter,
} constantCodecMatter2,
} constantCodecComplexity,
constantCodecComplexity2,
override fun <T : Any> decode(ops: DynamicOps<T>, input: T): DataResult<Pair<Constant, T>> { constantCodecConcise,
var result = constantCodecVeryConcise.decode(ops, input) constantCodecFunctions,
constantCodecValues,
if (result.result().isPresent) { constantCodecFull,
return result )
}
result = constantCodecVeryConcise2.decode(ops, input)
if (result.result().isPresent) {
return result
}
return constantCodec.decode(ops, input)
}
} }
private val valueCodecConcise: Codec<Value> by lazy { private val valueCodecConcise: Codec<Value> by lazy {
@ -140,7 +226,7 @@ class ComputeAction(
it.group( it.group(
TargetCodec.fieldOf("id").forGetter(ComputeAction::id), TargetCodec.fieldOf("id").forGetter(ComputeAction::id),
Codec.list(Codec Codec.list(Codec
.either(ActualConstantCodec, ActualValueCodec) .either(constantCodec, ActualValueCodec)
.xmap({ it.map({ it }, { it }) }, { if (it is Constant) Either.left(it) else Either.right(it as Value) })) .xmap({ it.map({ it }, { it }) }, { if (it is Constant) Either.left(it) else Either.right(it as Value) }))
.fieldOf("values") .fieldOf("values")
.forGetter(ComputeAction::values), .forGetter(ComputeAction::values),
@ -179,12 +265,6 @@ class ComputeAction(
matterFunction: IMatterFunction, matterFunction: IMatterFunction,
complexityFunction: IMatterFunction = matterFunction, complexityFunction: IMatterFunction = matterFunction,
) : Source(matterFunction, complexityFunction) { ) : Source(matterFunction, complexityFunction) {
constructor(
value: Decimal,
matterFunction: IMatterFunction,
complexityFunction: IMatterFunction = matterFunction
) : this(value, value.toDouble(), matterFunction, complexityFunction)
constructor( constructor(
value: Double, value: Double,
matterFunction: IMatterFunction, matterFunction: IMatterFunction,