Redo mattery worker machine

This commit is contained in:
DBotThePony 2022-09-01 20:41:29 +07:00
parent 427fd43ea0
commit b35cb71a80
Signed by: DBot
GPG Key ID: DCC23B5715498507
26 changed files with 613 additions and 551 deletions

View File

@ -14,7 +14,7 @@ import net.minecraftforge.fml.common.Mod
import net.minecraftforge.data.event.GatherDataEvent
import ru.dbotthepony.mc.otm.OverdriveThatMatters
import ru.dbotthepony.mc.otm.block.*
import ru.dbotthepony.mc.otm.block.entity.worker.WorkerState
import ru.dbotthepony.mc.otm.block.entity.WorkerState
import ru.dbotthepony.mc.otm.block.matter.MatterBottlerBlock
import ru.dbotthepony.mc.otm.block.matter.PatternStorageBlock
import ru.dbotthepony.mc.otm.block.storage.DriveViewerBlock

View File

@ -7,7 +7,7 @@ import net.minecraftforge.client.model.generators.BlockStateProvider
import net.minecraftforge.client.model.generators.ConfiguredModel
import net.minecraftforge.data.event.GatherDataEvent
import ru.dbotthepony.mc.otm.block.RotatableMatteryBlock
import ru.dbotthepony.mc.otm.block.entity.worker.WorkerState
import ru.dbotthepony.mc.otm.block.entity.WorkerState
import ru.dbotthepony.mc.otm.datagen.DataGen
import ru.dbotthepony.mc.otm.datagen.getValueNullable
import ru.dbotthepony.mc.otm.datagen.toXRotBlockstate

View File

@ -15,7 +15,7 @@ import net.minecraft.world.phys.shapes.CollisionContext
import net.minecraft.world.phys.shapes.VoxelShape
import ru.dbotthepony.mc.otm.addPreWorldTickerOnce
import ru.dbotthepony.mc.otm.block.entity.ChemicalGeneratorBlockEntity
import ru.dbotthepony.mc.otm.block.entity.worker.WorkerState
import ru.dbotthepony.mc.otm.block.entity.WorkerState
import ru.dbotthepony.mc.otm.registry.MBlockEntities
import ru.dbotthepony.mc.otm.shapes.BlockShapes

View File

@ -25,7 +25,7 @@ import net.minecraft.world.phys.shapes.VoxelShape
import ru.dbotthepony.mc.otm.addPreWorldTickerOnce
import ru.dbotthepony.mc.otm.block.entity.GravitationStabilizerBlockEntity
import ru.dbotthepony.mc.otm.block.entity.blackhole.BlackHoleBlockEntity
import ru.dbotthepony.mc.otm.block.entity.worker.WorkerState
import ru.dbotthepony.mc.otm.block.entity.WorkerState
import ru.dbotthepony.mc.otm.core.plus
import ru.dbotthepony.mc.otm.core.times
import ru.dbotthepony.mc.otm.registry.MBlockEntities

View File

@ -14,7 +14,7 @@ import net.minecraft.world.level.block.state.StateDefinition
import net.minecraft.world.phys.shapes.CollisionContext
import net.minecraft.world.phys.shapes.VoxelShape
import ru.dbotthepony.mc.otm.block.entity.PlatePressBlockEntity
import ru.dbotthepony.mc.otm.block.entity.worker.WorkerState
import ru.dbotthepony.mc.otm.block.entity.WorkerState
import ru.dbotthepony.mc.otm.registry.MBlockEntities
import ru.dbotthepony.mc.otm.shapes.BlockShapes

View File

@ -19,7 +19,6 @@ import net.minecraftforge.common.util.LazyOptional
import net.minecraftforge.energy.IEnergyStorage
import ru.dbotthepony.mc.otm.*
import ru.dbotthepony.mc.otm.block.RotatableMatteryBlock
import ru.dbotthepony.mc.otm.block.entity.worker.WorkerState
import ru.dbotthepony.mc.otm.capability.*
import ru.dbotthepony.mc.otm.container.MatteryContainer
import ru.dbotthepony.mc.otm.core.ImpreciseFraction

View File

@ -14,7 +14,6 @@ import ru.dbotthepony.mc.otm.block.BlockGravitationStabilizer
import ru.dbotthepony.mc.otm.block.BlockGravitationStabilizerLens
import ru.dbotthepony.mc.otm.block.RotatableMatteryBlock
import ru.dbotthepony.mc.otm.block.entity.blackhole.BlackHoleBlockEntity
import ru.dbotthepony.mc.otm.block.entity.worker.WorkerState
import ru.dbotthepony.mc.otm.core.plus
import ru.dbotthepony.mc.otm.core.times
import ru.dbotthepony.mc.otm.registry.MBlockEntities

View File

