Update matter entangler recipe, replace ingredient matrix with helpers over shaped recipe pattern

This commit is contained in:
DBotThePony 2024-08-22 18:13:16 +07:00
parent e80947d8d7
commit 7cb6949e27
Signed by: DBot
GPG Key ID: DCC23B5715498507
8 changed files with 220 additions and 320 deletions

View File

@ -1,113 +0,0 @@
package ru.dbotthepony.mc.otm.data
import com.mojang.datafixers.util.Pair
import com.mojang.serialization.Codec
import com.mojang.serialization.DataResult
import com.mojang.serialization.DynamicOps
import com.mojang.serialization.JsonOps
import com.mojang.serialization.codecs.RecordCodecBuilder
import net.minecraft.world.item.crafting.Ingredient
import ru.dbotthepony.mc.otm.core.collect.allEqual
import ru.dbotthepony.mc.otm.core.collect.map
import ru.dbotthepony.mc.otm.core.collect.toList
import ru.dbotthepony.mc.otm.core.collect.toStream
import ru.dbotthepony.mc.otm.core.stream
import ru.dbotthepony.mc.otm.recipe.IIngredientMatrix
import ru.dbotthepony.mc.otm.recipe.IngredientMatrix
import java.util.function.Supplier
class IngredientMatrixCodec(ingredientCodec: Codec<Ingredient>) : Codec<IIngredientMatrix> {
private val ingredientList = Codec.list(ingredientCodec)
private val doubleIngredientList = Codec.list(ingredientList)
override fun <T : Any> encode(input: IIngredientMatrix, ops: DynamicOps<T>, prefix: T): DataResult<T> {
return doubleIngredientList.encode(
(0 until input.height).iterator().map { row ->
(0 until input.width).iterator().map { column ->
input[column, row]
}.toList()
}.toList(),
ops,
prefix
)
}
private data class Handwritten(val pattern: List<String>, val key: Map<Char, Ingredient>)
private val handwrittenCodec = RecordCodecBuilder.create<Handwritten> {
it.group(
Codec.list(Codec.STRING)
.flatXmap(
{ DataResult.success(it) },
{ if (it.iterator().map { it.length }.allEqual()) DataResult.success(it) else DataResult.error { "One or more of patten strings differ in length" } }
)
.fieldOf("pattern").forGetter(Handwritten::pattern),
Codec.unboundedMap(
Codec.STRING
.flatXmap(
{ if (it.length == 1) DataResult.success(it[0]) else DataResult.error { "Ingredient key must be exactly 1 symbol in length, '$it' is invalid" } },
{ DataResult.success(it.toString()) }
), ingredientCodec).fieldOf("key").forGetter(Handwritten::key)
).apply(it, ::Handwritten)
}
override fun <T : Any> decode(ops: DynamicOps<T>, input: T): DataResult<Pair<IIngredientMatrix, T>> {
return ops.getList(input).mapOrElse(
{
val lines = ArrayList<DataResult<List<Ingredient>>>()
it.accept {
lines.add(ingredientList.decode(ops, it).map { it.first })
}
val errors = ArrayList<Supplier<String>>()
val ingredients = ArrayList<List<Ingredient>>()
lines.withIndex().forEach {
val (line, result) = it
result.mapOrElse({ ingredients.add(it) }, { errors.add { "Line $line: ${it.message()}" } })
}
if (errors.isNotEmpty()) {
DataResult.error { "Failed to decode ingredient matrix: ${errors.joinToString { it.get() }}" }
} else if (ingredients.isEmpty()) {
DataResult.error { "Ingredient list is empty" }
} else if (!ingredients.iterator().map { it.size }.allEqual()) {
DataResult.error { "Ingredient list is not a matrix (one or multiple of rows are mismatched size)" }
} else {
val result = IngredientMatrix(ingredients.first().size, ingredients.size)
for ((row, columns) in ingredients.withIndex()) {
for ((column, ingredient) in columns.withIndex()) {
result[column, row] = ingredient
}
}
DataResult.success(Pair(result, ops.empty()))
}
},
{ err1 ->
handwrittenCodec.decode(ops, input).mapOrElse(
{
DataResult.success(it)
},
{
DataResult.error { "Failed to decode ingredients as list: ${err1.message()} and as pattern/dictionary: ${it.message()}" }
}
).flatMap {
val handwritten = it.first
val result = IngredientMatrix(handwritten.pattern.first().length, handwritten.pattern.size)
for ((row, pattern) in handwritten.pattern.withIndex()) {
for ((column, symbol) in pattern.withIndex()) {
val ingredient = if (symbol == ' ') handwritten.key[symbol] ?: Ingredient.EMPTY else handwritten.key[symbol] ?: return@flatMap DataResult.error { "Unknown ingredient with index '$symbol'" }
result[column, row] = ingredient
}
}
DataResult.success(Pair(result, ops.empty()))
}
}
)
}
}

