Revamp job event loops, move jobs to codecs

Add PatternState codecs
Add ReplicationTask codecs
Instead of returning job status on job tick, now downstream code directly update status of provided job status object
Make Matter Replicator and Matter Recycler not do full stop when there is not enough matter/nowhere to put matter
This commit is contained in:
DBotThePony 2023-07-25 14:19:35 +07:00
parent d4fb6d0b24
commit 8a78b299c5
Signed by: DBot
GPG Key ID: DCC23B5715498507
18 changed files with 614 additions and 431 deletions

View File

@ -1,114 +1,229 @@
package ru.dbotthepony.mc.otm.block.entity 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.CompoundTag
import net.minecraft.nbt.NbtOps
import net.minecraft.world.item.ItemStack import net.minecraft.world.item.ItemStack
import net.minecraftforge.common.util.INBTSerializable 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.IMatteryUpgrade
import ru.dbotthepony.mc.otm.capability.energy.IMatteryEnergyStorage import ru.dbotthepony.mc.otm.capability.energy.IMatteryEnergyStorage
import ru.dbotthepony.mc.otm.config.MachinesConfig import ru.dbotthepony.mc.otm.config.MachinesConfig
import ru.dbotthepony.mc.otm.core.math.Decimal 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.weakEqualDoubles
import ru.dbotthepony.mc.otm.core.math.weakGreaterThan import ru.dbotthepony.mc.otm.core.math.weakGreaterThan
import ru.dbotthepony.mc.otm.core.math.weakLessThan 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.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 fun isReason(status: Any?, reason: Any) = status == null || status == reason
private val LOGGER = LogManager.getLogger()
interface IMachineJob { interface IJob {
val ticks: Double val ticks: Double
val powerUsage: Decimal val powerUsage: Decimal
val experience: Float get() = 0f val experience: Float get() = 0f
fun serializeNBT(): CompoundTag
} }
open class MachineJob : IMachineJob { open class Job(
final override val ticks: Double final override val ticks: Double,
final override val powerUsage: Decimal final override val powerUsage: Decimal = Decimal.ZERO,
final override val experience: Float final override val experience: Float = 0f
) : IJob {
constructor( companion object {
ticks: Double, fun <T : Job> basicCodec(builder: RecordCodecBuilder.Instance<T>): Products.P3<RecordCodecBuilder.Mu<T>, Double, Decimal, Float> {
powerUsage: Decimal = Decimal.ZERO, return builder.group(
experience: Float = 0f, Codec.doubleRange(0.0, Double.MAX_VALUE).fieldOf("ticks").forGetter(Job::ticks),
) { DecimalCodec.fieldOf("powerUsage").forGetter(Job::powerUsage), // не надо указывать минимальную энергию как 0,
this.ticks = ticks // ибо мы можем таким образом использовать это для создания работ генератора
this.powerUsage = powerUsage Codec.floatRange(0f, Float.MAX_VALUE).optionalFieldOf("experience", 0f).forGetter(Job::experience),
this.experience = experience )
} }
constructor( fun <T : Job> plainCodec(builder: RecordCodecBuilder.Instance<T>): Products.P2<RecordCodecBuilder.Mu<T>, Double, Decimal> {
tag: CompoundTag return builder.group(
) : this(tag.getDouble("Ticks"), tag.getDecimal("EnergyUsage"), tag.getFloat("Experience")) Codec.doubleRange(0.0, Double.MAX_VALUE).fieldOf("ticks").forGetter(Job::ticks),
DecimalCodec.fieldOf("powerUsage").forGetter(Job::powerUsage), // не надо указывать минимальную энергию как 0,
// ибо мы можем таким образом использовать это для создания работ генератора
)
}
override fun serializeNBT(): CompoundTag { val CODEC: Codec<Job> by lazy {
return CompoundTag().also { RecordCodecBuilder.create {
it["Ticks"] = ticks basicCodec(it).apply(it, ::Job)
it["EnergyUsage"] = powerUsage }
it["Experience"] = experience
} }
} }
} }
open class MachineItemJob : MachineJob { open class ItemJob(
val itemStack: ItemStack val itemStack: ItemStack,
constructor(
itemStack: ItemStack,
ticks: Double, ticks: Double,
power: Decimal = Decimal.ZERO, power: Decimal = Decimal.ZERO,
experience: Float = 0f, experience: Float = 0f
) : super(ticks, power, experience) { ) : Job(ticks, power, experience) {
this.itemStack = itemStack companion object {
fun <T : ItemJob> itemCodec(builder: RecordCodecBuilder.Instance<T>): Products.P4<RecordCodecBuilder.Mu<T>, ItemStack, Double, Decimal, Float> {
return builder.group(ItemStack.CODEC.fieldOf("itemStack").forGetter(ItemJob::itemStack)).and(basicCodec(builder))
} }
constructor( val CODEC: Codec<ItemJob> by lazy {
tag: CompoundTag RecordCodecBuilder.create {
) : super(tag) { itemCodec(it).apply(it, ::ItemJob)
this.itemStack = (tag["Item"] as? CompoundTag)?.let { ItemStack.of(it) } ?: ItemStack.EMPTY
} }
override fun serializeNBT(): CompoundTag {
return super.serializeNBT().also {
it["Item"] = itemStack.serializeNBT()
} }
} }
} }
data class JobStatus( class JobStatus<T : IJob>(
val success: Boolean, var requiredPower: Decimal,
val throttleTicks: Int = 0, val extractedPower: Decimal,
val idleReason: MachineJobEventLoop.IdleReason? = null, ticksAdvanced: Double,
val newDrainedPower: Decimal? = null val job: T,
) { val workTicks: Double,
init { ) : IJob by job {
require(throttleTicks >= 0) { "Negative amount of ticks to throttle: $throttleTicks" } 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()
} }
companion object { var success = true
val SUCCESS = JobStatus(true)
val FAILURE = JobStatus(false) var ticksAdvanced = ticksAdvanced
val FAILURE_ITEM = JobStatus(false, 20, MachineJobEventLoop.IdleReason.ITEM) set(value) {
val FAILURE_MATTER = JobStatus(false, 20, MachineJobEventLoop.IdleReason.MATTER) require(value >= 0.0) { "Invalid amount of ticks to advance: $value" }
val FAILURE_WAIT = JobStatus(false, 100) field = value
val FAILURE_WAIT_FAST = JobStatus(false, 20) }
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
}
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<JobType : IMachineJob>(val job: JobType? = null, val idleReason: Any? = null, val throttleTicks: Int = 0) { data class JobContainer<JobType : IJob>(val job: JobType? = null, val idleReason: Any? = null, val throttleTicks: Int = 0) {
init { init {
require(throttleTicks >= 0) { "Negative amount of ticks to throttle: $throttleTicks" } 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 { companion object {
fun <JobType : IMachineJob> success(job: JobType): JobContainer<JobType> { fun <JobType : IJob> success(job: JobType): JobContainer<JobType> {
return JobContainer(job, null) return JobContainer(job, null)
} }
fun <JobType : IMachineJob> failure(reason: Any?): JobContainer<JobType> { fun <JobType : IJob> failure(reason: Any?): JobContainer<JobType> {
return JobContainer(null, reason) return JobContainer(null, reason)
} }
@ -120,41 +235,40 @@ data class JobContainer<JobType : IMachineJob>(val job: JobType? = null, val idl
private val observe = JobContainer(null, MachineJobEventLoop.IdleReason.OBSERVING) private val observe = JobContainer(null, MachineJobEventLoop.IdleReason.OBSERVING)
@Suppress("unchecked_cast") @Suppress("unchecked_cast")
fun <JobType : IMachineJob> failure(): JobContainer<JobType> { fun <JobType : IJob> failure(): JobContainer<JobType> {
return empty as JobContainer<JobType> return empty as JobContainer<JobType>
} }
@Suppress("unchecked_cast") @Suppress("unchecked_cast")
fun <JobType : IMachineJob> noItem(): JobContainer<JobType> { fun <JobType : IJob> noItem(): JobContainer<JobType> {
return noItem as JobContainer<JobType> return noItem as JobContainer<JobType>
} }
@Suppress("unchecked_cast") @Suppress("unchecked_cast")
fun <JobType : IMachineJob> noEnergy(): JobContainer<JobType> { fun <JobType : IJob> noEnergy(): JobContainer<JobType> {
return noEnergy as JobContainer<JobType> return noEnergy as JobContainer<JobType>
} }
@Suppress("unchecked_cast") @Suppress("unchecked_cast")
fun <JobType : IMachineJob> noMatter(): JobContainer<JobType> { fun <JobType : IJob> noMatter(): JobContainer<JobType> {
return noMatter as JobContainer<JobType> return noMatter as JobContainer<JobType>
} }
@Suppress("unchecked_cast") @Suppress("unchecked_cast")
fun <JobType : IMachineJob> noPattern(): JobContainer<JobType> { fun <JobType : IJob> noPattern(): JobContainer<JobType> {
return noPattern as JobContainer<JobType> return noPattern as JobContainer<JobType>
} }
@Suppress("unchecked_cast") @Suppress("unchecked_cast")
fun <JobType : IMachineJob> observe(): JobContainer<JobType> { fun <JobType : IJob> observe(): JobContainer<JobType> {
return observe as JobContainer<JobType> return observe as JobContainer<JobType>
} }
} }
} }
abstract class MachineJobEventLoop<JobType : IMachineJob> : INBTSerializable<CompoundTag?> { abstract class MachineJobEventLoop<JobType : IJob>(val codec: Codec<JobType>) : INBTSerializable<CompoundTag?> {
protected abstract val energy: IMatteryEnergyStorage? protected abstract val energy: IMatteryEnergyStorage?
protected abstract val isBlockedByRedstone: Boolean protected abstract val isBlockedByRedstone: Boolean
protected abstract fun deserializeJob(nbt: CompoundTag): JobType?
protected abstract val upgrades: IMatteryUpgrade? protected abstract val upgrades: IMatteryUpgrade?
var currentJob: JobType? = null var currentJob: JobType? = null
@ -212,7 +326,17 @@ abstract class MachineJobEventLoop<JobType : IMachineJob> : INBTSerializable<Com
override fun serializeNBT(): CompoundTag { override fun serializeNBT(): CompoundTag {
return CompoundTag().also { nbt -> return CompoundTag().also { nbt ->
nbt["WorkTicks"] = workTicks 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<JobType : IMachineJob> : INBTSerializable<Com
nbt ?: return nbt ?: return
workTicks = nbt.getDouble("WorkTicks") workTicks = nbt.getDouble("WorkTicks")
currentJob = nbt.map("Job", ::deserializeJob) currentJob = null
if ("Job" in nbt) {
codec.decode(NbtOps.INSTANCE, nbt["Job"]!!).get().map(
{
currentJob = it.first
},
{
LOGGER.error("Failed to deserialize job data from storage", RuntimeException(it.message()))
}
)
}
if (currentJob == null) if (currentJob == null)
workTicks = 0.0 workTicks = 0.0
} }
protected abstract fun jobUpdated(new: JobType?, old: JobType?) protected open fun jobUpdated(new: JobType?, old: JobType?) {}
/** /**
* Called whenever reaching desired amount of ticks at job * Called whenever reaching desired amount of ticks at job
*/ */
protected abstract fun onJobFinish(job: JobType): JobStatus protected abstract fun onJobFinish(status: JobStatus<JobType>)
/** /**
* Called when there is nothing to do and we are not idling * Called when there is nothing to do and we are not idling
*/ */
protected abstract fun computeNextJob(): JobContainer<JobType> protected abstract fun computeNextJob(): JobContainer<JobType>
protected open fun onWorkTick(requiredPower: Decimal, extractedPower: Decimal, ticksAdvanced: Double, job: JobType): JobStatus { protected open fun onJobTick(status: JobStatus<JobType>) {}
return JobStatus.SUCCESS
}
/** /**
* Advances job loop by specified [ticks]. If machine is speed up by upgrades, * Advances job loop by specified [ticks]. If machine is speed up by upgrades,
@ -317,8 +450,11 @@ abstract class MachineJobEventLoop<JobType : IMachineJob> : INBTSerializable<Com
if (requiredPower.isPositive) { if (requiredPower.isPositive) {
extractedPower = energy!!.extractEnergy(requiredPower, true) extractedPower = energy!!.extractEnergy(requiredPower, true)
ticksAdvanced = (extractedPower / requiredPower).toDouble().coerceIn(0.0, 1.0) * ticksLeft ticksAdvanced = (extractedPower / requiredPower).toDouble().coerceIn(0.0, 1.0) * ticksLeft
} else { } else if (requiredPower.isZero) {
ticksAdvanced = ticksLeft ticksAdvanced = ticksLeft
} else {
extractedPower = -energy!!.receiveEnergy(-requiredPower, true)
ticksAdvanced = (extractedPower / requiredPower).toDouble().coerceIn(0.0, 1.0) * ticksLeft
} }
} }
@ -326,7 +462,8 @@ abstract class MachineJobEventLoop<JobType : IMachineJob> : INBTSerializable<Com
break break
} }
val status = onWorkTick(requiredPower ?: Decimal.ZERO, extractedPower ?: Decimal.ZERO, ticksAdvanced, currentJob) val status = JobStatus(requiredPower ?: Decimal.ZERO, extractedPower ?: Decimal.ZERO, ticksAdvanced, currentJob, workTicks)
onJobTick(status)
if (!status.success) { if (!status.success) {
throttleTicks += status.throttleTicks throttleTicks += status.throttleTicks
@ -336,25 +473,31 @@ abstract class MachineJobEventLoop<JobType : IMachineJob> : INBTSerializable<Com
isIdling = true isIdling = true
} }
break
} else if (status.requiredPower.isPositive && energy == null) {
idleReason = IdleReason.POWER
isIdling = true
idleTicksAnim++
break break
} }
workingTicksAnim++ workingTicksAnim++
errorTicksAnim = 0 errorTicksAnim = 0
workTicks += ticksAdvanced workTicks += status.ticksAdvanced
availableTicks -= ticksAdvanced availableTicks -= status.ticksAdvanced
extractedPower = status.newDrainedPower ?: extractedPower if (energy != null && status.requiredPower.isPositive) {
energy.extractEnergy(status.requiredPower, false)
if (extractedPower != null) { } else if (energy != null && status.requiredPower.isNegative) {
energy!!.extractEnergy(extractedPower, false) energy.receiveEnergy(-status.requiredPower, false)
} }
continue continue
} }
val status = onJobFinish(currentJob) val status = JobStatus(currentJob, workTicks)
onJobFinish(status)
if (status.success) { if (status.success) {
this.currentJob = null this.currentJob = null

View File

@ -1,6 +1,7 @@
package ru.dbotthepony.mc.otm.block.entity package ru.dbotthepony.mc.otm.block.entity
import com.google.common.collect.ImmutableList import com.google.common.collect.ImmutableList
import com.mojang.serialization.Codec
import net.minecraft.core.BlockPos import net.minecraft.core.BlockPos
import net.minecraft.nbt.CompoundTag import net.minecraft.nbt.CompoundTag
import net.minecraft.nbt.ListTag import net.minecraft.nbt.ListTag
@ -11,12 +12,12 @@ import net.minecraft.world.level.BlockGetter
import net.minecraft.world.level.block.Block import net.minecraft.world.level.block.Block
import net.minecraft.world.level.block.entity.BlockEntityType import net.minecraft.world.level.block.entity.BlockEntityType
import net.minecraft.world.level.block.state.BlockState import net.minecraft.world.level.block.state.BlockState
import org.apache.logging.log4j.LogManager
import ru.dbotthepony.mc.otm.capability.IMatteryUpgrade import ru.dbotthepony.mc.otm.capability.IMatteryUpgrade
import ru.dbotthepony.mc.otm.capability.energy.IMatteryEnergyStorage import ru.dbotthepony.mc.otm.capability.energy.IMatteryEnergyStorage
import ru.dbotthepony.mc.otm.capability.energy.WorkerEnergyStorage import ru.dbotthepony.mc.otm.capability.energy.WorkerEnergyStorage
import ru.dbotthepony.mc.otm.capability.matteryEnergy import ru.dbotthepony.mc.otm.capability.matteryEnergy
import ru.dbotthepony.mc.otm.core.immutableList import ru.dbotthepony.mc.otm.core.immutableList
import ru.dbotthepony.mc.otm.core.math.Decimal
import ru.dbotthepony.mc.otm.core.nbt.getCompoundList import ru.dbotthepony.mc.otm.core.nbt.getCompoundList
import ru.dbotthepony.mc.otm.core.nbt.set import ru.dbotthepony.mc.otm.core.nbt.set
@ -25,15 +26,15 @@ import ru.dbotthepony.mc.otm.core.nbt.set
* *
* From technical point, this is a specialized use case of [MachineJobEventLoop]. * From technical point, this is a specialized use case of [MachineJobEventLoop].
*/ */
abstract class MatteryWorkerBlockEntity<JobType : IMachineJob>( abstract class MatteryWorkerBlockEntity<JobType : IJob>(
type: BlockEntityType<*>, type: BlockEntityType<*>,
blockPos: BlockPos, blockPos: BlockPos,
blockState: BlockState, blockState: BlockState,
val jobDeserializer: (tag: CompoundTag) -> JobType?, jobCodec: Codec<JobType>,
maxJobs: Int = 1, maxJobs: Int = 1,
) : MatteryPoweredBlockEntity(type, blockPos, blockState) { ) : MatteryPoweredBlockEntity(type, blockPos, blockState) {
val jobEventLoops: ImmutableList<MachineJobEventLoop<JobType>> = immutableList(maxJobs) { id -> val jobEventLoops: ImmutableList<MachineJobEventLoop<JobType>> = immutableList(maxJobs) { id ->
object : MachineJobEventLoop<JobType>() { object : MachineJobEventLoop<JobType>(jobCodec) {
override val energy: IMatteryEnergyStorage? override val energy: IMatteryEnergyStorage?
get() = matteryEnergy get() = matteryEnergy
override val isBlockedByRedstone: Boolean override val isBlockedByRedstone: Boolean
@ -41,12 +42,8 @@ abstract class MatteryWorkerBlockEntity<JobType : IMachineJob>(
override val upgrades: IMatteryUpgrade? override val upgrades: IMatteryUpgrade?
get() = this@MatteryWorkerBlockEntity.upgrades get() = this@MatteryWorkerBlockEntity.upgrades
override fun deserializeJob(nbt: CompoundTag): JobType? { override fun onJobFinish(status: JobStatus<JobType>) {
return jobDeserializer.invoke(nbt) return this@MatteryWorkerBlockEntity.onJobFinish(status, id)
}
override fun onJobFinish(job: JobType): JobStatus {
return this@MatteryWorkerBlockEntity.onJobFinish(job, id)
} }
override fun computeNextJob(): JobContainer<JobType> { override fun computeNextJob(): JobContainer<JobType> {
@ -57,8 +54,8 @@ abstract class MatteryWorkerBlockEntity<JobType : IMachineJob>(
this@MatteryWorkerBlockEntity.jobUpdated(new, old, id) this@MatteryWorkerBlockEntity.jobUpdated(new, old, id)
} }
override fun onWorkTick(requiredPower: Decimal, extractedPower: Decimal, ticksAdvanced: Double, job: JobType): JobStatus { override fun onJobTick(status: JobStatus<JobType>) {
return this@MatteryWorkerBlockEntity.onWorkTick(requiredPower, extractedPower, ticksAdvanced, job, id) return this@MatteryWorkerBlockEntity.onJobTick(status, id)
} }
} }
} }
@ -72,12 +69,10 @@ abstract class MatteryWorkerBlockEntity<JobType : IMachineJob>(
} }
protected open fun jobUpdated(new: JobType?, old: JobType?, id: Int) {} 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<JobType>, id: Int)
protected abstract fun computeNextJob(id: Int): JobContainer<JobType> protected abstract fun computeNextJob(id: Int): JobContainer<JobType>
protected open fun onWorkTick(requiredPower: Decimal, extractedPower: Decimal, ticksAdvanced: Double, job: JobType, id: Int): JobStatus { protected open fun onJobTick(status: JobStatus<JobType>, id: Int) {}
return JobStatus.SUCCESS
}
override fun saveShared(nbt: CompoundTag) { override fun saveShared(nbt: CompoundTag) {
super.saveShared(nbt) super.saveShared(nbt)
@ -148,6 +143,8 @@ abstract class MatteryWorkerBlockEntity<JobType : IMachineJob>(
} }
companion object { companion object {
private val LOGGER = LogManager.getLogger()
fun appendHoverText(itemStack: ItemStack, blockGetter: BlockGetter?, tooltips: MutableList<Component>, flag: TooltipFlag) { fun appendHoverText(itemStack: ItemStack, blockGetter: BlockGetter?, tooltips: MutableList<Component>, flag: TooltipFlag) {
itemStack.tag ?: return itemStack.tag ?: return

View File

@ -1,5 +1,7 @@
package ru.dbotthepony.mc.otm.block.entity.matter 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.core.BlockPos
import net.minecraft.nbt.CompoundTag import net.minecraft.nbt.CompoundTag
import net.minecraft.world.entity.player.Inventory 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 net.minecraftforge.common.ForgeConfigSpec
import ru.dbotthepony.mc.otm.block.entity.JobContainer import ru.dbotthepony.mc.otm.block.entity.JobContainer
import ru.dbotthepony.mc.otm.block.entity.JobStatus 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.block.entity.MatteryWorkerBlockEntity
import ru.dbotthepony.mc.otm.capability.FlowDirection import ru.dbotthepony.mc.otm.capability.FlowDirection
import ru.dbotthepony.mc.otm.capability.MatteryCapability 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.math.set
import ru.dbotthepony.mc.otm.core.nbt.set import ru.dbotthepony.mc.otm.core.nbt.set
import ru.dbotthepony.mc.otm.core.util.WriteOnce 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.MatterGraph
import ru.dbotthepony.mc.otm.graph.matter.SimpleMatterNode import ru.dbotthepony.mc.otm.graph.matter.SimpleMatterNode
import ru.dbotthepony.mc.otm.item.matter.MatterDustItem import ru.dbotthepony.mc.otm.item.matter.MatterDustItem
@ -93,32 +97,23 @@ fun moveMatterAsDustIntoContainer(_matterValue: Decimal, container: MatteryConta
} }
class MatterDecomposerBlockEntity(pos: BlockPos, state: BlockState) class MatterDecomposerBlockEntity(pos: BlockPos, state: BlockState)
: MatteryWorkerBlockEntity<MatterDecomposerBlockEntity.DecomposerJob>(MBlockEntities.MATTER_DECOMPOSER, pos, state, ::DecomposerJob) { : MatteryWorkerBlockEntity<MatterDecomposerBlockEntity.DecomposerJob>(MBlockEntities.MATTER_DECOMPOSER, pos, state, DecomposerJob.CODEC) {
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
}
}
class DecomposerJob(
val toDust: Boolean,
var matterValue: Decimal,
ticks: Double,
) : Job(ticks, BASE_CONSUMPTION) {
companion object { companion object {
const val TO_DUST_KEY = "toDust" val CODEC: Codec<DecomposerJob> by lazy {
const val MATTER_VALUE_KEY = "matterValue" 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) return MatterDecomposerMenu(containerID, inventory, this)
} }
override fun onJobFinish(job: DecomposerJob, id: Int): JobStatus { override fun onJobFinish(status: JobStatus<DecomposerJob>, id: Int) {
if (job.toDust) { if (status.job.toDust) {
job.matterValue = moveMatterAsDustIntoContainer(job.matterValue, outputContainer, 0, 1) status.job.matterValue = moveMatterAsDustIntoContainer(status.job.matterValue, outputContainer, 0, 1)
if (!job.matterValue.isZero) if (!status.job.matterValue.isZero)
return JobStatus.FAILURE_WAIT_FAST 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<DecomposerJob> { override fun computeNextJob(id: Int): JobContainer<DecomposerJob> {

View File

@ -9,8 +9,6 @@ import net.minecraft.world.entity.player.Inventory
import net.minecraft.world.entity.player.Player import net.minecraft.world.entity.player.Player
import net.minecraft.world.inventory.AbstractContainerMenu import net.minecraft.world.inventory.AbstractContainerMenu
import ru.dbotthepony.mc.otm.capability.MatteryCapability import ru.dbotthepony.mc.otm.capability.MatteryCapability
import java.util.HashMap
import java.util.UUID
import net.minecraft.nbt.CompoundTag import net.minecraft.nbt.CompoundTag
import net.minecraft.nbt.ListTag import net.minecraft.nbt.ListTag
import net.minecraft.server.level.ServerLevel 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.MatterNode
import ru.dbotthepony.mc.otm.graph.matter.SimpleMatterNode import ru.dbotthepony.mc.otm.graph.matter.SimpleMatterNode
import ru.dbotthepony.mc.otm.registry.MBlockEntities import ru.dbotthepony.mc.otm.registry.MBlockEntities
import java.util.ArrayList import java.util.*
import java.util.stream.Stream import java.util.stream.Stream
class MatterPanelBlockEntity(p_155229_: BlockPos, p_155230_: BlockState) : 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) { for ((key, task) in _tasks) {
if (task.required > 0) { if (task.required > 0) {
val pattern = task.patternId?.let(graph::getPattern) ?: continue val pattern = task.patternId.map(graph::getPattern).orElse(null) ?: continue
if (!simulate) { if (!simulate) {
val new = task.allocate() val new = task.allocate()
@ -198,7 +196,7 @@ class MatterPanelBlockEntity(p_155229_: BlockPos, p_155230_: BlockState) :
} }
fun addTask(state: IPatternState, count: Int): IReplicationTask<*> { 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 _tasks[task.id] = task
matterNode.graph.onMatterTaskCreated(task) matterNode.graph.onMatterTaskCreated(task)

View File

@ -1,22 +1,19 @@
package ru.dbotthepony.mc.otm.block.entity.matter 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.core.BlockPos
import net.minecraft.nbt.CompoundTag
import net.minecraft.world.entity.player.Inventory import net.minecraft.world.entity.player.Inventory
import net.minecraft.world.entity.player.Player import net.minecraft.world.entity.player.Player
import net.minecraft.world.inventory.AbstractContainerMenu import net.minecraft.world.inventory.AbstractContainerMenu
import net.minecraft.world.item.ItemStack import net.minecraft.world.item.ItemStack
import net.minecraft.world.level.Level import net.minecraft.world.level.Level
import net.minecraft.world.level.block.state.BlockState 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.JobContainer
import ru.dbotthepony.mc.otm.block.entity.JobStatus 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.config.ConciseBalanceValues
import ru.dbotthepony.mc.otm.block.entity.MatteryWorkerBlockEntity import ru.dbotthepony.mc.otm.block.entity.MatteryWorkerBlockEntity
import ru.dbotthepony.mc.otm.capability.FlowDirection 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.MatteryCapability
import ru.dbotthepony.mc.otm.capability.energy.ProfiledEnergyStorage import ru.dbotthepony.mc.otm.capability.energy.ProfiledEnergyStorage
import ru.dbotthepony.mc.otm.capability.energy.WorkerEnergyStorage 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.item.matter.MatterDustItem
import ru.dbotthepony.mc.otm.menu.matter.MatterRecyclerMenu import ru.dbotthepony.mc.otm.menu.matter.MatterRecyclerMenu
import ru.dbotthepony.mc.otm.registry.MBlockEntities import ru.dbotthepony.mc.otm.registry.MBlockEntities
import ru.dbotthepony.mc.otm.core.math.DecimalConfigValue import ru.dbotthepony.mc.otm.data.DecimalCodec
import ru.dbotthepony.mc.otm.core.math.defineDecimal import ru.dbotthepony.mc.otm.data.minRange
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.graph.matter.SimpleMatterNode import ru.dbotthepony.mc.otm.graph.matter.SimpleMatterNode
class MatterRecyclerBlockEntity(blockPos: BlockPos, blockState: BlockState) class MatterRecyclerBlockEntity(blockPos: BlockPos, blockState: BlockState)
: MatteryWorkerBlockEntity<MatterRecyclerBlockEntity.RecyclerJob>(MBlockEntities.MATTER_RECYCLER, blockPos, blockState, ::RecyclerJob) { : MatteryWorkerBlockEntity<MatterRecyclerBlockEntity.RecyclerJob>(MBlockEntities.MATTER_RECYCLER, blockPos, blockState, RecyclerJob.CODEC) {
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
}
}
class RecyclerJob(ticks: Double, powerUsage: Decimal, var totalMatter: Decimal) : Job(ticks, powerUsage) {
companion object { companion object {
const val KEY = "totalMatter" val CODEC: Codec<RecyclerJob> 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) return MatterRecyclerMenu(containerID, inventory, this)
} }
override fun onJobFinish(job: RecyclerJob, id: Int): JobStatus { override fun onJobFinish(status: JobStatus<RecyclerJob>, id: Int) {
val job = status.job
if (job.totalMatter.isPositive) { if (job.totalMatter.isPositive) {
val received = matter.receiveMatter(job.totalMatter, true) val received = matter.receiveMatter(job.totalMatter, true)
if (job.totalMatter != received) if (job.totalMatter != received)
return JobStatus.FAILURE_MATTER return status.noMatter()
matter.receiveMatter(job.totalMatter, false) matter.receiveMatter(job.totalMatter, false)
job.totalMatter -= received job.totalMatter -= received
} }
return JobStatus.SUCCESS
} }
override fun computeNextJob(id: Int): JobContainer<RecyclerJob> { override fun computeNextJob(id: Int): JobContainer<RecyclerJob> {
@ -147,20 +124,16 @@ class MatterRecyclerBlockEntity(blockPos: BlockPos, blockState: BlockState)
) )
} }
override fun onWorkTick(requiredPower: Decimal, extractedPower: Decimal, ticksAdvanced: Double, job: RecyclerJob, id: Int): JobStatus { override fun onJobTick(status: JobStatus<RecyclerJob>, id: Int) {
val receive = job.totalMatter.coerceAtMost(MachinesConfig.MATTER_RECYCLER_MATTER_PER_TICK.get() * ticksAdvanced) val job = status.job
val toReceive = job.totalMatter.coerceAtMost(MachinesConfig.MATTER_RECYCLER_MATTER_PER_TICK.get() * status.ticksAdvanced)
if (receive.isZero) if (toReceive.isZero)
return JobStatus.SUCCESS return status.success()
val received = matter.receiveMatter(receive, true) val received = matter.receiveMatter(toReceive, false)
status.scale(received / toReceive)
if (receive != received)
return JobStatus.FAILURE_MATTER
matter.receiveMatter(receive, false)
job.totalMatter -= received job.totalMatter -= received
return JobStatus.SUCCESS
} }
override fun tick() { override fun tick() {

View File

@ -1,7 +1,8 @@
package ru.dbotthepony.mc.otm.block.entity.matter 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.core.BlockPos
import net.minecraft.nbt.CompoundTag
import net.minecraft.world.entity.player.Inventory import net.minecraft.world.entity.player.Inventory
import net.minecraft.world.entity.player.Player import net.minecraft.world.entity.player.Player
import net.minecraft.world.inventory.AbstractContainerMenu import net.minecraft.world.inventory.AbstractContainerMenu
@ -11,7 +12,7 @@ import net.minecraft.world.level.block.state.BlockState
import net.minecraftforge.common.ForgeConfigSpec import net.minecraftforge.common.ForgeConfigSpec
import ru.dbotthepony.mc.otm.block.entity.JobContainer import ru.dbotthepony.mc.otm.block.entity.JobContainer
import ru.dbotthepony.mc.otm.block.entity.JobStatus 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.MachineJobEventLoop
import ru.dbotthepony.mc.otm.block.entity.MatteryWorkerBlockEntity import ru.dbotthepony.mc.otm.block.entity.MatteryWorkerBlockEntity
import ru.dbotthepony.mc.otm.capability.FlowDirection 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.Decimal
import ru.dbotthepony.mc.otm.core.math.DecimalConfigValue import ru.dbotthepony.mc.otm.core.math.DecimalConfigValue
import ru.dbotthepony.mc.otm.core.math.defineDecimal 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.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.graph.matter.MatterNode
import ru.dbotthepony.mc.otm.matter.MatterManager import ru.dbotthepony.mc.otm.matter.MatterManager
import ru.dbotthepony.mc.otm.menu.matter.MatterReplicatorMenu import ru.dbotthepony.mc.otm.menu.matter.MatterReplicatorMenu
import ru.dbotthepony.mc.otm.registry.MBlockEntities import ru.dbotthepony.mc.otm.registry.MBlockEntities
import ru.dbotthepony.mc.otm.registry.MNames import ru.dbotthepony.mc.otm.registry.MNames
import java.util.*
class MatterReplicatorBlockEntity(p_155229_: BlockPos, p_155230_: BlockState) : class MatterReplicatorBlockEntity(p_155229_: BlockPos, p_155230_: BlockState) :
MatteryWorkerBlockEntity<MatterReplicatorBlockEntity.ReplicatorJob>(MBlockEntities.MATTER_REPLICATOR, p_155229_, p_155230_, { MatteryWorkerBlockEntity<MatterReplicatorBlockEntity.ReplicatorJob>(MBlockEntities.MATTER_REPLICATOR, p_155229_, p_155230_, ReplicatorJob.CODEC) {
try {
ReplicatorJob(it)
} catch(err: NoSuchElementException) {
null
}
}) {
class ReplicatorJob : MachineItemJob { class ReplicatorJob(
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, itemStack: ItemStack,
matterPerTick: Decimal, val matterPerTick: Decimal,
task: ReplicationTask, val task: UUID,
matterValue: Decimal, var matterValue: Decimal,
pattern: PatternState?, val pattern: Optional<PatternState>,
asDust: Boolean, val asDust: Boolean,
ticks: Double, ticks: Double,
) : super(itemStack, ticks, BASE_CONSUMPTION) { ) : ItemJob(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
}
}
companion object { companion object {
const val MATTER_PER_TICK_KEY = "matterPerTick" val CODEC: Codec<ReplicatorJob> by lazy {
const val MATTER_VALUE_KEY = "matterValue" RecordCodecBuilder.create {
const val PATTERN_KEY = "pattern" it.group(
const val AS_DUST_KEY = "asDust" ItemStack.CODEC.fieldOf("itemStack").forGetter(ReplicatorJob::itemStack),
const val TASK_KEY = "task" 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) return MatterReplicatorMenu(containerID, inventory, this)
} }
override fun onJobFinish(job: ReplicatorJob, id: Int): JobStatus { override fun onJobFinish(status: JobStatus<ReplicatorJob>, id: Int) {
val job = status.job
if (job.asDust) { if (job.asDust) {
job.matterValue = moveMatterAsDustIntoContainer(job.matterValue, container, OUTPUT_DUST_MAIN, OUTPUT_DUST_STACKING) job.matterValue = moveMatterAsDustIntoContainer(job.matterValue, container, OUTPUT_DUST_MAIN, OUTPUT_DUST_STACKING)
if (!job.matterValue.isZero) { 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)) { if (!container.fullyAddItem(job.itemStack, FIRST_ACTUAL_OUTPUT_SLOT .. LAST_ACTUAL_OUTPUT_SLOT)) {
return JobStatus.FAILURE_ITEM return status.noItem()
} }
matterNode.graph.notifyTaskCompletion(job.task.id) matterNode.graph.notifyTaskCompletion(job.task)
return JobStatus.SUCCESS }
} }
override fun setRemoved() { override fun setRemoved() {
@ -211,64 +179,29 @@ class MatterReplicatorBlockEntity(p_155229_: BlockPos, p_155230_: BlockState) :
return JobContainer.success(ReplicatorJob( return JobContainer.success(ReplicatorJob(
itemStack = stack, itemStack = stack,
matterPerTick = matter.matter / ticks, matterPerTick = matter.matter / ticks,
task = allocation.task.asImmutable(), task = allocation.task.id,
matterValue = matter.matter, 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), asDust = (level?.random?.nextDouble() ?: 1.0) * upgrades.failureMultiplier > (allocation.pattern?.researchPercent ?: 2.0),
ticks = ticks, ticks = ticks,
)) ))
} }
override fun onWorkTick(requiredPower: Decimal, extractedPower: Decimal, ticksAdvanced: Double, job: ReplicatorJob, id: Int): JobStatus { override fun onJobTick(status: JobStatus<ReplicatorJob>, id: Int) {
val drainPerTick = job.matterPerTick * ticksAdvanced val job = status.job
val drainPerTick = job.matterPerTick * status.ticksAdvanced
if (matter.extractMatter(drainPerTick, true) < drainPerTick) { if (matter.extractMatter(drainPerTick, true) < drainPerTick) {
// в машине недостаточно материи val toDrain = (drainPerTick * DRAIN_MULT)
.coerceAtMost(job.matterPerTick * (status.ticks - status.workTicks + status.ticksAdvanced))
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) .coerceAtLeast(Decimal.ONE)
.coerceAtMost(matter.missingMatter), false) .coerceAtMost(matter.missingMatter)
if (drain.isZero) { matter.receiveMatter(matterNode.graph.extractMatter(toDrain, false), false)
// в сети нет материи
return JobStatus.FAILURE_MATTER
} }
matter.receiveMatter(drain, false) status.scale(matter.extractMatter(drainPerTick, false) / drainPerTick)
visualProgress = status.workProgress
// получили материю, проверяем возможность работы
if (matter.extractMatter(drainPerTick, true) >= drainPerTick) {
matter.extractMatter(drainPerTick, false)
return JobStatus.SUCCESS
} else {
// :(
return JobStatus.FAILURE_WAIT
}
}
}
// в машине достаточно материи
matter.extractMatter(drainPerTick, false)
visualProgress = jobEventLoops[0].workProgress
return JobStatus.SUCCESS
} }
companion object { companion object {

View File

@ -10,7 +10,7 @@ import net.minecraft.world.level.block.state.BlockState
import net.minecraftforge.common.ForgeConfigSpec import net.minecraftforge.common.ForgeConfigSpec
import ru.dbotthepony.mc.otm.block.entity.JobContainer import ru.dbotthepony.mc.otm.block.entity.JobContainer
import ru.dbotthepony.mc.otm.block.entity.JobStatus 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.MachineJobEventLoop
import ru.dbotthepony.mc.otm.config.ConciseBalanceValues import ru.dbotthepony.mc.otm.config.ConciseBalanceValues
import ru.dbotthepony.mc.otm.block.entity.MatteryWorkerBlockEntity import ru.dbotthepony.mc.otm.block.entity.MatteryWorkerBlockEntity
@ -35,7 +35,7 @@ import java.util.*
import kotlin.math.pow import kotlin.math.pow
class MatterScannerBlockEntity(p_155229_: BlockPos, p_155230_: BlockState) : class MatterScannerBlockEntity(p_155229_: BlockPos, p_155230_: BlockState) :
MatteryWorkerBlockEntity<MachineItemJob>(MBlockEntities.MATTER_SCANNER, p_155229_, p_155230_, ::MachineItemJob) { MatteryWorkerBlockEntity<ItemJob>(MBlockEntities.MATTER_SCANNER, p_155229_, p_155230_, ItemJob.CODEC) {
val container = MatteryContainer(::itemContainerUpdated, 1).also(::addDroppableContainer) val container = MatteryContainer(::itemContainerUpdated, 1).also(::addDroppableContainer)
val energy = ProfiledEnergyStorage(WorkerEnergyStorage(::energyLevelUpdated, ENERGY_VALUES)) val energy = ProfiledEnergyStorage(WorkerEnergyStorage(::energyLevelUpdated, ENERGY_VALUES))
@ -87,9 +87,9 @@ class MatterScannerBlockEntity(p_155229_: BlockPos, p_155230_: BlockState) :
return MatterScannerMenu(containerID, inventory, this) return MatterScannerMenu(containerID, inventory, this)
} }
override fun onJobFinish(job: MachineItemJob, id: Int): JobStatus { override fun onJobFinish(status: JobStatus<ItemJob>, id: Int) {
val stack = job.itemStack val stack = status.job.itemStack
if (stack.isEmpty || !MatterManager.hasMatterValue(stack)) return JobStatus.SUCCESS if (stack.isEmpty || !MatterManager.hasMatterValue(stack)) return status.success()
var findState: IPatternState? = null var findState: IPatternState? = null
@ -110,14 +110,12 @@ class MatterScannerBlockEntity(p_155229_: BlockPos, p_155230_: BlockState) :
PatternState(UUID.randomUUID(), stack.item, researchAdvance) PatternState(UUID.randomUUID(), stack.item, researchAdvance)
} }
if (!matterNode.graph.insertPattern(new, onlyUpdate = false, simulate = false).isFailed) { if (matterNode.graph.insertPattern(new, onlyUpdate = false, simulate = false).isFailed) {
return JobStatus.SUCCESS status.throttle()
} else {
return JobStatus.FAILURE_WAIT
} }
} }
override fun computeNextJob(id: Int): JobContainer<MachineItemJob> { override fun computeNextJob(id: Int): JobContainer<ItemJob> {
if (energy.batteryLevel.isZero) { if (energy.batteryLevel.isZero) {
return JobContainer.noEnergy() return JobContainer.noEnergy()
} }
@ -151,7 +149,7 @@ class MatterScannerBlockEntity(p_155229_: BlockPos, p_155230_: BlockState) :
stack.shrink(1) stack.shrink(1)
container.setChanged() container.setChanged()
val complexity = MatterManager.get(copy).complexity 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() 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 visualItemStack = new?.itemStack ?: ItemStack.EMPTY
visualProgress = 0f visualProgress = 0f
} }
@ -174,14 +172,11 @@ class MatterScannerBlockEntity(p_155229_: BlockPos, p_155230_: BlockState) :
var visualProgress by synchronizer.float().property var visualProgress by synchronizer.float().property
private set private set
override fun onWorkTick( override fun onJobTick(
requiredPower: Decimal, status: JobStatus<ItemJob>,
extractedPower: Decimal,
ticksAdvanced: Double,
job: MachineItemJob,
id: Int id: Int
): JobStatus { ) {
val result = super.onWorkTick(requiredPower, extractedPower, ticksAdvanced, job, id) val result = super.onJobTick(status, id)
visualProgress = jobEventLoops[0].workProgress visualProgress = jobEventLoops[0].workProgress

View File

@ -7,7 +7,7 @@ import net.minecraft.world.inventory.AbstractContainerMenu
import net.minecraft.world.item.ItemStack import net.minecraft.world.item.ItemStack
import net.minecraft.world.item.Items import net.minecraft.world.item.Items
import net.minecraft.world.level.block.state.BlockState 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.JobContainer
import ru.dbotthepony.mc.otm.block.entity.JobStatus import ru.dbotthepony.mc.otm.block.entity.JobStatus
import ru.dbotthepony.mc.otm.block.entity.MatteryWorkerBlockEntity 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 import ru.dbotthepony.mc.otm.registry.MBlockEntities
class CobblerBlockEntity(blockPos: BlockPos, blockState: BlockState) class CobblerBlockEntity(blockPos: BlockPos, blockState: BlockState)
: MatteryWorkerBlockEntity<MachineItemJob>(MBlockEntities.COBBLESTONE_GENERATOR, blockPos, blockState, ::MachineItemJob) { : MatteryWorkerBlockEntity<ItemJob>(MBlockEntities.COBBLESTONE_GENERATOR, blockPos, blockState, ItemJob.CODEC) {
override fun createMenu(containerID: Int, inventory: Inventory, ply: Player): AbstractContainerMenu { override fun createMenu(containerID: Int, inventory: Inventory, ply: Player): AbstractContainerMenu {
return CobblerMenu(containerID, inventory, this) return CobblerMenu(containerID, inventory, this)
} }
@ -36,16 +36,14 @@ class CobblerBlockEntity(blockPos: BlockPos, blockState: BlockState)
savetable(::container, INVENTORY_KEY) savetable(::container, INVENTORY_KEY)
} }
override fun onJobFinish(job: MachineItemJob, id: Int): JobStatus { override fun onJobFinish(status: JobStatus<ItemJob>, id: Int) {
if (!container.fullyAddItem(job.itemStack)) { if (!container.fullyAddItem(status.job.itemStack)) {
return JobStatus.FAILURE_ITEM status.noItem()
}
} }
return JobStatus.SUCCESS override fun computeNextJob(id: Int): JobContainer<ItemJob> {
} return JobContainer.success(ItemJob(ItemStack(Items.COBBLESTONE), 40.0))
override fun computeNextJob(id: Int): JobContainer<MachineItemJob> {
return JobContainer.success(MachineItemJob(ItemStack(Items.COBBLESTONE), 40.0))
} }
companion object { companion object {

View File

@ -10,7 +10,7 @@ import net.minecraft.world.inventory.AbstractContainerMenu
import net.minecraft.world.level.block.state.BlockState import net.minecraft.world.level.block.state.BlockState
import ru.dbotthepony.mc.otm.block.entity.JobContainer import ru.dbotthepony.mc.otm.block.entity.JobContainer
import ru.dbotthepony.mc.otm.block.entity.JobStatus 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.block.entity.MatteryWorkerBlockEntity
import ru.dbotthepony.mc.otm.capability.UpgradeType import ru.dbotthepony.mc.otm.capability.UpgradeType
import ru.dbotthepony.mc.otm.capability.energy.ProfiledEnergyStorage 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.HandlerFilter
import ru.dbotthepony.mc.otm.container.UpgradeContainer import ru.dbotthepony.mc.otm.container.UpgradeContainer
import ru.dbotthepony.mc.otm.container.balance 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.PlatePressMenu
import ru.dbotthepony.mc.otm.menu.tech.TwinPlatePressMenu import ru.dbotthepony.mc.otm.menu.tech.TwinPlatePressMenu
import ru.dbotthepony.mc.otm.registry.MBlockEntities import ru.dbotthepony.mc.otm.registry.MBlockEntities
@ -30,7 +29,7 @@ class PlatePressBlockEntity(
p_155229_: BlockPos, p_155229_: BlockPos,
p_155230_: BlockState, p_155230_: BlockState,
val isTwin: Boolean = false, val isTwin: Boolean = false,
) : MatteryWorkerBlockEntity<MachineItemJob>(if (isTwin) MBlockEntities.TWIN_PLATE_PRESS else MBlockEntities.PLATE_PRESS, p_155229_, p_155230_, ::MachineItemJob, if (isTwin) 2 else 1) { ) : MatteryWorkerBlockEntity<ItemJob>(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) 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 energy = ProfiledEnergyStorage(WorkerEnergyStorage(this::energyLevelUpdated, upgrades.transform(MachinesConfig.PLATE_PRESS)))
val inputContainer = MatteryContainer(this::itemContainerUpdated, if (isTwin) 2 else 1).also(::addDroppableContainer) val inputContainer = MatteryContainer(this::itemContainerUpdated, if (isTwin) 2 else 1).also(::addDroppableContainer)
@ -69,18 +68,17 @@ class PlatePressBlockEntity(
return PlatePressMenu(containerID, inventory, this) return PlatePressMenu(containerID, inventory, this)
} }
override fun onJobFinish(job: MachineItemJob, id: Int): JobStatus { override fun onJobFinish(status: JobStatus<ItemJob>, id: Int) {
if (job.itemStack.isEmpty) if (status.job.itemStack.isEmpty)
return JobStatus.SUCCESS return status.success()
if (!outputContainer.fullyAddItem(job.itemStack, start = id, end = id) && !outputContainer.fullyAddItem(job.itemStack)) if (!outputContainer.fullyAddItem(status.job.itemStack, start = id, end = id) && !outputContainer.fullyAddItem(status.job.itemStack))
return JobStatus.FAILURE_ITEM return status.noItem()
experience = (experience + job.experience).coerceAtMost(100.0) experience = (experience + status.experience).coerceAtMost(100.0)
return JobStatus.SUCCESS
} }
override fun computeNextJob(id: Int): JobContainer<MachineItemJob> { override fun computeNextJob(id: Int): JobContainer<ItemJob> {
if (energy.batteryLevel.isZero) { if (energy.batteryLevel.isZero) {
return JobContainer.noEnergy() return JobContainer.noEnergy()
} }
@ -101,7 +99,7 @@ class PlatePressBlockEntity(
inputContainer.setChanged(id) inputContainer.setChanged(id)
return JobContainer.success( return JobContainer.success(
MachineItemJob( ItemJob(
recipe.getResultItem(level.registryAccess()).copyWithCount(toProcess), recipe.getResultItem(level.registryAccess()).copyWithCount(toProcess),
recipe.workTime * MachinesConfig.PLATE_PRESS.workTimeMultiplier, recipe.workTime * MachinesConfig.PLATE_PRESS.workTimeMultiplier,
MachinesConfig.PLATE_PRESS.powerConsumption * toProcess, MachinesConfig.PLATE_PRESS.powerConsumption * toProcess,

View File

@ -3,19 +3,17 @@ package ru.dbotthepony.mc.otm.block.entity.tech
import net.minecraft.core.BlockPos import net.minecraft.core.BlockPos
import net.minecraft.server.level.ServerLevel import net.minecraft.server.level.ServerLevel
import net.minecraft.server.level.ServerPlayer import net.minecraft.server.level.ServerPlayer
import net.minecraft.world.Container
import net.minecraft.world.entity.ExperienceOrb import net.minecraft.world.entity.ExperienceOrb
import net.minecraft.world.entity.player.Inventory import net.minecraft.world.entity.player.Inventory
import net.minecraft.world.entity.player.Player import net.minecraft.world.entity.player.Player
import net.minecraft.world.inventory.AbstractContainerMenu import net.minecraft.world.inventory.AbstractContainerMenu
import net.minecraft.world.item.crafting.AbstractCookingRecipe 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.item.crafting.RecipeType
import net.minecraft.world.level.block.entity.BlockEntityType import net.minecraft.world.level.block.entity.BlockEntityType
import net.minecraft.world.level.block.state.BlockState import net.minecraft.world.level.block.state.BlockState
import ru.dbotthepony.mc.otm.block.entity.JobContainer import ru.dbotthepony.mc.otm.block.entity.JobContainer
import ru.dbotthepony.mc.otm.block.entity.JobStatus 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.block.entity.MatteryWorkerBlockEntity
import ru.dbotthepony.mc.otm.capability.CombinedItemHandler import ru.dbotthepony.mc.otm.capability.CombinedItemHandler
import ru.dbotthepony.mc.otm.capability.UpgradeType import ru.dbotthepony.mc.otm.capability.UpgradeType
@ -36,7 +34,7 @@ class PoweredFurnaceBlockEntity(
blockState: BlockState, blockState: BlockState,
val recipeType: RecipeType<out AbstractCookingRecipe>, val recipeType: RecipeType<out AbstractCookingRecipe>,
val config: WorkerBalanceValues val config: WorkerBalanceValues
) : MatteryWorkerBlockEntity<MachineItemJob>(type, blockPos, blockState, ::MachineItemJob, 2) { ) : MatteryWorkerBlockEntity<ItemJob>(type, blockPos, blockState, ItemJob.CODEC, 2) {
override val upgrades = UpgradeContainer(this::setChangedLight, 2, UpgradeType.BASIC_PROCESSING) override val upgrades = UpgradeContainer(this::setChangedLight, 2, UpgradeType.BASIC_PROCESSING)
val energy = ProfiledEnergyStorage(WorkerEnergyStorage(this::energyLevelUpdated, upgrades.transform(config))) val energy = ProfiledEnergyStorage(WorkerEnergyStorage(this::energyLevelUpdated, upgrades.transform(config)))
@ -86,16 +84,15 @@ class PoweredFurnaceBlockEntity(
return PoweredFurnaceMenu(containerID, inventory, this) return PoweredFurnaceMenu(containerID, inventory, this)
} }
override fun onJobFinish(job: MachineItemJob, id: Int): JobStatus { override fun onJobFinish(status: JobStatus<ItemJob>, id: Int) {
if (outputs[id].fullyAddItem(job.itemStack)) { if (outputs[id].fullyAddItem(status.job.itemStack)) {
experience += job.experience experience += status.experience
return JobStatus.SUCCESS } else {
status.noItem()
}
} }
return JobStatus.FAILURE_ITEM override fun computeNextJob(id: Int): JobContainer<ItemJob> {
}
override fun computeNextJob(id: Int): JobContainer<MachineItemJob> {
if (!energy.batteryLevel.isPositive) if (!energy.batteryLevel.isPositive)
return JobContainer.noEnergy() return JobContainer.noEnergy()
@ -109,7 +106,7 @@ class PoweredFurnaceBlockEntity(
val toProcess = inputs[id][0].count.coerceAtMost(upgrades.processingItems + 1) val toProcess = inputs[id][0].count.coerceAtMost(upgrades.processingItems + 1)
inputs[id][0].shrink(toProcess) inputs[id][0].shrink(toProcess)
JobContainer.success(MachineItemJob( JobContainer.success(ItemJob(
output.copyWithCount(toProcess), it.cookingTime * config.workTimeMultiplier, config.powerConsumption * toProcess, it.experience * toProcess output.copyWithCount(toProcess), it.cookingTime * config.workTimeMultiplier, config.powerConsumption * toProcess, it.experience * toProcess
)) ))
}.orElse(JobContainer.noItem()) }.orElse(JobContainer.noItem())

View File

@ -63,7 +63,7 @@ import ru.dbotthepony.mc.otm.android.AndroidResearchManager
import ru.dbotthepony.mc.otm.android.AndroidResearchType import ru.dbotthepony.mc.otm.android.AndroidResearchType
import ru.dbotthepony.mc.otm.block.entity.JobContainer import ru.dbotthepony.mc.otm.block.entity.JobContainer
import ru.dbotthepony.mc.otm.block.entity.JobStatus 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.MachineJobEventLoop
import ru.dbotthepony.mc.otm.capability.energy.BatteryBackedEnergyStorage import ru.dbotthepony.mc.otm.capability.energy.BatteryBackedEnergyStorage
import ru.dbotthepony.mc.otm.capability.energy.IMatteryEnergyStorage import ru.dbotthepony.mc.otm.capability.energy.IMatteryEnergyStorage
@ -411,33 +411,26 @@ class MatteryPlayerCapability(val ply: Player) : ICapabilityProvider, INBTSerial
} }
}).property }).property
inner class SmelterBelt(index: Int) : MachineJobEventLoop<MachineItemJob>() { inner class SmelterBelt(index: Int) : MachineJobEventLoop<ItemJob>(ItemJob.CODEC) {
override val energy: IMatteryEnergyStorage override val energy: IMatteryEnergyStorage
get() = exoPackEnergy get() = exoPackEnergy
override val isBlockedByRedstone: Boolean override val isBlockedByRedstone: Boolean
get() = false get() = false
override fun deserializeJob(nbt: CompoundTag): MachineItemJob {
return MachineItemJob(nbt)
}
override val upgrades: IMatteryUpgrade? override val upgrades: IMatteryUpgrade?
get() = null get() = null
override fun jobUpdated(new: MachineItemJob?, old: MachineItemJob?) {} override fun onJobFinish(status: JobStatus<ItemJob>) {
if (output.fullyAddItem(status.job.itemStack)) {
override fun onJobFinish(job: MachineItemJob): JobStatus { exoPackSmelterExperience += status.job.experience
if (output.fullyAddItem(job.itemStack)) {
exoPackSmelterExperience += job.experience
return JobStatus.SUCCESS
} else { } else {
return JobStatus.FAILURE_ITEM status.noItem()
} }
} }
private val cache = RecipeManager.createCheck(RecipeType.SMELTING) private val cache = RecipeManager.createCheck(RecipeType.SMELTING)
override fun computeNextJob(): JobContainer<MachineItemJob> { override fun computeNextJob(): JobContainer<ItemJob> {
if (!exoPackEnergy.batteryLevel.isPositive) return JobContainer.noEnergy() if (!exoPackEnergy.batteryLevel.isPositive) return JobContainer.noEnergy()
val level = ply.level() as? ServerLevel ?: return JobContainer.failure() val level = ply.level() as? ServerLevel ?: return JobContainer.failure()
val recipe = cache.getRecipeFor(input, level) val recipe = cache.getRecipeFor(input, level)
@ -449,7 +442,7 @@ class MatteryPlayerCapability(val ply: Player) : ICapabilityProvider, INBTSerial
val item = actual.assemble(input, level.registryAccess()) val item = actual.assemble(input, level.registryAccess())
input[0].shrink(1) input[0].shrink(1)
input.setChanged(0) 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))
} }
} }

View File

@ -1,6 +1,10 @@
package ru.dbotthepony.mc.otm.capability.matter 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.CompoundTag
import net.minecraft.nbt.NbtOps
import net.minecraft.nbt.Tag import net.minecraft.nbt.Tag
import net.minecraft.network.FriendlyByteBuf import net.minecraft.network.FriendlyByteBuf
import net.minecraft.resources.ResourceLocation 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.*
import ru.dbotthepony.mc.otm.core.nbt.contains import ru.dbotthepony.mc.otm.core.nbt.contains
import ru.dbotthepony.mc.otm.core.nbt.set import ru.dbotthepony.mc.otm.core.nbt.set
import ru.dbotthepony.mc.otm.data.UUIDCodec
import java.util.* import java.util.*
sealed interface IPatternState { sealed interface IPatternState {
@ -45,13 +50,7 @@ sealed interface IPatternState {
return ItemStack(item, count) return ItemStack(item, count)
} }
fun serializeNBT(): CompoundTag { fun serializeNBT(): CompoundTag
return CompoundTag().also {
it["id"] = id
it["item"] = item.registryName!!.toString()
it["researchPercent"] = researchPercent
}
}
fun write(buff: FriendlyByteBuf) { fun write(buff: FriendlyByteBuf) {
buff.writeUUID(id) buff.writeUUID(id)
@ -60,6 +59,14 @@ sealed interface IPatternState {
} }
} }
private fun <T : IPatternState> codec(builder: RecordCodecBuilder.Instance<T>): Products.P3<RecordCodecBuilder.Mu<T>, 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? { private fun deserializeNBT(nbt: Tag?, mutable: Boolean): IPatternState? {
if (nbt !is CompoundTag) if (nbt !is CompoundTag)
return null return null
@ -108,6 +115,10 @@ data class PatternState(
return this 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 { companion object {
fun deserializeNBT(tag: Tag?): PatternState? { fun deserializeNBT(tag: Tag?): PatternState? {
return deserializeNBT(tag, false) as PatternState? return deserializeNBT(tag, false) as PatternState?
@ -116,6 +127,12 @@ data class PatternState(
fun read(buff: FriendlyByteBuf): PatternState? { fun read(buff: FriendlyByteBuf): PatternState? {
return read(buff, false) as PatternState? return read(buff, false) as PatternState?
} }
val CODEC: Codec<PatternState> by lazy {
RecordCodecBuilder.create {
codec(it).apply(it, ::PatternState)
}
}
} }
} }
@ -132,6 +149,10 @@ data class MutablePatternState(
return PatternState(id, item, researchPercent) 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 { companion object {
fun deserializeNBT(tag: Tag?): MutablePatternState? { fun deserializeNBT(tag: Tag?): MutablePatternState? {
return deserializeNBT(tag, true) as MutablePatternState? return deserializeNBT(tag, true) as MutablePatternState?
@ -140,6 +161,12 @@ data class MutablePatternState(
fun read(buff: FriendlyByteBuf): MutablePatternState? { fun read(buff: FriendlyByteBuf): MutablePatternState? {
return read(buff, true) as MutablePatternState? return read(buff, true) as MutablePatternState?
} }
val CODEC: Codec<MutablePatternState> by lazy {
RecordCodecBuilder.create {
codec(it).apply(it, ::MutablePatternState)
}
}
} }
} }

View File

@ -1,6 +1,10 @@
package ru.dbotthepony.mc.otm.capability.matter 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.CompoundTag
import net.minecraft.nbt.NbtOps
import net.minecraft.nbt.Tag import net.minecraft.nbt.Tag
import net.minecraft.network.FriendlyByteBuf import net.minecraft.network.FriendlyByteBuf
import net.minecraft.resources.ResourceLocation import net.minecraft.resources.ResourceLocation
@ -9,14 +13,14 @@ import net.minecraft.world.item.ItemStack
import net.minecraft.world.item.Items import net.minecraft.world.item.Items
import net.minecraftforge.registries.ForgeRegistries import net.minecraftforge.registries.ForgeRegistries
import ru.dbotthepony.mc.otm.core.readItemType 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.core.writeItemType
import ru.dbotthepony.mc.otm.data.UUIDCodec
import java.util.Optional
import java.util.UUID import java.util.UUID
sealed interface IReplicationTask<S : IReplicationTask<S>> { sealed interface IReplicationTask<S : IReplicationTask<S>> {
val id: UUID val id: UUID
val patternId: UUID? val patternId: Optional<UUID>
val item: Item val item: Item
val inProgress: Int val inProgress: Int
val finished: Int val finished: Int
@ -29,7 +33,7 @@ sealed interface IReplicationTask<S : IReplicationTask<S>> {
fun copyAsMutable( fun copyAsMutable(
id: UUID = this.id, id: UUID = this.id,
patternId: UUID? = this.patternId, patternId: Optional<UUID> = this.patternId,
item: Item = this.item, item: Item = this.item,
inProgress: Int = this.inProgress, inProgress: Int = this.inProgress,
finished: Int = this.finished, finished: Int = this.finished,
@ -40,7 +44,7 @@ sealed interface IReplicationTask<S : IReplicationTask<S>> {
fun copyAsImmutable( fun copyAsImmutable(
id: UUID = this.id, id: UUID = this.id,
patternId: UUID? = this.patternId, patternId: Optional<UUID> = this.patternId,
item: Item = this.item, item: Item = this.item,
inProgress: Int = this.inProgress, inProgress: Int = this.inProgress,
finished: Int = this.finished, finished: Int = this.finished,
@ -60,25 +64,13 @@ sealed interface IReplicationTask<S : IReplicationTask<S>> {
fun allocate(amount: Int = 1): S fun allocate(amount: Int = 1): S
fun finish(amount: Int = 1): S fun finish(amount: Int = 1): S
fun serializeNBT(): CompoundTag { 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 write(buff: FriendlyByteBuf) { fun write(buff: FriendlyByteBuf) {
buff.writeUUID(id) buff.writeUUID(id)
buff.writeBoolean(patternId != null) buff.writeBoolean(patternId.isPresent)
patternId?.let(buff::writeUUID) patternId.ifPresent(buff::writeUUID)
buff.writeItemType(item) buff.writeItemType(item)
@ -88,6 +80,17 @@ sealed interface IReplicationTask<S : IReplicationTask<S>> {
} }
} }
private fun <T : IReplicationTask<T>> codec(builder: RecordCodecBuilder.Instance<T>): Products.P6<RecordCodecBuilder.Mu<T>, UUID, Optional<UUID>, 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<*>? { private fun deserializeNBT(nbt: Tag?, mutable: Boolean): IReplicationTask<*>? {
if (nbt !is CompoundTag) if (nbt !is CompoundTag)
return null return null
@ -108,9 +111,9 @@ private fun deserializeNBT(nbt: Tag?, mutable: Boolean): IReplicationTask<*>? {
val required = nbt.getInt("required") val required = nbt.getInt("required")
if (mutable) { if (mutable) {
return MutableReplicationTask(id, patternId, item, inProgress, finished, required) return MutableReplicationTask(id, Optional.ofNullable(patternId), item, inProgress, finished, required)
} else { } 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 item ?: return null
if (mutable) { if (mutable) {
return MutableReplicationTask(id, patternId, item, inProgress, finished, required) return MutableReplicationTask(id, Optional.ofNullable(patternId), item, inProgress, finished, required)
} else { } else {
return ReplicationTask(id, patternId, item, inProgress, finished, required) return ReplicationTask(id, Optional.ofNullable(patternId), item, inProgress, finished, required)
} }
} }
data class ReplicationTask( data class ReplicationTask(
override val id: UUID, override val id: UUID,
override val patternId: UUID?, override val patternId: Optional<UUID>,
override val item: Item, override val item: Item,
override val inProgress: Int, override val inProgress: Int,
override val finished: Int, override val finished: Int,
@ -155,6 +158,10 @@ data class ReplicationTask(
return ReplicationTask(id, patternId, item, inProgress - amount, finished + amount, required) 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 { companion object {
fun deserializeNBT(nbt: Tag?): ReplicationTask? { fun deserializeNBT(nbt: Tag?): ReplicationTask? {
return deserializeNBT(nbt, false) as ReplicationTask? return deserializeNBT(nbt, false) as ReplicationTask?
@ -163,12 +170,18 @@ data class ReplicationTask(
fun read(buff: FriendlyByteBuf): ReplicationTask? { fun read(buff: FriendlyByteBuf): ReplicationTask? {
return read(buff, false) as ReplicationTask? return read(buff, false) as ReplicationTask?
} }
val CODEC: Codec<ReplicationTask> by lazy {
RecordCodecBuilder.create {
codec(it).apply(it, ::ReplicationTask)
}
}
} }
} }
data class MutableReplicationTask( data class MutableReplicationTask(
override val id: UUID, override val id: UUID,
override val patternId: UUID?, override val patternId: Optional<UUID>,
override val item: Item, override val item: Item,
override var inProgress: Int, override var inProgress: Int,
override var finished: Int, override var finished: Int,
@ -194,6 +207,10 @@ data class MutableReplicationTask(
return this 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 { companion object {
fun deserializeNBT(nbt: Tag?): MutableReplicationTask? { fun deserializeNBT(nbt: Tag?): MutableReplicationTask? {
return deserializeNBT(nbt, true) as MutableReplicationTask? return deserializeNBT(nbt, true) as MutableReplicationTask?
@ -202,6 +219,12 @@ data class MutableReplicationTask(
fun read(buff: FriendlyByteBuf): MutableReplicationTask? { fun read(buff: FriendlyByteBuf): MutableReplicationTask? {
return read(buff, true) as MutableReplicationTask? return read(buff, true) as MutableReplicationTask?
} }
val CODEC: Codec<MutableReplicationTask> by lazy {
RecordCodecBuilder.create {
codec(it).apply(it, ::MutableReplicationTask)
}
}
} }
} }

View File

@ -3,7 +3,7 @@ package ru.dbotthepony.mc.otm.compat.jade.providers
import net.minecraft.nbt.CompoundTag import net.minecraft.nbt.CompoundTag
import net.minecraft.nbt.ListTag import net.minecraft.nbt.ListTag
import net.minecraft.resources.ResourceLocation 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.block.entity.MatteryWorkerBlockEntity
import ru.dbotthepony.mc.otm.compat.jade.JadeTagKeys import ru.dbotthepony.mc.otm.compat.jade.JadeTagKeys
import ru.dbotthepony.mc.otm.compat.jade.JadeUids import ru.dbotthepony.mc.otm.compat.jade.JadeUids
@ -34,8 +34,8 @@ object MatteryWorkerProvider : IBlockComponentProvider, IServerDataProvider<Bloc
jobData.putBoolean("isIdling", job.isIdling) jobData.putBoolean("isIdling", job.isIdling)
jobData.putBoolean("isUnableToProcess", job.isUnableToProcess) jobData.putBoolean("isUnableToProcess", job.isUnableToProcess)
if (job.currentJob is MachineItemJob) { if (job.currentJob is ItemJob) {
val currentJob = job.currentJob as MachineItemJob val currentJob = job.currentJob as ItemJob
jobData.put("itemStack", currentJob.itemStack.serializeNBT()) jobData.put("itemStack", currentJob.itemStack.serializeNBT())
} }

View File

@ -0,0 +1,51 @@
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
class ComparableCodec<S : Comparable<S>>(val parent: Codec<S>, val min: S? = null, val max: S? = null, val minExclusive: Boolean = false, val maxExclusive: Boolean = false) : Codec<S> {
private fun <T : Any> check(input: S): DataResult<T>? {
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 <T : Any> encode(input: S, ops: DynamicOps<T>, prefix: T): DataResult<T> {
check<T>(input)?.let { return it }
return parent.encode(input, ops, prefix)
}
override fun <T : Any> decode(ops: DynamicOps<T>, input: T): DataResult<Pair<S, T>> {
return parent.decode(ops, input).flatMap {
check<Pair<S, T>>(it.first) ?: DataResult.success(it)
}
}
}
fun <S : Comparable<S>> Codec<S>.minRange(min: S, exclusive: Boolean = false) = ComparableCodec(this, min = min, minExclusive = exclusive)
fun <S : Comparable<S>> Codec<S>.maxRange(max: S, exclusive: Boolean = false) = ComparableCodec(this, max = max, maxExclusive = exclusive)
fun <S : Comparable<S>> Codec<S>.inRange(min: S, minExclusive: Boolean = false, max: S, maxExclusive: Boolean = false) = ComparableCodec(this, min, max, minExclusive, maxExclusive)

View File

@ -4,28 +4,63 @@ import com.mojang.datafixers.util.Pair
import com.mojang.serialization.Codec import com.mojang.serialization.Codec
import com.mojang.serialization.DataResult import com.mojang.serialization.DataResult
import com.mojang.serialization.DynamicOps 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 ru.dbotthepony.mc.otm.core.math.Decimal
import java.nio.ByteBuffer
import java.util.stream.Collector
import java.util.stream.Stream
object DecimalCodec : Codec<Decimal> { object DecimalCodec : Codec<Decimal> {
override fun <T : Any> encode(input: Decimal, ops: DynamicOps<T>, prefix: T): DataResult<T> { override fun <T : Any> encode(input: Decimal, ops: DynamicOps<T>, prefix: T): DataResult<T> {
if (ops === NbtOps.INSTANCE) {
return DataResult.success((ops as DynamicOps<T>).createByteList(ByteBuffer.wrap(input.toByteArray())))
}
return DataResult.success(ops.createString(input.toString())) return DataResult.success(ops.createString(input.toString()))
} }
override fun <T : Any> decode(ops: DynamicOps<T>, input: T): DataResult<Pair<Decimal, T>> { override fun <T : Any> decode(ops: DynamicOps<T>, input: T): DataResult<Pair<Decimal, T>> {
val result = ops.getStringValue(input).flatMap { return ops.getStringValue(input).flatMap {
try { try {
DataResult.success(Pair(Decimal(it), ops.empty())) DataResult.success(Pair(Decimal(it), ops.empty()))
} catch (err: NumberFormatException) { } catch (err: NumberFormatException) {
DataResult.error { "Not a valid number for converting into Decimal: $it" } DataResult.error { "Not a valid number for converting into Decimal: $it" }
} }
}.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(
if (result.result().isPresent) { {
return result DataResult.success(it)
} },
{ e1 ->
return ops.getNumberValue(input).flatMap { ops.getNumberValue(input).flatMap {
DataResult.success(Pair(Decimal(it.toString()), ops.empty())) 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()}" }
} }
)
}
)
}
)
} }
} }

View File

@ -3,12 +3,17 @@ package ru.dbotthepony.mc.otm.data
import com.google.gson.JsonArray import com.google.gson.JsonArray
import com.google.gson.JsonNull import com.google.gson.JsonNull
import com.google.gson.JsonObject 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.DataResult
import com.mojang.serialization.codecs.RecordCodecBuilder
import it.unimi.dsi.fastutil.ints.IntAVLTreeSet import it.unimi.dsi.fastutil.ints.IntAVLTreeSet
import net.minecraft.world.entity.player.Player import net.minecraft.world.entity.player.Player
import net.minecraft.world.level.storage.loot.LootContext 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.LootContextParam
import net.minecraft.world.level.storage.loot.parameters.LootContextParams import net.minecraft.world.level.storage.loot.parameters.LootContextParams
import java.util.Optional
import kotlin.reflect.KProperty1
fun JsonArray.getRidOfNulls(): JsonArray { fun JsonArray.getRidOfNulls(): JsonArray {
val toRemove = IntAVLTreeSet() val toRemove = IntAVLTreeSet()

View File

@ -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<UUID> {
override fun <T : Any> encode(input: UUID, ops: DynamicOps<T>, prefix: T): DataResult<T> {
return DataResult.success(ops.createLongList(LongStream.of(input.mostSignificantBits, input.leastSignificantBits)))
}
override fun <T : Any> decode(ops: DynamicOps<T>, input: T): DataResult<Pair<UUID, T>> {
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()))
}
}
}
}