@ -0,0 +1,341 @@
package ru.dbotthepony.mc.otm.block.entity
import net.minecraft.world.level.block.entity.BlockEntityType
import net.minecraft.core.BlockPos
import net.minecraft.world.level.block.state.BlockState
import net.minecraft.nbt.CompoundTag
import net.minecraft.world.item.ItemStack
import net.minecraft.world.level.block.Block
import ru.dbotthepony.mc.otm.core.*
import ru.dbotthepony.mc.otm.map
import ru.dbotthepony.mc.otm.set
private fun isReason(status: Any?, reason: Any) = status == null || status == reason
abstract class MatteryWorkerBlockEntity<JobType : MatteryWorkerBlockEntity.Job>(
type: BlockEntityType<*>,
blockPos: BlockPos,
blockState: BlockState,
val jobDeserializer: (tag: CompoundTag) -> JobType
) : MatteryPoweredBlockEntity(type, blockPos, blockState) {
open class Job(
open val ticks: Double,
open val powerUsage: ImpreciseFraction = ImpreciseFraction.ZERO
) {
constructor(
tag: CompoundTag
) : this(tag.getDouble("ticks"), tag.getImpreciseFraction("power"))
open fun serializeNBT(): CompoundTag {
return CompoundTag().also {
it["ticks"] = ticks
it["power"] = powerUsage
}
}
}
@Suppress("LeakingThis")
open class ItemJob : Job {
open val itemStack: ItemStack
constructor(
itemStack: ItemStack,
ticks: Double,
power: ImpreciseFraction = ImpreciseFraction.ZERO
) : super(ticks, power) {
this.itemStack = itemStack
}
constructor(
tag: CompoundTag
) : super(tag) {
this.itemStack = (tag["item"] as? CompoundTag)?.let { ItemStack.of(it) } ?: ItemStack.EMPTY
}
override fun serializeNBT(): CompoundTag {
return super.serializeNBT().also {
it["item"] = itemStack.serializeNBT()
}
}
}
enum class IdleReason {
ITEM,
POWER,
MATTER,
/**
* Observing external factor, such as waiting for matter/item network tasks
*/
OBSERVING
}
data class Status(val success: Boolean, val throttleTicks: Int = 0, val idleReason: IdleReason? = null) {
companion object {
val SUCCESS = Status(true)
val FAILURE = Status(false)
val FAILURE_ITEM = Status(false, 20, IdleReason.ITEM)
val FAILURE_MATTER = Status(false, 20, IdleReason.MATTER)
val FAILURE_WAIT = Status(false, 100)
val FAILURE_WAIT_FAST = Status(false, 20)
}
}
var workTicks = 0.0
protected set
var throttleTicks = 0
protected set
var currentJob: JobType? = null
protected set
/**
* Can be whatever you want, but [IdleReason] certainly contains all cases
*/
var idleReason: Any? = null
private set
var isIdling = false
protected set
val isUnableToProcess: Boolean get() = throttleTicks > 0
val workProgress: Float
get() {
val currentJob = currentJob ?: return 0.0f
return (workTicks / currentJob.ticks).coerceAtMost(1.0).toFloat()
}
override fun saveAdditional(nbt: CompoundTag) {
super.saveAdditional(nbt)
nbt["work_ticks"] = workTicks
currentJob?.let { nbt["job"] = it.serializeNBT() }
}
override fun load(nbt: CompoundTag) {
super.load(nbt)
workTicks = nbt.getDouble("work_ticks")
currentJob = nbt.map("job", jobDeserializer::invoke)
if (currentJob == null)
workTicks = 0.0
}
override fun setChanged() {
super.setChanged()
isIdling = false
}
override fun setChangedLight() {
super.setChangedLight()
isIdling = false
}
protected fun powerLevelUpdated() {
super.setChangedLight()
if (isReason(idleReason, IdleReason.POWER)) {
isIdling = false
throttleTicks = 0
}
}
protected fun itemContainerUpdated() {
super.setChanged()
if (isReason(idleReason, IdleReason.ITEM)) {
isIdling = false
throttleTicks = 0
}
}
protected fun matterLevelUpdated() {
super.setChangedLight()
if (isReason(idleReason, IdleReason.MATTER)) {
isIdling = false
throttleTicks = 0
}
}
/**
* Called whenever reaching desired amount of ticks at job
*/
protected abstract fun onJobFinish(job: JobType): Status
/**
* [Pair.second] is reason why job can't be performed
*
* If not null, it is written to [idleReason]
*/
protected abstract fun computeNextJob(): Pair<JobType?, Any?>
protected open fun onWorkTick(requiredPower: ImpreciseFraction, extractedPower: ImpreciseFraction, ticksAdvanced: Double): Status {
return Status.SUCCESS
}
private var idleTicksAnim = 0
private var workingTicksAnim = 0
private var errorTicksAnim = 0
override fun redstoneStatusUpdated(new_blocked: Boolean, old_blocked: Boolean) {
super.redstoneStatusUpdated(new_blocked, old_blocked)
isIdling = new_blocked
}
protected fun workerLoop() {
if (errorTicksAnim > 20 && blockState.hasProperty(WorkerState.WORKER_STATE) && blockState.getValue(WorkerState.WORKER_STATE) != WorkerState.ERROR) {
level?.setBlock(blockPos, blockState.setValue(WorkerState.WORKER_STATE, WorkerState.ERROR), Block.UPDATE_CLIENTS)
}
if (throttleTicks > 0) {
workingTicksAnim = 0
idleTicksAnim = 0
throttleTicks--
errorTicksAnim++
if (throttleTicks > 0)
return
}
if (isIdling) {
workingTicksAnim = 0
errorTicksAnim = 0
idleTicksAnim++
if (idleTicksAnim > 20 && blockState.hasProperty(WorkerState.WORKER_STATE) && blockState.getValue(
WorkerState.WORKER_STATE
) != WorkerState.IDLE
) {
level?.setBlock(blockPos, blockState.setValue(WorkerState.WORKER_STATE, WorkerState.IDLE), Block.UPDATE_CLIENTS)
}
return
}
var availableTicks = 1.0
while (!isIdling && weakGreaterThan(availableTicks, 0.0)) {
if (isBlockedByRedstone) {
isIdling = true
break
}
var currentJob = currentJob
if (currentJob == null) {
if (isBlockedByRedstone) {
isIdling = true
break
}
val (job, reason) = computeNextJob()
if (job == null) {
idleReason = reason
isIdling = reason != null
workingTicksAnim = 0
break
}
this.currentJob = job
currentJob = job
}
if (!currentJob.powerUsage.isZero && energy.batteryLevel.isZero) {
idleReason = IdleReason.POWER
isIdling = true
idleTicksAnim++
if (idleTicksAnim > 20 && blockState.hasProperty(WorkerState.WORKER_STATE) && blockState.getValue(
WorkerState.WORKER_STATE
) != WorkerState.IDLE
) {
level?.setBlock(blockPos, blockState.setValue(WorkerState.WORKER_STATE, WorkerState.IDLE), Block.UPDATE_CLIENTS)
}
break
}
idleTicksAnim = 0
if (weakLessThan(workTicks, currentJob.ticks)) {
val ticksLeft = currentJob.ticks - workTicks
val ticksAdvanced: Double
var requiredPower: ImpreciseFraction? = null
var extractedPower: ImpreciseFraction? = null
if (currentJob.powerUsage.isZero) {
ticksAdvanced = availableTicks.coerceAtMost(ticksLeft)
} else {
requiredPower = currentJob.powerUsage * ticksLeft.coerceAtMost(availableTicks)
extractedPower = energy.extractEnergyInner(requiredPower, true)
ticksAdvanced = (extractedPower / requiredPower).toDouble().coerceAtMost(ticksLeft).coerceAtMost(availableTicks)
}
if (weakEqualDoubles(ticksAdvanced, 0.0)) {
break
}
val status = onWorkTick(requiredPower ?: ImpreciseFraction.ZERO, extractedPower ?: ImpreciseFraction.ZERO, ticksAdvanced)
if (!status.success) {
throttleTicks += status.throttleTicks
if (status.idleReason != null) {
idleReason = status.idleReason
isIdling = true
}
break
}
workingTicksAnim++
errorTicksAnim = 0
workTicks += ticksAdvanced
availableTicks -= ticksAdvanced
if (extractedPower != null) {
energy.extractEnergyInner(extractedPower, false)
}
continue
}
val status = onJobFinish(currentJob)
if (status.success) {
this.currentJob = null
workTicks = 0.0
errorTicksAnim = 0
} else {
throttleTicks += status.throttleTicks
if (status.idleReason != null) {
idleReason = status.idleReason
isIdling = true
}
errorTicksAnim++
}
}
if (workingTicksAnim > 20 &&
errorTicksAnim == 0 &&
blockState.hasProperty(WorkerState.WORKER_STATE) &&
blockState.getValue(WorkerState.WORKER_STATE) != WorkerState.WORKING
)
{
level?.setBlock(blockPos, blockState.setValue(WorkerState.WORKER_STATE, WorkerState.WORKING), Block.UPDATE_CLIENTS)
}
}
fun basicTicker() {
batteryChargeLoop()
workerLoop()
}
}

View File

