From e238ceceaf7b1de6a4714b730ae5da026a7efcef Mon Sep 17 00:00:00 2001 From: DBotThePony Date: Wed, 8 Feb 2023 23:15:11 +0700 Subject: [PATCH] Plate press recipes experience points Fixes #139 --- .../datagen/recipes/MatteryRecipeProvider.kt | 7 +- .../recipes/PlatePressFinishedRecipe.kt | 13 +- .../otm/datagen/recipes/PlatePressRecipes.kt | 20 +-- .../dbotthepony/mc/otm/GlobalEventHandler.kt | 13 ++ .../block/entity/MatteryWorkerBlockEntity.kt | 14 +- .../entity/tech/PlatePressBlockEntity.kt | 25 ++- .../mc/otm/block/tech/EnergyCounterBlock.kt | 2 +- .../compat/jei/PlatePressRecipeCategory.kt | 6 + .../mc/otm/core/UnOverengineering.kt | 39 +++++ .../mc/otm/core/util/FriendlyStreams.kt | 145 ++++++++++++++++++ .../ru/dbotthepony/mc/otm/menu/Slots.kt | 12 +- .../mc/otm/menu/tech/PlatePressMenu.kt | 3 +- .../mc/otm/recipe/PlatePressRecipe.kt | 15 +- 13 files changed, 286 insertions(+), 28 deletions(-) diff --git a/src/data/kotlin/ru/dbotthepony/mc/otm/datagen/recipes/MatteryRecipeProvider.kt b/src/data/kotlin/ru/dbotthepony/mc/otm/datagen/recipes/MatteryRecipeProvider.kt index 54d3e6ab0..9472720e7 100644 --- a/src/data/kotlin/ru/dbotthepony/mc/otm/datagen/recipes/MatteryRecipeProvider.kt +++ b/src/data/kotlin/ru/dbotthepony/mc/otm/datagen/recipes/MatteryRecipeProvider.kt @@ -10,6 +10,8 @@ import net.minecraft.data.recipes.RecipeBuilder import net.minecraft.data.recipes.RecipeProvider import net.minecraft.resources.ResourceLocation import net.minecraft.tags.TagKey +import net.minecraft.util.valueproviders.ConstantFloat +import net.minecraft.util.valueproviders.FloatProvider import net.minecraft.world.item.Item import net.minecraft.world.item.crafting.Ingredient import net.minecraft.world.level.ItemLike @@ -77,14 +79,15 @@ class MatteryRecipeProvider(generatorIn: DataGenerator) : RecipeProvider(generat } } - fun plate(id: String, count: Int = 1, workTicks: Int = 200) { + fun plate(id: String, count: Int = 1, workTicks: Int = 200, experience: FloatProvider = ConstantFloat.ZERO) { exec { _, consumer -> consumer.accept(PlatePressShallowFinishedRecipe( ResourceLocation(DataGen.MOD_ID, "plates/$id"), ResourceLocation("forge", "ingots/$id"), ResourceLocation("forge", "plates/$id"), count, - workTicks + workTicks, + experience = experience )) } } diff --git a/src/data/kotlin/ru/dbotthepony/mc/otm/datagen/recipes/PlatePressFinishedRecipe.kt b/src/data/kotlin/ru/dbotthepony/mc/otm/datagen/recipes/PlatePressFinishedRecipe.kt index 996211d7f..4fac241ba 100644 --- a/src/data/kotlin/ru/dbotthepony/mc/otm/datagen/recipes/PlatePressFinishedRecipe.kt +++ b/src/data/kotlin/ru/dbotthepony/mc/otm/datagen/recipes/PlatePressFinishedRecipe.kt @@ -2,14 +2,18 @@ package ru.dbotthepony.mc.otm.datagen.recipes import com.google.gson.JsonObject import com.google.gson.JsonPrimitive +import com.mojang.serialization.JsonOps import net.minecraft.data.recipes.FinishedRecipe import net.minecraft.resources.ResourceLocation +import net.minecraft.util.valueproviders.ConstantFloat +import net.minecraft.util.valueproviders.FloatProvider +import net.minecraft.util.valueproviders.UniformFloat import net.minecraft.world.item.crafting.RecipeSerializer import ru.dbotthepony.mc.otm.recipe.PlatePressRecipe import ru.dbotthepony.mc.otm.recipe.PlatePressRecipeFactory -import ru.dbotthepony.mc.otm.container.set -import ru.dbotthepony.mc.otm.core.nbt.set import ru.dbotthepony.mc.otm.core.set +import ru.dbotthepony.mc.otm.core.toJsonStrict +import ru.dbotthepony.mc.otm.data.getOrNull class PlatePressFinishedRecipe(private val recipe: PlatePressRecipe) : FinishedRecipe { override fun serializeRecipeData(it: JsonObject) { @@ -21,6 +25,7 @@ class PlatePressFinishedRecipe(private val recipe: PlatePressRecipe) : FinishedR } it["work_time"] = JsonPrimitive(recipe.workTime) + it["experience"] = FloatProvider.CODEC.toJsonStrict(recipe.experience) } override fun getId(): ResourceLocation { @@ -45,7 +50,8 @@ class PlatePressShallowFinishedRecipe( private val input: ResourceLocation, private val output: ResourceLocation, private val count: Int = 1, - private val workTime: Int = 200 + private val workTime: Int = 200, + private val experience: FloatProvider = ConstantFloat.ZERO, ) : FinishedRecipe { override fun serializeRecipeData(it: JsonObject) { it["input"] = JsonObject().also { @@ -60,6 +66,7 @@ class PlatePressShallowFinishedRecipe( } it["work_time"] = JsonPrimitive(workTime) + it["experience"] = FloatProvider.CODEC.toJsonStrict(experience) } override fun getId(): ResourceLocation { diff --git a/src/data/kotlin/ru/dbotthepony/mc/otm/datagen/recipes/PlatePressRecipes.kt b/src/data/kotlin/ru/dbotthepony/mc/otm/datagen/recipes/PlatePressRecipes.kt index 2131d5383..40dc88454 100644 --- a/src/data/kotlin/ru/dbotthepony/mc/otm/datagen/recipes/PlatePressRecipes.kt +++ b/src/data/kotlin/ru/dbotthepony/mc/otm/datagen/recipes/PlatePressRecipes.kt @@ -1,19 +1,21 @@ package ru.dbotthepony.mc.otm.datagen.recipes +import net.minecraft.util.valueproviders.ConstantFloat + fun addPlatePressRecipes(provider: MatteryRecipeProvider) { - val baselineMetals = arrayOf("iron", "silver", "bronze", "lead", "constantan", "brass") - val softMetals = arrayOf("gold", "aluminum", "aluminium", "copper", "electrum", "zinc") - val hardMetals = arrayOf("tritanium", "steel", "tungsten", "uranium") + val baselineMetals = arrayOf("iron" to 0.2f, "silver" to 0.3f, "bronze" to 0.3f, "lead" to 0.3f, "constantan" to 0.4f, "brass" to 0.3f) + val softMetals = arrayOf("gold" to 0.4f, "aluminum" to 0.3f, "aluminium" to 0.3f, "copper" to 0.2f, "electrum" to 0.4f, "zinc" to 0.3f) + val hardMetals = arrayOf("tritanium" to 0.5f, "steel" to 0.5f, "tungsten" to 0.55f, "uranium" to 0.5f) - for (thing in baselineMetals) { - provider.plate(thing) + for ((thing, exp) in baselineMetals) { + provider.plate(thing, experience = ConstantFloat.of(exp), workTicks = 160) } - for (thing in softMetals) { - provider.plate(thing, workTicks = 140) + for ((thing, exp) in softMetals) { + provider.plate(thing, workTicks = 120, experience = ConstantFloat.of(exp)) } - for (thing in hardMetals) { - provider.plate(thing, workTicks = 300) + for ((thing, exp) in hardMetals) { + provider.plate(thing, workTicks = 240, experience = ConstantFloat.of(exp)) } } diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/GlobalEventHandler.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/GlobalEventHandler.kt index 08527452f..1d5d80e8f 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/GlobalEventHandler.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/GlobalEventHandler.kt @@ -5,6 +5,7 @@ package ru.dbotthepony.mc.otm import net.minecraft.client.server.IntegratedServer import net.minecraft.server.MinecraftServer +import net.minecraft.server.level.ServerLevel import net.minecraft.world.level.Level import net.minecraftforge.api.distmarker.Dist import net.minecraftforge.event.TickEvent @@ -195,6 +196,8 @@ fun tickWhileServer(condition: () -> Boolean, ticker: () -> Unit) { } fun Level.once(ticker: ITickable) { + if (this.isClientSide) return + if (!SERVER_IS_LIVE) { LOGGER.error("Refusing to add ticker $ticker while server is dying", IllegalStateException("Server is stopping")) return @@ -204,6 +207,8 @@ fun Level.once(ticker: ITickable) { } fun Level.oncePre(ticker: ITickable) { + if (this.isClientSide) return + if (!SERVER_IS_LIVE) { LOGGER.error("Refusing to add ticker $ticker while server is dying", IllegalStateException("Server is stopping")) return @@ -213,6 +218,8 @@ fun Level.oncePre(ticker: ITickable) { } fun Level.addTicker(ticker: IConditionalTickable) { + if (this.isClientSide) return + if (!SERVER_IS_LIVE) { LOGGER.error("Refusing to add ticker $ticker while server is dying", IllegalStateException("Server is stopping")) return @@ -222,6 +229,8 @@ fun Level.addTicker(ticker: IConditionalTickable) { } fun Level.addTickerPre(ticker: IConditionalTickable) { + if (this.isClientSide) return + if (!SERVER_IS_LIVE) { LOGGER.error("Refusing to add ticker $ticker while server is dying", IllegalStateException("Server is stopping")) return @@ -231,18 +240,22 @@ fun Level.addTickerPre(ticker: IConditionalTickable) { } fun Level.until(ticker: () -> Boolean) { + if (this.isClientSide) return addTicker(IConditionalTickable.wrap(ticker)) } fun Level.untilPre(ticker: () -> Boolean) { + if (this.isClientSide) return addTickerPre(IConditionalTickable.wrap(ticker)) } fun Level.`while`(condition: () -> Boolean, ticker: () -> Unit) { + if (this.isClientSide) return addTicker(IConditionalTickable.wrap(condition, ticker)) } fun Level.whilePre(condition: () -> Boolean, ticker: () -> Unit) { + if (this.isClientSide) return addTickerPre(IConditionalTickable.wrap(condition, ticker)) } diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/MatteryWorkerBlockEntity.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/MatteryWorkerBlockEntity.kt index c829fd509..6084266ac 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/MatteryWorkerBlockEntity.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/MatteryWorkerBlockEntity.kt @@ -32,29 +32,34 @@ abstract class MatteryWorkerBlockEntity( open class Job { open val ticks: Double open val powerUsage: Decimal + open val experience: Float constructor( ticks: Double, - powerUsage: Decimal = Decimal.ZERO + powerUsage: Decimal = Decimal.ZERO, + experience: Float = 0f, ) { this.ticks = ticks this.powerUsage = powerUsage + this.experience = experience } constructor( tag: CompoundTag - ) : this(tag.getDouble(TICKS_KEY), tag.getDecimal(POWER_KEY)) + ) : this(tag.getDouble(TICKS_KEY), tag.getDecimal(POWER_KEY), tag.getFloat(EXPERIENCE_KEY)) open fun serializeNBT(): CompoundTag { return CompoundTag().also { it[TICKS_KEY] = ticks it[POWER_KEY] = powerUsage + it[EXPERIENCE_KEY] = experience } } companion object { const val TICKS_KEY = "ticks" const val POWER_KEY = "power" + const val EXPERIENCE_KEY = "power" } } @@ -65,8 +70,9 @@ abstract class MatteryWorkerBlockEntity( constructor( itemStack: ItemStack, ticks: Double, - power: Decimal = Decimal.ZERO - ) : super(ticks, power) { + power: Decimal = Decimal.ZERO, + experience: Float = 0f, + ) : super(ticks, power, experience) { this.itemStack = itemStack } diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/tech/PlatePressBlockEntity.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/tech/PlatePressBlockEntity.kt index 9b6804ed4..aeb8085a4 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/tech/PlatePressBlockEntity.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/tech/PlatePressBlockEntity.kt @@ -4,7 +4,10 @@ import net.minecraft.core.BlockPos import net.minecraft.core.Direction import net.minecraft.nbt.CompoundTag import net.minecraft.network.chat.Component +import net.minecraft.server.level.ServerLevel +import net.minecraft.server.level.ServerPlayer import net.minecraft.world.Container +import net.minecraft.world.entity.ExperienceOrb import net.minecraft.world.entity.player.Inventory import net.minecraft.world.entity.player.Player import net.minecraft.world.inventory.AbstractContainerMenu @@ -26,16 +29,27 @@ import ru.dbotthepony.mc.otm.registry.MBlockEntities import ru.dbotthepony.mc.otm.registry.MRecipes import ru.dbotthepony.mc.otm.core.nbt.map import ru.dbotthepony.mc.otm.core.nbt.set +import ru.dbotthepony.mc.otm.once +import ru.dbotthepony.mc.otm.onceServer class PlatePressBlockEntity( p_155229_: BlockPos, p_155230_: BlockState -) : MatteryWorkerBlockEntity(MBlockEntities.PLATE_PRESS, p_155229_, p_155230_, - ::ItemJob -), IDroppableContainer { +) : MatteryWorkerBlockEntity(MBlockEntities.PLATE_PRESS, p_155229_, p_155230_, ::ItemJob), IDroppableContainer { val container = MatteryContainer(this::setChangedLight, 2) override val energy = WorkerEnergyStorage(this::setChangedLight, ServerConfig.PLATE_PRESS) + var experience = 0.0 + + fun popExperience(player: ServerPlayer) { + val whole = experience.toInt() + + if (whole > 0) { + experience -= whole + ExperienceOrb.award(player.level as ServerLevel, player.position(), whole) + } + } + override val droppableContainer: Container get() = container val itemHandler = container.handler(object : MatteryContainerHooks { @@ -68,11 +82,13 @@ class PlatePressBlockEntity( override fun saveAdditional(nbt: CompoundTag) { super.saveAdditional(nbt) nbt[INVENTORY_KEY] = container.serializeNBT() + nbt["experience"] = experience } override fun load(nbt: CompoundTag) { super.load(nbt) nbt.map(INVENTORY_KEY, container::deserializeNBT) + experience = nbt.getDouble("experience") } override val defaultDisplayName: Component @@ -89,6 +105,7 @@ class PlatePressBlockEntity( if (!container.fullyAddItem(job.itemStack, start = SLOT_OUTPUT, end = SLOT_OUTPUT)) return Status.FAILURE_ITEM + experience = (experience + job.experience).coerceAtMost(100.0) return Status.SUCCESS } @@ -99,7 +116,7 @@ class PlatePressBlockEntity( val recipe = level?.recipeManager?.getRecipeFor(MRecipes.PLATE_PRESS, container, level!!)?.orElse(null) ?: return null to IdleReason.ITEM container[SLOT_INPUT].shrink(1) - return ItemJob(recipe.resultItem, recipe.workTime.toDouble(), BASELINE_CONSUMPTION) to null + return ItemJob(recipe.resultItem, recipe.workTime.toDouble(), BASELINE_CONSUMPTION, experience = recipe.experience.sample(level!!.random)) to null } companion object { diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/block/tech/EnergyCounterBlock.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/block/tech/EnergyCounterBlock.kt index cb617b551..e393d52f5 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/block/tech/EnergyCounterBlock.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/block/tech/EnergyCounterBlock.kt @@ -73,7 +73,7 @@ class EnergyCounterBlock : MatteryBlock(), EntityBlock { ) { super.neighborChanged(state, level, pos, neighbour, neighbourPos, movedByPiston) - if (!level.isClientSide && SERVER_IS_LIVE) { + if (SERVER_IS_LIVE) { level.once { (level.getBlockEntity(pos) as? EnergyCounterBlockEntity)?.checkSurroundings() } diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/compat/jei/PlatePressRecipeCategory.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/compat/jei/PlatePressRecipeCategory.kt index 408930ca5..61e9cb38c 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/compat/jei/PlatePressRecipeCategory.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/compat/jei/PlatePressRecipeCategory.kt @@ -71,6 +71,12 @@ object PlatePressRecipeCategory : IRecipeCategory, IDrawable { mouseY: Double ) { minecraft.font.drawAligned(stack, TranslatableComponent("otm.gui.recipe.ticks", recipe.workTime), TextAlign.TOP_CENTER, 40f, 30f, RGBAColor.BLACK) + + val average = recipe.experience.toString() + + if (average != "0.0") { + minecraft.font.drawAligned(stack, TranslatableComponent("gui.jei.category.smelting.experience", average), TextAlign.TOP_CENTER, 40f, 1f, RGBAColor.BLACK) + } } override fun getWidth(): Int { diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/core/UnOverengineering.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/core/UnOverengineering.kt index a409cfa02..c0fbd76df 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/core/UnOverengineering.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/core/UnOverengineering.kt @@ -1,5 +1,11 @@ package ru.dbotthepony.mc.otm.core +import com.google.gson.JsonElement +import com.google.gson.JsonSyntaxException +import com.mojang.serialization.Codec +import com.mojang.serialization.JsonOps +import net.minecraft.nbt.NbtOps +import net.minecraft.nbt.Tag import net.minecraft.network.FriendlyByteBuf import net.minecraft.network.chat.MutableComponent import net.minecraft.network.chat.contents.LiteralContents @@ -11,6 +17,39 @@ import net.minecraft.world.level.block.Block import net.minecraftforge.registries.ForgeRegistries import net.minecraftforge.registries.IForgeRegistry +// because doing it inline is ugly +fun Codec.fromJson(value: JsonElement): V? { + return decode(JsonOps.INSTANCE, value).get().map({ left -> left.first }, { null }) +} + +fun Codec.fromJsonStrict(value: JsonElement): V { + return decode(JsonOps.INSTANCE, value).get().map({ left -> left.first }, { throw JsonSyntaxException("Error decoding element: ${it.message()}") }) +} + +fun Codec.toJson(value: V): JsonElement? { + return encode(value, JsonOps.INSTANCE, JsonOps.INSTANCE.empty()).get().map({ it }, { null }) +} + +fun Codec.toJsonStrict(value: V): JsonElement { + return encode(value, JsonOps.INSTANCE, JsonOps.INSTANCE.empty()).get().map({ it }, { throw RuntimeException("Error encoding element: ${it.message()}") }) +} + +fun Codec.fromNbt(value: Tag): V? { + return decode(NbtOps.INSTANCE, value).get().map({ left -> left.first }, { null }) +} + +fun Codec.fromNbtStrict(value: Tag): V { + return decode(NbtOps.INSTANCE, value).get().map({ left -> left.first }, { throw RuntimeException("Error decoding element: ${it.message()}") }) +} + +fun Codec.toNbt(value: V): Tag? { + return encode(value, NbtOps.INSTANCE, NbtOps.INSTANCE.empty()).get().map({ it }, { null }) +} + +fun Codec.toNbtStrict(value: V): Tag { + return encode(value, NbtOps.INSTANCE, NbtOps.INSTANCE.empty()).get().map({ it }, { throw RuntimeException("Error encoding element: ${it.message()}") }) +} + // 1.19 being 1.19 fun TranslatableComponent(key: String, vararg values: Any): MutableComponent = MutableComponent.create(TranslatableContents(key, *values)) fun TextComponent(value: String): MutableComponent = MutableComponent.create(LiteralContents(value)) diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/core/util/FriendlyStreams.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/core/util/FriendlyStreams.kt index 4beddf9d8..01a6dc749 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/core/util/FriendlyStreams.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/core/util/FriendlyStreams.kt @@ -1,9 +1,19 @@ package ru.dbotthepony.mc.otm.core.util +import com.google.gson.JsonArray +import com.google.gson.JsonElement +import com.google.gson.JsonNull +import com.google.gson.JsonObject +import com.google.gson.JsonParseException +import com.google.gson.JsonPrimitive +import com.google.gson.JsonSyntaxException +import io.netty.buffer.ByteBufInputStream +import io.netty.buffer.ByteBufOutputStream import io.netty.handler.codec.EncoderException import net.minecraft.nbt.CompoundTag import net.minecraft.nbt.NbtAccounter import net.minecraft.nbt.NbtIo +import net.minecraft.network.FriendlyByteBuf import net.minecraft.world.item.Item import net.minecraft.world.item.ItemStack import net.minecraftforge.registries.ForgeRegistries @@ -11,6 +21,7 @@ import net.minecraftforge.registries.ForgeRegistry import java.io.* import java.math.BigDecimal import java.math.BigInteger +import kotlin.math.absoluteValue // But seriously, Mojang, why would you need to derive from ByteBuf directly, when you can implement // your own InputStream and OutputStream, since ByteBuf is meant to be operated on most time like a stream anyway? @@ -88,3 +99,137 @@ fun InputStream.readBigDecimal(): BigDecimal { return BigDecimal(BigInteger(bytes), scale) } +private const val TYPE_NULL = 0x01 +private const val TYPE_DOUBLE = 0x02 +private const val TYPE_BOOLEAN = 0x03 +private const val TYPE_INT = 0x04 +private const val TYPE_STRING = 0x05 +private const val TYPE_ARRAY = 0x06 +private const val TYPE_OBJECT = 0x07 + +private fun fixSignedInt(read: Long): Long { + val sign = read and 0x1L + @Suppress("name_shadowing") + val read = read ushr 1 + + if (sign == 1L) { + return -read - 1L + } else { + return read + } +} + +/** + * Writes binary json to stream in Starbound Object Notation format + * + * just copy pasted this code from my another project because i was lazy + */ +fun OutputStream.writeJson(element: JsonElement) { + if (element is JsonObject) { + write(TYPE_OBJECT) + writeVarIntLE(element.size()) + + for ((k, v) in element.entrySet()) { + writeBinaryString(k) + writeJson(v) + } + } else if (element is JsonArray) { + write(TYPE_ARRAY) + writeVarIntLE(element.size()) + + for (v in element) { + writeJson(v) + } + } else if (element is JsonPrimitive) { + if (element.isNumber) { + val num = element.asNumber + + if (num is Int || num is Long) { + write(TYPE_INT) + var int = num.toLong() + + if (int < 0) { + int = int.absoluteValue.shl(1).or(1) + } else { + int.shl(1) + } + + writeVarLongLE(int) + } else if (num is Float || num is Double) { + write(TYPE_DOUBLE) + writeDouble(num.toDouble()) + } else { + throw IllegalArgumentException("Unknown number type: ${num::class.qualifiedName}") + } + } else if (element.isString) { + write(TYPE_STRING) + writeBinaryString(element.asString) + } else if (element.isBoolean) { + write(TYPE_BOOLEAN) + write(if (element.asBoolean) 1 else 0) + } else { + write(TYPE_NULL) + } + } else { + throw IllegalArgumentException("Unknown element type: ${element::class.qualifiedName}") + } +} + +/** + * Reads binary json from stream in Starbound Object Notation format + * + * just copy pasted this code from my another project because i was lazy + */ +fun InputStream.readJson(): JsonElement { + return when (val id = read()) { + TYPE_NULL -> JsonNull.INSTANCE + TYPE_DOUBLE -> JsonPrimitive(readDouble()) + TYPE_BOOLEAN -> JsonPrimitive(read() > 1) + TYPE_INT -> JsonPrimitive(fixSignedInt(readVarLongLE())) + TYPE_STRING -> JsonPrimitive(readBinaryString()) + TYPE_ARRAY -> { + val values = readVarIntLE() + + if (values == 0) return JsonArray() + if (values < 0) throw JsonSyntaxException("Tried to read json array with $values elements in it") + + val build = JsonArray(values) + for (i in 0 until values) build.add(readJson()) + return build + } + TYPE_OBJECT -> { + val values = readVarIntLE() + if (values == 0) return JsonObject() + if (values < 0) throw JsonSyntaxException("Tried to read json object with $values elements in it") + + val build = JsonObject() + + for (i in 0 until values) { + val key: String + + try { + key = readBinaryString() + } catch(err: Throwable) { + throw JsonSyntaxException("Reading json object at $i", err) + } + + try { + build.add(key, readJson()) + } catch(err: Throwable) { + throw JsonSyntaxException("Reading json object at $i with name $key", err) + } + } + + return build + } + else -> throw JsonParseException("Unknown element type $id") + } +} + +fun FriendlyByteBuf.readJson(): JsonElement { + return ByteBufInputStream(this).readJson() +} + +fun FriendlyByteBuf.writeJson(value: JsonElement) { + ByteBufOutputStream(this).writeJson(value) +} diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/menu/Slots.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/menu/Slots.kt index 7eac16375..3b545428b 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/menu/Slots.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/menu/Slots.kt @@ -26,10 +26,20 @@ open class MatterySlot @JvmOverloads constructor(container: Container, index: In } } -open class MachineOutputSlot @JvmOverloads constructor(container: Container, index: Int, x: Int = 0, y: Int = 0) : MatterySlot(container, index, x, y) { +open class MachineOutputSlot @JvmOverloads constructor(container: Container, index: Int, x: Int = 0, y: Int = 0, val onTake: () -> Unit = {}) : MatterySlot(container, index, x, y) { override fun mayPlace(itemStack: ItemStack): Boolean { return false } + + override fun onTake(pPlayer: Player, pStack: ItemStack) { + super.onTake(pPlayer, pStack) + this.onTake.invoke() + } + + override fun onQuickCraft(pStack: ItemStack, pAmount: Int) { + super.onQuickCraft(pStack, pAmount) + this.onTake.invoke() + } } open class BatterySlot @JvmOverloads constructor(container: Container, index: Int, x: Int = 0, y: Int = 0) : MatterySlot(container, index, x, y) { diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/menu/tech/PlatePressMenu.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/menu/tech/PlatePressMenu.kt index ff88899f7..cd4f123cc 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/menu/tech/PlatePressMenu.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/menu/tech/PlatePressMenu.kt @@ -1,6 +1,7 @@ package ru.dbotthepony.mc.otm.menu.tech import com.google.common.collect.ImmutableList +import net.minecraft.server.level.ServerPlayer import net.minecraft.world.SimpleContainer import net.minecraft.world.entity.player.Inventory import ru.dbotthepony.mc.otm.block.entity.tech.PlatePressBlockEntity @@ -18,7 +19,7 @@ class PlatePressMenu @JvmOverloads constructor( val container = tile?.container ?: SimpleContainer(2) val inputSlot = MatterySlot(container, PlatePressBlockEntity.SLOT_INPUT) - val outputSlot = MachineOutputSlot(container, PlatePressBlockEntity.SLOT_OUTPUT) + val outputSlot = MachineOutputSlot(container, PlatePressBlockEntity.SLOT_OUTPUT) { tile?.popExperience(ply as ServerPlayer) } override val storageSlots: List = ImmutableList.of(inputSlot, outputSlot) diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/recipe/PlatePressRecipe.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/recipe/PlatePressRecipe.kt index 29f1431c4..70d2a8f6e 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/recipe/PlatePressRecipe.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/recipe/PlatePressRecipe.kt @@ -5,6 +5,8 @@ import com.google.gson.JsonPrimitive import net.minecraft.core.NonNullList import net.minecraft.network.FriendlyByteBuf import net.minecraft.resources.ResourceLocation +import net.minecraft.util.valueproviders.ConstantFloat +import net.minecraft.util.valueproviders.FloatProvider import net.minecraft.world.Container import net.minecraft.world.item.ItemStack import net.minecraft.world.item.crafting.Ingredient @@ -16,16 +18,21 @@ import org.apache.logging.log4j.LogManager import ru.dbotthepony.mc.otm.OverdriveThatMatters import ru.dbotthepony.mc.otm.block.entity.tech.PlatePressBlockEntity 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.readJson +import ru.dbotthepony.mc.otm.core.util.writeJson class PlatePressRecipe( private val id: ResourceLocation, val input: Ingredient, val output: Ingredient, val count: Int, - val workTime: Int = 200 + val workTime: Int = 200, + val experience: FloatProvider = ConstantFloat.ZERO ) : Recipe { override fun matches(container: Container, p_44003_: Level): Boolean { if (output.isActuallyEmpty || input.isActuallyEmpty) @@ -103,11 +110,12 @@ object PlatePressRecipeFactory : RecipeSerializer { val count = ((obj["result"] as JsonObject)["count"] as? JsonPrimitive)?.let { return@let try {it.asInt} catch(err: Throwable) {throw IllegalStateException("Invalid result.count")} } ?: 1 - return PlatePressRecipe(loc, input, result, count, workTime) + 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()) + return PlatePressRecipe(loc, Ingredient.fromNetwork(buff), Ingredient.fromNetwork(buff), buff.readInt(), buff.readInt(), FloatProvider.CODEC.fromJsonStrict(buff.readJson())) } override fun toNetwork(buff: FriendlyByteBuf, recipe: PlatePressRecipe) { @@ -115,5 +123,6 @@ object PlatePressRecipeFactory : RecipeSerializer { recipe.output.toNetwork(buff) buff.writeInt(recipe.count) buff.writeInt(recipe.workTime) + buff.writeJson(FloatProvider.CODEC.toJsonStrict(recipe.experience)) } }