View File

@ -13,6 +13,7 @@ import net.minecraft.network.protocol.common.custom.CustomPacketPayload
import net.neoforged.neoforge.network.connection.ConnectionType
import net.neoforged.neoforge.network.handling.IPayloadContext
import net.neoforged.neoforge.network.registration.PayloadRegistrar
import java.util.Optional
import kotlin.reflect.KFunction1
fun <T : CustomPacketPayload> PayloadRegistrar.playToClient(
@ -74,6 +75,7 @@ fun FriendlyByteBuf.readByteList(): ByteArrayList {
fun <S : ByteBuf, V> StreamCodec<S, V>.wrap(): MatteryStreamCodec<S, V> = MatteryStreamCodec.Wrapper(this)
fun <S : ByteBuf, V> MatteryStreamCodec<S, V>.nullable(): MatteryStreamCodec<S, V?> = MatteryStreamCodec.Nullable(this)
fun <S : ByteBuf, V : Any> MatteryStreamCodec<S, V>.optional(): MatteryStreamCodec<S, Optional<V>> = MatteryStreamCodec.Optional(this)
fun <V> StreamCodec<in RegistryFriendlyByteBuf, V>.encode(registry: RegistryAccess, value: V): ByteArrayList {
return encodePayload(registry) {

View File

@ -52,6 +52,31 @@ interface MatteryStreamCodec<S : ByteBuf, V> : StreamCodec<S, V> {
}
}
class Optional<S : ByteBuf, V : Any>(val parent: MatteryStreamCodec<S, V>) : MatteryStreamCodec<S, java.util.Optional<V>> {
override fun decode(stream: S): java.util.Optional<V> {
return if (!stream.readBoolean()) java.util.Optional.empty() else java.util.Optional.of(parent.decode(stream))
}
override fun encode(stream: S, value: java.util.Optional<V>) {
if (value.isEmpty)
stream.writeBoolean(false)
else {
stream.writeBoolean(true)
parent.encode(stream, value.get())
}
}
override fun copy(value: java.util.Optional<V>): java.util.Optional<V> {
return value.map(parent::copy)
}
override fun compare(a: java.util.Optional<V>, b: java.util.Optional<V>): Boolean {
if (a.isEmpty && b.isEmpty) return true
if (a.isEmpty || b.isEmpty) return false
return parent.compare(a.get(), b.get())
}
}
class Collection<S : FriendlyByteBuf, E, C : MutableCollection<E>>(val elementCodec: MatteryStreamCodec<in S, E>, val collectionFactory: (Int) -> C) : MatteryStreamCodec<S, C> {
override fun decode(stream: S): C {
val size = stream.readVarInt()

View File

@ -3,17 +3,14 @@ package ru.dbotthepony.mc.otm.network
import io.netty.buffer.ByteBuf
import net.minecraft.core.UUIDUtil
import net.minecraft.network.FriendlyByteBuf
import net.minecraft.network.RegistryFriendlyByteBuf
import net.minecraft.network.codec.ByteBufCodecs
import net.minecraft.network.codec.StreamCodec
import net.minecraft.resources.ResourceLocation
import net.minecraft.util.valueproviders.FloatProvider
import ru.dbotthepony.kommons.math.RGBAColor
import ru.dbotthepony.mc.otm.core.math.readDecimal
import ru.dbotthepony.mc.otm.core.math.writeDecimal
import ru.dbotthepony.mc.otm.core.readItemType
import ru.dbotthepony.mc.otm.core.writeItemType
import kotlin.reflect.KMutableProperty1
object StreamCodecs {
val NOTHING: MatteryStreamCodec<ByteBuf, Nothing?> = StreamCodec.of<ByteBuf, Nothing?>({ _, _ -> }, { null }).wrap()
@ -38,4 +35,38 @@ object StreamCodecs {
val ITEM_TYPE = StreamCodec.of(FriendlyByteBuf::writeItemType, FriendlyByteBuf::readItemType).wrap()
val ITEM_TYPE_NULLABLE = ITEM_TYPE.nullable()
val DECIMAL = StreamCodec.of(FriendlyByteBuf::writeDecimal, FriendlyByteBuf::readDecimal).wrap()
fun <S : ByteBuf, T, T0, T1, T2, T3, T4, T5, T6> composite(
c0: StreamCodec<in S, T0>, g0: (T) -> T0,
c1: StreamCodec<in S, T1>, g1: (T) -> T1,
c2: StreamCodec<in S, T2>, g2: (T) -> T2,
c3: StreamCodec<in S, T3>, g3: (T) -> T3,
c4: StreamCodec<in S, T4>, g4: (T) -> T4,
c5: StreamCodec<in S, T5>, g5: (T) -> T5,
c6: StreamCodec<in S, T6>, g6: (T) -> T6,
factory: (T0, T1, T2, T3, T4, T5, T6) -> T
): StreamCodec<S, T> {
return object : StreamCodec<S, T> {
override fun decode(stream: S): T {
val v0 = c0.decode(stream)
val v1 = c1.decode(stream)
val v2 = c2.decode(stream)
val v3 = c3.decode(stream)
val v4 = c4.decode(stream)
val v5 = c5.decode(stream)
val v6 = c6.decode(stream)
return factory(v0, v1, v2, v3, v4, v5, v6)
}
override fun encode(stream: S, value: T) {
c0.encode(stream, g0(value))
c1.encode(stream, g1(value))
c2.encode(stream, g2(value))
c3.encode(stream, g3(value))
c4.encode(stream, g4(value))
c5.encode(stream, g5(value))
c6.encode(stream, g6(value))
}
}
}
}

View File

@ -0,0 +1,78 @@
package ru.dbotthepony.mc.otm.recipe
import net.minecraft.world.item.crafting.CraftingInput
import net.minecraft.world.item.crafting.Ingredient
import net.minecraft.world.item.crafting.ShapedRecipePattern
import ru.dbotthepony.mc.otm.core.get
import ru.dbotthepony.mc.otm.core.isNotEmpty
val ShapedRecipePattern.isIncomplete: Boolean
get() = ingredients().any { it.items.size == 1 && it.hasNoItems() }
val ShapedRecipePattern.width: Int get() = width()
val ShapedRecipePattern.height: Int get() = height()
operator fun ShapedRecipePattern.get(column: Int, row: Int): Ingredient {
return ingredients()[column + row * width]
}
operator fun ShapedRecipePattern.get(column: Int, row: Int, flop: Boolean): Ingredient {
return if (flop)
get(width - column - 1, row)
else
get(column, row)
}
fun ShapedRecipePattern.preemptiveTest(t: CraftingInput, fromColumn: Int, fromRow: Int, flop: Boolean): Boolean {
if (t.width() - fromColumn < width || t.height() - fromRow < height)
return false
for (column in 0 until width) {
for (row in 0 until height) {
val item = t[fromColumn + column, fromRow + row, flop]
val ingredient = this[column, row, flop]
if (!ingredient.test(item) && item.isNotEmpty) {
return false
}
}
}
return true
}
fun ShapedRecipePattern.preemptiveTest(t: CraftingInput): Boolean {
if (t.width() < width || t.height() < height)
return false
for (column in 0 .. t.width() - width)
for (row in 0 .. t.height() - height)
if (preemptiveTest(t, column, row, false) || preemptiveTest(t, column, row, true))
return true
return false
}
fun ShapedRecipePattern.test(t: CraftingInput, fromColumn: Int, fromRow: Int, flop: Boolean): Boolean {
if (t.width() - fromColumn < width || t.height() - fromRow < height)
return false
for (column in 0 until width)
for (row in 0 until height)
if (!this[column, row, flop].test(t[fromColumn + column, fromRow + row, flop]))
return false
return true
}
fun ShapedRecipePattern.test(t: CraftingInput): Boolean {
if (t.width() < width || t.height() < height)
return false
for (column in 0 .. t.width() - width)
for (row in 0 .. t.height() - height)
if (test(t, column, row, false) || test(t, column, row, true))
return true
return false
}

View File

@ -1,10 +1,14 @@
package ru.dbotthepony.mc.otm.recipe
import net.minecraft.core.HolderLookup
import net.minecraft.core.NonNullList
import net.minecraft.world.item.ItemStack
import net.minecraft.world.item.crafting.Ingredient
import net.minecraft.world.item.crafting.Recipe
import net.minecraft.world.item.crafting.RecipeInput
import net.minecraft.world.item.crafting.RecipeSerializer
import net.minecraft.world.item.crafting.RecipeType
import net.minecraft.world.level.Level
// passthrough all default methods to fix Kotlin bug related to implementation delegation not properly working on Java interfaces
// https://youtrack.jetbrains.com/issue/KT-55080/Change-the-behavior-of-inheritance-delegation-to-delegates-implementing-Java-interfaces-with-default-methods

View File

@ -1,162 +0,0 @@
package ru.dbotthepony.mc.otm.recipe
import net.minecraft.core.NonNullList
import net.minecraft.world.item.crafting.CraftingInput
import net.minecraft.world.item.crafting.Ingredient
import ru.dbotthepony.mc.otm.core.collect.allEqual
import ru.dbotthepony.mc.otm.core.collect.any
import ru.dbotthepony.mc.otm.core.collect.flatMap
import ru.dbotthepony.mc.otm.core.collect.map
import ru.dbotthepony.mc.otm.core.get
import ru.dbotthepony.mc.otm.core.isNotEmpty
import ru.dbotthepony.mc.otm.core.util.InvalidableLazy
import java.util.function.Predicate
interface IIngredientMatrix : Predicate<CraftingInput>, Iterable<Ingredient> {
val width: Int
val height: Int
val isEmpty: Boolean
get() = width == 0 || height == 0
val isIncomplete: Boolean
get() = iterator().any { it.hasNoItems() }
operator fun get(column: Int, row: Int): Ingredient
operator fun get(column: Int, row: Int, flop: Boolean): Ingredient {
return if (flop)
get(width - column - 1, row)
else
get(column, row)
}
override fun iterator(): Iterator<Ingredient> {
return (0 until width).iterator().flatMap { x ->
(0 until height).iterator().map { y ->
get(x, y)
}
}
}
val ingredients: NonNullList<Ingredient> get() {
val result = NonNullList.createWithCapacity<Ingredient>(width * height)
for (x in 0 until width) {
for (y in 0 until height) {
result.add(get(x, y))
}
}
return result
}
fun test(t: CraftingInput, fromColumn: Int, fromRow: Int, flop: Boolean): Boolean {
if (t.width() - fromColumn < width || t.height() - fromRow < height)
return false
for (column in 0 until width)
for (row in 0 until height)
if (!this[column, row, flop].test(t[fromColumn + column, fromRow + row, flop]))
return false
return true
}
fun preemptiveTest(t: CraftingInput, fromColumn: Int, fromRow: Int, flop: Boolean): Boolean {
if (t.width() - fromColumn < width || t.height() - fromRow < height)
return false
for (column in 0 until width) {
for (row in 0 until height) {
val item = t[fromColumn + column, fromRow + row, flop]
val ingredient = this[column, row, flop]
if (!ingredient.test(item) && item.isNotEmpty) {
return false
}
}
}
return true
}
override fun test(t: CraftingInput): Boolean {
if (t.width() < width || t.height() < height)
return false
for (column in 0 .. t.width() - width)
for (row in 0 .. t.height() - height)
if (test(t, column, row, false) || test(t, column, row, true))
return true
return false
}
fun preemptiveTest(t: CraftingInput): Boolean {
if (t.width() < width || t.height() < height)
return false
for (column in 0 .. t.width() - width)
for (row in 0 .. t.height() - height)
if (preemptiveTest(t, column, row, false) || preemptiveTest(t, column, row, true))
return true
return false
}
companion object : IIngredientMatrix {
override val width: Int
get() = 0
override val height: Int
get() = 0
override fun get(column: Int, row: Int): Ingredient {
return Ingredient.EMPTY
}
}
}
class IngredientMatrix(override val width: Int, override val height: Int) : IIngredientMatrix {
private val data = Array(width * height) { Ingredient.EMPTY }
private val lazy = InvalidableLazy.Impl {
super.isIncomplete
}
override val isIncomplete by lazy
override fun iterator(): Iterator<Ingredient> {
return data.iterator()
}
override fun get(column: Int, row: Int): Ingredient {
require(column in 0 until width) { "Column out of bounds: $column (matrix width: $width)" }
require(row in 0 until height) { "Row out of bounds: $row > (matrix height: $height)" }
return data[column + row * width]
}
operator fun set(column: Int, row: Int, value: Ingredient) {
require(column in 0 until width) { "Column out of bounds: $column (matrix width: $width)" }
require(row in 0 until height) { "Row out of bounds: $row > (matrix height: $height)" }
data[column + row * width] = value
lazy.invalidate()
}
companion object {
fun of(vararg values: Collection<Ingredient>): IngredientMatrix {
if (!values.iterator().map { it.size }.allEqual()) {
throw IllegalArgumentException("One or more rows have different number of columns than the rest")
}
val result = IngredientMatrix(values.first().size, values.size)
for ((y, l) in values.withIndex()) {
for ((x, i) in l.withIndex()) {
result[x, y] = i
}
}
return result
}
}
}

View File

@ -1,26 +1,31 @@
package ru.dbotthepony.mc.otm.recipe
import com.mojang.serialization.Codec
import com.mojang.serialization.MapCodec
import com.mojang.serialization.codecs.RecordCodecBuilder
import net.minecraft.core.HolderLookup
import net.minecraft.core.NonNullList
import net.minecraft.core.RegistryAccess
import net.minecraft.core.UUIDUtil
import net.minecraft.resources.ResourceLocation
import net.minecraft.core.component.DataComponentType
import net.minecraft.network.RegistryFriendlyByteBuf
import net.minecraft.network.codec.StreamCodec
import net.minecraft.world.item.ItemStack
import net.minecraft.world.item.crafting.CraftingInput
import net.minecraft.world.item.crafting.Ingredient
import net.minecraft.world.item.crafting.RecipeSerializer
import net.minecraft.world.item.crafting.RecipeType
import net.minecraft.world.item.crafting.ShapedRecipePattern
import net.minecraft.world.level.Level
import ru.dbotthepony.mc.otm.capability.MatteryCapability
import ru.dbotthepony.mc.otm.capability.matteryEnergy
import ru.dbotthepony.mc.otm.core.collect.filterNotNull
import ru.dbotthepony.mc.otm.core.collect.map
import ru.dbotthepony.mc.otm.core.math.Decimal
import ru.dbotthepony.mc.otm.core.nbt.set
import ru.dbotthepony.mc.otm.data.DecimalCodec
import ru.dbotthepony.mc.otm.data.IngredientMatrixCodec
import ru.dbotthepony.mc.otm.data.minRange
import ru.dbotthepony.mc.otm.network.StreamCodecs
import ru.dbotthepony.mc.otm.network.optional
import ru.dbotthepony.mc.otm.network.wrap
import ru.dbotthepony.mc.otm.registry.MItems
import ru.dbotthepony.mc.otm.registry.MRecipes
import java.util.Optional
@ -30,7 +35,7 @@ import kotlin.jvm.optionals.getOrElse
interface IMatterEntanglerRecipe : IMatteryRecipe<CraftingInput> {
val matter: Decimal
val ticks: Double
val ingredients: IIngredientMatrix
val ingredients: ShapedRecipePattern
val result: ItemStack
val experience: Float
@ -38,12 +43,12 @@ interface IMatterEntanglerRecipe : IMatteryRecipe<CraftingInput> {
}
open class MatterEntanglerRecipe(
override val ingredients: IIngredientMatrix,
override val ingredients: ShapedRecipePattern,
override val matter: Decimal,
override val ticks: Double,
override val result: ItemStack,
override val experience: Float = 0f,
val uuidKey: String = "uuid",
val uuidKey: Optional<DataComponentType<UUID>> = Optional.empty(),
val fixedUuid: Optional<UUID> = Optional.empty()
) : IMatterEntanglerRecipe {
override fun matches(container: CraftingInput, level: Level): Boolean {
@ -58,7 +63,9 @@ open class MatterEntanglerRecipe(
override fun assemble(container: CraftingInput, registry: HolderLookup.Provider): ItemStack {
return result.copy().also {
it.tagNotNull[uuidKey] = fixedUuid.getOrElse { UUID.randomUUID() }
uuidKey.ifPresent { id ->
it[id] = fixedUuid.getOrElse { UUID.randomUUID() }
}
}
}
@ -66,12 +73,12 @@ open class MatterEntanglerRecipe(
return width >= ingredients.width && height >= ingredients.height
}
override fun getResultItem(registry: RegistryAccess): ItemStack {
override fun getResultItem(registry: HolderLookup.Provider): ItemStack {
return result
}
override fun getSerializer(): RecipeSerializer<*> {
return SERIALIZER
return Companion
}
override fun getType(): RecipeType<*> {
@ -79,7 +86,7 @@ open class MatterEntanglerRecipe(
}
override fun getIngredients(): NonNullList<Ingredient> {
return ingredients.ingredients
return ingredients.ingredients()
}
override fun isIncomplete(): Boolean {
@ -94,10 +101,6 @@ open class MatterEntanglerRecipe(
return ItemStack(MItems.MATTER_ENTANGLER)
}
fun toFinished(id: ResourceLocation): FinishedRecipe {
return SERIALIZER.toFinished(this, id)
}
fun energetic() = Energy(this)
fun matter() = Matter(this)
@ -110,49 +113,81 @@ open class MatterEntanglerRecipe(
}
}
fun toFinished(id: ResourceLocation): FinishedRecipe {
return ENERGY_SERIALIZER.toFinished(this, id)
}
override fun getSerializer(): RecipeSerializer<*> {
return ENERGY_SERIALIZER
return EnergySerializer
}
}
open class Matter(val parent: MatterEntanglerRecipe) : IMatterEntanglerRecipe by parent {
override fun assemble(container: CraftingInput, registry: HolderLookup.Provider): ItemStack {
return parent.assemble(container, registry).also { result ->
container.items().iterator().map { it.matter }.filterNotNull().forEach {
result.matter!!.storedMatter += it.storedMatter
container.items().iterator().map { it.getCapability(MatteryCapability.MATTER_ITEM) }.filterNotNull().forEach {
result.getCapability(MatteryCapability.MATTER_ITEM)!!.storedMatter += it.storedMatter
}
}
}
fun toFinished(id: ResourceLocation): FinishedRecipe {
return MATTER_SERIALIZER.toFinished(this, id)
}
override fun getSerializer(): RecipeSerializer<*> {
return MATTER_SERIALIZER
return MatterSerializer
}
}
companion object {
val SERIALIZER = Codec2RecipeSerializer<MatterEntanglerRecipe> { context ->
RecordCodecBuilder.create {
it.group(
IngredientMatrixCodec(context.ingredients).fieldOf("ingredients").forGetter(MatterEntanglerRecipe::ingredients),
DecimalCodec.minRange(Decimal.ZERO).fieldOf("matter").forGetter(MatterEntanglerRecipe::matter),
Codec.DOUBLE.minRange(0.0).fieldOf("ticks").forGetter(MatterEntanglerRecipe::ticks),
ItemStack.CODEC.fieldOf("result").forGetter(MatterEntanglerRecipe::result),
Codec.FLOAT.minRange(0f).optionalFieldOf("experience", 0f).forGetter(MatterEntanglerRecipe::experience),
Codec.STRING.optionalFieldOf("uuidKey", "uuid").forGetter(MatterEntanglerRecipe::uuidKey),
UUIDUtil.STRING_CODEC.optionalFieldOf("fixedUuid").forGetter(MatterEntanglerRecipe::fixedUuid)
).apply(it, ::MatterEntanglerRecipe)
}
private object EnergySerializer : RecipeSerializer<Energy> {
private val energyCodec: MapCodec<Energy> = codec.xmap(::Energy, Energy::parent)
private val energyStreamCodec: StreamCodec<RegistryFriendlyByteBuf, Energy> = streamCodec.map(::Energy, Energy::parent)
override fun codec(): MapCodec<Energy> {
return energyCodec
}
val ENERGY_SERIALIZER = SERIALIZER.xmap(::Energy, Energy::parent)
val MATTER_SERIALIZER = SERIALIZER.xmap(::Matter, Matter::parent)
override fun streamCodec(): StreamCodec<RegistryFriendlyByteBuf, Energy> {
return energyStreamCodec
}
}
private object MatterSerializer : RecipeSerializer<Matter> {
private val matterCodec: MapCodec<Matter> = codec.xmap(::Matter, Matter::parent)
private val matterStreamCodec: StreamCodec<RegistryFriendlyByteBuf, Matter> = streamCodec.map(::Matter, Matter::parent)
override fun codec(): MapCodec<Matter> {
return matterCodec
}
override fun streamCodec(): StreamCodec<RegistryFriendlyByteBuf, Matter> {
return matterStreamCodec
}
}
companion object : RecipeSerializer<MatterEntanglerRecipe> {
val codec: MapCodec<MatterEntanglerRecipe> = RecordCodecBuilder.mapCodec {
it.group(
ShapedRecipePattern.MAP_CODEC.fieldOf("ingredients").forGetter(MatterEntanglerRecipe::ingredients),
DecimalCodec.minRange(Decimal.ZERO).fieldOf("matter").forGetter(MatterEntanglerRecipe::matter),
Codec.DOUBLE.minRange(0.0).fieldOf("ticks").forGetter(MatterEntanglerRecipe::ticks),
ItemStack.CODEC.fieldOf("result").forGetter(MatterEntanglerRecipe::result),
Codec.FLOAT.minRange(0f).optionalFieldOf("experience", 0f).forGetter(MatterEntanglerRecipe::experience),
(DataComponentType.CODEC as Codec<DataComponentType<UUID>>).optionalFieldOf("uuidKey").forGetter(MatterEntanglerRecipe::uuidKey),
UUIDUtil.STRING_CODEC.optionalFieldOf("fixedUuid").forGetter(MatterEntanglerRecipe::fixedUuid)
).apply(it, ::MatterEntanglerRecipe)
}
val streamCodec: StreamCodec<RegistryFriendlyByteBuf, MatterEntanglerRecipe> = StreamCodecs.composite(
ShapedRecipePattern.STREAM_CODEC, MatterEntanglerRecipe::ingredients,
StreamCodecs.DECIMAL, { it.matter },
StreamCodecs.DOUBLE, MatterEntanglerRecipe::ticks,
ItemStack.STREAM_CODEC, MatterEntanglerRecipe::result,
StreamCodecs.FLOAT, MatterEntanglerRecipe::experience,
DataComponentType.STREAM_CODEC.wrap().optional() as StreamCodec<RegistryFriendlyByteBuf, Optional<DataComponentType<UUID>>>, MatterEntanglerRecipe::uuidKey,
UUIDUtil.STREAM_CODEC.wrap().optional(), MatterEntanglerRecipe::fixedUuid,
::MatterEntanglerRecipe
)
override fun codec(): MapCodec<MatterEntanglerRecipe> {
return codec
}
override fun streamCodec(): StreamCodec<RegistryFriendlyByteBuf, MatterEntanglerRecipe> {
return streamCodec
}
}
}