@ -10,12 +10,9 @@ import net.minecraft.world.inventory.AbstractContainerMenu
import net.minecraft.world.item.ItemStack
import net.minecraft.world.level.block.state.BlockState
import net.minecraftforge.common.capabilities.Capability
import net.minecraftforge.common.capabilities.ForgeCapabilities
import net.minecraftforge.common.util.LazyOptional
import net.minecraftforge.items.CapabilityItemHandler
import ru.dbotthepony.mc.otm.TranslatableComponent
import ru.dbotthepony.mc.otm.block.entity.worker.MatteryWorkerBlockEntity
import ru.dbotthepony.mc.otm.block.entity.worker.WorkerJob
import ru.dbotthepony.mc.otm.block.entity.worker.WorkerJobStatus
import ru.dbotthepony.mc.otm.capability.WorkerEnergyStorage
import ru.dbotthepony.mc.otm.container.MatteryContainer
import ru.dbotthepony.mc.otm.container.MatteryContainerFilter
@ -25,7 +22,10 @@ import ru.dbotthepony.mc.otm.registry.MBlockEntities
import ru.dbotthepony.mc.otm.registry.MRecipes
import ru.dbotthepony.mc.otm.set
class PlatePressBlockEntity(p_155229_: BlockPos, p_155230_: BlockState) : MatteryWorkerBlockEntity(MBlockEntities.PLATE_PRESS, p_155229_, p_155230_) {
class PlatePressBlockEntity(
p_155229_: BlockPos,
p_155230_: BlockState
) : MatteryWorkerBlockEntity<MatteryWorkerBlockEntity.ItemJob>(MBlockEntities.PLATE_PRESS, p_155229_, p_155230_, ::ItemJob) {
val container = MatteryContainer(this::setChangedLight, 2)
override val energy = WorkerEnergyStorage(this::setChangedLight)
@ -50,7 +50,7 @@ class PlatePressBlockEntity(p_155229_: BlockPos, p_155230_: BlockState) : Matter
}
override fun <T> getCapability(cap: Capability<T>, side: Direction?): LazyOptional<T> {
if (cap === CapabilityItemHandler.ITEM_HANDLER_CAPABILITY)
if (cap == ForgeCapabilities.ITEM_HANDLER)
return itemHandler.get().cast()
return super.getCapability(cap, side)
@ -73,32 +73,24 @@ class PlatePressBlockEntity(p_155229_: BlockPos, p_155230_: BlockState) : Matter
return PlatePressMenu(containerID, inventory, this)
}
override fun onJobFinish(job: WorkerJob): WorkerJobStatus {
val resultTag = job.data["result"] as? CompoundTag ?: return WorkerJobStatus()
val result = ItemStack.of(resultTag)
override fun onJobFinish(job: ItemJob): Status {
if (job.itemStack.isEmpty)
return Status.SUCCESS
if (result.isEmpty)
return WorkerJobStatus()
if (!container.fullyAddItem(job.itemStack, start = SLOT_OUTPUT, end = SLOT_OUTPUT))
return Status.FAILURE_ITEM
if (!container.fullyAddItem(result, start = SLOT_OUTPUT, end = SLOT_OUTPUT)) {
return WorkerJobStatus(false, 20)
}
return WorkerJobStatus()
return Status.SUCCESS
}
override fun computeNextJob(): WorkerJob? {
val level = level ?: return null
val recipe = level.recipeManager.getRecipeFor(MRecipes.PLATE_PRESS, container, level).orElse(null) ?: return null
override fun computeNextJob(): Pair<ItemJob?, IdleReason?> {
if (energy.batteryLevel.isZero) {
return null to IdleReason.POWER
}
val copy = container[SLOT_INPUT].copy()
val recipe = level?.recipeManager?.getRecipeFor(MRecipes.PLATE_PRESS, container, level!!)?.orElse(null) ?: return null to IdleReason.ITEM
container[SLOT_INPUT].shrink(1)
container.setChanged(SLOT_INPUT)
copy.count = 1
return WorkerJob(copy, recipe.workTime.toDouble(), BASELINE_CONSUMPTION, CompoundTag().also {
it["result"] = recipe.resultItem.serializeNBT()
})
return ItemJob(recipe.resultItem, recipe.workTime.toDouble(), BASELINE_CONSUMPTION) to null
}
companion object {

View File

@ -0,0 +1,21 @@
package ru.dbotthepony.mc.otm.block.entity
import net.minecraft.util.StringRepresentable
import net.minecraft.world.level.block.state.properties.EnumProperty
enum class WorkerState : StringRepresentable {
IDLE,
WORKING,
ERROR;
companion object {
@JvmField
val WORKER_STATE: EnumProperty<WorkerState> = EnumProperty.create("worker", WorkerState::class.java)
@JvmField
val SEMI_WORKER_STATE: EnumProperty<WorkerState> = EnumProperty.create("worker", WorkerState::class.java, IDLE, WORKING)
}
override fun getSerializedName(): String {
return if (this == IDLE) "idle" else if (this == WORKING) "working" else "error"
}
}

View File

@ -18,7 +18,7 @@ import net.minecraftforge.items.CapabilityItemHandler
import ru.dbotthepony.mc.otm.TranslatableComponent
import ru.dbotthepony.mc.otm.block.matter.MatterBottlerBlock
import ru.dbotthepony.mc.otm.block.entity.MatteryPoweredBlockEntity
import ru.dbotthepony.mc.otm.block.entity.worker.WorkerState
import ru.dbotthepony.mc.otm.block.entity.WorkerState
import ru.dbotthepony.mc.otm.capability.MatteryCapability
import ru.dbotthepony.mc.otm.capability.WorkerEnergyStorage
import ru.dbotthepony.mc.otm.capability.matter.IMatterHandler

View File

@ -12,20 +12,21 @@ import net.minecraft.world.item.ItemStack
import net.minecraft.world.level.Level
import net.minecraft.world.level.block.state.BlockState
import net.minecraftforge.common.capabilities.Capability
import net.minecraftforge.common.capabilities.ForgeCapabilities
import net.minecraftforge.common.util.LazyOptional
import net.minecraftforge.items.CapabilityItemHandler
import net.minecraftforge.items.IItemHandler
import ru.dbotthepony.mc.otm.TranslatableComponent
import ru.dbotthepony.mc.otm.block.entity.worker.MatteryWorkerBlockEntity
import ru.dbotthepony.mc.otm.block.entity.worker.WorkerJob
import ru.dbotthepony.mc.otm.block.entity.worker.WorkerJobStatus
import ru.dbotthepony.mc.otm.block.entity.MatteryWorkerBlockEntity
import ru.dbotthepony.mc.otm.capability.MatteryCapability
import ru.dbotthepony.mc.otm.capability.WorkerEnergyStorage
import ru.dbotthepony.mc.otm.capability.matter.IMatterHandler
import ru.dbotthepony.mc.otm.capability.matter.MatterDirection
import ru.dbotthepony.mc.otm.capability.matter.MatterHandlerImpl
import ru.dbotthepony.mc.otm.container.MatteryContainer
import ru.dbotthepony.mc.otm.container.MatteryContainerFilter
import ru.dbotthepony.mc.otm.core.ImpreciseFraction
import ru.dbotthepony.mc.otm.core.getImpreciseFraction
import ru.dbotthepony.mc.otm.core.set
import ru.dbotthepony.mc.otm.graph.Graph6Node
import ru.dbotthepony.mc.otm.graph.matter.IMatterGraphNode
import ru.dbotthepony.mc.otm.graph.matter.MatterNetworkGraph
@ -93,7 +94,29 @@ fun moveMatterAsDustIntoContainer(_matterValue: ImpreciseFraction, container: Ma
}
class MatterDecomposerBlockEntity(pos: BlockPos, state: BlockState)
: MatteryWorkerBlockEntity(MBlockEntities.MATTER_DECOMPOSER, pos, state), IMatterGraphNode {
: MatteryWorkerBlockEntity<MatterDecomposerBlockEntity.DecomposerJob>(MBlockEntities.MATTER_DECOMPOSER, pos, state, ::DecomposerJob), IMatterGraphNode {
class DecomposerJob : Job {
val toDust: Boolean
var matterValue: ImpreciseFraction
constructor(tag: CompoundTag) : super(tag) {
toDust = tag.getBoolean("to_dust")
matterValue = tag.getImpreciseFraction("value")
}
constructor(toDust: Boolean, matterValue: ImpreciseFraction, ticks: Double) : super(ticks, BASE_CONSUMPTION) {
this.toDust = toDust
this.matterValue = matterValue
}
override fun serializeNBT(): CompoundTag {
return super.serializeNBT().also {
it["to_dust"] = toDust
it["value"] = matterValue
}
}
}
override val energy = WorkerEnergyStorage(this, ENERGY_STORAGE, MAX_IO)
private var valid = true
@ -110,13 +133,18 @@ class MatterDecomposerBlockEntity(pos: BlockPos, state: BlockState)
private var resolverNode = LazyOptional.of { this }
// вход, выход
@JvmField
val container = MatteryContainer(this::setChangedLight, 3)
private val itemHandler = LazyOptional.of<IItemHandler> {
container.handler(
{ slot: Int, stack: ItemStack -> slot == INPUT_SLOT && canDecompose(stack) },
{ slot: Int, _: Int, _: ItemStack -> slot != INPUT_SLOT })
container.handler(object : MatteryContainerFilter {
override fun canInsert(slot: Int, stack: ItemStack): Boolean {
return slot == INPUT_SLOT && canDecompose(stack)
}
override fun canExtract(slot: Int, amount: Int, stack: ItemStack): Boolean {
return slot != INPUT_SLOT
}
})
}
override val defaultDisplayName: Component
@ -156,39 +184,35 @@ class MatterDecomposerBlockEntity(pos: BlockPos, state: BlockState)
override fun <T> getCapability(cap: Capability<T>, side: Direction?): LazyOptional<T> {
if (valid) {
if (cap === MatteryCapability.MATTER) return resolverMatter.cast()
if (cap === MatteryCapability.MATTER_NODE) return resolverNode.cast()
if (cap === CapabilityItemHandler.ITEM_HANDLER_CAPABILITY) return itemHandler.cast()
if (cap == MatteryCapability.MATTER) return resolverMatter.cast()
if (cap == MatteryCapability.MATTER_NODE) return resolverNode.cast()
if (cap == ForgeCapabilities.ITEM_HANDLER) return itemHandler.cast()
}
return super.getCapability(cap, side)
}
override fun onJobFinish(job: WorkerJob): WorkerJobStatus {
var matterValue = ImpreciseFraction.deserializeNBT(job.data["value"])
override fun onJobFinish(job: DecomposerJob): Status {
if (job.toDust) {
job.matterValue = moveMatterAsDustIntoContainer(job.matterValue, container, OUTPUT_DUST_MAIN, OUTPUT_DUST_STACKING)
if (job.data.getBoolean("to_dust")) {
matterValue = moveMatterAsDustIntoContainer(matterValue, container, OUTPUT_DUST_MAIN, OUTPUT_DUST_STACKING)
if (!matterValue.isZero) {
job.data["value"] = matterValue.serializeNBT()
return WorkerJobStatus(20)
if (!job.matterValue.isZero) {
return Status.FAILURE_WAIT_FAST
}
return WorkerJobStatus()
return Status.SUCCESS
}
matterValue -= matter.receiveMatterInner(matterValue, false)
job.matterValue -= matter.receiveMatterInner(job.matterValue, false)
if (matterValue.isPositive) {
job.data["value"] = matterValue.serializeNBT()
return WorkerJobStatus(false, 20)
if (job.matterValue.isPositive) {
return Status.FAILURE_MATTER
}
return WorkerJobStatus()
return Status.SUCCESS
}
override fun computeNextJob(): WorkerJob? {
override fun computeNextJob(): Pair<DecomposerJob?, IdleReason?> {
val stack = container[INPUT_SLOT]
if (!stack.isEmpty) {
@ -197,19 +221,13 @@ class MatterDecomposerBlockEntity(pos: BlockPos, state: BlockState)
if (canDecompose(copy)) {
val matter = getMatterValue(copy)
stack.count--
if (!matter.isZero) {
stack.count--
return WorkerJob(copy, matter.complexity * baselineComplexityDecomposeTicks, BASE_CONSUMPTION, CompoundTag().also {
it["to_dust"] = (level?.random?.nextDouble() ?: 1.0) <= 0.2
it["value"] = matter.value.serializeNBT()
})
}
return DecomposerJob((level?.random?.nextDouble() ?: 1.0) <= 0.2, matter.value, matter.complexity * baselineComplexityDecomposeTicks) to null
}
}
return null
return null to IdleReason.ITEM
}
override fun setRemoved() {

View File

@ -12,13 +12,10 @@ import net.minecraft.world.item.ItemStack
import net.minecraft.world.level.Level
import net.minecraft.world.level.block.state.BlockState
import net.minecraftforge.common.capabilities.Capability
import net.minecraftforge.common.capabilities.ForgeCapabilities
import net.minecraftforge.common.util.LazyOptional
import net.minecraftforge.items.CapabilityItemHandler
import ru.dbotthepony.mc.otm.TranslatableComponent
import ru.dbotthepony.mc.otm.block.entity.worker.MatteryWorkerBlockEntity
import ru.dbotthepony.mc.otm.block.entity.worker.WorkerJob
import ru.dbotthepony.mc.otm.block.entity.worker.WorkerJobStatus
import ru.dbotthepony.mc.otm.block.entity.worker.WorkerTickContext
import ru.dbotthepony.mc.otm.block.entity.MatteryWorkerBlockEntity
import ru.dbotthepony.mc.otm.capability.MatteryCapability
import ru.dbotthepony.mc.otm.capability.WorkerEnergyStorage
import ru.dbotthepony.mc.otm.capability.matter.IMatterHandler
@ -30,26 +27,26 @@ import ru.dbotthepony.mc.otm.core.ImpreciseFraction
import ru.dbotthepony.mc.otm.graph.Graph6Node
import ru.dbotthepony.mc.otm.graph.matter.IMatterGraphNode
import ru.dbotthepony.mc.otm.graph.matter.MatterNetworkGraph
import ru.dbotthepony.mc.otm.ifHas
import ru.dbotthepony.mc.otm.item.MatterDustItem
import ru.dbotthepony.mc.otm.map
import ru.dbotthepony.mc.otm.menu.MatterRecyclerMenu
import ru.dbotthepony.mc.otm.registry.MBlockEntities
import ru.dbotthepony.mc.otm.set
class MatterRecyclerBlockEntity(blockPos: BlockPos, blockState: BlockState)
: MatteryWorkerBlockEntity(MBlockEntities.MATTER_RECYCLER, blockPos, blockState), IMatterGraphNode {
: MatteryWorkerBlockEntity<MatteryWorkerBlockEntity.Job>(MBlockEntities.MATTER_RECYCLER, blockPos, blockState, ::Job), IMatterGraphNode {
val matter = MatterHandlerImpl(
this::setChangedLight,
this::matterLevelUpdated,
MatterDirection.EXTRACT,
STORAGE
)
val container = MatteryContainer(this::setChangedLight, 1)
val container = MatteryContainer(this::itemContainerUpdated, 1)
override val matterNode = Graph6Node<IMatterGraphNode>(this)
private var resolverNode = LazyOptional.of { this }
private var valid = true
override val energy = WorkerEnergyStorage(this, MAX_POWER)
override val energy = WorkerEnergyStorage(this::powerLevelUpdated, MAX_POWER)
override fun getMatterHandler(): IMatterHandler {
return matter
@ -99,7 +96,7 @@ class MatterRecyclerBlockEntity(blockPos: BlockPos, blockState: BlockState)
override fun load(nbt: CompoundTag) {
super.load(nbt)
nbt.ifHas("matter", CompoundTag::class.java, matter::deserializeNBT)
nbt.map("matter", matter::deserializeNBT)
container.deserializeNBT(nbt["container"])
}
@ -107,15 +104,12 @@ class MatterRecyclerBlockEntity(blockPos: BlockPos, blockState: BlockState)
if (!valid)
return super.getCapability(cap, side)
if (cap === CapabilityItemHandler.ITEM_HANDLER_CAPABILITY) {
return itemHandler.get().cast()
} else if (cap === MatteryCapability.MATTER) {
return matter.get().cast()
} else if (cap === MatteryCapability.MATTER_NODE) {
return resolverNode.cast()
return when (cap) {
ForgeCapabilities.ITEM_HANDLER -> itemHandler.get().cast()
MatteryCapability.MATTER -> matter.get().cast()
MatteryCapability.MATTER_NODE -> resolverNode.cast()
else -> super.getCapability(cap, side)
}
return super.getCapability(cap, side)
}
override val defaultDisplayName: Component
@ -125,41 +119,39 @@ class MatterRecyclerBlockEntity(blockPos: BlockPos, blockState: BlockState)
return MatterRecyclerMenu(containerID, inventory, this)
}
override fun onJobFinish(job: WorkerJob): WorkerJobStatus {
override fun onJobFinish(job: Job): Status {
// вся логика в onWorkTick
return WorkerJobStatus()
return Status.SUCCESS
}
override fun computeNextJob(): WorkerJob? {
override fun computeNextJob(): Pair<Job?, IdleReason?> {
if (matter.missingMatter.isZero)
return null
return null to IdleReason.ITEM
val stack = container[0]
if (stack.isEmpty || stack.item !is MatterDustItem) {
return null
return null to IdleReason.ITEM
}
val copy = stack.copy()
copy.count = 1
val dustMatter = (stack.item as MatterDustItem).getMatterValue(copy) ?: return null
val dustMatter = (stack.item as MatterDustItem).getMatterValue(stack.copy().also { it.count = 1 }) ?: return null to IdleReason.ITEM
stack.shrink(1)
return WorkerJob(copy, dustMatter.value.toDouble() * MATTER_TICKS, POWER_CONSUMPTION)
container.setChanged(0)
return Job(dustMatter.value.toDouble() * MATTER_TICKS, POWER_CONSUMPTION) to null
}
override fun onWorkTick(context: WorkerTickContext): WorkerJobStatus {
override fun onWorkTick(requiredPower: ImpreciseFraction, extractedPower: ImpreciseFraction, ticksAdvanced: Double): Status {
if ((level?.random?.nextDouble() ?: 1.0) <= 0.4)
return WorkerJobStatus()
return Status.SUCCESS
val receive = if (context.ticksAdvanced == 1.0) MATTER_PER_TICK else MATTER_PER_TICK * context.ticksAdvanced
val receive = MATTER_PER_TICK * ticksAdvanced
val received = matter.receiveMatterInner(receive, true)
if (receive != received)
return WorkerJobStatus(false, 20)
return Status.FAILURE_MATTER
matter.receiveMatterInner(receive, false)
return WorkerJobStatus()
return Status.SUCCESS
}
fun tick() {

View File

@ -8,26 +8,25 @@ import net.minecraft.server.level.ServerLevel
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.capabilities.Capability
import net.minecraftforge.common.capabilities.ForgeCapabilities
import net.minecraftforge.common.util.LazyOptional
import net.minecraftforge.items.CapabilityItemHandler
import ru.dbotthepony.mc.otm.TranslatableComponent
import ru.dbotthepony.mc.otm.block.entity.worker.MatteryWorkerBlockEntity
import ru.dbotthepony.mc.otm.block.entity.worker.WorkerJob
import ru.dbotthepony.mc.otm.block.entity.worker.WorkerJobStatus
import ru.dbotthepony.mc.otm.block.entity.worker.WorkerTickContext
import ru.dbotthepony.mc.otm.block.entity.MatteryWorkerBlockEntity
import ru.dbotthepony.mc.otm.capability.MatteryCapability
import ru.dbotthepony.mc.otm.capability.WorkerEnergyStorage
import ru.dbotthepony.mc.otm.capability.matter.*
import ru.dbotthepony.mc.otm.container.MatteryContainer
import ru.dbotthepony.mc.otm.container.MatteryContainerFilterOnlyOut
import ru.dbotthepony.mc.otm.core.ImpreciseFraction
import ru.dbotthepony.mc.otm.core.getImpreciseFraction
import ru.dbotthepony.mc.otm.graph.Graph6Node
import ru.dbotthepony.mc.otm.graph.matter.IMatterGraphNode
import ru.dbotthepony.mc.otm.graph.matter.MatterNetworkGraph
import ru.dbotthepony.mc.otm.ifHas
import ru.dbotthepony.mc.otm.map
import ru.dbotthepony.mc.otm.matter.baselineComplexityReplicateTicks
import ru.dbotthepony.mc.otm.matter.getMatterValue
import ru.dbotthepony.mc.otm.menu.MatterReplicatorMenu
@ -35,22 +34,51 @@ import ru.dbotthepony.mc.otm.registry.MBlockEntities
import ru.dbotthepony.mc.otm.set
class MatterReplicatorBlockEntity(p_155229_: BlockPos, p_155230_: BlockState) :
MatteryWorkerBlockEntity(MBlockEntities.MATTER_REPLICATOR, p_155229_, p_155230_), IMatterGraphNode {
MatteryWorkerBlockEntity<MatterReplicatorBlockEntity.ReplicatorJob>(MBlockEntities.MATTER_REPLICATOR, p_155229_, p_155230_, ::ReplicatorJob), IMatterGraphNode {
override val energy = WorkerEnergyStorage(this, STORAGE, MAX_IO)
class ReplicatorJob : ItemJob {
val matterPerTick: ImpreciseFraction
val task: MatterTask
var matterValue: ImpreciseFraction
val pattern: PatternState?
val asDust: Boolean
constructor(tag: CompoundTag) : super(tag) {
matterPerTick = tag.getImpreciseFraction("matter_per_tick")
matterValue = tag.getImpreciseFraction("value")
pattern = tag.map("pattern", PatternState::deserializeNBT)
asDust = tag.getBoolean("as_dust")
task = tag.map("task", MatterTask::deserializeNBT) ?: throw NullPointerException("Unable to deserialize matter task")
}
constructor(
itemStack: ItemStack,
matterPerTick: ImpreciseFraction,
task: MatterTask,
matterValue: ImpreciseFraction,
pattern: PatternState?,
asDust: Boolean,
ticks: Double,
) : super(itemStack, ticks, BASE_CONSUMPTION) {
this.matterPerTick = matterPerTick
this.task = task
this.matterValue = matterValue
this.pattern = pattern
this.asDust = asDust
}
}
override val energy = WorkerEnergyStorage(this::powerLevelUpdated, STORAGE, MAX_IO)
override val matterNode = Graph6Node<IMatterGraphNode>(this)
private val resolverNode = LazyOptional.of { this }
@JvmField
val matter = MatterHandlerImpl(
this::setChangedLight,
this::matterLevelUpdated,
MatterDirection.RECEIVE,
ImpreciseFraction(2)
)
// обычные запросы
@JvmField
val container = MatteryContainer(this::setChangedLight, 5)
val container = MatteryContainer(this::itemContainerUpdated, 5)
private val itemHandler = container.handler(MatteryContainerFilterOnlyOut)
override val defaultDisplayName: Component
@ -60,38 +88,42 @@ class MatterReplicatorBlockEntity(p_155229_: BlockPos, p_155230_: BlockState) :
return MatterReplicatorMenu(containerID, inventory, this)
}
override fun onJobFinish(job: WorkerJob): WorkerJobStatus {
if (job.data.getBoolean("as_dust")) {
val matterValue = moveMatterAsDustIntoContainer(ImpreciseFraction.deserializeNBT(job["matter"]), container, OUTPUT_DUST_MAIN, OUTPUT_DUST_STACKING)
override fun onJobFinish(job: ReplicatorJob): Status {
if (job.asDust) {
job.matterValue = moveMatterAsDustIntoContainer(job.matterValue, container, OUTPUT_DUST_MAIN, OUTPUT_DUST_STACKING)
if (!matterValue.isZero) {
job["matter"] = matterValue.serializeNBT()
return WorkerJobStatus(false, 20)
if (!job.matterValue.isZero) {
return Status.FAILURE_WAIT
}
(matterNode.graph as MatterNetworkGraph?)?.notifyTaskCompletion(MatterTask.deserializeNBT(job["task"])!!)
return WorkerJobStatus()
(matterNode.graph as MatterNetworkGraph?)?.notifyTaskCompletion(job.task)
return Status.SUCCESS
}
if (!container.fullyAddItem(job.stack, FIRST_ACTUAL_OUTPUT_SLOT .. LAST_ACTUAL_OUTPUT_SLOT)) {
return WorkerJobStatus(false, 20)
if (!container.fullyAddItem(job.itemStack, FIRST_ACTUAL_OUTPUT_SLOT .. LAST_ACTUAL_OUTPUT_SLOT)) {
return Status.FAILURE_ITEM
}
(matterNode.graph as MatterNetworkGraph?)?.notifyTaskCompletion(MatterTask.deserializeNBT(job["task"])!!)
return WorkerJobStatus()
(matterNode.graph as MatterNetworkGraph?)?.notifyTaskCompletion(job.task)
return Status.SUCCESS
}
override fun onMatterTaskCreated(task: MatterTask) {
isIdling = false
if (idleReason == IdleReason.OBSERVING) {
isIdling = false
}
}
override fun onMatterTaskUpdated(new_state: MatterTask, old_state: MatterTask) {
isIdling = false
if (idleReason == IdleReason.OBSERVING) {
isIdling = false
}
}
override fun onPatternAdded(state: PatternState) {
isIdling = false
if (idleReason == IdleReason.OBSERVING) {
isIdling = false
}
}
override fun setRemoved() {
@ -106,33 +138,31 @@ class MatterReplicatorBlockEntity(p_155229_: BlockPos, p_155230_: BlockState) :
MatterNetworkGraph.discoverFull(this, matterNode)
}
override fun computeNextJob(): WorkerJob? {
val graph = matterNode.graph as MatterNetworkGraph? ?: return null
val allocation = graph.allocateTask(false) ?: return null
override fun computeNextJob(): Pair<ReplicatorJob?, IdleReason?> {
val graph = matterNode.graph as MatterNetworkGraph? ?: return null to null
val allocation = graph.allocateTask(simulate = false) ?: return null to IdleReason.OBSERVING
val stack = allocation.task.stack(1)
val matter = getMatterValue(stack)
// ????????
if (matter.isZero) return null
if (matter.isZero) return null to null
val ticks = matter.complexity * baselineComplexityReplicateTicks
return WorkerJob(stack, ticks, BASE_CONSUMPTION, CompoundTag().also {
it["matter_per_tick"] = (matter.value / ticks).serializeNBT()
it["task"] = allocation.task.serializeNBT()
it["matter"] = matter.value.serializeNBT()
if (allocation.pattern != null)
it["pattern"] = allocation.pattern.serializeNBT()
if ((level?.random?.nextDouble() ?: 1.0) > (allocation.pattern?.research ?: 2.0))
it["as_dust"] = true
})
return ReplicatorJob(
itemStack = stack,
matterPerTick = matter.value / ticks,
task = allocation.task,
matterValue = matter.value,
pattern = allocation.pattern,
asDust = (level?.random?.nextDouble() ?: 1.0) > (allocation.pattern?.research ?: 2.0),
ticks = ticks,
) to null
}
override fun onWorkTick(context: WorkerTickContext): WorkerJobStatus {
val drainPerTick = ImpreciseFraction.deserializeNBT(context.job.data["matter_per_tick"])
val graph = matterNode.graph as MatterNetworkGraph? ?: return WorkerJobStatus(false, 20)
override fun onWorkTick(requiredPower: ImpreciseFraction, extractedPower: ImpreciseFraction, ticksAdvanced: Double): Status {
val drainPerTick = currentJob!!.matterPerTick * ticksAdvanced
val graph = matterNode.graph as MatterNetworkGraph? ?: return Status.FAILURE_WAIT_FAST
if (matter.extractMatterInner(drainPerTick, true) < drainPerTick) {
// в машине недостаточно материи
@ -142,44 +172,42 @@ class MatterReplicatorBlockEntity(p_155229_: BlockPos, p_155230_: BlockState) :
val toExtract = drainPerTick - matter.extractMatterInner(drainPerTick, true)
val drain = graph.extractMatter(toExtract, true)
if (drain < toExtract) {
if (drain != toExtract) {
// недостаточно материи в сети
return WorkerJobStatus(false, 200)
return Status.FAILURE_MATTER
}
// достаточно материи в сети + внутри машины
matter.extractMatterInner(drainPerTick, false)
graph.extractMatter(drain, false)
return WorkerJobStatus()
return Status.SUCCESS
} else {
// в тик требуется меньше материи, чем её может хранить репликатор
// примем из сети недостающее количество бака материи, или 200 тиков репликации, что меньше
val toExtract =
matter.missingMatter.coerceAtMost(drainPerTick.times(DRAIN_MULT))
val drain = graph.extractMatter(toExtract, true)
val drain = graph.extractMatter(matter.missingMatter.coerceAtMost(drainPerTick * DRAIN_MULT), true)
if (drain.isZero) {
// в сети нет материи
return WorkerJobStatus(false, 200)
return Status.FAILURE_MATTER
}
val received = matter.receiveMatterOuter(drain, false)
graph.extractMatter(received, false)
// получили материю, проверяем возможность работы
if (matter.extractMatterInner(drainPerTick, false) >= drainPerTick) {
return WorkerJobStatus()
if (matter.extractMatterInner(drainPerTick, true) >= drainPerTick) {
matter.extractMatterInner(drainPerTick, false)
return Status.SUCCESS
} else {
// :(
return WorkerJobStatus(false, 200)
return Status.FAILURE_WAIT
}
}
}
// в машине достаточно материи
matter.extractMatterInner(drainPerTick, false)
return WorkerJobStatus()
return Status.SUCCESS
}
override fun saveAdditional(nbt: CompoundTag) {
@ -191,10 +219,7 @@ class MatterReplicatorBlockEntity(p_155229_: BlockPos, p_155230_: BlockState) :
override fun load(nbt: CompoundTag) {
super.load(nbt)
container.deserializeNBT(nbt["container"])
nbt.ifHas("matter", CompoundTag::class.java) {
matter.deserializeNBT(it)
}
nbt.map("matter", matter::deserializeNBT)
}
private var valid = true
@ -213,8 +238,8 @@ class MatterReplicatorBlockEntity(p_155229_: BlockPos, p_155230_: BlockState) :
override fun <T> getCapability(cap: Capability<T>, side: Direction?): LazyOptional<T> {
if (valid) {
if (cap === MatteryCapability.MATTER_NODE) return resolverNode.cast()
if (cap === CapabilityItemHandler.ITEM_HANDLER_CAPABILITY) return itemHandler.get().cast()
if (cap == MatteryCapability.MATTER_NODE) return resolverNode.cast()
if (cap == ForgeCapabilities.ITEM_HANDLER) return itemHandler.get().cast()
}
return super.getCapability(cap, side)

View File

@ -12,16 +12,16 @@ import net.minecraft.world.item.ItemStack
import net.minecraft.world.level.Level
import net.minecraft.world.level.block.state.BlockState
import net.minecraftforge.common.capabilities.Capability
import net.minecraftforge.common.capabilities.ForgeCapabilities
import net.minecraftforge.common.util.LazyOptional
import net.minecraftforge.items.CapabilityItemHandler
import ru.dbotthepony.mc.otm.TranslatableComponent
import ru.dbotthepony.mc.otm.block.entity.worker.MatteryWorkerBlockEntity
import ru.dbotthepony.mc.otm.block.entity.worker.WorkerJob
import ru.dbotthepony.mc.otm.block.entity.worker.WorkerJobStatus
import ru.dbotthepony.mc.otm.block.entity.MatteryWorkerBlockEntity
import ru.dbotthepony.mc.otm.capability.MatteryCapability
import ru.dbotthepony.mc.otm.capability.WorkerEnergyStorage
import ru.dbotthepony.mc.otm.capability.matter.PatternState
import ru.dbotthepony.mc.otm.container.MatteryContainer
import ru.dbotthepony.mc.otm.container.MatteryContainerFilter
import ru.dbotthepony.mc.otm.core.ImpreciseFraction
import ru.dbotthepony.mc.otm.graph.Graph6Node
import ru.dbotthepony.mc.otm.graph.matter.IMatterGraphNode
@ -32,26 +32,38 @@ import ru.dbotthepony.mc.otm.registry.MBlockEntities
import java.util.*
class MatterScannerBlockEntity(p_155229_: BlockPos, p_155230_: BlockState) :
MatteryWorkerBlockEntity(MBlockEntities.MATTER_SCANNER, p_155229_, p_155230_), IMatterGraphNode {
MatteryWorkerBlockEntity<MatteryWorkerBlockEntity.ItemJob>(MBlockEntities.MATTER_SCANNER, p_155229_, p_155230_, ::ItemJob), IMatterGraphNode {
val container = MatteryContainer(this::setChanged, 1)
override val energy = WorkerEnergyStorage(this, STORAGE, MAX_IO)
private val itemHandler = container.handler(
{ _: Int, stack: ItemStack -> canDecompose(stack) },
{ _: Int, _: Int, _: ItemStack -> isIdling }
)
val container = MatteryContainer(this::itemContainerUpdated, 1)
override val energy = WorkerEnergyStorage(this::powerLevelUpdated, STORAGE, MAX_IO)
private val itemHandler = container.handler(object : MatteryContainerFilter {
override fun canInsert(slot: Int, stack: ItemStack): Boolean {
return canDecompose(stack)
}
override fun canExtract(slot: Int, amount: Int, stack: ItemStack): Boolean {
return isIdling
}
})
// IMatterGraphNode
override fun onPatternAdded(state: PatternState) {
isIdling = false
if (idleReason == IdleReason.OBSERVING) {
isIdling = false
}
}
override fun onPatternRemoved(state: PatternState) {
isIdling = false
if (idleReason == IdleReason.OBSERVING) {
isIdling = false
}
}
override fun onPatternUpdated(new_state: PatternState, old_state: PatternState) {
isIdling = false
if (idleReason == IdleReason.OBSERVING) {
isIdling = false
}
}
// /IMatterGraphNode
@ -61,7 +73,7 @@ class MatterScannerBlockEntity(p_155229_: BlockPos, p_155230_: BlockState) :
override fun <T> getCapability(cap: Capability<T>, side: Direction?): LazyOptional<T> {
if (valid) {
if (cap === CapabilityItemHandler.ITEM_HANDLER_CAPABILITY) return itemHandler.get().cast()
if (cap == ForgeCapabilities.ITEM_HANDLER) return itemHandler.get().cast()
if (cap == MatteryCapability.MATTER_NODE) return resolverNode.cast()
}
@ -105,11 +117,11 @@ class MatterScannerBlockEntity(p_155229_: BlockPos, p_155230_: BlockState) :
super.load(nbt)
}
override fun onJobFinish(job: WorkerJob): WorkerJobStatus {
val grid = matterNode.graph as MatterNetworkGraph? ?: return WorkerJobStatus(false, 100)
override fun onJobFinish(job: ItemJob): Status {
val grid = matterNode.graph as MatterNetworkGraph? ?: return Status.FAILURE_WAIT
val stack = job.stack
if (stack.isEmpty || !hasMatterValue(stack)) return WorkerJobStatus()
val stack = job.itemStack
if (stack.isEmpty || !hasMatterValue(stack)) return Status.SUCCESS
val getState = grid.findPatterns(stack.item)
var findState: PatternState? = null
@ -132,17 +144,21 @@ class MatterScannerBlockEntity(p_155229_: BlockPos, p_155230_: BlockState) :
}
if (!grid.insertPattern(new, onlyUpdate = false, simulate = false).isFailed) {
return WorkerJobStatus()
return Status.SUCCESS
} else {
return WorkerJobStatus(false, 200)
return Status.FAILURE_WAIT
}
}
override fun computeNextJob(): WorkerJob? {
val grid = matterNode.graph as MatterNetworkGraph? ?: return null
override fun computeNextJob(): Pair<ItemJob?, IdleReason?> {
if (energy.batteryLevel.isZero) {
return null to IdleReason.POWER
}
val grid = matterNode.graph as MatterNetworkGraph? ?: return null to null
val stack = container.getItem(0)
if (stack.isEmpty || !canDecompose(stack)) return null
if (stack.isEmpty || !canDecompose(stack)) return null to IdleReason.ITEM
val getState = grid.findPatterns(stack.item)
var findState: PatternState? = null
@ -151,7 +167,7 @@ class MatterScannerBlockEntity(p_155229_: BlockPos, p_155230_: BlockState) :
if (state.item === stack.item && state.research < 1.0) {
findState = state
} else if (state.item === stack.item && state.research >= 1.0) {
return null
return null to IdleReason.OBSERVING
}
}
@ -163,14 +179,13 @@ class MatterScannerBlockEntity(p_155229_: BlockPos, p_155230_: BlockState) :
}
if (!grid.insertPattern(new, onlyUpdate = false, simulate = true).isFailed) {
val copy = stack.copy()
copy.count = 1
val copy = stack.copy().also { it.count = 1 }
stack.shrink(1)
container.setChanged()
return WorkerJob(copy, getMatterValue(copy).complexity * baselineComplexityScanTicks, BASE_CONSUMPTION)
return ItemJob(copy, getMatterValue(copy).complexity * baselineComplexityScanTicks, BASE_CONSUMPTION) to null
}
return null
return null to IdleReason.ITEM
}
override fun setLevel(p_155231_: Level) {

View File

@ -14,7 +14,7 @@ import ru.dbotthepony.mc.otm.OverdriveThatMatters
import ru.dbotthepony.mc.otm.TranslatableComponent
import ru.dbotthepony.mc.otm.block.entity.MatteryPoweredBlockEntity
import ru.dbotthepony.mc.otm.block.storage.DriveViewerBlock
import ru.dbotthepony.mc.otm.block.entity.worker.WorkerState
import ru.dbotthepony.mc.otm.block.entity.WorkerState
import ru.dbotthepony.mc.otm.capability.MatteryCapability
import ru.dbotthepony.mc.otm.capability.WorkerEnergyStorage
import ru.dbotthepony.mc.otm.container.MatteryContainer

View File

@ -1,261 +0,0 @@
package ru.dbotthepony.mc.otm.block.entity.worker
import net.minecraft.world.level.block.entity.BlockEntityType
import net.minecraft.core.BlockPos
import net.minecraft.world.level.block.state.BlockState
import ru.dbotthepony.mc.otm.block.entity.MatteryPoweredBlockEntity
import net.minecraft.nbt.CompoundTag
import net.minecraft.nbt.DoubleTag
import net.minecraft.world.level.block.Block
import ru.dbotthepony.mc.otm.core.ImpreciseFraction
import ru.dbotthepony.mc.otm.core.weakGreaterOrEqual
import ru.dbotthepony.mc.otm.core.weakLessThan
import ru.dbotthepony.mc.otm.ifHas
import ru.dbotthepony.mc.otm.set
abstract class MatteryWorkerBlockEntity(p_155228_: BlockEntityType<*>, p_155229_: BlockPos, p_155230_: BlockState) :
MatteryPoweredBlockEntity(p_155228_, p_155229_, p_155230_) {
var workTicks = 0.0
protected set
var throttleTicks = 0
protected set
var currentJob: WorkerJob? = null
protected set
// если isIdling То ничего не делать
// isIdling должна быть выставлена в true если что то изменилось, что могло создать работу
var isIdling = false
protected set
val isUnableToProcess: Boolean get() = throttleTicks > 0
val workProgress: Float
get() {
val currentJob = currentJob ?: return 0.0f
return (workTicks / currentJob.ticks).coerceAtMost(1.0).toFloat()
}
override fun saveAdditional(nbt: CompoundTag) {
super.saveAdditional(nbt)
nbt["work_ticks"] = workTicks
currentJob?.let { nbt["current_job"] = it.serializeNBT() }
}
override fun load(nbt: CompoundTag) {
super.load(nbt)
nbt.ifHas("work_ticks", DoubleTag::class.java) {
workTicks = it.asDouble
}
currentJob = WorkerJob.deserializeNBT(nbt["current_job"])
if (currentJob == null)
workTicks = 0.0
}
override fun setChanged() {
super.setChanged()
isIdling = false
}
override fun setChangedLight() {
super.setChangedLight()
isIdling = false
}
/**
* @param job current job
* @return whenever machine can finish it's job. return false if machine for whatever reason can't finish it's job,
* waiting on conditions to be met
*/
protected abstract fun onJobFinish(job: WorkerJob): WorkerJobStatus
/**
* @param context context for current job
* @return whenever machine can perform it
*/
protected open fun onWorkTick(context: WorkerTickContext): WorkerJobStatus {
return WorkerJobStatus()
}
private var idleTicksAnim = 0
private var workingTicksAnim = 0
private var errorTicksAnim = 0
override fun redstoneStatusUpdated(new_blocked: Boolean, old_blocked: Boolean) {
super.redstoneStatusUpdated(new_blocked, old_blocked)
isIdling = new_blocked
}
protected fun workerLoop() {
if (errorTicksAnim > 20 && blockState.hasProperty(WorkerState.WORKER_STATE) && blockState.getValue(WorkerState.WORKER_STATE) != WorkerState.ERROR) {
level?.setBlock(blockPos, blockState.setValue(WorkerState.WORKER_STATE, WorkerState.ERROR), Block.UPDATE_CLIENTS)
}
if (throttleTicks > 0) {
workingTicksAnim = 0
idleTicksAnim = 0
throttleTicks--
errorTicksAnim++
if (throttleTicks > 0)
return
}
if (isIdling) {
workingTicksAnim = 0
errorTicksAnim = 0
idleTicksAnim++
if (idleTicksAnim > 20 && blockState.hasProperty(WorkerState.WORKER_STATE) && blockState.getValue(WorkerState.WORKER_STATE) != WorkerState.IDLE) {
level?.setBlock(blockPos, blockState.setValue(WorkerState.WORKER_STATE, WorkerState.IDLE), Block.UPDATE_CLIENTS)
}
return
}
var currentJob = currentJob
if (currentJob == null) {
if (isBlockedByRedstone) {
isIdling = true
return
}
val input = computeNextJob()
if (input == null) {
isIdling = true
workingTicksAnim = 0
return
}
this.currentJob = input
currentJob = input
}
if (isBlockedByRedstone) {
isIdling = true
return
}
if (!currentJob.power.isZero && energy.batteryLevel.isZero) {
idleTicksAnim++
if (idleTicksAnim > 20 && blockState.hasProperty(WorkerState.WORKER_STATE) && blockState.getValue(WorkerState.WORKER_STATE) != WorkerState.IDLE) {
level?.setBlock(blockPos, blockState.setValue(WorkerState.WORKER_STATE, WorkerState.IDLE), Block.UPDATE_CLIENTS)
}
return
}
idleTicksAnim = 0
if (weakLessThan(workTicks, currentJob.ticks)) {
if (!currentJob.power.isZero) {
// сколько осталось тиков работать
val ticksLeft = currentJob.ticks - workTicks
val requiredPower: ImpreciseFraction
// запрос энергии на то количество, сколько действительно осталось работать
if (ticksLeft > 1.0) {
requiredPower = currentJob.power
} else {
requiredPower = currentJob.power * ticksLeft
}
val extractedPower = if (requiredPower.isZero) ImpreciseFraction.ZERO else energy.extractEnergyInner(requiredPower, true)
// сколько тиков мы "проработали"
// может быть меньше, чем единица, если недостаточно питания или мы завершаем работу,
// для которой осталось дробное количество тиков
val ticksAdvanced = (extractedPower / requiredPower).toDouble().coerceAtMost(ticksLeft)
val ticksAdvancedWeak = if (requiredPower.isZero) 1.0 else ticksAdvanced
val status = onWorkTick(WorkerTickContext(currentJob, requiredPower, extractedPower, ticksAdvanced))
if (!status.valid) {
throttleTicks += status.throttle
return
}
workingTicksAnim++
errorTicksAnim = 0
val updatedWorkTicks = (workTicks + ticksAdvancedWeak).coerceAtMost(currentJob.ticks)
if (weakGreaterOrEqual(updatedWorkTicks, currentJob.ticks)) {
workTicks = currentJob.ticks
energy.extractEnergyInner(extractedPower * (1.0 - (updatedWorkTicks - currentJob.ticks)), false)
val finish = onJobFinish(currentJob)
if (finish.valid) {
this.currentJob = null
workTicks = 0.0
} else {
throttleTicks += finish.throttle
}
} else {
workTicks = updatedWorkTicks
energy.extractEnergyInner(extractedPower, false)
}
} else {
val ticksLeft = (currentJob.ticks - workTicks).coerceAtMost(1.0)
val status = onWorkTick(WorkerTickContext(currentJob, ImpreciseFraction.ZERO, ImpreciseFraction.ZERO, ticksLeft))
if (!status.valid) {
throttleTicks += status.throttle
return
}
workingTicksAnim++
errorTicksAnim = 0
workTicks = (workTicks + 1.0).coerceAtMost(currentJob.ticks)
if (weakGreaterOrEqual(workTicks, currentJob.ticks)) {
val finish = onJobFinish(currentJob)
if (finish.valid) {
this.currentJob = null
workTicks = 0.0
} else {
throttleTicks += finish.throttle
}
}
}
} else {
val finish = onJobFinish(currentJob)
if (finish.valid) {
this.currentJob = null
workTicks = 0.0
errorTicksAnim = 0
} else {
throttleTicks += finish.throttle
errorTicksAnim++
}
}
if (workingTicksAnim > 20 &&
errorTicksAnim == 0 &&
blockState.hasProperty(WorkerState.WORKER_STATE) &&
blockState.getValue(WorkerState.WORKER_STATE) != WorkerState.WORKING)
{
level?.setBlock(blockPos, blockState.setValue(WorkerState.WORKER_STATE, WorkerState.WORKING), Block.UPDATE_CLIENTS)
}
}
/**
* Determine which item can be processed from input slots if idling
* @return any item in input slots. null if no work is available
*/
protected abstract fun computeNextJob(): WorkerJob?
fun basicTicker() {
batteryChargeLoop()
workerLoop()
}
}

View File

@ -1,85 +0,0 @@
package ru.dbotthepony.mc.otm.block.entity.worker
import net.minecraft.nbt.CompoundTag
import net.minecraft.nbt.DoubleTag
import net.minecraft.nbt.Tag
import net.minecraft.util.StringRepresentable
import net.minecraft.world.item.ItemStack
import net.minecraft.world.level.block.state.properties.EnumProperty
import ru.dbotthepony.mc.otm.core.ImpreciseFraction
import ru.dbotthepony.mc.otm.set
@JvmRecord
data class WorkerTickContext(
val job: WorkerJob,
val requiredPower: ImpreciseFraction,
val extractedPower: ImpreciseFraction,
val ticksAdvanced: Double
)
enum class WorkerState : StringRepresentable {
IDLE,
WORKING,
ERROR;
companion object {
@JvmField
val WORKER_STATE: EnumProperty<WorkerState> = EnumProperty.create("worker", WorkerState::class.java)
@JvmField
val SEMI_WORKER_STATE: EnumProperty<WorkerState> = EnumProperty.create("worker", WorkerState::class.java, IDLE, WORKING)
}
override fun getSerializedName(): String {
return if (this == IDLE) "idle" else if (this == WORKING) "working" else "error"
}
}
@JvmRecord
data class WorkerJobStatus @JvmOverloads constructor(val valid: Boolean = true, val throttle: Int = 0) {
constructor(throttle: Int) : this(false, throttle)
}
@JvmRecord
data class WorkerJob @JvmOverloads constructor(
val stack: ItemStack,
val ticks: Double,
val power: ImpreciseFraction = ImpreciseFraction.ZERO,
val data: CompoundTag = CompoundTag()) {
fun serializeNBT(): CompoundTag {
return CompoundTag().also {
it["stack"] = stack.serializeNBT()
it["ticks"] = ticks
it["power"] = power.serializeNBT()
it["data"] = data
}
}
operator fun get(index: String) = data[index]
operator fun set(index: String, value: Tag) { data[index] = value }
operator fun set(index: String, value: Int) { data[index] = value }
operator fun set(index: String, value: Byte) { data[index] = value }
operator fun set(index: String, value: Short) { data[index] = value }
operator fun set(index: String, value: Long) { data[index] = value }
operator fun set(index: String, value: Float) { data[index] = value }
operator fun set(index: String, value: Double) { data[index] = value }
operator fun set(index: String, value: String) { data[index] = value }
operator fun set(index: String, value: Boolean) { data[index] = value }
operator fun set(index: String, value: ByteArray) { data[index] = value }
operator fun set(index: String, value: IntArray) { data[index] = value }
operator fun set(index: String, value: LongArray) { data[index] = value }
companion object {
@JvmStatic
fun deserializeNBT(tag: Tag?): WorkerJob? {
val nbt = tag as? CompoundTag ?: return null
return WorkerJob(
ItemStack.of(nbt["stack"] as? CompoundTag ?: return null),
(nbt["ticks"] as? DoubleTag ?: return null).asDouble,
ImpreciseFraction.deserializeNBT(nbt["power"]),
nbt["data"] as? CompoundTag ?: return null
)
}
}
}

View File

@ -19,7 +19,7 @@ import net.minecraft.world.level.block.state.properties.BooleanProperty
import net.minecraft.world.phys.shapes.CollisionContext
import net.minecraft.world.phys.shapes.VoxelShape
import ru.dbotthepony.mc.otm.block.RotatableMatteryBlock
import ru.dbotthepony.mc.otm.block.entity.worker.WorkerState
import ru.dbotthepony.mc.otm.block.entity.WorkerState
import ru.dbotthepony.mc.otm.registry.MBlockEntities
import ru.dbotthepony.mc.otm.shapes.BlockShapes

View File

@ -17,7 +17,7 @@ import net.minecraft.world.level.block.Block
import net.minecraft.world.phys.shapes.CollisionContext
import net.minecraft.world.phys.shapes.VoxelShape
import ru.dbotthepony.mc.otm.block.RotatableMatteryBlock
import ru.dbotthepony.mc.otm.block.entity.worker.WorkerState
import ru.dbotthepony.mc.otm.block.entity.WorkerState
import ru.dbotthepony.mc.otm.registry.MBlockEntities
import ru.dbotthepony.mc.otm.shapes.BlockShapes

View File

@ -15,7 +15,7 @@ import net.minecraft.world.phys.shapes.CollisionContext
import net.minecraft.world.phys.shapes.VoxelShape
import ru.dbotthepony.mc.otm.block.RotatableMatteryBlock
import ru.dbotthepony.mc.otm.block.entity.matter.MatterRecyclerBlockEntity
import ru.dbotthepony.mc.otm.block.entity.worker.WorkerState
import ru.dbotthepony.mc.otm.block.entity.WorkerState
import ru.dbotthepony.mc.otm.registry.MBlockEntities
import ru.dbotthepony.mc.otm.shapes.BlockShapes

View File

@ -15,7 +15,7 @@ import net.minecraft.world.phys.shapes.CollisionContext
import net.minecraft.world.phys.shapes.VoxelShape
import ru.dbotthepony.mc.otm.block.RotatableMatteryBlock
import ru.dbotthepony.mc.otm.block.entity.matter.MatterReplicatorBlockEntity
import ru.dbotthepony.mc.otm.block.entity.worker.WorkerState
import ru.dbotthepony.mc.otm.block.entity.WorkerState
import ru.dbotthepony.mc.otm.registry.MBlockEntities
import ru.dbotthepony.mc.otm.shapes.BlockShapes

View File

@ -15,7 +15,7 @@ import net.minecraft.world.phys.shapes.CollisionContext
import net.minecraft.world.phys.shapes.VoxelShape
import ru.dbotthepony.mc.otm.block.RotatableMatteryBlock
import ru.dbotthepony.mc.otm.block.entity.matter.MatterScannerBlockEntity
import ru.dbotthepony.mc.otm.block.entity.worker.WorkerState
import ru.dbotthepony.mc.otm.block.entity.WorkerState
import ru.dbotthepony.mc.otm.registry.MBlockEntities
import ru.dbotthepony.mc.otm.shapes.BlockShapes

View File

@ -17,7 +17,7 @@ import net.minecraft.world.phys.shapes.CollisionContext
import net.minecraft.world.phys.shapes.VoxelShape
import ru.dbotthepony.mc.otm.block.RotatableMatteryBlock
import ru.dbotthepony.mc.otm.block.entity.storage.DriveViewerBlockEntity
import ru.dbotthepony.mc.otm.block.entity.worker.WorkerState
import ru.dbotthepony.mc.otm.block.entity.WorkerState
import ru.dbotthepony.mc.otm.registry.MBlockEntities
import ru.dbotthepony.mc.otm.shapes.BlockShapes

View File

@ -15,7 +15,7 @@ import ru.dbotthepony.mc.otm.block.BlackHoleBlock
import ru.dbotthepony.mc.otm.block.RotatableMatteryBlock
import ru.dbotthepony.mc.otm.block.entity.GravitationStabilizerBlockEntity
import ru.dbotthepony.mc.otm.block.entity.blackhole.BlackHoleBlockEntity
import ru.dbotthepony.mc.otm.block.entity.worker.WorkerState
import ru.dbotthepony.mc.otm.block.entity.WorkerState
import ru.dbotthepony.mc.otm.client.render.*
import ru.dbotthepony.mc.otm.core.*
import kotlin.math.PI

View File

@ -1,6 +1,7 @@
package ru.dbotthepony.mc.otm.core
import net.minecraft.nbt.ByteArrayTag
import net.minecraft.nbt.CompoundTag
import net.minecraft.nbt.StringTag
import net.minecraft.nbt.Tag
import net.minecraft.network.FriendlyByteBuf
@ -660,3 +661,8 @@ fun OutputStream.writeImpreciseFraction(value: ImpreciseFraction) {
write(bytes)
writeDouble(value.decimal)
}
fun CompoundTag.getImpreciseFraction(key: String) = ImpreciseFraction.deserializeNBT(this[key])
fun CompoundTag.putImpreciseFraction(key: String, value: ImpreciseFraction) = put(key, value.serializeNBT())
operator fun CompoundTag.set(key: String, value: ImpreciseFraction) = putImpreciseFraction(key, value)