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
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
constructor(
ticks: Double,
powerUsage: Decimal = Decimal.ZERO,
experience: Float = 0f,
) {
this.ticks = ticks
this.powerUsage = powerUsage
this.experience = experience
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 <T : Job> basicCodec(builder: RecordCodecBuilder.Instance<T>): Products.P3<RecordCodecBuilder.Mu<T>, 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(
tag: CompoundTag
) : this(tag.getDouble("Ticks"), tag.getDecimal("EnergyUsage"), tag.getFloat("Experience"))
fun <T : Job> plainCodec(builder: RecordCodecBuilder.Instance<T>): Products.P2<RecordCodecBuilder.Mu<T>, Double, Decimal> {
return builder.group(
Codec.doubleRange(0.0, Double.MAX_VALUE).fieldOf("ticks").forGetter(Job::ticks),
DecimalCodec.fieldOf("powerUsage").forGetter(Job::powerUsage), // не надо указывать минимальную энергию как 0,
// ибо мы можем таким образом использовать это для создания работ генератора
)
}
override fun serializeNBT(): CompoundTag {
return CompoundTag().also {
it["Ticks"] = ticks
it["EnergyUsage"] = powerUsage
it["Experience"] = experience
val CODEC: Codec<Job> by lazy {
RecordCodecBuilder.create {
basicCodec(it).apply(it, ::Job)
}
}
}
}
open class MachineItemJob : MachineJob {
val itemStack: ItemStack
constructor(
itemStack: ItemStack,
open class ItemJob(
val itemStack: ItemStack,
ticks: Double,
power: Decimal = Decimal.ZERO,
experience: Float = 0f,
) : super(ticks, power, experience) {
this.itemStack = itemStack
experience: Float = 0f
) : Job(ticks, power, experience) {
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(
tag: CompoundTag
) : super(tag) {
this.itemStack = (tag["Item"] as? CompoundTag)?.let { ItemStack.of(it) } ?: ItemStack.EMPTY
val CODEC: Codec<ItemJob> by lazy {
RecordCodecBuilder.create {
itemCodec(it).apply(it, ::ItemJob)
}
override fun serializeNBT(): CompoundTag {
return super.serializeNBT().also {
it["Item"] = itemStack.serializeNBT()
}
}
}
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<T : IJob>(
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()
}
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)
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
}
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 {
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 <JobType : IMachineJob> success(job: JobType): JobContainer<JobType> {
fun <JobType : IJob> success(job: JobType): JobContainer<JobType> {
return JobContainer(job, null)
}
fun <JobType : IMachineJob> failure(reason: Any?): JobContainer<JobType> {
fun <JobType : IJob> failure(reason: Any?): JobContainer<JobType> {
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)
@Suppress("unchecked_cast")
fun <JobType : IMachineJob> failure(): JobContainer<JobType> {
fun <JobType : IJob> failure(): JobContainer<JobType> {
return empty as JobContainer<JobType>
}
@Suppress("unchecked_cast")
fun <JobType : IMachineJob> noItem(): JobContainer<JobType> {
fun <JobType : IJob> noItem(): JobContainer<JobType> {
return noItem as JobContainer<JobType>
}
@Suppress("unchecked_cast")
fun <JobType : IMachineJob> noEnergy(): JobContainer<JobType> {
fun <JobType : IJob> noEnergy(): JobContainer<JobType> {
return noEnergy as JobContainer<JobType>
}
@Suppress("unchecked_cast")
fun <JobType : IMachineJob> noMatter(): JobContainer<JobType> {
fun <JobType : IJob> noMatter(): JobContainer<JobType> {
return noMatter as JobContainer<JobType>
}
@Suppress("unchecked_cast")
fun <JobType : IMachineJob> noPattern(): JobContainer<JobType> {
fun <JobType : IJob> noPattern(): JobContainer<JobType> {
return noPattern as JobContainer<JobType>
}
@Suppress("unchecked_cast")
fun <JobType : IMachineJob> observe(): JobContainer<JobType> {
fun <JobType : IJob> observe(): 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 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<JobType : IMachineJob> : INBTSerializable<Com
override fun serializeNBT(): CompoundTag {
return CompoundTag().also { nbt ->
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
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)
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
*/
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
*/
protected abstract fun computeNextJob(): JobContainer<JobType>
protected open fun onWorkTick(requiredPower: Decimal, extractedPower: Decimal, ticksAdvanced: Double, job: JobType): JobStatus {
return JobStatus.SUCCESS
}
protected open fun onJobTick(status: JobStatus<JobType>) {}
/**
* 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) {
extractedPower = energy!!.extractEnergy(requiredPower, true)
ticksAdvanced = (extractedPower / requiredPower).toDouble().coerceIn(0.0, 1.0) * ticksLeft
} else {
} else if (requiredPower.isZero) {
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
}
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) {
throttleTicks += status.throttleTicks
@ -336,25 +473,31 @@ abstract class MachineJobEventLoop<JobType : IMachineJob> : INBTSerializable<Com
isIdling = true
}
break
} else if (status.requiredPower.isPositive && energy == null) {
idleReason = IdleReason.POWER
isIdling = true
idleTicksAnim++
break
}
workingTicksAnim++
errorTicksAnim = 0
workTicks += ticksAdvanced
availableTicks -= ticksAdvanced
workTicks += status.ticksAdvanced
availableTicks -= status.ticksAdvanced
extractedPower = status.newDrainedPower ?: extractedPower
if (extractedPower != null) {
energy!!.extractEnergy(extractedPower, false)
if (energy != null && status.requiredPower.isPositive) {
energy.extractEnergy(status.requiredPower, false)
} else if (energy != null && status.requiredPower.isNegative) {
energy.receiveEnergy(-status.requiredPower, false)
}
continue
}
val status = onJobFinish(currentJob)
val status = JobStatus(currentJob, workTicks)
onJobFinish(status)
if (status.success) {
this.currentJob = null

View File

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

View File

@ -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<MatterDecomposerBlockEntity.DecomposerJob>(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<MatterDecomposerBlockEntity.DecomposerJob>(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<DecomposerJob> 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<DecomposerJob>, 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<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.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)

View File

@ -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<MatterRecyclerBlockEntity.RecyclerJob>(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<MatterRecyclerBlockEntity.RecyclerJob>(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<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)
}
override fun onJobFinish(job: RecyclerJob, id: Int): JobStatus {
override fun onJobFinish(status: JobStatus<RecyclerJob>, 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<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 {
val receive = job.totalMatter.coerceAtMost(MachinesConfig.MATTER_RECYCLER_MATTER_PER_TICK.get() * ticksAdvanced)
override fun onJobTick(status: JobStatus<RecyclerJob>, 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() {

View File

@ -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<MatterReplicatorBlockEntity.ReplicatorJob>(MBlockEntities.MATTER_REPLICATOR, p_155229_, p_155230_, {
try {
ReplicatorJob(it)
} catch(err: NoSuchElementException) {
null
}
}) {
MatteryWorkerBlockEntity<MatterReplicatorBlockEntity.ReplicatorJob>(MBlockEntities.MATTER_REPLICATOR, p_155229_, p_155230_, ReplicatorJob.CODEC) {
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(
class ReplicatorJob(
itemStack: ItemStack,
matterPerTick: Decimal,
task: ReplicationTask,
matterValue: Decimal,
pattern: PatternState?,
asDust: Boolean,
val matterPerTick: Decimal,
val task: UUID,
var matterValue: Decimal,
val pattern: Optional<PatternState>,
val 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
}
}
) : 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<ReplicatorJob> 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<ReplicatorJob>, 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
}
matterNode.graph.notifyTaskCompletion(job.task.id)
return JobStatus.SUCCESS
return status.throttle()
}
matterNode.graph.notifyTaskCompletion(job.task)
} else {
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)
return JobStatus.SUCCESS
matterNode.graph.notifyTaskCompletion(job.task)
}
}
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<ReplicatorJob>, id: Int) {
val job = status.job
val drainPerTick = job.matterPerTick * status.ticksAdvanced
if (matter.extractMatter(drainPerTick, true) < drainPerTick) {
// в машине недостаточно материи
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))
val toDrain = (drainPerTick * DRAIN_MULT)
.coerceAtMost(job.matterPerTick * (status.ticks - status.workTicks + status.ticksAdvanced))
.coerceAtLeast(Decimal.ONE)
.coerceAtMost(matter.missingMatter), false)
.coerceAtMost(matter.missingMatter)
if (drain.isZero) {
// в сети нет материи
return JobStatus.FAILURE_MATTER
matter.receiveMatter(matterNode.graph.extractMatter(toDrain, false), false)
}
matter.receiveMatter(drain, false)
// получили материю, проверяем возможность работы
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
status.scale(matter.extractMatter(drainPerTick, false) / drainPerTick)
visualProgress = status.workProgress
}
companion object {

View File

@ -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<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 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<ItemJob>, 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<MachineItemJob> {
override fun computeNextJob(id: Int): JobContainer<ItemJob> {
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<ItemJob>,
id: Int
): JobStatus {
val result = super.onWorkTick(requiredPower, extractedPower, ticksAdvanced, job, id)
) {
val result = super.onJobTick(status, id)
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.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<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 {
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<ItemJob>, id: Int) {
if (!container.fullyAddItem(status.job.itemStack)) {
status.noItem()
}
}
return JobStatus.SUCCESS
}
override fun computeNextJob(id: Int): JobContainer<MachineItemJob> {
return JobContainer.success(MachineItemJob(ItemStack(Items.COBBLESTONE), 40.0))
override fun computeNextJob(id: Int): JobContainer<ItemJob> {
return JobContainer.success(ItemJob(ItemStack(Items.COBBLESTONE), 40.0))
}
companion object {

View File

@ -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<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)
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<ItemJob>, 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<MachineItemJob> {
override fun computeNextJob(id: Int): JobContainer<ItemJob> {
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,

View File

@ -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<out AbstractCookingRecipe>,
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)
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<ItemJob>, 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<MachineItemJob> {
override fun computeNextJob(id: Int): JobContainer<ItemJob> {
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())

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.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<MachineItemJob>() {
inner class SmelterBelt(index: Int) : MachineJobEventLoop<ItemJob>(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<ItemJob>) {
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<MachineItemJob> {
override fun computeNextJob(): JobContainer<ItemJob> {
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))
}
}

View File

@ -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 <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? {
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<PatternState> 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<MutablePatternState> by lazy {
RecordCodecBuilder.create {
codec(it).apply(it, ::MutablePatternState)
}
}
}
}

View File

@ -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<S : IReplicationTask<S>> {
val id: UUID
val patternId: UUID?
val patternId: Optional<UUID>
val item: Item
val inProgress: Int
val finished: Int
@ -29,7 +33,7 @@ sealed interface IReplicationTask<S : IReplicationTask<S>> {
fun copyAsMutable(
id: UUID = this.id,
patternId: UUID? = this.patternId,
patternId: Optional<UUID> = this.patternId,
item: Item = this.item,
inProgress: Int = this.inProgress,
finished: Int = this.finished,
@ -40,7 +44,7 @@ sealed interface IReplicationTask<S : IReplicationTask<S>> {
fun copyAsImmutable(
id: UUID = this.id,
patternId: UUID? = this.patternId,
patternId: Optional<UUID> = this.patternId,
item: Item = this.item,
inProgress: Int = this.inProgress,
finished: Int = this.finished,
@ -60,25 +64,13 @@ sealed interface IReplicationTask<S : IReplicationTask<S>> {
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<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<*>? {
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<UUID>,
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<ReplicationTask> by lazy {
RecordCodecBuilder.create {
codec(it).apply(it, ::ReplicationTask)
}
}
}
}
data class MutableReplicationTask(
override val id: UUID,
override val patternId: UUID?,
override val patternId: Optional<UUID>,
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<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.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<Bloc
jobData.putBoolean("isIdling", job.isIdling)
jobData.putBoolean("isUnableToProcess", job.isUnableToProcess)
if (job.currentJob is MachineItemJob) {
val currentJob = job.currentJob as MachineItemJob
if (job.currentJob is ItemJob) {
val currentJob = job.currentJob as ItemJob
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.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<Decimal> {
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()))
}
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 {
DataResult.success(Pair(Decimal(it), ops.empty()))
} catch (err: NumberFormatException) {
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" }
}
if (result.result().isPresent) {
return result
}
return ops.getNumberValue(input).flatMap {
}.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()}" }
}
)
}
)
}
)
}
}

View File

@ -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()

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()))
}
}
}
}