Codec2RecipeSerializer, move recipe registry to kotlin
This commit is contained in:
parent
7a2ce84e5f
commit
c0faf97bb8
@ -1,54 +0,0 @@
|
||||
package ru.dbotthepony.mc.otm.registry;
|
||||
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.world.item.crafting.Recipe;
|
||||
import net.minecraft.world.item.crafting.RecipeSerializer;
|
||||
import net.minecraft.world.item.crafting.RecipeType;
|
||||
import net.minecraftforge.eventbus.api.IEventBus;
|
||||
import net.minecraftforge.registries.DeferredRegister;
|
||||
import net.minecraftforge.registries.ForgeRegistries;
|
||||
import ru.dbotthepony.mc.otm.OverdriveThatMatters;
|
||||
import ru.dbotthepony.mc.otm.recipe.EnergyContainerRecipe;
|
||||
import ru.dbotthepony.mc.otm.recipe.ExplosiveHammerPrimingRecipe;
|
||||
import ru.dbotthepony.mc.otm.recipe.PlatePressRecipe;
|
||||
import ru.dbotthepony.mc.otm.recipe.UpgradeRecipe;
|
||||
|
||||
public class MRecipes {
|
||||
public static class MatteryRecipeType<T extends Recipe<?>> implements RecipeType<T> {
|
||||
public final ResourceLocation name;
|
||||
|
||||
private MatteryRecipeType(ResourceLocation name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return name.toString();
|
||||
}
|
||||
}
|
||||
|
||||
public static final MatteryRecipeType<PlatePressRecipe> PLATE_PRESS = new MatteryRecipeType<>(OverdriveThatMatters.loc(MNames.PLATE_PRESS));
|
||||
public static final MatteryRecipeType<PlatePressRecipe> ENERGY_CONTAINER = new MatteryRecipeType<>(OverdriveThatMatters.loc("energy_container"));
|
||||
public static final MatteryRecipeType<PlatePressRecipe> UPGRADE = new MatteryRecipeType<>(OverdriveThatMatters.loc("upgrade"));
|
||||
public static final MatteryRecipeType<PlatePressRecipe> HAMMER_PRIMING = new MatteryRecipeType<>(OverdriveThatMatters.loc("hammer_priming"));
|
||||
|
||||
private static final DeferredRegister<RecipeSerializer<?>> serializerRegistry = DeferredRegister.create(ForgeRegistries.RECIPE_SERIALIZERS, OverdriveThatMatters.MOD_ID);
|
||||
private static final DeferredRegister<RecipeType<?>> typeRegistry = DeferredRegister.create(ForgeRegistries.RECIPE_TYPES, OverdriveThatMatters.MOD_ID);
|
||||
|
||||
static {
|
||||
serializerRegistry.register(MNames.PLATE_PRESS, () -> PlatePressRecipe.Companion);
|
||||
serializerRegistry.register(ENERGY_CONTAINER.name.getPath(), () -> EnergyContainerRecipe.Companion);
|
||||
serializerRegistry.register(UPGRADE.name.getPath(), () -> UpgradeRecipe.Companion);
|
||||
serializerRegistry.register(HAMMER_PRIMING.name.getPath(), () -> ExplosiveHammerPrimingRecipe.Companion);
|
||||
|
||||
typeRegistry.register(MNames.PLATE_PRESS, () -> PLATE_PRESS);
|
||||
typeRegistry.register(ENERGY_CONTAINER.name.getPath(), () -> ENERGY_CONTAINER);
|
||||
typeRegistry.register(UPGRADE.name.getPath(), () -> UPGRADE);
|
||||
typeRegistry.register(HAMMER_PRIMING.name.getPath(), () -> HAMMER_PRIMING);
|
||||
}
|
||||
|
||||
public static void register(IEventBus bus) {
|
||||
serializerRegistry.register(bus);
|
||||
typeRegistry.register(bus);
|
||||
}
|
||||
}
|
@ -2,6 +2,7 @@ package ru.dbotthepony.mc.otm.core.util
|
||||
|
||||
import com.google.gson.JsonElement
|
||||
import com.mojang.serialization.Codec
|
||||
import com.mojang.serialization.DataResult
|
||||
import com.mojang.serialization.JsonOps
|
||||
import io.netty.buffer.ByteBufInputStream
|
||||
import io.netty.buffer.ByteBufOutputStream
|
||||
@ -29,6 +30,10 @@ fun <S> FriendlyByteBuf.readBinaryJsonWithCodec(codec: Codec<S>, sizeLimit: NbtA
|
||||
.get().map({ it.first }, { throw DecoderException("Failed to decode data from network: ${it.message()}") })
|
||||
}
|
||||
|
||||
fun <S> FriendlyByteBuf.readBinaryJsonWithCodecIndirect(codec: Codec<S>, sizeLimit: NbtAccounter = NbtAccounter(1L shl 18)): DataResult<S> {
|
||||
return codec.decode(JsonOps.INSTANCE, readBinaryJson(sizeLimit)).map { it.first }
|
||||
}
|
||||
|
||||
fun FriendlyByteBuf.readBinaryComponent(): Component {
|
||||
return Component.Serializer.fromJson(readBinaryJson()) ?: throw NullPointerException("Received null component")
|
||||
}
|
||||
|
@ -0,0 +1,86 @@
|
||||
package ru.dbotthepony.mc.otm.data
|
||||
|
||||
import com.google.gson.JsonObject
|
||||
import com.google.gson.JsonSyntaxException
|
||||
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 net.minecraft.network.FriendlyByteBuf
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
import net.minecraft.world.item.crafting.Recipe
|
||||
import net.minecraft.world.item.crafting.RecipeSerializer
|
||||
import org.apache.logging.log4j.LogManager
|
||||
import ru.dbotthepony.mc.otm.core.util.readBinaryJsonWithCodecIndirect
|
||||
import ru.dbotthepony.mc.otm.core.util.writeBinaryJsonWithCodec
|
||||
import java.util.*
|
||||
import kotlin.NoSuchElementException
|
||||
import kotlin.concurrent.getOrSet
|
||||
|
||||
class Codec2RecipeSerializer<S : Recipe<*>>(val empty: S?, val codec: Codec<S>) : Codec<S> by codec, RecipeSerializer<S> {
|
||||
constructor(supplier: (() -> ResourceLocation) -> Codec<S>) : this(null, supplier.invoke(::context))
|
||||
constructor(empty: S, supplier: (() -> ResourceLocation) -> Codec<S>) : this(empty, supplier.invoke(::context))
|
||||
|
||||
override fun fromJson(p_44103_: ResourceLocation, p_44104_: JsonObject): S {
|
||||
try {
|
||||
deck.getOrSet(::LinkedList).addLast(p_44103_)
|
||||
|
||||
return codec.decode(JsonOps.INSTANCE, p_44104_).get().map(
|
||||
{
|
||||
it.first
|
||||
},
|
||||
{
|
||||
empty ?: throw JsonSyntaxException("Failed to deserialize recipe from JSON: ${it.message()}")
|
||||
}
|
||||
)
|
||||
} finally {
|
||||
deck.get().removeLast()
|
||||
}
|
||||
}
|
||||
|
||||
override fun fromNetwork(p_44105_: ResourceLocation, p_44106_: FriendlyByteBuf): S? {
|
||||
try {
|
||||
deck.getOrSet(::LinkedList).addLast(p_44105_)
|
||||
|
||||
return p_44106_.readBinaryJsonWithCodecIndirect(codec)
|
||||
.resultOrPartial { LOGGER.error("Failed to read recipe $p_44105_ from network: $it") }.orElse(null)
|
||||
} finally {
|
||||
deck.get().removeLast()
|
||||
}
|
||||
}
|
||||
|
||||
override fun toNetwork(p_44101_: FriendlyByteBuf, p_44102_: S) {
|
||||
p_44101_.writeBinaryJsonWithCodec(codec, p_44102_)
|
||||
}
|
||||
|
||||
companion object : Codec<ResourceLocation> {
|
||||
private val deck = ThreadLocal<LinkedList<ResourceLocation>>()
|
||||
|
||||
private fun context(): ResourceLocation {
|
||||
val deck = deck.getOrSet(::LinkedList)
|
||||
|
||||
if (deck.isEmpty()) {
|
||||
throw NoSuchElementException("Context stack is empty")
|
||||
} else {
|
||||
return deck.last
|
||||
}
|
||||
}
|
||||
|
||||
override fun <T : Any> encode(input: ResourceLocation, ops: DynamicOps<T>, prefix: T): DataResult<T> {
|
||||
return DataResult.success(ops.empty())
|
||||
}
|
||||
|
||||
override fun <T : Any> decode(ops: DynamicOps<T>, input: T): DataResult<Pair<ResourceLocation, T>> {
|
||||
val deck = deck.getOrSet(::LinkedList)
|
||||
|
||||
if (deck.isEmpty()) {
|
||||
return DataResult.error { "Attempt to use recipe serializer codec ResourceLocation' hack outside Codec2RecipeSerializer" }
|
||||
} else {
|
||||
return DataResult.success(Pair(deck.last, ops.empty()))
|
||||
}
|
||||
}
|
||||
|
||||
private val LOGGER = LogManager.getLogger()
|
||||
}
|
||||
}
|
@ -1,10 +1,9 @@
|
||||
package ru.dbotthepony.mc.otm.recipe
|
||||
|
||||
import com.google.gson.JsonObject
|
||||
import com.google.gson.JsonPrimitive
|
||||
import com.mojang.serialization.Codec
|
||||
import com.mojang.serialization.codecs.RecordCodecBuilder
|
||||
import net.minecraft.core.NonNullList
|
||||
import net.minecraft.core.RegistryAccess
|
||||
import net.minecraft.network.FriendlyByteBuf
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
import net.minecraft.util.valueproviders.ConstantFloat
|
||||
import net.minecraft.util.valueproviders.FloatProvider
|
||||
@ -15,16 +14,14 @@ import net.minecraft.world.item.crafting.Recipe
|
||||
import net.minecraft.world.item.crafting.RecipeSerializer
|
||||
import net.minecraft.world.item.crafting.RecipeType
|
||||
import net.minecraft.world.level.Level
|
||||
import org.apache.logging.log4j.LogManager
|
||||
import ru.dbotthepony.mc.otm.OverdriveThatMatters
|
||||
import ru.dbotthepony.mc.otm.container.get
|
||||
import ru.dbotthepony.mc.otm.core.fromJsonStrict
|
||||
import ru.dbotthepony.mc.otm.core.isActuallyEmpty
|
||||
import ru.dbotthepony.mc.otm.registry.MRecipes
|
||||
import ru.dbotthepony.mc.otm.core.registryName
|
||||
import ru.dbotthepony.mc.otm.core.toJsonStrict
|
||||
import ru.dbotthepony.mc.otm.core.util.readBinaryJson
|
||||
import ru.dbotthepony.mc.otm.core.util.writeBinaryJson
|
||||
import ru.dbotthepony.mc.otm.data.Codec2RecipeSerializer
|
||||
import ru.dbotthepony.mc.otm.data.IngredientCodec
|
||||
import ru.dbotthepony.mc.otm.data.minRange
|
||||
|
||||
class PlatePressRecipe(
|
||||
private val id: ResourceLocation,
|
||||
@ -35,21 +32,21 @@ class PlatePressRecipe(
|
||||
val experience: FloatProvider = ConstantFloat.ZERO
|
||||
) : Recipe<Container> {
|
||||
override fun matches(container: Container, p_44003_: Level): Boolean {
|
||||
if (output.isActuallyEmpty || input.isActuallyEmpty)
|
||||
if (isIncomplete)
|
||||
return false
|
||||
|
||||
return input.test(container[0])
|
||||
}
|
||||
|
||||
fun matches(container: Container, slot: Int): Boolean {
|
||||
if (output.isActuallyEmpty || input.isActuallyEmpty)
|
||||
if (isIncomplete)
|
||||
return false
|
||||
|
||||
return input.test(container[slot])
|
||||
}
|
||||
|
||||
private val outputStack: ItemStack by lazy {
|
||||
if (output.isActuallyEmpty || input.isActuallyEmpty) {
|
||||
if (isIncomplete) {
|
||||
ItemStack.EMPTY
|
||||
} else {
|
||||
val items = output.items
|
||||
@ -59,7 +56,7 @@ class PlatePressRecipe(
|
||||
}
|
||||
|
||||
override fun getIngredients(): NonNullList<Ingredient> {
|
||||
if (input.isActuallyEmpty || output.isActuallyEmpty)
|
||||
if (isIncomplete)
|
||||
return super.getIngredients()
|
||||
|
||||
return NonNullList.of(Ingredient.EMPTY, input)
|
||||
@ -76,61 +73,23 @@ class PlatePressRecipe(
|
||||
override fun getId() = id
|
||||
|
||||
override fun getSerializer(): RecipeSerializer<*> {
|
||||
return Companion
|
||||
return SERIALIZER
|
||||
}
|
||||
|
||||
override fun getType(): RecipeType<PlatePressRecipe> = MRecipes.PLATE_PRESS
|
||||
|
||||
companion object : RecipeSerializer<PlatePressRecipe> {
|
||||
private val EMPTY = PlatePressRecipe(ResourceLocation(OverdriveThatMatters.MOD_ID, "empty"), Ingredient.EMPTY, Ingredient.EMPTY, 1)
|
||||
private val LOGGER = LogManager.getLogger()
|
||||
|
||||
override fun fromJson(loc: ResourceLocation, obj: JsonObject): PlatePressRecipe {
|
||||
val input = try {
|
||||
Ingredient.fromJson(obj["input"] ?: throw IllegalStateException("Recipe $loc has no input field defined"))
|
||||
} catch (err: Throwable) {
|
||||
if (err.message?.lowercase()?.contains("unknown item tag") == true) {
|
||||
LOGGER.warn("Ignoring recipe Input of $loc deserialization error, defaulting to empty recipe")
|
||||
LOGGER.warn(err)
|
||||
return EMPTY
|
||||
} else {
|
||||
throw IllegalStateException("Input of $loc is malformed", err)
|
||||
companion object {
|
||||
val SERIALIZER = Codec2RecipeSerializer<PlatePressRecipe>(PlatePressRecipe(ResourceLocation(OverdriveThatMatters.MOD_ID, "empty"), Ingredient.EMPTY, Ingredient.EMPTY, 1)) { context ->
|
||||
RecordCodecBuilder.create {
|
||||
it.group(
|
||||
IngredientCodec.fieldOf("input").forGetter(PlatePressRecipe::input),
|
||||
IngredientCodec.fieldOf("output").forGetter(PlatePressRecipe::output),
|
||||
Codec.INT.minRange(1).fieldOf("count").forGetter(PlatePressRecipe::count),
|
||||
Codec.INT.minRange(0).optionalFieldOf("workTime", 200).forGetter(PlatePressRecipe::workTime),
|
||||
FloatProvider.CODEC.optionalFieldOf("experience", ConstantFloat.ZERO).forGetter(PlatePressRecipe::experience)
|
||||
).apply(it) { a, b, c, d, e -> PlatePressRecipe(context.invoke(), a, b, c, d, e) }
|
||||
}
|
||||
}
|
||||
|
||||
val result = try {
|
||||
Ingredient.fromJson(obj["result"] ?: throw IllegalStateException("Recipe $loc has no result field defined"))
|
||||
} catch (err: Throwable) {
|
||||
if (err.message?.lowercase()?.contains("unknown item tag") == true) {
|
||||
LOGGER.warn("Ignoring recipe Output of $loc deserialization error, defaulting to empty recipe")
|
||||
LOGGER.warn(err)
|
||||
return EMPTY
|
||||
} else {
|
||||
throw IllegalStateException("Result of $loc is malformed", err)
|
||||
}
|
||||
}
|
||||
|
||||
val workTime = (obj["work_time"] as? JsonPrimitive)?.let { return@let try {it.asInt} catch(err: Throwable) {throw IllegalStateException("Invalid work_time")} } ?: 200
|
||||
|
||||
check(workTime >= 0) { "work_time of $loc does not make any sense" }
|
||||
|
||||
val count = ((obj["result"] as JsonObject)["count"] as? JsonPrimitive)?.let { return@let try {it.asInt} catch(err: Throwable) {throw IllegalStateException("Invalid result.count")} } ?: 1
|
||||
|
||||
val experience = obj["experience"]?.let { FloatProvider.CODEC.fromJsonStrict(it) } ?: ConstantFloat.ZERO
|
||||
return PlatePressRecipe(loc, input, result, count, workTime, experience)
|
||||
}
|
||||
|
||||
override fun fromNetwork(loc: ResourceLocation, buff: FriendlyByteBuf): PlatePressRecipe {
|
||||
return PlatePressRecipe(loc, Ingredient.fromNetwork(buff), Ingredient.fromNetwork(buff), buff.readInt(), buff.readInt(), FloatProvider.CODEC.fromJsonStrict(buff.readBinaryJson()))
|
||||
}
|
||||
|
||||
override fun toNetwork(buff: FriendlyByteBuf, recipe: PlatePressRecipe) {
|
||||
recipe.input.toNetwork(buff)
|
||||
recipe.output.toNetwork(buff)
|
||||
buff.writeInt(recipe.count)
|
||||
buff.writeInt(recipe.workTime)
|
||||
buff.writeBinaryJson(FloatProvider.CODEC.toJsonStrict(recipe.experience))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
46
src/main/kotlin/ru/dbotthepony/mc/otm/registry/MRecipes.kt
Normal file
46
src/main/kotlin/ru/dbotthepony/mc/otm/registry/MRecipes.kt
Normal file
@ -0,0 +1,46 @@
|
||||
package ru.dbotthepony.mc.otm.registry
|
||||
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
import net.minecraft.world.item.crafting.Recipe
|
||||
import net.minecraft.world.item.crafting.RecipeType
|
||||
import net.minecraftforge.eventbus.api.IEventBus
|
||||
import net.minecraftforge.registries.DeferredRegister
|
||||
import net.minecraftforge.registries.ForgeRegistries
|
||||
import net.minecraftforge.registries.RegistryObject
|
||||
import ru.dbotthepony.mc.otm.OverdriveThatMatters
|
||||
import ru.dbotthepony.mc.otm.recipe.EnergyContainerRecipe
|
||||
import ru.dbotthepony.mc.otm.recipe.ExplosiveHammerPrimingRecipe
|
||||
import ru.dbotthepony.mc.otm.recipe.PlatePressRecipe
|
||||
import ru.dbotthepony.mc.otm.recipe.UpgradeRecipe
|
||||
|
||||
@Suppress("SameParameterValue")
|
||||
object MRecipes {
|
||||
class Type<T : Recipe<*>>(id: String) : RecipeType<T> {
|
||||
val id = ResourceLocation(OverdriveThatMatters.MOD_ID, id)
|
||||
|
||||
override fun toString(): String {
|
||||
return id.toString()
|
||||
}
|
||||
}
|
||||
|
||||
private val types = DeferredRegister.create(ForgeRegistries.RECIPE_TYPES, OverdriveThatMatters.MOD_ID)
|
||||
private val serializers = DeferredRegister.create(ForgeRegistries.RECIPE_SERIALIZERS, OverdriveThatMatters.MOD_ID)
|
||||
|
||||
internal fun register(bus: IEventBus) {
|
||||
types.register(bus)
|
||||
serializers.register(bus)
|
||||
}
|
||||
|
||||
private fun <T : Recipe<*>> register(name: String): RegistryObject<Type<T>> {
|
||||
return types.register(name) { Type(name) }
|
||||
}
|
||||
|
||||
val PLATE_PRESS by register<PlatePressRecipe>("plate_press")
|
||||
|
||||
init {
|
||||
serializers.register("plate_press") { PlatePressRecipe.SERIALIZER }
|
||||
serializers.register("energy_container") { EnergyContainerRecipe.Companion }
|
||||
serializers.register("upgrade") { UpgradeRecipe.Companion }
|
||||
serializers.register("hammer_priming") { ExplosiveHammerPrimingRecipe.Companion }
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user