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.connection.ConnectionType
import net.neoforged.neoforge.network.handling.IPayloadContext import net.neoforged.neoforge.network.handling.IPayloadContext
import net.neoforged.neoforge.network.registration.PayloadRegistrar import net.neoforged.neoforge.network.registration.PayloadRegistrar
import java.util.Optional
import kotlin.reflect.KFunction1 import kotlin.reflect.KFunction1
fun <T : CustomPacketPayload> PayloadRegistrar.playToClient( 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> 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> 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 { fun <V> StreamCodec<in RegistryFriendlyByteBuf, V>.encode(registry: RegistryAccess, value: V): ByteArrayList {
return encodePayload(registry) { 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> { 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 { override fun decode(stream: S): C {
val size = stream.readVarInt() val size = stream.readVarInt()

View File

@ -3,17 +3,14 @@ package ru.dbotthepony.mc.otm.network
import io.netty.buffer.ByteBuf import io.netty.buffer.ByteBuf
import net.minecraft.core.UUIDUtil import net.minecraft.core.UUIDUtil
import net.minecraft.network.FriendlyByteBuf import net.minecraft.network.FriendlyByteBuf
import net.minecraft.network.RegistryFriendlyByteBuf
import net.minecraft.network.codec.ByteBufCodecs import net.minecraft.network.codec.ByteBufCodecs
import net.minecraft.network.codec.StreamCodec import net.minecraft.network.codec.StreamCodec
import net.minecraft.resources.ResourceLocation import net.minecraft.resources.ResourceLocation
import net.minecraft.util.valueproviders.FloatProvider
import ru.dbotthepony.kommons.math.RGBAColor import ru.dbotthepony.kommons.math.RGBAColor
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 ru.dbotthepony.mc.otm.core.readItemType import ru.dbotthepony.mc.otm.core.readItemType
import ru.dbotthepony.mc.otm.core.writeItemType import ru.dbotthepony.mc.otm.core.writeItemType
import kotlin.reflect.KMutableProperty1
object StreamCodecs { object StreamCodecs {
val NOTHING: MatteryStreamCodec<ByteBuf, Nothing?> = StreamCodec.of<ByteBuf, Nothing?>({ _, _ -> }, { null }).wrap() 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 = StreamCodec.of(FriendlyByteBuf::writeItemType, FriendlyByteBuf::readItemType).wrap()
val ITEM_TYPE_NULLABLE = ITEM_TYPE.nullable() val ITEM_TYPE_NULLABLE = ITEM_TYPE.nullable()
val DECIMAL = StreamCodec.of(FriendlyByteBuf::writeDecimal, FriendlyByteBuf::readDecimal).wrap() 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 package ru.dbotthepony.mc.otm.recipe
import net.minecraft.core.HolderLookup
import net.minecraft.core.NonNullList import net.minecraft.core.NonNullList
import net.minecraft.world.item.ItemStack import net.minecraft.world.item.ItemStack
import net.minecraft.world.item.crafting.Ingredient import net.minecraft.world.item.crafting.Ingredient
import net.minecraft.world.item.crafting.Recipe import net.minecraft.world.item.crafting.Recipe
import net.minecraft.world.item.crafting.RecipeInput 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 // 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 // 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 package ru.dbotthepony.mc.otm.recipe
import com.mojang.serialization.Codec import com.mojang.serialization.Codec
import com.mojang.serialization.MapCodec
import com.mojang.serialization.codecs.RecordCodecBuilder import com.mojang.serialization.codecs.RecordCodecBuilder
import net.minecraft.core.HolderLookup import net.minecraft.core.HolderLookup
import net.minecraft.core.NonNullList import net.minecraft.core.NonNullList
import net.minecraft.core.RegistryAccess
import net.minecraft.core.UUIDUtil 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.ItemStack
import net.minecraft.world.item.crafting.CraftingInput import net.minecraft.world.item.crafting.CraftingInput
import net.minecraft.world.item.crafting.Ingredient import net.minecraft.world.item.crafting.Ingredient
import net.minecraft.world.item.crafting.RecipeSerializer import net.minecraft.world.item.crafting.RecipeSerializer
import net.minecraft.world.item.crafting.RecipeType import net.minecraft.world.item.crafting.RecipeType
import net.minecraft.world.item.crafting.ShapedRecipePattern
import net.minecraft.world.level.Level 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.capability.matteryEnergy
import ru.dbotthepony.mc.otm.core.collect.filterNotNull import ru.dbotthepony.mc.otm.core.collect.filterNotNull
import ru.dbotthepony.mc.otm.core.collect.map import ru.dbotthepony.mc.otm.core.collect.map
import ru.dbotthepony.mc.otm.core.math.Decimal 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.DecimalCodec
import ru.dbotthepony.mc.otm.data.IngredientMatrixCodec
import ru.dbotthepony.mc.otm.data.minRange 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.MItems
import ru.dbotthepony.mc.otm.registry.MRecipes import ru.dbotthepony.mc.otm.registry.MRecipes
import java.util.Optional import java.util.Optional
@ -30,7 +35,7 @@ import kotlin.jvm.optionals.getOrElse
interface IMatterEntanglerRecipe : IMatteryRecipe<CraftingInput> { interface IMatterEntanglerRecipe : IMatteryRecipe<CraftingInput> {
val matter: Decimal val matter: Decimal
val ticks: Double val ticks: Double
val ingredients: IIngredientMatrix val ingredients: ShapedRecipePattern
val result: ItemStack val result: ItemStack
val experience: Float val experience: Float
@ -38,12 +43,12 @@ interface IMatterEntanglerRecipe : IMatteryRecipe<CraftingInput> {
} }
open class MatterEntanglerRecipe( open class MatterEntanglerRecipe(
override val ingredients: IIngredientMatrix, override val ingredients: ShapedRecipePattern,
override val matter: Decimal, override val matter: Decimal,
override val ticks: Double, override val ticks: Double,
override val result: ItemStack, override val result: ItemStack,
override val experience: Float = 0f, override val experience: Float = 0f,
val uuidKey: String = "uuid", val uuidKey: Optional<DataComponentType<UUID>> = Optional.empty(),
val fixedUuid: Optional<UUID> = Optional.empty() val fixedUuid: Optional<UUID> = Optional.empty()
) : IMatterEntanglerRecipe { ) : IMatterEntanglerRecipe {
override fun matches(container: CraftingInput, level: Level): Boolean { override fun matches(container: CraftingInput, level: Level): Boolean {
@ -58,7 +63,9 @@ open class MatterEntanglerRecipe(
override fun assemble(container: CraftingInput, registry: HolderLookup.Provider): ItemStack { override fun assemble(container: CraftingInput, registry: HolderLookup.Provider): ItemStack {
return result.copy().also { 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 return width >= ingredients.width && height >= ingredients.height
} }
override fun getResultItem(registry: RegistryAccess): ItemStack { override fun getResultItem(registry: HolderLookup.Provider): ItemStack {
return result return result
} }
override fun getSerializer(): RecipeSerializer<*> { override fun getSerializer(): RecipeSerializer<*> {
return SERIALIZER return Companion
} }
override fun getType(): RecipeType<*> { override fun getType(): RecipeType<*> {
@ -79,7 +86,7 @@ open class MatterEntanglerRecipe(
} }
override fun getIngredients(): NonNullList<Ingredient> { override fun getIngredients(): NonNullList<Ingredient> {
return ingredients.ingredients return ingredients.ingredients()
} }
override fun isIncomplete(): Boolean { override fun isIncomplete(): Boolean {
@ -94,10 +101,6 @@ open class MatterEntanglerRecipe(
return ItemStack(MItems.MATTER_ENTANGLER) return ItemStack(MItems.MATTER_ENTANGLER)
} }
fun toFinished(id: ResourceLocation): FinishedRecipe {
return SERIALIZER.toFinished(this, id)
}
fun energetic() = Energy(this) fun energetic() = Energy(this)
fun matter() = Matter(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<*> { override fun getSerializer(): RecipeSerializer<*> {
return ENERGY_SERIALIZER return EnergySerializer
} }
} }
open class Matter(val parent: MatterEntanglerRecipe) : IMatterEntanglerRecipe by parent { open class Matter(val parent: MatterEntanglerRecipe) : IMatterEntanglerRecipe by parent {
override fun assemble(container: CraftingInput, registry: HolderLookup.Provider): ItemStack { override fun assemble(container: CraftingInput, registry: HolderLookup.Provider): ItemStack {
return parent.assemble(container, registry).also { result -> return parent.assemble(container, registry).also { result ->
container.items().iterator().map { it.matter }.filterNotNull().forEach { container.items().iterator().map { it.getCapability(MatteryCapability.MATTER_ITEM) }.filterNotNull().forEach {
result.matter!!.storedMatter += it.storedMatter result.getCapability(MatteryCapability.MATTER_ITEM)!!.storedMatter += it.storedMatter
} }
} }
} }
fun toFinished(id: ResourceLocation): FinishedRecipe {
return MATTER_SERIALIZER.toFinished(this, id)
}
override fun getSerializer(): RecipeSerializer<*> { override fun getSerializer(): RecipeSerializer<*> {
return MATTER_SERIALIZER return MatterSerializer
} }
} }
companion object { private object EnergySerializer : RecipeSerializer<Energy> {
val SERIALIZER = Codec2RecipeSerializer<MatterEntanglerRecipe> { context -> private val energyCodec: MapCodec<Energy> = codec.xmap(::Energy, Energy::parent)
RecordCodecBuilder.create { private val energyStreamCodec: StreamCodec<RegistryFriendlyByteBuf, Energy> = streamCodec.map(::Energy, Energy::parent)
it.group(
IngredientMatrixCodec(context.ingredients).fieldOf("ingredients").forGetter(MatterEntanglerRecipe::ingredients), override fun codec(): MapCodec<Energy> {
DecimalCodec.minRange(Decimal.ZERO).fieldOf("matter").forGetter(MatterEntanglerRecipe::matter), return energyCodec
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)
}
} }
val ENERGY_SERIALIZER = SERIALIZER.xmap(::Energy, Energy::parent) override fun streamCodec(): StreamCodec<RegistryFriendlyByteBuf, Energy> {
val MATTER_SERIALIZER = SERIALIZER.xmap(::Matter, Matter::parent) 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
}
} }
} }