Make Replication Task be only immutable, network Replication Tasks using codecs

This commit is contained in:
DBotThePony 2023-07-26 11:20:48 +07:00
parent a673ed966e
commit 4638899b20
Signed by: DBot
GPG Key ID: DCC23B5715498507
10 changed files with 71 additions and 212 deletions

View File

@ -179,7 +179,7 @@ class MatterPanelBlockEntity(p_155229_: BlockPos, p_155230_: BlockState) :
}
override fun getTask(id: UUID): ReplicationTask? {
return _tasks[id]?.asImmutable()
return _tasks[id]
}
fun removeTask(id: UUID) {
@ -192,7 +192,7 @@ class MatterPanelBlockEntity(p_155229_: BlockPos, p_155230_: BlockState) :
setChanged()
}
fun addTask(state: PatternState, count: Int): IReplicationTask<*> {
fun addTask(state: PatternState, count: Int): ReplicationTask {
val task = ReplicationTask(UUID.randomUUID(), Optional.of(state.id), state.item, 0, 0, count)
_tasks[task.id] = task

View File

@ -83,11 +83,11 @@ class MatterReplicatorBlockEntity(p_155229_: BlockPos, p_155230_: BlockState) :
return matter
}
override fun onMatterTaskCreated(task: IReplicationTask<*>) {
override fun onMatterTaskCreated(task: ReplicationTask) {
jobEventLoops[0].notify(MachineJobEventLoop.IdleReason.OBSERVING)
}
override fun <T : IReplicationTask<*>> onMatterTaskUpdated(newState: T, oldState: T) {
override fun onMatterTaskUpdated(newState: ReplicationTask, oldState: ReplicationTask) {
jobEventLoops[0].notify(MachineJobEventLoop.IdleReason.OBSERVING)
}

View File

@ -21,4 +21,4 @@ class PatternInsertUpdated(new: PatternState, old: PatternState) : PatternInsert
class PatternInsertInserted(new: PatternState) : PatternInsertStatus(PatternInsertResult.INSERTED, new, null)
@JvmRecord
data class ReplicationTaskAllocation(val task: IReplicationTask<*>, val pattern: PatternState?)
data class ReplicationTaskAllocation(val task: ReplicationTask, val pattern: PatternState?)

View File

@ -7,19 +7,19 @@ interface IReplicationTaskProvider {
/**
* It must return new stream each time
*/
val replicationTasks: Stream<out IReplicationTask<*>>
val replicationTasks: Stream<ReplicationTask>
/**
* It must return new stream each time
*/
val allReplicationTasks: Stream<out IReplicationTask<*>>
val allReplicationTasks: Stream<ReplicationTask>
/**
* Allocates (marks as work-in-progress) a task
* by incrementing it's [IReplicationTask.inProgress] by 1
* and shrinking [IReplicationTask.required] by 1
* by incrementing it's [ReplicationTask.inProgress] by 1
* and shrinking [ReplicationTask.required] by 1
*
* If [IReplicationTask.required] == 0, it should not be returned by this method
* If [ReplicationTask.required] == 0, it should not be returned by this method
* @param simulate whenever to change internal state
* @return MatterTaskAllocation(task, pattern) that should be performed, or null if no work is available
*/
@ -38,7 +38,7 @@ interface IReplicationTaskProvider {
* @param id uuid of task
* @return MatterTask that this capability holds with this id, or null
*/
fun getTask(id: UUID): IReplicationTask<*>? {
fun getTask(id: UUID): ReplicationTask? {
return allReplicationTasks.filter { it.id == id }.findAny().orElse(null)
}

View File

@ -1,59 +1,31 @@
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
import net.minecraft.world.item.Item
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.writeItemType
import ru.dbotthepony.mc.otm.core.util.readBinaryJsonWithCodec
import ru.dbotthepony.mc.otm.core.util.writeBinaryJsonWithCodec
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: Optional<UUID>
val item: Item
val inProgress: Int
val finished: Int
data class ReplicationTask(
val id: UUID,
val patternId: Optional<UUID>,
val item: Item,
val inProgress: Int,
val finished: Int,
val required: Int
fun asMutable(): MutableReplicationTask
fun asImmutable(): ReplicationTask
) {
val total get() = inProgress + finished + required
fun copyAsMutable(
id: UUID = this.id,
patternId: Optional<UUID> = this.patternId,
item: Item = this.item,
inProgress: Int = this.inProgress,
finished: Int = this.finished,
required: Int = this.required,
): MutableReplicationTask {
return MutableReplicationTask(id, patternId, item, inProgress, finished, required)
}
fun copyAsImmutable(
id: UUID = this.id,
patternId: Optional<UUID> = this.patternId,
item: Item = this.item,
inProgress: Int = this.inProgress,
finished: Int = this.finished,
required: Int = this.required,
): ReplicationTask {
return ReplicationTask(id, patternId, item, inProgress, finished, required)
}
fun matchId(other: IReplicationTask<*>): Boolean {
fun matchId(other: ReplicationTask): Boolean {
return other.id == id
}
@ -61,144 +33,41 @@ sealed interface IReplicationTask<S : IReplicationTask<S>> {
return ItemStack(item, count)
}
fun allocate(amount: Int = 1): S
fun finish(amount: Int = 1): S
fun serializeNBT(): CompoundTag
fun write(buff: FriendlyByteBuf) {
buff.writeUUID(id)
buff.writeBoolean(patternId.isPresent)
patternId.ifPresent(buff::writeUUID)
buff.writeItemType(item)
buff.writeInt(inProgress)
buff.writeInt(finished)
buff.writeInt(required)
}
}
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 read(buff: FriendlyByteBuf, mutable: Boolean): IReplicationTask<*>? {
val id = buff.readUUID()
val patternId: UUID? = if (buff.readBoolean()) buff.readUUID() else null
val item = buff.readItemType()
val inProgress = buff.readInt()
val finished = buff.readInt()
val required = buff.readInt()
item ?: return null
if (mutable) {
return MutableReplicationTask(id, Optional.ofNullable(patternId), item, inProgress, finished, required)
} else {
return ReplicationTask(id, Optional.ofNullable(patternId), item, inProgress, finished, required)
}
}
data class ReplicationTask(
override val id: UUID,
override val patternId: Optional<UUID>,
override val item: Item,
override val inProgress: Int,
override val finished: Int,
override val required: Int
) : IReplicationTask<ReplicationTask> {
override fun asMutable(): MutableReplicationTask {
return MutableReplicationTask(id, patternId, item, inProgress, finished, required)
}
override fun asImmutable(): ReplicationTask {
return this
}
override fun allocate(amount: Int): ReplicationTask {
fun allocate(amount: Int = 1): ReplicationTask {
return ReplicationTask(id, patternId, item, inProgress + amount, finished, required - amount)
}
override fun finish(amount: Int): ReplicationTask {
fun finish(amount: Int = 1): ReplicationTask {
return ReplicationTask(id, patternId, item, inProgress - amount, finished + amount, required)
}
override fun serializeNBT(): CompoundTag {
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()}") })
}
fun write(buff: FriendlyByteBuf) {
buff.writeBinaryJsonWithCodec(CODEC, this)
}
companion object {
fun deserializeNBT(nbt: Tag?): ReplicationTask? {
nbt ?: return null
return CODEC.decode(NbtOps.INSTANCE, nbt).result().map { it.first }.orElse(null)
return CODEC.decode(NbtOps.INSTANCE, nbt ?: return null).result().orElse(null)?.first
}
fun read(buff: FriendlyByteBuf): ReplicationTask? {
return read(buff, false) as ReplicationTask?
fun read(buff: FriendlyByteBuf): ReplicationTask {
return buff.readBinaryJsonWithCodec(CODEC)
}
val CODEC: Codec<ReplicationTask> by lazy {
RecordCodecBuilder.create {
codec(it).apply(it, ::ReplicationTask)
}
}
}
}
data class MutableReplicationTask(
override val id: UUID,
override val patternId: Optional<UUID>,
override val item: Item,
override var inProgress: Int,
override var finished: Int,
override var required: Int
) : IReplicationTask<MutableReplicationTask> {
override fun asMutable(): MutableReplicationTask {
return this
}
override fun asImmutable(): ReplicationTask {
return ReplicationTask(id, patternId, item, inProgress, finished, required)
}
override fun allocate(amount: Int): MutableReplicationTask {
inProgress += amount
finished -= amount
return this
}
override fun finish(amount: Int): MutableReplicationTask {
finished += amount
inProgress -= amount
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? {
nbt ?: return null
return CODEC.decode(NbtOps.INSTANCE, nbt).result().map { it.first }.orElse(null)
}
fun read(buff: FriendlyByteBuf): MutableReplicationTask? {
return read(buff, true) as MutableReplicationTask?
}
val CODEC: Codec<MutableReplicationTask> by lazy {
RecordCodecBuilder.create {
codec(it).apply(it, ::MutableReplicationTask)
it.group(
UUIDCodec.fieldOf("id").forGetter(ReplicationTask::id),
UUIDCodec.optionalFieldOf("patternId").forGetter(ReplicationTask::patternId),
ForgeRegistries.ITEMS.codec.fieldOf("item").forGetter(ReplicationTask::item),
Codec.intRange(0, Int.MAX_VALUE).fieldOf("inProgress").forGetter(ReplicationTask::inProgress),
Codec.intRange(0, Int.MAX_VALUE).fieldOf("finished").forGetter(ReplicationTask::finished),
Codec.intRange(0, Int.MAX_VALUE).fieldOf("required").forGetter(ReplicationTask::required),
).apply(it, ::ReplicationTask)
}
}
}

View File

@ -6,7 +6,7 @@ import net.minecraft.network.chat.Component
import net.minecraft.world.entity.player.Inventory
import net.minecraft.world.item.ItemStack
import ru.dbotthepony.mc.otm.capability.matter.PatternState
import ru.dbotthepony.mc.otm.capability.matter.IReplicationTask
import ru.dbotthepony.mc.otm.capability.matter.ReplicationTask
import ru.dbotthepony.mc.otm.client.ShiftPressedCond
import ru.dbotthepony.mc.otm.client.render.*
import ru.dbotthepony.mc.otm.client.screen.MatteryScreen
@ -251,7 +251,7 @@ class MatterPanelScreen(
return frame
}
private fun openTask(task: IReplicationTask<*>): FramePanel<MatterPanelScreen> {
private fun openTask(task: ReplicationTask): FramePanel<MatterPanelScreen> {
val frame = FramePanel.padded(this, null, 170f, 20f, TranslatableComponent("otm.gui.matter_panel.task"))
frame.behaveAsWindow()

View File

@ -294,6 +294,11 @@ fun FriendlyByteBuf.writeBinaryComponent(component: Component) {
writeBinaryJson(Component.Serializer.toJsonTree(component))
}
// обратный порядок аргументов у лямбда выражения
fun <T> FriendlyByteBuf.writeCollection(collection: Collection<T>, writer: (T, FriendlyByteBuf) -> Unit) {
writeCollection(collection) { a, b -> writer.invoke(b, a) }
}
fun <S : OutputStream, V> S.writeCollection(collection: Collection<V>, writer: S.(V) -> Unit) {
writeVarIntLE(collection.size)

View File

@ -7,8 +7,8 @@ interface IMatterGraphListener {
fun onPatternRemoved(state: PatternState) {}
fun onPatternUpdated(newState: PatternState, oldState: PatternState) {}
fun onMatterTaskCreated(task: IReplicationTask<*>) {}
fun <T : IReplicationTask<*>> onMatterTaskUpdated(newState: T, oldState: T) {}
fun onMatterTaskFinished(state: IReplicationTask<*>) {}
fun onMatterTaskRemoved(state: IReplicationTask<*>) {}
fun onMatterTaskCreated(task: ReplicationTask) {}
fun onMatterTaskUpdated(newState: ReplicationTask, oldState: ReplicationTask) {}
fun onMatterTaskFinished(state: ReplicationTask) {}
fun onMatterTaskRemoved(state: ReplicationTask) {}
}

View File

@ -193,11 +193,11 @@ class MatterGraph : Abstract6Graph<MatterNode, MatterGraph>(), IMatterGraphListe
return doInsertPattern(state, false, simulate)
}
val tasks: Stream<IReplicationTask<*>> get() {
val tasks: Stream<ReplicationTask> get() {
return nodes.stream().map { it.getTaskHandler()?.replicationTasks }.filterNotNull().flatMap { it }
}
val allTasks: Stream<IReplicationTask<*>> get() {
val allTasks: Stream<ReplicationTask> get() {
return nodes.stream().map { it.getTaskHandler()?.allReplicationTasks }.filterNotNull().flatMap { it }
}
@ -312,22 +312,22 @@ class MatterGraph : Abstract6Graph<MatterNode, MatterGraph>(), IMatterGraphListe
for (node in listeners) node.onPatternUpdated(newState, oldState)
}
override fun onMatterTaskCreated(task: IReplicationTask<*>) {
override fun onMatterTaskCreated(task: ReplicationTask) {
for (node in nodes) node.onMatterTaskCreated(task)
for (node in listeners) node.onMatterTaskCreated(task)
}
override fun <T : IReplicationTask<*>> onMatterTaskUpdated(newState: T, oldState: T) {
override fun onMatterTaskUpdated(newState: ReplicationTask, oldState: ReplicationTask) {
for (node in nodes) node.onMatterTaskUpdated(newState, oldState)
for (node in listeners) node.onMatterTaskUpdated(newState, oldState)
}
override fun onMatterTaskFinished(state: IReplicationTask<*>) {
override fun onMatterTaskFinished(state: ReplicationTask) {
for (node in nodes) node.onMatterTaskFinished(state)
for (node in listeners) node.onMatterTaskFinished(state)
}
override fun onMatterTaskRemoved(state: IReplicationTask<*>) {
override fun onMatterTaskRemoved(state: ReplicationTask) {
for (node in nodes) node.onMatterTaskRemoved(state)
for (node in listeners) node.onMatterTaskRemoved(state)
}

View File

@ -18,6 +18,7 @@ import ru.dbotthepony.mc.otm.core.util.BooleanValueCodec
import ru.dbotthepony.mc.otm.core.util.DecimalValueCodec
import ru.dbotthepony.mc.otm.core.util.ItemSorter
import ru.dbotthepony.mc.otm.core.util.codec
import ru.dbotthepony.mc.otm.core.util.writeCollection
import ru.dbotthepony.mc.otm.graph.matter.IMatterGraphListener
import ru.dbotthepony.mc.otm.graph.matter.MatterGraph
import ru.dbotthepony.mc.otm.menu.MatteryMenu
@ -52,11 +53,7 @@ class CancelTaskPacket(val id: UUID) : MatteryPacket {
class PatternsChangePacket(val isUpdate: Boolean, val patterns: Collection<PatternState>) : MatteryPacket {
override fun write(buff: FriendlyByteBuf) {
buff.writeBoolean(isUpdate)
buff.writeInt(patterns.size)
for (pattern in patterns) {
pattern.write(buff)
}
buff.writeCollection(patterns, PatternState::write)
}
override fun play(context: Supplier<NetworkEvent.Context>) {
@ -74,23 +71,15 @@ class PatternsChangePacket(val isUpdate: Boolean, val patterns: Collection<Patte
companion object {
fun read(buff: FriendlyByteBuf): PatternsChangePacket {
val isUpdate = buff.readBoolean()
val size = buff.readInt()
return PatternsChangePacket(isUpdate, ArrayList<PatternState>(size).also {
for (i in 0 until size) it.add(PatternState.read(buff) ?: throw NullPointerException("Unable to read PatternState from network at $i")) })
return PatternsChangePacket(buff.readBoolean(), buff.readCollection(::ArrayList, PatternState::read))
}
}
}
class TasksChangePacket(val isUpdate: Boolean, val tasks: Collection<IReplicationTask<*>>) : MatteryPacket {
class TasksChangePacket(val isUpdate: Boolean, val tasks: Collection<ReplicationTask>) : MatteryPacket {
override fun write(buff: FriendlyByteBuf) {
buff.writeBoolean(isUpdate)
buff.writeInt(tasks.size)
for (task in tasks) {
task.write(buff)
}
buff.writeCollection(tasks, ReplicationTask::write)
}
override fun play(context: Supplier<NetworkEvent.Context>) {
@ -108,11 +97,7 @@ class TasksChangePacket(val isUpdate: Boolean, val tasks: Collection<IReplicatio
companion object {
fun read(buff: FriendlyByteBuf): TasksChangePacket {
val isUpdate = buff.readBoolean()
val size = buff.readInt()
return TasksChangePacket(isUpdate, ArrayList<IReplicationTask<*>>(size).also {
for (i in 0 until size) it.add(ReplicationTask.read(buff) ?: throw NullPointerException("Unable to read Replication Task from network at $i")) })
return TasksChangePacket(buff.readBoolean(), buff.readCollection(::ArrayList, ReplicationTask::read))
}
}
}
@ -142,16 +127,16 @@ class ReplicationRequestPacket(val id: UUID, amount: Int) : MatteryPacket {
}
}
class MatterPanelMenu @JvmOverloads constructor(
class MatterPanelMenu(
p_38852_: Int,
inventory: Inventory,
tile: MatterPanelBlockEntity? = null
) : MatteryMenu(MMenus.MATTER_PANEL, p_38852_, inventory, tile), IMatterGraphListener {
fun taskUpdated(task: IReplicationTask<*>) {
fun taskUpdated(task: ReplicationTask) {
sendNetwork(TasksChangePacket(true, listOf(task)))
}
fun taskRemoved(task: IReplicationTask<*>) {
fun taskRemoved(task: ReplicationTask) {
sendNetwork(TasksChangePacket(false, listOf(task)))
}
@ -206,13 +191,13 @@ class MatterPanelMenu @JvmOverloads constructor(
val isAscendingGS = GetterSetter.of(::isAscending, changeIsAscending::input)
private val actualComparator = Comparator<PatternState> { o1, o2 -> sorting.comparator.compare(o1.item, o2.item) * (if (isAscending) 1 else -1) }
private val actualTaskComparator = Comparator<IReplicationTask<*>> { o1, o2 -> sorting.comparator.compare(o1.item, o2.item) * (if (isAscending) 1 else -1) }
private val actualTaskComparator = Comparator<ReplicationTask> { o1, o2 -> sorting.comparator.compare(o1.item, o2.item) * (if (isAscending) 1 else -1) }
private val patterns = ArrayList<PatternState>()
private val tasks = ArrayList<IReplicationTask<*>>()
private val tasks = ArrayList<ReplicationTask>()
val patternsFiltered = ArrayList<PatternState>()
val tasksFiltered = ArrayList<IReplicationTask<*>>()
val tasksFiltered = ArrayList<ReplicationTask>()
fun networkPatternsUpdated(patterns: Collection<PatternState>) {
for (pattern in patterns) {
@ -242,7 +227,7 @@ class MatterPanelMenu @JvmOverloads constructor(
}
}
fun networkTasksUpdated(tasks: Collection<IReplicationTask<*>>) {
fun networkTasksUpdated(tasks: Collection<ReplicationTask>) {
for (task in tasks) {
val index = this.tasks.indexOfFirst(task::matchId)
@ -264,7 +249,7 @@ class MatterPanelMenu @JvmOverloads constructor(
}
}
fun networkTasksRemoved(tasks: Collection<IReplicationTask<*>>) {
fun networkTasksRemoved(tasks: Collection<ReplicationTask>) {
for (task in tasks) {
val index = this.tasks.indexOfFirst(task::matchId)