diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/Jobs.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/Jobs.kt index e6517b2df..33f9e9b5c 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/Jobs.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/Jobs.kt @@ -1,114 +1,229 @@ package ru.dbotthepony.mc.otm.block.entity +import com.mojang.datafixers.Products +import com.mojang.serialization.Codec +import com.mojang.serialization.codecs.RecordCodecBuilder import net.minecraft.nbt.CompoundTag +import net.minecraft.nbt.NbtOps import net.minecraft.world.item.ItemStack import net.minecraftforge.common.util.INBTSerializable +import org.apache.logging.log4j.LogManager import ru.dbotthepony.mc.otm.capability.IMatteryUpgrade import ru.dbotthepony.mc.otm.capability.energy.IMatteryEnergyStorage import ru.dbotthepony.mc.otm.config.MachinesConfig import ru.dbotthepony.mc.otm.core.math.Decimal -import ru.dbotthepony.mc.otm.core.math.getDecimal -import ru.dbotthepony.mc.otm.core.math.set import ru.dbotthepony.mc.otm.core.math.weakEqualDoubles import ru.dbotthepony.mc.otm.core.math.weakGreaterThan import ru.dbotthepony.mc.otm.core.math.weakLessThan -import ru.dbotthepony.mc.otm.core.nbt.map import ru.dbotthepony.mc.otm.core.nbt.set +import ru.dbotthepony.mc.otm.data.DecimalCodec +import ru.dbotthepony.mc.otm.data.minRange private fun isReason(status: Any?, reason: Any) = status == null || status == reason +private val LOGGER = LogManager.getLogger() -interface IMachineJob { +interface IJob { val ticks: Double val powerUsage: Decimal val experience: Float get() = 0f - - fun serializeNBT(): CompoundTag } -open class MachineJob : IMachineJob { - final override val ticks: Double - final override val powerUsage: Decimal - final override val experience: Float +open class Job( + final override val ticks: Double, + final override val powerUsage: Decimal = Decimal.ZERO, + final override val experience: Float = 0f +) : IJob { + companion object { + fun basicCodec(builder: RecordCodecBuilder.Instance): Products.P3, Double, Decimal, Float> { + return builder.group( + Codec.doubleRange(0.0, Double.MAX_VALUE).fieldOf("ticks").forGetter(Job::ticks), + DecimalCodec.fieldOf("powerUsage").forGetter(Job::powerUsage), // не надо указывать минимальную энергию как 0, + // ибо мы можем таким образом использовать это для создания работ генератора + Codec.floatRange(0f, Float.MAX_VALUE).optionalFieldOf("experience", 0f).forGetter(Job::experience), + ) + } - constructor( - ticks: Double, - powerUsage: Decimal = Decimal.ZERO, - experience: Float = 0f, - ) { - this.ticks = ticks - this.powerUsage = powerUsage - this.experience = experience - } + fun plainCodec(builder: RecordCodecBuilder.Instance): Products.P2, Double, Decimal> { + return builder.group( + Codec.doubleRange(0.0, Double.MAX_VALUE).fieldOf("ticks").forGetter(Job::ticks), + DecimalCodec.fieldOf("powerUsage").forGetter(Job::powerUsage), // не надо указывать минимальную энергию как 0, + // ибо мы можем таким образом использовать это для создания работ генератора + ) + } - constructor( - tag: CompoundTag - ) : this(tag.getDouble("Ticks"), tag.getDecimal("EnergyUsage"), tag.getFloat("Experience")) - - override fun serializeNBT(): CompoundTag { - return CompoundTag().also { - it["Ticks"] = ticks - it["EnergyUsage"] = powerUsage - it["Experience"] = experience + val CODEC: Codec by lazy { + RecordCodecBuilder.create { + basicCodec(it).apply(it, ::Job) + } } } } -open class MachineItemJob : MachineJob { - val itemStack: ItemStack +open class ItemJob( + val itemStack: ItemStack, + ticks: Double, + power: Decimal = Decimal.ZERO, + experience: Float = 0f +) : Job(ticks, power, experience) { + companion object { + fun itemCodec(builder: RecordCodecBuilder.Instance): Products.P4, ItemStack, Double, Decimal, Float> { + return builder.group(ItemStack.CODEC.fieldOf("itemStack").forGetter(ItemJob::itemStack)).and(basicCodec(builder)) + } - constructor( - itemStack: ItemStack, - ticks: Double, - power: Decimal = Decimal.ZERO, - experience: Float = 0f, - ) : super(ticks, power, experience) { - this.itemStack = itemStack - } - - constructor( - tag: CompoundTag - ) : super(tag) { - this.itemStack = (tag["Item"] as? CompoundTag)?.let { ItemStack.of(it) } ?: ItemStack.EMPTY - } - - override fun serializeNBT(): CompoundTag { - return super.serializeNBT().also { - it["Item"] = itemStack.serializeNBT() + val CODEC: Codec by lazy { + RecordCodecBuilder.create { + itemCodec(it).apply(it, ::ItemJob) + } } } } -data class JobStatus( - val success: Boolean, - val throttleTicks: Int = 0, - val idleReason: MachineJobEventLoop.IdleReason? = null, - val newDrainedPower: Decimal? = null -) { - init { - require(throttleTicks >= 0) { "Negative amount of ticks to throttle: $throttleTicks" } +class JobStatus( + var requiredPower: Decimal, + val extractedPower: Decimal, + ticksAdvanced: Double, + val job: T, + val workTicks: Double, +) : IJob by job { + constructor(job: T, workTicks: Double) : this(Decimal.ZERO, Decimal.ZERO, 0.0, job, workTicks) + + val workProgress: Float + get() { + return ((workTicks + workTicks) / job.ticks).coerceAtMost(1.0).toFloat() + } + + var success = true + + var ticksAdvanced = ticksAdvanced + set(value) { + require(value >= 0.0) { "Invalid amount of ticks to advance: $value" } + field = value + } + + var throttleTicks = 0 + set(value) { + require(value >= 0) { "Invalid amount of ticks to throttle: $value" } + if (value != 0) success = false + field = value + } + + var idleReason: Any? = null + set(value) { + if (value != null) success = false + field = value + } + + /** + * You don't have to actually call this, JobStatus is in this state by default + * + * But this is handy to call this when doing early returns, to clearly state that "we are in success" + */ + fun success() { + this.success = true + this.throttleTicks = 0 + this.idleReason = null } - companion object { - val SUCCESS = JobStatus(true) - val FAILURE = JobStatus(false) - val FAILURE_ITEM = JobStatus(false, 20, MachineJobEventLoop.IdleReason.ITEM) - val FAILURE_MATTER = JobStatus(false, 20, MachineJobEventLoop.IdleReason.MATTER) - val FAILURE_WAIT = JobStatus(false, 100) - val FAILURE_WAIT_FAST = JobStatus(false, 20) + fun throttle() { + this.success = false + this.throttleTicks = 100 + } + + fun throttleFast() { + this.success = false + this.throttleTicks = 20 + } + + fun noItem(throttleTicks: Int = 20) { + this.success = false + idleReason = MachineJobEventLoop.IdleReason.ITEM + this.throttleTicks = throttleTicks + } + + fun noPower(throttleTicks: Int = 20) { + this.success = false + idleReason = MachineJobEventLoop.IdleReason.POWER + this.throttleTicks = throttleTicks + } + + fun noMatter(throttleTicks: Int = 20) { + this.success = false + idleReason = MachineJobEventLoop.IdleReason.MATTER + this.throttleTicks = throttleTicks + } + + fun noPattern(throttleTicks: Int = 20) { + this.success = false + idleReason = MachineJobEventLoop.IdleReason.PATTERN + this.throttleTicks = throttleTicks + } + + fun observe(throttleTicks: Int = 20) { + this.success = false + idleReason = MachineJobEventLoop.IdleReason.OBSERVING + this.throttleTicks = throttleTicks + } + + fun failure(reason: Any) { + this.success = false + idleReason = reason + } + + fun failure(throttleTicks: Int = 0) { + this.success = false + this.throttleTicks = throttleTicks + } + + fun scale(value: Decimal) { + require(value >= Decimal.ZERO) { "Attempted to scale by negative value: $value" } + if (value == Decimal.ONE) + return + + if (value.isZero) { + ticksAdvanced = 0.0 + requiredPower = Decimal.ZERO + success = false + } else { + requiredPower *= value + ticksAdvanced *= value.toDouble() + } + } + + fun scale(value: Double) { + require(value >= 0.0) { "Attempted to scale by negative value: $value" } + if (value == 1.0) + return + + if (value == 0.0) { + ticksAdvanced = 0.0 + requiredPower = Decimal.ZERO + success = false + } else { + requiredPower *= value + ticksAdvanced *= value + } } } -data class JobContainer(val job: JobType? = null, val idleReason: Any? = null, val throttleTicks: Int = 0) { +data class JobContainer(val job: JobType? = null, val idleReason: Any? = null, val throttleTicks: Int = 0) { init { require(throttleTicks >= 0) { "Negative amount of ticks to throttle: $throttleTicks" } + + if (job != null && idleReason != null) { + throw IllegalArgumentException("Can't have both job and idle reason specified") + } + + if (job != null && throttleTicks != 0) { + throw IllegalArgumentException("Can't have both job and throttle ticks specified") + } } companion object { - fun success(job: JobType): JobContainer { + fun success(job: JobType): JobContainer { return JobContainer(job, null) } - fun failure(reason: Any?): JobContainer { + fun failure(reason: Any?): JobContainer { return JobContainer(null, reason) } @@ -120,41 +235,40 @@ data class JobContainer(val job: JobType? = null, val idl private val observe = JobContainer(null, MachineJobEventLoop.IdleReason.OBSERVING) @Suppress("unchecked_cast") - fun failure(): JobContainer { + fun failure(): JobContainer { return empty as JobContainer } @Suppress("unchecked_cast") - fun noItem(): JobContainer { + fun noItem(): JobContainer { return noItem as JobContainer } @Suppress("unchecked_cast") - fun noEnergy(): JobContainer { + fun noEnergy(): JobContainer { return noEnergy as JobContainer } @Suppress("unchecked_cast") - fun noMatter(): JobContainer { + fun noMatter(): JobContainer { return noMatter as JobContainer } @Suppress("unchecked_cast") - fun noPattern(): JobContainer { + fun noPattern(): JobContainer { return noPattern as JobContainer } @Suppress("unchecked_cast") - fun observe(): JobContainer { + fun observe(): JobContainer { return observe as JobContainer } } } -abstract class MachineJobEventLoop : INBTSerializable { +abstract class MachineJobEventLoop(val codec: Codec) : INBTSerializable { protected abstract val energy: IMatteryEnergyStorage? protected abstract val isBlockedByRedstone: Boolean - protected abstract fun deserializeJob(nbt: CompoundTag): JobType? protected abstract val upgrades: IMatteryUpgrade? var currentJob: JobType? = null @@ -212,7 +326,17 @@ abstract class MachineJobEventLoop : INBTSerializable nbt["WorkTicks"] = workTicks - currentJob?.let { nbt["Job"] = it.serializeNBT() } + + currentJob?.let { + codec.encode(it, NbtOps.INSTANCE, NbtOps.INSTANCE.empty()).get().map( + { + nbt["Job"] = it + }, + { + LOGGER.error("Failed to serialize job data, it will not persist", RuntimeException(it.message())) + } + ) + } } } @@ -220,27 +344,36 @@ abstract class MachineJobEventLoop : INBTSerializable) /** * Called when there is nothing to do and we are not idling */ protected abstract fun computeNextJob(): JobContainer - protected open fun onWorkTick(requiredPower: Decimal, extractedPower: Decimal, ticksAdvanced: Double, job: JobType): JobStatus { - return JobStatus.SUCCESS - } + protected open fun onJobTick(status: JobStatus) {} /** * Advances job loop by specified [ticks]. If machine is speed up by upgrades, @@ -317,8 +450,11 @@ abstract class MachineJobEventLoop : INBTSerializable : INBTSerializable : INBTSerializable( +abstract class MatteryWorkerBlockEntity( type: BlockEntityType<*>, blockPos: BlockPos, blockState: BlockState, - val jobDeserializer: (tag: CompoundTag) -> JobType?, + jobCodec: Codec, maxJobs: Int = 1, ) : MatteryPoweredBlockEntity(type, blockPos, blockState) { val jobEventLoops: ImmutableList> = immutableList(maxJobs) { id -> - object : MachineJobEventLoop() { + object : MachineJobEventLoop(jobCodec) { override val energy: IMatteryEnergyStorage? get() = matteryEnergy override val isBlockedByRedstone: Boolean @@ -41,12 +42,8 @@ abstract class MatteryWorkerBlockEntity( override val upgrades: IMatteryUpgrade? get() = this@MatteryWorkerBlockEntity.upgrades - override fun deserializeJob(nbt: CompoundTag): JobType? { - return jobDeserializer.invoke(nbt) - } - - override fun onJobFinish(job: JobType): JobStatus { - return this@MatteryWorkerBlockEntity.onJobFinish(job, id) + override fun onJobFinish(status: JobStatus) { + return this@MatteryWorkerBlockEntity.onJobFinish(status, id) } override fun computeNextJob(): JobContainer { @@ -57,8 +54,8 @@ abstract class MatteryWorkerBlockEntity( this@MatteryWorkerBlockEntity.jobUpdated(new, old, id) } - override fun onWorkTick(requiredPower: Decimal, extractedPower: Decimal, ticksAdvanced: Double, job: JobType): JobStatus { - return this@MatteryWorkerBlockEntity.onWorkTick(requiredPower, extractedPower, ticksAdvanced, job, id) + override fun onJobTick(status: JobStatus) { + return this@MatteryWorkerBlockEntity.onJobTick(status, id) } } } @@ -72,12 +69,10 @@ abstract class MatteryWorkerBlockEntity( } protected open fun jobUpdated(new: JobType?, old: JobType?, id: Int) {} - protected abstract fun onJobFinish(job: JobType, id: Int): JobStatus + protected abstract fun onJobFinish(status: JobStatus, id: Int) protected abstract fun computeNextJob(id: Int): JobContainer - protected open fun onWorkTick(requiredPower: Decimal, extractedPower: Decimal, ticksAdvanced: Double, job: JobType, id: Int): JobStatus { - return JobStatus.SUCCESS - } + protected open fun onJobTick(status: JobStatus, id: Int) {} override fun saveShared(nbt: CompoundTag) { super.saveShared(nbt) @@ -148,6 +143,8 @@ abstract class MatteryWorkerBlockEntity( } companion object { + private val LOGGER = LogManager.getLogger() + fun appendHoverText(itemStack: ItemStack, blockGetter: BlockGetter?, tooltips: MutableList, flag: TooltipFlag) { itemStack.tag ?: return diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/matter/MatterDecomposerBlockEntity.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/matter/MatterDecomposerBlockEntity.kt index 3fdb11ce5..94af8254c 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/matter/MatterDecomposerBlockEntity.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/matter/MatterDecomposerBlockEntity.kt @@ -1,5 +1,7 @@ package ru.dbotthepony.mc.otm.block.entity.matter +import com.mojang.serialization.Codec +import com.mojang.serialization.codecs.RecordCodecBuilder import net.minecraft.core.BlockPos import net.minecraft.nbt.CompoundTag import net.minecraft.world.entity.player.Inventory @@ -11,7 +13,7 @@ import net.minecraft.world.level.block.state.BlockState import net.minecraftforge.common.ForgeConfigSpec import ru.dbotthepony.mc.otm.block.entity.JobContainer import ru.dbotthepony.mc.otm.block.entity.JobStatus -import ru.dbotthepony.mc.otm.block.entity.MachineJob +import ru.dbotthepony.mc.otm.block.entity.Job import ru.dbotthepony.mc.otm.block.entity.MatteryWorkerBlockEntity import ru.dbotthepony.mc.otm.capability.FlowDirection import ru.dbotthepony.mc.otm.capability.MatteryCapability @@ -30,6 +32,8 @@ import ru.dbotthepony.mc.otm.core.math.getDecimal import ru.dbotthepony.mc.otm.core.math.set import ru.dbotthepony.mc.otm.core.nbt.set import ru.dbotthepony.mc.otm.core.util.WriteOnce +import ru.dbotthepony.mc.otm.data.DecimalCodec +import ru.dbotthepony.mc.otm.data.minRange import ru.dbotthepony.mc.otm.graph.matter.MatterGraph import ru.dbotthepony.mc.otm.graph.matter.SimpleMatterNode import ru.dbotthepony.mc.otm.item.matter.MatterDustItem @@ -93,32 +97,23 @@ fun moveMatterAsDustIntoContainer(_matterValue: Decimal, container: MatteryConta } class MatterDecomposerBlockEntity(pos: BlockPos, state: BlockState) - : MatteryWorkerBlockEntity(MBlockEntities.MATTER_DECOMPOSER, pos, state, ::DecomposerJob) { - - class DecomposerJob : MachineJob { - val toDust: Boolean - var matterValue: Decimal - - constructor(tag: CompoundTag) : super(tag) { - toDust = tag.getBoolean(TO_DUST_KEY) - matterValue = tag.getDecimal(MATTER_VALUE_KEY) - } - - constructor(toDust: Boolean, matterValue: Decimal, ticks: Double) : super(ticks, BASE_CONSUMPTION) { - this.toDust = toDust - this.matterValue = matterValue - } - - override fun serializeNBT(): CompoundTag { - return super.serializeNBT().also { - it[TO_DUST_KEY] = toDust - it[MATTER_VALUE_KEY] = matterValue - } - } + : MatteryWorkerBlockEntity(MBlockEntities.MATTER_DECOMPOSER, pos, state, DecomposerJob.CODEC) { + class DecomposerJob( + val toDust: Boolean, + var matterValue: Decimal, + ticks: Double, + ) : Job(ticks, BASE_CONSUMPTION) { companion object { - const val TO_DUST_KEY = "toDust" - const val MATTER_VALUE_KEY = "matterValue" + val CODEC: Codec by lazy { + RecordCodecBuilder.create { + it.group( + Codec.BOOL.fieldOf("toDust").forGetter(DecomposerJob::toDust), + DecimalCodec.minRange(Decimal.ZERO).fieldOf("matterValue").forGetter(DecomposerJob::matterValue), + Codec.DOUBLE.minRange(0.0).fieldOf("ticks").forGetter(DecomposerJob::ticks) + ).apply(it, ::DecomposerJob) + } + } } } @@ -165,22 +160,18 @@ class MatterDecomposerBlockEntity(pos: BlockPos, state: BlockState) return MatterDecomposerMenu(containerID, inventory, this) } - override fun onJobFinish(job: DecomposerJob, id: Int): JobStatus { - if (job.toDust) { - job.matterValue = moveMatterAsDustIntoContainer(job.matterValue, outputContainer, 0, 1) + override fun onJobFinish(status: JobStatus, id: Int) { + if (status.job.toDust) { + status.job.matterValue = moveMatterAsDustIntoContainer(status.job.matterValue, outputContainer, 0, 1) - if (!job.matterValue.isZero) - return JobStatus.FAILURE_WAIT_FAST + if (!status.job.matterValue.isZero) + status.throttleFast() + } else { + status.job.matterValue -= matter.receiveMatter(status.job.matterValue, false) - return JobStatus.SUCCESS + if (status.job.matterValue.isPositive) + status.noMatter() } - - job.matterValue -= matter.receiveMatter(job.matterValue, false) - - if (job.matterValue.isPositive) - return JobStatus.FAILURE_MATTER - - return JobStatus.SUCCESS } override fun computeNextJob(id: Int): JobContainer { diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/matter/MatterPanelBlockEntity.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/matter/MatterPanelBlockEntity.kt index b51146f0e..12b9fc174 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/matter/MatterPanelBlockEntity.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/matter/MatterPanelBlockEntity.kt @@ -9,8 +9,6 @@ import net.minecraft.world.entity.player.Inventory import net.minecraft.world.entity.player.Player import net.minecraft.world.inventory.AbstractContainerMenu import ru.dbotthepony.mc.otm.capability.MatteryCapability -import java.util.HashMap -import java.util.UUID import net.minecraft.nbt.CompoundTag import net.minecraft.nbt.ListTag import net.minecraft.server.level.ServerLevel @@ -29,7 +27,7 @@ import ru.dbotthepony.mc.otm.graph.matter.MatterGraph import ru.dbotthepony.mc.otm.graph.matter.MatterNode import ru.dbotthepony.mc.otm.graph.matter.SimpleMatterNode import ru.dbotthepony.mc.otm.registry.MBlockEntities -import java.util.ArrayList +import java.util.* import java.util.stream.Stream class MatterPanelBlockEntity(p_155229_: BlockPos, p_155230_: BlockState) : @@ -100,7 +98,7 @@ class MatterPanelBlockEntity(p_155229_: BlockPos, p_155230_: BlockState) : for ((key, task) in _tasks) { if (task.required > 0) { - val pattern = task.patternId?.let(graph::getPattern) ?: continue + val pattern = task.patternId.map(graph::getPattern).orElse(null) ?: continue if (!simulate) { val new = task.allocate() @@ -198,7 +196,7 @@ class MatterPanelBlockEntity(p_155229_: BlockPos, p_155230_: BlockState) : } fun addTask(state: IPatternState, count: Int): IReplicationTask<*> { - val task = ReplicationTask(UUID.randomUUID(), state.id, state.item, 0, 0, count) + val task = ReplicationTask(UUID.randomUUID(), Optional.of(state.id), state.item, 0, 0, count) _tasks[task.id] = task matterNode.graph.onMatterTaskCreated(task) diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/matter/MatterRecyclerBlockEntity.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/matter/MatterRecyclerBlockEntity.kt index b47136e3f..b67971e95 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/matter/MatterRecyclerBlockEntity.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/matter/MatterRecyclerBlockEntity.kt @@ -1,22 +1,19 @@ package ru.dbotthepony.mc.otm.block.entity.matter +import com.mojang.serialization.Codec +import com.mojang.serialization.codecs.RecordCodecBuilder import net.minecraft.core.BlockPos -import net.minecraft.nbt.CompoundTag import net.minecraft.world.entity.player.Inventory import net.minecraft.world.entity.player.Player import net.minecraft.world.inventory.AbstractContainerMenu import net.minecraft.world.item.ItemStack import net.minecraft.world.level.Level import net.minecraft.world.level.block.state.BlockState -import net.minecraftforge.common.ForgeConfigSpec -import net.minecraftforge.common.ForgeConfigSpec.ConfigValue import ru.dbotthepony.mc.otm.block.entity.JobContainer import ru.dbotthepony.mc.otm.block.entity.JobStatus -import ru.dbotthepony.mc.otm.block.entity.MachineJob -import ru.dbotthepony.mc.otm.config.ConciseBalanceValues +import ru.dbotthepony.mc.otm.block.entity.Job import ru.dbotthepony.mc.otm.block.entity.MatteryWorkerBlockEntity import ru.dbotthepony.mc.otm.capability.FlowDirection -import ru.dbotthepony.mc.otm.capability.energy.BlockEnergyStorageImpl import ru.dbotthepony.mc.otm.capability.MatteryCapability import ru.dbotthepony.mc.otm.capability.energy.ProfiledEnergyStorage import ru.dbotthepony.mc.otm.capability.energy.WorkerEnergyStorage @@ -30,40 +27,20 @@ import ru.dbotthepony.mc.otm.graph.matter.MatterGraph import ru.dbotthepony.mc.otm.item.matter.MatterDustItem import ru.dbotthepony.mc.otm.menu.matter.MatterRecyclerMenu import ru.dbotthepony.mc.otm.registry.MBlockEntities -import ru.dbotthepony.mc.otm.core.math.DecimalConfigValue -import ru.dbotthepony.mc.otm.core.math.defineDecimal -import ru.dbotthepony.mc.otm.registry.MNames -import ru.dbotthepony.mc.otm.core.util.WriteOnce -import ru.dbotthepony.mc.otm.core.math.getDecimal -import ru.dbotthepony.mc.otm.core.math.set +import ru.dbotthepony.mc.otm.data.DecimalCodec +import ru.dbotthepony.mc.otm.data.minRange import ru.dbotthepony.mc.otm.graph.matter.SimpleMatterNode class MatterRecyclerBlockEntity(blockPos: BlockPos, blockState: BlockState) - : MatteryWorkerBlockEntity(MBlockEntities.MATTER_RECYCLER, blockPos, blockState, ::RecyclerJob) { - - class RecyclerJob : MachineJob { - var totalMatter: Decimal - - constructor( - ticks: Double, - powerUsage: Decimal, - totalMatter: Decimal - ) : super(ticks, powerUsage) { - this.totalMatter = totalMatter - } - - constructor(tag: CompoundTag) : super(tag) { - this.totalMatter = tag.getDecimal(KEY) - } - - override fun serializeNBT(): CompoundTag { - return super.serializeNBT().also { - it[KEY] = totalMatter - } - } + : MatteryWorkerBlockEntity(MBlockEntities.MATTER_RECYCLER, blockPos, blockState, RecyclerJob.CODEC) { + class RecyclerJob(ticks: Double, powerUsage: Decimal, var totalMatter: Decimal) : Job(ticks, powerUsage) { companion object { - const val KEY = "totalMatter" + val CODEC: Codec by lazy { + RecordCodecBuilder.create { + plainCodec(it).and(DecimalCodec.minRange(Decimal.ZERO).fieldOf("totalMatter").forGetter(RecyclerJob::totalMatter)).apply(it, ::RecyclerJob) + } + } } } @@ -108,18 +85,18 @@ class MatterRecyclerBlockEntity(blockPos: BlockPos, blockState: BlockState) return MatterRecyclerMenu(containerID, inventory, this) } - override fun onJobFinish(job: RecyclerJob, id: Int): JobStatus { + override fun onJobFinish(status: JobStatus, id: Int) { + val job = status.job + if (job.totalMatter.isPositive) { val received = matter.receiveMatter(job.totalMatter, true) if (job.totalMatter != received) - return JobStatus.FAILURE_MATTER + return status.noMatter() matter.receiveMatter(job.totalMatter, false) job.totalMatter -= received } - - return JobStatus.SUCCESS } override fun computeNextJob(id: Int): JobContainer { @@ -147,20 +124,16 @@ class MatterRecyclerBlockEntity(blockPos: BlockPos, blockState: BlockState) ) } - override fun onWorkTick(requiredPower: Decimal, extractedPower: Decimal, ticksAdvanced: Double, job: RecyclerJob, id: Int): JobStatus { - val receive = job.totalMatter.coerceAtMost(MachinesConfig.MATTER_RECYCLER_MATTER_PER_TICK.get() * ticksAdvanced) + override fun onJobTick(status: JobStatus, id: Int) { + val job = status.job + val toReceive = job.totalMatter.coerceAtMost(MachinesConfig.MATTER_RECYCLER_MATTER_PER_TICK.get() * status.ticksAdvanced) - if (receive.isZero) - return JobStatus.SUCCESS + if (toReceive.isZero) + return status.success() - val received = matter.receiveMatter(receive, true) - - if (receive != received) - return JobStatus.FAILURE_MATTER - - matter.receiveMatter(receive, false) + val received = matter.receiveMatter(toReceive, false) + status.scale(received / toReceive) job.totalMatter -= received - return JobStatus.SUCCESS } override fun tick() { diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/matter/MatterReplicatorBlockEntity.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/matter/MatterReplicatorBlockEntity.kt index a71405fc6..8ee668a7b 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/matter/MatterReplicatorBlockEntity.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/matter/MatterReplicatorBlockEntity.kt @@ -1,7 +1,8 @@ package ru.dbotthepony.mc.otm.block.entity.matter +import com.mojang.serialization.Codec +import com.mojang.serialization.codecs.RecordCodecBuilder import net.minecraft.core.BlockPos -import net.minecraft.nbt.CompoundTag import net.minecraft.world.entity.player.Inventory import net.minecraft.world.entity.player.Player import net.minecraft.world.inventory.AbstractContainerMenu @@ -11,7 +12,7 @@ import net.minecraft.world.level.block.state.BlockState import net.minecraftforge.common.ForgeConfigSpec import ru.dbotthepony.mc.otm.block.entity.JobContainer import ru.dbotthepony.mc.otm.block.entity.JobStatus -import ru.dbotthepony.mc.otm.block.entity.MachineItemJob +import ru.dbotthepony.mc.otm.block.entity.ItemJob import ru.dbotthepony.mc.otm.block.entity.MachineJobEventLoop import ru.dbotthepony.mc.otm.block.entity.MatteryWorkerBlockEntity import ru.dbotthepony.mc.otm.capability.FlowDirection @@ -28,76 +29,43 @@ import ru.dbotthepony.mc.otm.container.UpgradeContainer import ru.dbotthepony.mc.otm.core.math.Decimal import ru.dbotthepony.mc.otm.core.math.DecimalConfigValue import ru.dbotthepony.mc.otm.core.math.defineDecimal -import ru.dbotthepony.mc.otm.core.math.getDecimal -import ru.dbotthepony.mc.otm.core.math.set -import ru.dbotthepony.mc.otm.core.nbt.map -import ru.dbotthepony.mc.otm.core.nbt.set import ru.dbotthepony.mc.otm.core.util.WriteOnce +import ru.dbotthepony.mc.otm.data.DecimalCodec +import ru.dbotthepony.mc.otm.data.UUIDCodec +import ru.dbotthepony.mc.otm.data.minRange import ru.dbotthepony.mc.otm.graph.matter.MatterNode import ru.dbotthepony.mc.otm.matter.MatterManager import ru.dbotthepony.mc.otm.menu.matter.MatterReplicatorMenu import ru.dbotthepony.mc.otm.registry.MBlockEntities import ru.dbotthepony.mc.otm.registry.MNames +import java.util.* class MatterReplicatorBlockEntity(p_155229_: BlockPos, p_155230_: BlockState) : - MatteryWorkerBlockEntity(MBlockEntities.MATTER_REPLICATOR, p_155229_, p_155230_, { - try { - ReplicatorJob(it) - } catch(err: NoSuchElementException) { - null - } - }) { - - class ReplicatorJob : MachineItemJob { - val matterPerTick: Decimal - val task: ReplicationTask - var matterValue: Decimal - val pattern: PatternState? - val asDust: Boolean - - constructor(tag: CompoundTag) : super(tag) { - matterPerTick = tag.getDecimal(MATTER_PER_TICK_KEY) - matterValue = tag.getDecimal(MATTER_VALUE_KEY) - pattern = tag.map(PATTERN_KEY, PatternState::deserializeNBT) - asDust = tag.getBoolean(AS_DUST_KEY) - task = tag.map(TASK_KEY, ReplicationTask::deserializeNBT) ?: throw NoSuchElementException("Unable to deserialize matter task") - } - - constructor( - itemStack: ItemStack, - matterPerTick: Decimal, - task: ReplicationTask, - matterValue: Decimal, - pattern: PatternState?, - asDust: Boolean, - ticks: Double, - ) : super(itemStack, ticks, BASE_CONSUMPTION) { - this.matterPerTick = matterPerTick - this.task = task - this.matterValue = matterValue - this.pattern = pattern - this.asDust = asDust - } - - override fun serializeNBT(): CompoundTag { - return super.serializeNBT().also { - it[MATTER_PER_TICK_KEY] = this.matterPerTick - it[TASK_KEY] = this.task.serializeNBT() - it[MATTER_VALUE_KEY] = this.matterValue - - if (this.pattern != null) - it[PATTERN_KEY] = this.pattern.serializeNBT() - - it[AS_DUST_KEY] = this.asDust - } - } + MatteryWorkerBlockEntity(MBlockEntities.MATTER_REPLICATOR, p_155229_, p_155230_, ReplicatorJob.CODEC) { + class ReplicatorJob( + itemStack: ItemStack, + val matterPerTick: Decimal, + val task: UUID, + var matterValue: Decimal, + val pattern: Optional, + val asDust: Boolean, + ticks: Double, + ) : ItemJob(itemStack, ticks, BASE_CONSUMPTION) { companion object { - const val MATTER_PER_TICK_KEY = "matterPerTick" - const val MATTER_VALUE_KEY = "matterValue" - const val PATTERN_KEY = "pattern" - const val AS_DUST_KEY = "asDust" - const val TASK_KEY = "task" + val CODEC: Codec by lazy { + RecordCodecBuilder.create { + it.group( + ItemStack.CODEC.fieldOf("itemStack").forGetter(ReplicatorJob::itemStack), + DecimalCodec.minRange(Decimal.ZERO).fieldOf("matterPerTick").forGetter(ReplicatorJob::matterPerTick), + UUIDCodec.fieldOf("task").forGetter(ReplicatorJob::task), + DecimalCodec.minRange(Decimal.ZERO).fieldOf("matterValue").forGetter(ReplicatorJob::matterValue), + PatternState.CODEC.optionalFieldOf("pattern").forGetter(ReplicatorJob::pattern), + Codec.BOOL.fieldOf("asDust").forGetter(ReplicatorJob::asDust), + Codec.doubleRange(0.0, Double.MAX_VALUE).fieldOf("ticks").forGetter(ReplicatorJob::ticks), + ).apply(it, ::ReplicatorJob) + } + } } } @@ -141,24 +109,24 @@ class MatterReplicatorBlockEntity(p_155229_: BlockPos, p_155230_: BlockState) : return MatterReplicatorMenu(containerID, inventory, this) } - override fun onJobFinish(job: ReplicatorJob, id: Int): JobStatus { + override fun onJobFinish(status: JobStatus, id: Int) { + val job = status.job + if (job.asDust) { job.matterValue = moveMatterAsDustIntoContainer(job.matterValue, container, OUTPUT_DUST_MAIN, OUTPUT_DUST_STACKING) if (!job.matterValue.isZero) { - return JobStatus.FAILURE_WAIT + return status.throttle() } - matterNode.graph.notifyTaskCompletion(job.task.id) - return JobStatus.SUCCESS - } + matterNode.graph.notifyTaskCompletion(job.task) + } else { + if (!container.fullyAddItem(job.itemStack, FIRST_ACTUAL_OUTPUT_SLOT .. LAST_ACTUAL_OUTPUT_SLOT)) { + return status.noItem() + } - if (!container.fullyAddItem(job.itemStack, FIRST_ACTUAL_OUTPUT_SLOT .. LAST_ACTUAL_OUTPUT_SLOT)) { - return JobStatus.FAILURE_ITEM + matterNode.graph.notifyTaskCompletion(job.task) } - - matterNode.graph.notifyTaskCompletion(job.task.id) - return JobStatus.SUCCESS } override fun setRemoved() { @@ -211,64 +179,29 @@ class MatterReplicatorBlockEntity(p_155229_: BlockPos, p_155230_: BlockState) : return JobContainer.success(ReplicatorJob( itemStack = stack, matterPerTick = matter.matter / ticks, - task = allocation.task.asImmutable(), + task = allocation.task.id, matterValue = matter.matter, - pattern = allocation.pattern?.asImmutable(), + pattern = Optional.ofNullable(allocation.pattern?.asImmutable()), asDust = (level?.random?.nextDouble() ?: 1.0) * upgrades.failureMultiplier > (allocation.pattern?.researchPercent ?: 2.0), ticks = ticks, )) } - override fun onWorkTick(requiredPower: Decimal, extractedPower: Decimal, ticksAdvanced: Double, job: ReplicatorJob, id: Int): JobStatus { - val drainPerTick = job.matterPerTick * ticksAdvanced + override fun onJobTick(status: JobStatus, id: Int) { + val job = status.job + val drainPerTick = job.matterPerTick * status.ticksAdvanced if (matter.extractMatter(drainPerTick, true) < drainPerTick) { - // в машине недостаточно материи + val toDrain = (drainPerTick * DRAIN_MULT) + .coerceAtMost(job.matterPerTick * (status.ticks - status.workTicks + status.ticksAdvanced)) + .coerceAtLeast(Decimal.ONE) + .coerceAtMost(matter.missingMatter) - if (drainPerTick > matter.maxStoredMatter) { - // в тик требуется больше материи, чем её может хранить репликатор - val toExtract = drainPerTick - matter.extractMatter(drainPerTick, true) - val drain = matterNode.graph.extractMatter(toExtract, true) - - if (drain != toExtract) { - // недостаточно материи в сети - return JobStatus.FAILURE_MATTER - } - - // достаточно материи в сети + внутри машины - matter.extractMatter(drainPerTick, false) - matterNode.graph.extractMatter(drain, false) - return JobStatus.SUCCESS - } else { - // в тик требуется меньше материи, чем её может хранить репликатор - // примем из сети недостающее количество бака материи, или 200 тиков репликации, что меньше - val drain = matterNode.graph.extractMatter((drainPerTick * DRAIN_MULT) - .coerceAtMost(job.matterPerTick * (job.ticks - jobEventLoops[0].workTicks - ticksAdvanced)) - .coerceAtLeast(Decimal.ONE) - .coerceAtMost(matter.missingMatter), false) - - if (drain.isZero) { - // в сети нет материи - return JobStatus.FAILURE_MATTER - } - - matter.receiveMatter(drain, false) - - // получили материю, проверяем возможность работы - if (matter.extractMatter(drainPerTick, true) >= drainPerTick) { - matter.extractMatter(drainPerTick, false) - return JobStatus.SUCCESS - } else { - // :( - return JobStatus.FAILURE_WAIT - } - } + matter.receiveMatter(matterNode.graph.extractMatter(toDrain, false), false) } - // в машине достаточно материи - matter.extractMatter(drainPerTick, false) - visualProgress = jobEventLoops[0].workProgress - return JobStatus.SUCCESS + status.scale(matter.extractMatter(drainPerTick, false) / drainPerTick) + visualProgress = status.workProgress } companion object { diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/matter/MatterScannerBlockEntity.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/matter/MatterScannerBlockEntity.kt index ad51f4083..683957405 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/matter/MatterScannerBlockEntity.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/matter/MatterScannerBlockEntity.kt @@ -10,7 +10,7 @@ import net.minecraft.world.level.block.state.BlockState import net.minecraftforge.common.ForgeConfigSpec import ru.dbotthepony.mc.otm.block.entity.JobContainer import ru.dbotthepony.mc.otm.block.entity.JobStatus -import ru.dbotthepony.mc.otm.block.entity.MachineItemJob +import ru.dbotthepony.mc.otm.block.entity.ItemJob import ru.dbotthepony.mc.otm.block.entity.MachineJobEventLoop import ru.dbotthepony.mc.otm.config.ConciseBalanceValues import ru.dbotthepony.mc.otm.block.entity.MatteryWorkerBlockEntity @@ -35,7 +35,7 @@ import java.util.* import kotlin.math.pow class MatterScannerBlockEntity(p_155229_: BlockPos, p_155230_: BlockState) : - MatteryWorkerBlockEntity(MBlockEntities.MATTER_SCANNER, p_155229_, p_155230_, ::MachineItemJob) { + MatteryWorkerBlockEntity(MBlockEntities.MATTER_SCANNER, p_155229_, p_155230_, ItemJob.CODEC) { val container = MatteryContainer(::itemContainerUpdated, 1).also(::addDroppableContainer) val energy = ProfiledEnergyStorage(WorkerEnergyStorage(::energyLevelUpdated, ENERGY_VALUES)) @@ -87,9 +87,9 @@ class MatterScannerBlockEntity(p_155229_: BlockPos, p_155230_: BlockState) : return MatterScannerMenu(containerID, inventory, this) } - override fun onJobFinish(job: MachineItemJob, id: Int): JobStatus { - val stack = job.itemStack - if (stack.isEmpty || !MatterManager.hasMatterValue(stack)) return JobStatus.SUCCESS + override fun onJobFinish(status: JobStatus, id: Int) { + val stack = status.job.itemStack + if (stack.isEmpty || !MatterManager.hasMatterValue(stack)) return status.success() var findState: IPatternState? = null @@ -110,14 +110,12 @@ class MatterScannerBlockEntity(p_155229_: BlockPos, p_155230_: BlockState) : PatternState(UUID.randomUUID(), stack.item, researchAdvance) } - if (!matterNode.graph.insertPattern(new, onlyUpdate = false, simulate = false).isFailed) { - return JobStatus.SUCCESS - } else { - return JobStatus.FAILURE_WAIT + if (matterNode.graph.insertPattern(new, onlyUpdate = false, simulate = false).isFailed) { + status.throttle() } } - override fun computeNextJob(id: Int): JobContainer { + override fun computeNextJob(id: Int): JobContainer { if (energy.batteryLevel.isZero) { return JobContainer.noEnergy() } @@ -151,7 +149,7 @@ class MatterScannerBlockEntity(p_155229_: BlockPos, p_155230_: BlockState) : stack.shrink(1) container.setChanged() val complexity = MatterManager.get(copy).complexity - return JobContainer.success(MachineItemJob(copy, (if (complexity > 1.0) complexity.pow(1.25) else complexity.pow(0.5)), BASE_CONSUMPTION)) + return JobContainer.success(ItemJob(copy, (if (complexity > 1.0) complexity.pow(1.25) else complexity.pow(0.5)), BASE_CONSUMPTION)) } return JobContainer.noItem() @@ -163,7 +161,7 @@ class MatterScannerBlockEntity(p_155229_: BlockPos, p_155230_: BlockState) : } - override fun jobUpdated(new: MachineItemJob?, old: MachineItemJob?, id: Int) { + override fun jobUpdated(new: ItemJob?, old: ItemJob?, id: Int) { visualItemStack = new?.itemStack ?: ItemStack.EMPTY visualProgress = 0f } @@ -174,14 +172,11 @@ class MatterScannerBlockEntity(p_155229_: BlockPos, p_155230_: BlockState) : var visualProgress by synchronizer.float().property private set - override fun onWorkTick( - requiredPower: Decimal, - extractedPower: Decimal, - ticksAdvanced: Double, - job: MachineItemJob, + override fun onJobTick( + status: JobStatus, id: Int - ): JobStatus { - val result = super.onWorkTick(requiredPower, extractedPower, ticksAdvanced, job, id) + ) { + val result = super.onJobTick(status, id) visualProgress = jobEventLoops[0].workProgress diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/tech/CobblerBlockEntity.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/tech/CobblerBlockEntity.kt index 04249f93d..c6f20e2d0 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/tech/CobblerBlockEntity.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/tech/CobblerBlockEntity.kt @@ -7,7 +7,7 @@ import net.minecraft.world.inventory.AbstractContainerMenu import net.minecraft.world.item.ItemStack import net.minecraft.world.item.Items import net.minecraft.world.level.block.state.BlockState -import ru.dbotthepony.mc.otm.block.entity.MachineItemJob +import ru.dbotthepony.mc.otm.block.entity.ItemJob import ru.dbotthepony.mc.otm.block.entity.JobContainer import ru.dbotthepony.mc.otm.block.entity.JobStatus import ru.dbotthepony.mc.otm.block.entity.MatteryWorkerBlockEntity @@ -17,7 +17,7 @@ import ru.dbotthepony.mc.otm.menu.tech.CobblerMenu import ru.dbotthepony.mc.otm.registry.MBlockEntities class CobblerBlockEntity(blockPos: BlockPos, blockState: BlockState) - : MatteryWorkerBlockEntity(MBlockEntities.COBBLESTONE_GENERATOR, blockPos, blockState, ::MachineItemJob) { + : MatteryWorkerBlockEntity(MBlockEntities.COBBLESTONE_GENERATOR, blockPos, blockState, ItemJob.CODEC) { override fun createMenu(containerID: Int, inventory: Inventory, ply: Player): AbstractContainerMenu { return CobblerMenu(containerID, inventory, this) } @@ -36,16 +36,14 @@ class CobblerBlockEntity(blockPos: BlockPos, blockState: BlockState) savetable(::container, INVENTORY_KEY) } - override fun onJobFinish(job: MachineItemJob, id: Int): JobStatus { - if (!container.fullyAddItem(job.itemStack)) { - return JobStatus.FAILURE_ITEM + override fun onJobFinish(status: JobStatus, id: Int) { + if (!container.fullyAddItem(status.job.itemStack)) { + status.noItem() } - - return JobStatus.SUCCESS } - override fun computeNextJob(id: Int): JobContainer { - return JobContainer.success(MachineItemJob(ItemStack(Items.COBBLESTONE), 40.0)) + override fun computeNextJob(id: Int): JobContainer { + return JobContainer.success(ItemJob(ItemStack(Items.COBBLESTONE), 40.0)) } companion object { 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 e973cc320..df68623fc 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 @@ -10,7 +10,7 @@ import net.minecraft.world.inventory.AbstractContainerMenu import net.minecraft.world.level.block.state.BlockState import ru.dbotthepony.mc.otm.block.entity.JobContainer import ru.dbotthepony.mc.otm.block.entity.JobStatus -import ru.dbotthepony.mc.otm.block.entity.MachineItemJob +import ru.dbotthepony.mc.otm.block.entity.ItemJob import ru.dbotthepony.mc.otm.block.entity.MatteryWorkerBlockEntity import ru.dbotthepony.mc.otm.capability.UpgradeType import ru.dbotthepony.mc.otm.capability.energy.ProfiledEnergyStorage @@ -20,7 +20,6 @@ import ru.dbotthepony.mc.otm.container.MatteryContainer import ru.dbotthepony.mc.otm.container.HandlerFilter import ru.dbotthepony.mc.otm.container.UpgradeContainer import ru.dbotthepony.mc.otm.container.balance -import ru.dbotthepony.mc.otm.core.math.Decimal import ru.dbotthepony.mc.otm.menu.tech.PlatePressMenu import ru.dbotthepony.mc.otm.menu.tech.TwinPlatePressMenu import ru.dbotthepony.mc.otm.registry.MBlockEntities @@ -30,7 +29,7 @@ class PlatePressBlockEntity( p_155229_: BlockPos, p_155230_: BlockState, val isTwin: Boolean = false, -) : MatteryWorkerBlockEntity(if (isTwin) MBlockEntities.TWIN_PLATE_PRESS else MBlockEntities.PLATE_PRESS, p_155229_, p_155230_, ::MachineItemJob, if (isTwin) 2 else 1) { +) : MatteryWorkerBlockEntity(if (isTwin) MBlockEntities.TWIN_PLATE_PRESS else MBlockEntities.PLATE_PRESS, p_155229_, p_155230_, ItemJob.CODEC, if (isTwin) 2 else 1) { override val upgrades = UpgradeContainer(this::setChangedLight, if (isTwin) 4 else 3, UpgradeType.BASIC_PROCESSING) val energy = ProfiledEnergyStorage(WorkerEnergyStorage(this::energyLevelUpdated, upgrades.transform(MachinesConfig.PLATE_PRESS))) val inputContainer = MatteryContainer(this::itemContainerUpdated, if (isTwin) 2 else 1).also(::addDroppableContainer) @@ -69,18 +68,17 @@ class PlatePressBlockEntity( return PlatePressMenu(containerID, inventory, this) } - override fun onJobFinish(job: MachineItemJob, id: Int): JobStatus { - if (job.itemStack.isEmpty) - return JobStatus.SUCCESS + override fun onJobFinish(status: JobStatus, id: Int) { + if (status.job.itemStack.isEmpty) + return status.success() - if (!outputContainer.fullyAddItem(job.itemStack, start = id, end = id) && !outputContainer.fullyAddItem(job.itemStack)) - return JobStatus.FAILURE_ITEM + if (!outputContainer.fullyAddItem(status.job.itemStack, start = id, end = id) && !outputContainer.fullyAddItem(status.job.itemStack)) + return status.noItem() - experience = (experience + job.experience).coerceAtMost(100.0) - return JobStatus.SUCCESS + experience = (experience + status.experience).coerceAtMost(100.0) } - override fun computeNextJob(id: Int): JobContainer { + override fun computeNextJob(id: Int): JobContainer { if (energy.batteryLevel.isZero) { return JobContainer.noEnergy() } @@ -101,7 +99,7 @@ class PlatePressBlockEntity( inputContainer.setChanged(id) return JobContainer.success( - MachineItemJob( + ItemJob( recipe.getResultItem(level.registryAccess()).copyWithCount(toProcess), recipe.workTime * MachinesConfig.PLATE_PRESS.workTimeMultiplier, MachinesConfig.PLATE_PRESS.powerConsumption * toProcess, diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/tech/PoweredFurnaceBlockEntity.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/tech/PoweredFurnaceBlockEntity.kt index afcdcdaac..e11236c03 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/tech/PoweredFurnaceBlockEntity.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/tech/PoweredFurnaceBlockEntity.kt @@ -3,19 +3,17 @@ package ru.dbotthepony.mc.otm.block.entity.tech import net.minecraft.core.BlockPos 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 import net.minecraft.world.item.crafting.AbstractCookingRecipe -import net.minecraft.world.item.crafting.Recipe import net.minecraft.world.item.crafting.RecipeType import net.minecraft.world.level.block.entity.BlockEntityType import net.minecraft.world.level.block.state.BlockState import ru.dbotthepony.mc.otm.block.entity.JobContainer import ru.dbotthepony.mc.otm.block.entity.JobStatus -import ru.dbotthepony.mc.otm.block.entity.MachineItemJob +import ru.dbotthepony.mc.otm.block.entity.ItemJob import ru.dbotthepony.mc.otm.block.entity.MatteryWorkerBlockEntity import ru.dbotthepony.mc.otm.capability.CombinedItemHandler import ru.dbotthepony.mc.otm.capability.UpgradeType @@ -36,7 +34,7 @@ class PoweredFurnaceBlockEntity( blockState: BlockState, val recipeType: RecipeType, val config: WorkerBalanceValues -) : MatteryWorkerBlockEntity(type, blockPos, blockState, ::MachineItemJob, 2) { +) : MatteryWorkerBlockEntity(type, blockPos, blockState, ItemJob.CODEC, 2) { override val upgrades = UpgradeContainer(this::setChangedLight, 2, UpgradeType.BASIC_PROCESSING) val energy = ProfiledEnergyStorage(WorkerEnergyStorage(this::energyLevelUpdated, upgrades.transform(config))) @@ -86,16 +84,15 @@ class PoweredFurnaceBlockEntity( return PoweredFurnaceMenu(containerID, inventory, this) } - override fun onJobFinish(job: MachineItemJob, id: Int): JobStatus { - if (outputs[id].fullyAddItem(job.itemStack)) { - experience += job.experience - return JobStatus.SUCCESS + override fun onJobFinish(status: JobStatus, id: Int) { + if (outputs[id].fullyAddItem(status.job.itemStack)) { + experience += status.experience + } else { + status.noItem() } - - return JobStatus.FAILURE_ITEM } - override fun computeNextJob(id: Int): JobContainer { + override fun computeNextJob(id: Int): JobContainer { if (!energy.batteryLevel.isPositive) return JobContainer.noEnergy() @@ -109,7 +106,7 @@ class PoweredFurnaceBlockEntity( val toProcess = inputs[id][0].count.coerceAtMost(upgrades.processingItems + 1) inputs[id][0].shrink(toProcess) - JobContainer.success(MachineItemJob( + JobContainer.success(ItemJob( output.copyWithCount(toProcess), it.cookingTime * config.workTimeMultiplier, config.powerConsumption * toProcess, it.experience * toProcess )) }.orElse(JobContainer.noItem()) diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/capability/MatteryPlayerCapability.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/capability/MatteryPlayerCapability.kt index 1896bbc82..e7d735336 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/capability/MatteryPlayerCapability.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/capability/MatteryPlayerCapability.kt @@ -63,7 +63,7 @@ import ru.dbotthepony.mc.otm.android.AndroidResearchManager import ru.dbotthepony.mc.otm.android.AndroidResearchType import ru.dbotthepony.mc.otm.block.entity.JobContainer import ru.dbotthepony.mc.otm.block.entity.JobStatus -import ru.dbotthepony.mc.otm.block.entity.MachineItemJob +import ru.dbotthepony.mc.otm.block.entity.ItemJob import ru.dbotthepony.mc.otm.block.entity.MachineJobEventLoop import ru.dbotthepony.mc.otm.capability.energy.BatteryBackedEnergyStorage import ru.dbotthepony.mc.otm.capability.energy.IMatteryEnergyStorage @@ -411,33 +411,26 @@ class MatteryPlayerCapability(val ply: Player) : ICapabilityProvider, INBTSerial } }).property - inner class SmelterBelt(index: Int) : MachineJobEventLoop() { + inner class SmelterBelt(index: Int) : MachineJobEventLoop(ItemJob.CODEC) { override val energy: IMatteryEnergyStorage get() = exoPackEnergy override val isBlockedByRedstone: Boolean get() = false - override fun deserializeJob(nbt: CompoundTag): MachineItemJob { - return MachineItemJob(nbt) - } - override val upgrades: IMatteryUpgrade? get() = null - override fun jobUpdated(new: MachineItemJob?, old: MachineItemJob?) {} - - override fun onJobFinish(job: MachineItemJob): JobStatus { - if (output.fullyAddItem(job.itemStack)) { - exoPackSmelterExperience += job.experience - return JobStatus.SUCCESS + override fun onJobFinish(status: JobStatus) { + if (output.fullyAddItem(status.job.itemStack)) { + exoPackSmelterExperience += status.job.experience } else { - return JobStatus.FAILURE_ITEM + status.noItem() } } private val cache = RecipeManager.createCheck(RecipeType.SMELTING) - override fun computeNextJob(): JobContainer { + override fun computeNextJob(): JobContainer { if (!exoPackEnergy.batteryLevel.isPositive) return JobContainer.noEnergy() val level = ply.level() as? ServerLevel ?: return JobContainer.failure() val recipe = cache.getRecipeFor(input, level) @@ -449,7 +442,7 @@ class MatteryPlayerCapability(val ply: Player) : ICapabilityProvider, INBTSerial val item = actual.assemble(input, level.registryAccess()) input[0].shrink(1) input.setChanged(0) - return JobContainer.success(MachineItemJob(item, actual.cookingTime.toDouble(), ExopackConfig.FURNACE_POWER_CONSUMPTION, actual.experience)) + return JobContainer.success(ItemJob(item, actual.cookingTime.toDouble(), ExopackConfig.FURNACE_POWER_CONSUMPTION, actual.experience)) } } diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/capability/matter/PatternState.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/capability/matter/PatternState.kt index c1c17a410..a982c17b5 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/capability/matter/PatternState.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/capability/matter/PatternState.kt @@ -1,6 +1,10 @@ package ru.dbotthepony.mc.otm.capability.matter +import com.mojang.datafixers.Products +import com.mojang.serialization.Codec +import com.mojang.serialization.codecs.RecordCodecBuilder import net.minecraft.nbt.CompoundTag +import net.minecraft.nbt.NbtOps import net.minecraft.nbt.Tag import net.minecraft.network.FriendlyByteBuf import net.minecraft.resources.ResourceLocation @@ -11,6 +15,7 @@ import net.minecraftforge.registries.ForgeRegistries import ru.dbotthepony.mc.otm.core.* import ru.dbotthepony.mc.otm.core.nbt.contains import ru.dbotthepony.mc.otm.core.nbt.set +import ru.dbotthepony.mc.otm.data.UUIDCodec import java.util.* sealed interface IPatternState { @@ -45,13 +50,7 @@ sealed interface IPatternState { return ItemStack(item, count) } - fun serializeNBT(): CompoundTag { - return CompoundTag().also { - it["id"] = id - it["item"] = item.registryName!!.toString() - it["researchPercent"] = researchPercent - } - } + fun serializeNBT(): CompoundTag fun write(buff: FriendlyByteBuf) { buff.writeUUID(id) @@ -60,6 +59,14 @@ sealed interface IPatternState { } } +private fun codec(builder: RecordCodecBuilder.Instance): Products.P3, UUID, Item, Double> { + return builder.group( + UUIDCodec.fieldOf("id").forGetter(IPatternState::id), + ForgeRegistries.ITEMS.codec.fieldOf("item").forGetter(IPatternState::item), + Codec.doubleRange(0.0, 1.0).fieldOf("researchPercent").forGetter(IPatternState::researchPercent) + ) +} + private fun deserializeNBT(nbt: Tag?, mutable: Boolean): IPatternState? { if (nbt !is CompoundTag) return null @@ -108,6 +115,10 @@ data class PatternState( return this } + override fun serializeNBT(): CompoundTag { + return CODEC.encode(this, NbtOps.INSTANCE, NbtOps.INSTANCE.empty()).get().map({ it as CompoundTag }, { throw RuntimeException("Failed to serialize PatternState: ${it.message()}") }) + } + companion object { fun deserializeNBT(tag: Tag?): PatternState? { return deserializeNBT(tag, false) as PatternState? @@ -116,6 +127,12 @@ data class PatternState( fun read(buff: FriendlyByteBuf): PatternState? { return read(buff, false) as PatternState? } + + val CODEC: Codec by lazy { + RecordCodecBuilder.create { + codec(it).apply(it, ::PatternState) + } + } } } @@ -132,6 +149,10 @@ data class MutablePatternState( return PatternState(id, item, researchPercent) } + override fun serializeNBT(): CompoundTag { + return CODEC.encode(this, NbtOps.INSTANCE, NbtOps.INSTANCE.empty()).get().map({ it as CompoundTag }, { throw RuntimeException("Failed to serialize MutablePatternState: ${it.message()}") }) + } + companion object { fun deserializeNBT(tag: Tag?): MutablePatternState? { return deserializeNBT(tag, true) as MutablePatternState? @@ -140,6 +161,12 @@ data class MutablePatternState( fun read(buff: FriendlyByteBuf): MutablePatternState? { return read(buff, true) as MutablePatternState? } + + val CODEC: Codec by lazy { + RecordCodecBuilder.create { + codec(it).apply(it, ::MutablePatternState) + } + } } } diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/capability/matter/ReplicationTask.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/capability/matter/ReplicationTask.kt index b93b12d7f..234293cff 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/capability/matter/ReplicationTask.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/capability/matter/ReplicationTask.kt @@ -1,6 +1,10 @@ package ru.dbotthepony.mc.otm.capability.matter +import com.mojang.datafixers.Products +import com.mojang.serialization.Codec +import com.mojang.serialization.codecs.RecordCodecBuilder import net.minecraft.nbt.CompoundTag +import net.minecraft.nbt.NbtOps import net.minecraft.nbt.Tag import net.minecraft.network.FriendlyByteBuf import net.minecraft.resources.ResourceLocation @@ -9,14 +13,14 @@ import net.minecraft.world.item.ItemStack import net.minecraft.world.item.Items import net.minecraftforge.registries.ForgeRegistries import ru.dbotthepony.mc.otm.core.readItemType -import ru.dbotthepony.mc.otm.core.registryName -import ru.dbotthepony.mc.otm.core.nbt.set import ru.dbotthepony.mc.otm.core.writeItemType +import ru.dbotthepony.mc.otm.data.UUIDCodec +import java.util.Optional import java.util.UUID sealed interface IReplicationTask> { val id: UUID - val patternId: UUID? + val patternId: Optional val item: Item val inProgress: Int val finished: Int @@ -29,7 +33,7 @@ sealed interface IReplicationTask> { fun copyAsMutable( id: UUID = this.id, - patternId: UUID? = this.patternId, + patternId: Optional = this.patternId, item: Item = this.item, inProgress: Int = this.inProgress, finished: Int = this.finished, @@ -40,7 +44,7 @@ sealed interface IReplicationTask> { fun copyAsImmutable( id: UUID = this.id, - patternId: UUID? = this.patternId, + patternId: Optional = this.patternId, item: Item = this.item, inProgress: Int = this.inProgress, finished: Int = this.finished, @@ -60,25 +64,13 @@ sealed interface IReplicationTask> { fun allocate(amount: Int = 1): S fun finish(amount: Int = 1): S - fun serializeNBT(): CompoundTag { - return CompoundTag().also { - it["id"] = id - - if (patternId != null) - it["patternId"] = patternId!! - - it["item"] = item.registryName!!.toString() - it["inProgress"] = inProgress - it["finished"] = finished - it["required"] = required - } - } + fun serializeNBT(): CompoundTag fun write(buff: FriendlyByteBuf) { buff.writeUUID(id) - buff.writeBoolean(patternId != null) - patternId?.let(buff::writeUUID) + buff.writeBoolean(patternId.isPresent) + patternId.ifPresent(buff::writeUUID) buff.writeItemType(item) @@ -88,6 +80,17 @@ sealed interface IReplicationTask> { } } +private fun > codec(builder: RecordCodecBuilder.Instance): Products.P6, UUID, Optional, Item, Int, Int, Int> { + return builder.group( + UUIDCodec.fieldOf("id").forGetter(IReplicationTask<*>::id), + UUIDCodec.optionalFieldOf("patternId").forGetter(IReplicationTask<*>::patternId), + ForgeRegistries.ITEMS.codec.fieldOf("item").forGetter(IReplicationTask<*>::item), + Codec.intRange(0, Int.MAX_VALUE).fieldOf("inProgress").forGetter(IReplicationTask<*>::inProgress), + Codec.intRange(0, Int.MAX_VALUE).fieldOf("finished").forGetter(IReplicationTask<*>::finished), + Codec.intRange(0, Int.MAX_VALUE).fieldOf("required").forGetter(IReplicationTask<*>::required), + ) +} + private fun deserializeNBT(nbt: Tag?, mutable: Boolean): IReplicationTask<*>? { if (nbt !is CompoundTag) return null @@ -108,9 +111,9 @@ private fun deserializeNBT(nbt: Tag?, mutable: Boolean): IReplicationTask<*>? { val required = nbt.getInt("required") if (mutable) { - return MutableReplicationTask(id, patternId, item, inProgress, finished, required) + return MutableReplicationTask(id, Optional.ofNullable(patternId), item, inProgress, finished, required) } else { - return ReplicationTask(id, patternId, item, inProgress, finished, required) + return ReplicationTask(id, Optional.ofNullable(patternId), item, inProgress, finished, required) } } @@ -125,15 +128,15 @@ private fun read(buff: FriendlyByteBuf, mutable: Boolean): IReplicationTask<*>? item ?: return null if (mutable) { - return MutableReplicationTask(id, patternId, item, inProgress, finished, required) + return MutableReplicationTask(id, Optional.ofNullable(patternId), item, inProgress, finished, required) } else { - return ReplicationTask(id, patternId, item, inProgress, finished, required) + return ReplicationTask(id, Optional.ofNullable(patternId), item, inProgress, finished, required) } } data class ReplicationTask( override val id: UUID, - override val patternId: UUID?, + override val patternId: Optional, override val item: Item, override val inProgress: Int, override val finished: Int, @@ -155,6 +158,10 @@ data class ReplicationTask( return ReplicationTask(id, patternId, item, inProgress - amount, finished + amount, required) } + override fun serializeNBT(): CompoundTag { + return CODEC.encode(this, NbtOps.INSTANCE, NbtOps.INSTANCE.empty()).get().map({ it as CompoundTag }, { throw RuntimeException("Failed to serialize ReplicationTask: ${it.message()}") }) + } + companion object { fun deserializeNBT(nbt: Tag?): ReplicationTask? { return deserializeNBT(nbt, false) as ReplicationTask? @@ -163,12 +170,18 @@ data class ReplicationTask( fun read(buff: FriendlyByteBuf): ReplicationTask? { return read(buff, false) as ReplicationTask? } + + val CODEC: Codec by lazy { + RecordCodecBuilder.create { + codec(it).apply(it, ::ReplicationTask) + } + } } } data class MutableReplicationTask( override val id: UUID, - override val patternId: UUID?, + override val patternId: Optional, override val item: Item, override var inProgress: Int, override var finished: Int, @@ -194,6 +207,10 @@ data class MutableReplicationTask( return this } + override fun serializeNBT(): CompoundTag { + return CODEC.encode(this, NbtOps.INSTANCE, NbtOps.INSTANCE.empty()).get().map({ it as CompoundTag }, { throw RuntimeException("Failed to serialize MutableReplicationTask: ${it.message()}") }) + } + companion object { fun deserializeNBT(nbt: Tag?): MutableReplicationTask? { return deserializeNBT(nbt, true) as MutableReplicationTask? @@ -202,6 +219,12 @@ data class MutableReplicationTask( fun read(buff: FriendlyByteBuf): MutableReplicationTask? { return read(buff, true) as MutableReplicationTask? } + + val CODEC: Codec by lazy { + RecordCodecBuilder.create { + codec(it).apply(it, ::MutableReplicationTask) + } + } } } diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/compat/jade/providers/MatteryWorkerProvider.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/compat/jade/providers/MatteryWorkerProvider.kt index a0b62cda3..9da096393 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/compat/jade/providers/MatteryWorkerProvider.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/compat/jade/providers/MatteryWorkerProvider.kt @@ -3,7 +3,7 @@ package ru.dbotthepony.mc.otm.compat.jade.providers import net.minecraft.nbt.CompoundTag import net.minecraft.nbt.ListTag import net.minecraft.resources.ResourceLocation -import ru.dbotthepony.mc.otm.block.entity.MachineItemJob +import ru.dbotthepony.mc.otm.block.entity.ItemJob import ru.dbotthepony.mc.otm.block.entity.MatteryWorkerBlockEntity import ru.dbotthepony.mc.otm.compat.jade.JadeTagKeys import ru.dbotthepony.mc.otm.compat.jade.JadeUids @@ -34,8 +34,8 @@ object MatteryWorkerProvider : IBlockComponentProvider, IServerDataProvider>(val parent: Codec, val min: S? = null, val max: S? = null, val minExclusive: Boolean = false, val maxExclusive: Boolean = false) : Codec { + private fun check(input: S): DataResult? { + if (min != null) { + if (minExclusive) { + if (input <= min) { + return DataResult.error { "Value $input is smaller or equal to minimal $min" } + } + } else { + if (input < min) { + return DataResult.error { "Value $input is smaller than minimal $min" } + } + } + } + + if (max != null) { + if (maxExclusive) { + if (input >= max) { + return DataResult.error { "Value $input is bigger or equal to maximal $max" } + } + } else { + if (input > max) { + return DataResult.error { "Value $input is bigger than maximal $max" } + } + } + } + + return null + } + + override fun encode(input: S, ops: DynamicOps, prefix: T): DataResult { + check(input)?.let { return it } + return parent.encode(input, ops, prefix) + } + + override fun decode(ops: DynamicOps, input: T): DataResult> { + return parent.decode(ops, input).flatMap { + check>(it.first) ?: DataResult.success(it) + } + } +} + +fun > Codec.minRange(min: S, exclusive: Boolean = false) = ComparableCodec(this, min = min, minExclusive = exclusive) +fun > Codec.maxRange(max: S, exclusive: Boolean = false) = ComparableCodec(this, max = max, maxExclusive = exclusive) +fun > Codec.inRange(min: S, minExclusive: Boolean = false, max: S, maxExclusive: Boolean = false) = ComparableCodec(this, min, max, minExclusive, maxExclusive) diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/data/DecimalCodec.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/data/DecimalCodec.kt index 140bd0848..dccd158c9 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/data/DecimalCodec.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/data/DecimalCodec.kt @@ -4,28 +4,63 @@ import com.mojang.datafixers.util.Pair import com.mojang.serialization.Codec import com.mojang.serialization.DataResult import com.mojang.serialization.DynamicOps +import it.unimi.dsi.fastutil.bytes.ByteArrayList +import net.minecraft.nbt.NbtOps import ru.dbotthepony.mc.otm.core.math.Decimal +import java.nio.ByteBuffer +import java.util.stream.Collector +import java.util.stream.Stream object DecimalCodec : Codec { override fun encode(input: Decimal, ops: DynamicOps, prefix: T): DataResult { + if (ops === NbtOps.INSTANCE) { + return DataResult.success((ops as DynamicOps).createByteList(ByteBuffer.wrap(input.toByteArray()))) + } + return DataResult.success(ops.createString(input.toString())) } override fun decode(ops: DynamicOps, input: T): DataResult> { - val result = ops.getStringValue(input).flatMap { + return ops.getStringValue(input).flatMap { try { DataResult.success(Pair(Decimal(it), ops.empty())) } catch (err: NumberFormatException) { DataResult.error { "Not a valid number for converting into Decimal: $it" } } - } - - if (result.result().isPresent) { - return result - } - - return ops.getNumberValue(input).flatMap { - DataResult.success(Pair(Decimal(it.toString()), ops.empty())) - } + }.get().map( + { + DataResult.success(it) + }, + { e0 -> + ops.getIntStream(input).flatMap { + try { + DataResult.success(Pair(Decimal.fromByteArray( + it.mapToObj { val v = it.toLong(); Stream.of(v and 0xFFL, (v and 0xFF00L) ushr 8, (v and 0xFF0000L) ushr 16, (v and 0xFF000000L) ushr 24) } + .flatMap { it } + .collect(::ByteArrayList, { v, a -> v.add(a.toByte()) }, ByteArrayList::addAll) + .toByteArray() + ), ops.empty())) + } catch (err: NumberFormatException) { + DataResult.error { "Failed to convert array of bytes into Decimal: $it" } + } + }.get().map( + { + DataResult.success(it) + }, + { e1 -> + ops.getNumberValue(input).flatMap { + DataResult.success(Pair(Decimal(it.toString()), ops.empty())) + }.get().map( + { + DataResult.success(it) + }, + { e2 -> + DataResult.error { "None of attempts at decoding Decimal were successful: ${e0.message()}; ${e1.message()}; ${e2.message()}" } + } + ) + } + ) + } + ) } } diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/data/Ext.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/data/Ext.kt index fcc744d47..da9e8fde5 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/data/Ext.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/data/Ext.kt @@ -3,12 +3,17 @@ package ru.dbotthepony.mc.otm.data import com.google.gson.JsonArray import com.google.gson.JsonNull import com.google.gson.JsonObject +import com.mojang.datafixers.kinds.App +import com.mojang.serialization.Codec import com.mojang.serialization.DataResult +import com.mojang.serialization.codecs.RecordCodecBuilder import it.unimi.dsi.fastutil.ints.IntAVLTreeSet import net.minecraft.world.entity.player.Player import net.minecraft.world.level.storage.loot.LootContext import net.minecraft.world.level.storage.loot.parameters.LootContextParam import net.minecraft.world.level.storage.loot.parameters.LootContextParams +import java.util.Optional +import kotlin.reflect.KProperty1 fun JsonArray.getRidOfNulls(): JsonArray { val toRemove = IntAVLTreeSet() diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/data/UUIDCodec.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/data/UUIDCodec.kt new file mode 100644 index 000000000..4accf6e75 --- /dev/null +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/data/UUIDCodec.kt @@ -0,0 +1,26 @@ +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 java.util.UUID +import java.util.stream.LongStream + +object UUIDCodec : Codec { + override fun encode(input: UUID, ops: DynamicOps, prefix: T): DataResult { + return DataResult.success(ops.createLongList(LongStream.of(input.mostSignificantBits, input.leastSignificantBits))) + } + + override fun decode(ops: DynamicOps, input: T): DataResult> { + return ops.getLongStream(input).flatMap { + val l = it.limit(2).toArray() + + if (l.size != 2) { + DataResult.error { "Can't construct UUID from ${l.size} elements" } + } else { + DataResult.success(Pair(UUID(l[0], l[1]), ops.empty())) + } + } + } +}