From 284e4d404ab94fb3d8b1160329275788f08e972d Mon Sep 17 00:00:00 2001 From: DBotThePony Date: Thu, 29 Sep 2022 20:18:03 +0700 Subject: [PATCH] Move energy counter synchronization to field synchronizer --- .../block/entity/EnergyCounterBlockEntity.kt | 66 ++----------------- .../mc/otm/block/entity/MatteryBlockEntity.kt | 53 +++++++++++++++ .../block/entity/MatteryWorkerBlockEntity.kt | 1 + .../ru/dbotthepony/mc/otm/core/EuclidMath.kt | 4 ++ .../mc/otm/network/FieldSynchronizer.kt | 64 +++++++++++++++++- .../mc/otm/network/WorldNetworkChannel.kt | 39 ++++++++++- 6 files changed, 162 insertions(+), 65 deletions(-) diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/EnergyCounterBlockEntity.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/EnergyCounterBlockEntity.kt index e324025cb..5ef596f5f 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/EnergyCounterBlockEntity.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/EnergyCounterBlockEntity.kt @@ -1,12 +1,10 @@ package ru.dbotthepony.mc.otm.block.entity -import net.minecraft.client.Minecraft import net.minecraft.core.BlockPos import net.minecraft.core.Direction import net.minecraft.nbt.CompoundTag import net.minecraft.nbt.IntTag import net.minecraft.nbt.ListTag -import net.minecraft.network.FriendlyByteBuf import net.minecraft.network.chat.Component import net.minecraft.server.level.ServerLevel import net.minecraft.world.entity.player.Inventory @@ -18,7 +16,6 @@ import net.minecraftforge.common.capabilities.Capability import net.minecraftforge.common.capabilities.ForgeCapabilities import net.minecraftforge.common.util.LazyOptional import net.minecraftforge.energy.IEnergyStorage -import net.minecraftforge.network.NetworkEvent import net.minecraftforge.network.PacketDistributor import ru.dbotthepony.mc.otm.* import ru.dbotthepony.mc.otm.block.EnergyCounterBlock @@ -26,62 +23,17 @@ import ru.dbotthepony.mc.otm.capability.* import ru.dbotthepony.mc.otm.compat.mekanism.Mattery2MekanismEnergyWrapper import ru.dbotthepony.mc.otm.core.* import ru.dbotthepony.mc.otm.menu.EnergyCounterMenu -import ru.dbotthepony.mc.otm.network.MatteryPacket -import ru.dbotthepony.mc.otm.network.WorldNetworkChannel import ru.dbotthepony.mc.otm.registry.MBlockEntities import java.lang.ref.WeakReference import java.util.* -import java.util.function.Supplier - -data class EnergyCounterPacket(val pos: BlockPos, val thisTick: ImpreciseFraction, val total: ImpreciseFraction, val index: Int, val value: ImpreciseFraction) : MatteryPacket { - override fun write(buff: FriendlyByteBuf) { - buff.writeBlockPos(pos) - thisTick.write(buff) - total.write(buff) - buff.writeInt(index) - value.write(buff) - } - - override fun play(context: Supplier) { - context.get().packetHandled = true - - context.get().enqueueWork { - val ply = Minecraft.getInstance().player ?: return@enqueueWork - val level = ply.level ?: return@enqueueWork - val tile = level.getBlockEntity(pos) as? EnergyCounterBlockEntity ?: return@enqueueWork - - tile.lastTick = thisTick - tile.passed = total - tile[index] = value - tile.historyTick = index - } - } - - companion object { - fun read(buff: FriendlyByteBuf): EnergyCounterPacket { - val pos = buff.readBlockPos() - val thisTick = ImpreciseFraction.read(buff) - val total = ImpreciseFraction.read(buff) - val index = buff.readInt() - val value = ImpreciseFraction.read(buff) - return EnergyCounterPacket(pos, thisTick, total, index, value) - } - } -} class EnergyCounterBlockEntity(p_155229_: BlockPos, p_155230_: BlockState) : MatteryBlockEntity(MBlockEntities.ENERGY_COUNTER, p_155229_, p_155230_) { - var passed = ImpreciseFraction.ZERO + var passed by synchronizer.fraction() private val history = Array(10 * 20) { ImpreciseFraction.ZERO } internal var historyTick = 0 - fun size() = history.size - operator fun get(i: Int) = history[i] - internal operator fun set(i: Int, value: ImpreciseFraction) { - history[i] = value - } - - var lastTick = ImpreciseFraction.ZERO + var lastTick by synchronizer.fraction() internal set var ioLimit: ImpreciseFraction? = null @@ -438,24 +390,16 @@ class EnergyCounterBlockEntity(p_155229_: BlockPos, p_155230_: BlockState) : Mat return super.getCapability(cap, side) } - private fun distributor(): PacketDistributor.TargetPoint { - val x = blockPos.x.toDouble() - val y = blockPos.y.toDouble() - val z = blockPos.z.toDouble() - return PacketDistributor.TargetPoint(x, y, z, 32.0, level!!.dimension()) - } - fun tick() { lastTick = history[historyTick] - val index = historyTick - val accumulated = history[historyTick] historyTick = (historyTick + 1) % history.size history[historyTick] = ImpreciseFraction.ZERO - - WorldNetworkChannel.send(PacketDistributor.NEAR.with(this::distributor), EnergyCounterPacket(blockPos, lastTick, passed, index, accumulated)) + synchronizeToPlayers() } fun clientTick() { + historyTick = (historyTick + 1) % history.size + history[historyTick] = lastTick passed += lastTick } diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/MatteryBlockEntity.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/MatteryBlockEntity.kt index bc5267261..b19b59f3d 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/MatteryBlockEntity.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/MatteryBlockEntity.kt @@ -23,8 +23,15 @@ import net.minecraft.world.level.Level import net.minecraftforge.common.capabilities.Capability import ru.dbotthepony.mc.otm.core.TextComponent import ru.dbotthepony.mc.otm.core.TranslatableComponent +import ru.dbotthepony.mc.otm.core.component1 +import ru.dbotthepony.mc.otm.core.component2 +import ru.dbotthepony.mc.otm.core.component3 import ru.dbotthepony.mc.otm.core.ifHas +import ru.dbotthepony.mc.otm.core.position import ru.dbotthepony.mc.otm.core.set +import ru.dbotthepony.mc.otm.network.BlockEntitySyncPacket +import ru.dbotthepony.mc.otm.network.FieldSynchronizer +import ru.dbotthepony.mc.otm.network.WorldNetworkChannel import ru.dbotthepony.mc.otm.oncePre abstract class MatteryBlockEntity(p_155228_: BlockEntityType<*>, p_155229_: BlockPos, p_155230_: BlockState) : BlockEntity(p_155228_, p_155229_, p_155230_), MenuProvider { @@ -67,6 +74,52 @@ abstract class MatteryBlockEntity(p_155228_: BlockEntityType<*>, p_155229_: Bloc } } + val synchronizer = FieldSynchronizer() + + init { + synchronizer.defaultEndpoint.markUnused() + } + + open val synchronizationRadius: Double = 32.0 + + // TODO: Optimize, if required + fun synchronizeToPlayers() { + if (synchronizationRadius <= 0.0 || synchronizer.isEmpty) { + return + } + + val server = level?.server ?: return + + if (server.playerCount <= 0) { + return + } + + val (xi, yi, zi) = blockPos + val bx = xi + 0.5 + val by = yi + 0.5 + val bz = zi + 0.5 + + val double = synchronizationRadius * synchronizationRadius + + server.playerList.players.stream().filter { + if (!it.isAlive || it.level != level) { + return@filter false + } + + val (x, y, z) = it.position + + (x - bx) * (x - bx) + + (y - by) * (y - by) + + (z - bz) * (z - bz) <= double + }.forEach { + val payload = synchronizer.computeEndpointFor(it).collectNetworkPayload() + + if (payload != null) { + WorldNetworkChannel.send(it, BlockEntitySyncPacket(blockPos, payload.array, payload.length)) + } + } + } + protected open val defaultDisplayName: Component get() = level?.getBlockState(blockPos)?.block?.name ?: TextComponent("null at $blockPos") diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/MatteryWorkerBlockEntity.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/MatteryWorkerBlockEntity.kt index e08e04396..a27fa6efe 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/MatteryWorkerBlockEntity.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/MatteryWorkerBlockEntity.kt @@ -353,6 +353,7 @@ abstract class MatteryWorkerBlockEntity( fun basicTicker() { batteryChargeLoop() workerLoop() + synchronizeToPlayers() } companion object { diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/core/EuclidMath.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/core/EuclidMath.kt index afe7516a5..ff76ed1cd 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/core/EuclidMath.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/core/EuclidMath.kt @@ -70,6 +70,10 @@ operator fun Vector3f.component1() = x() operator fun Vector3f.component2() = y() operator fun Vector3f.component3() = z() +operator fun Vec3i.component1() = x +operator fun Vec3i.component2() = y +operator fun Vec3i.component3() = z + operator fun Vector.plus(direction: Vector): Vector = this.add(direction) operator fun Vector.minus(direction: Vector): Vector = this.subtract(direction) operator fun Vector.unaryMinus(): Vector = Vector(-x, -y, -z) diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/network/FieldSynchronizer.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/network/FieldSynchronizer.kt index d4dcc6cf9..3ba010266 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/network/FieldSynchronizer.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/network/FieldSynchronizer.kt @@ -73,6 +73,12 @@ class FieldSynchronizer { private val fields = ArrayList>() private val observers = LinkedList>() + val isEmpty: Boolean get() = fields.isEmpty() + val isNotEmpty: Boolean get() = fields.isNotEmpty() + + var hasChanges: Boolean = false + private set + fun byte( value: Byte = 0, getter: FieldGetter? = null, @@ -222,6 +228,8 @@ class FieldSynchronizer { private var lastEndpointsCleanup = System.nanoTime() private fun notifyEndpoints(dirtyField: IField<*>) { + hasChanges = true + forEachEndpoint { it.addDirtyField(dirtyField) } @@ -267,6 +275,25 @@ class FieldSynchronizer { private val dirtyFields = LinkedList>() private val mapBacklogs = Reference2ObjectOpenHashMap, LinkedList Unit>>>() + var unused: Boolean = false + private set + + fun markUnused() { + require(this === defaultEndpoint) { "This is not a default endpoint" } + if (unused) return + unused = true + mapBacklogs.clear() + dirtyFields.clear() + + val iterator = endpoints.listIterator() + + for (value in iterator) { + if (value.get() === this) { + iterator.remove() + } + } + } + init { for (field in fields) { field.markDirty(this) @@ -274,19 +301,27 @@ class FieldSynchronizer { } internal fun addDirtyField(field: IField<*>) { + if (unused) { + return + } + if (field !in dirtyFields) { dirtyFields.addLast(field) } } internal fun getMapBacklog(map: Map): LinkedList Unit>> { + if (unused) { + return LinkedList() + } + return mapBacklogs.computeIfAbsent(map, Reference2ObjectFunction { LinkedList() }) } fun collectNetworkPayload(): FastByteArrayOutputStream? { - if (dirtyFields.isEmpty()) { + if (unused || dirtyFields.isEmpty()) { return null } @@ -566,6 +601,15 @@ class FieldSynchronizer { return } + if (endpoints.isEmpty()) { + val observingBackingMap = observingBackingMap ?: return + + for ((key, value) in backingMap) + observingBackingMap[key] = valueCodec.copy(value) + + return + } + isDirty = true val backlogs = LinkedList Unit>>>() @@ -621,6 +665,10 @@ class FieldSynchronizer { return } + if (endpoints.isEmpty()) { + return + } + observingBackingMap?.clear() forEachEndpoint { @@ -634,6 +682,8 @@ class FieldSynchronizer { notifyEndpoints(this@Map) isDirty = true } + + hasChanges = true } override fun onValueAdded(key: K, value: V) { @@ -641,6 +691,10 @@ class FieldSynchronizer { return } + if (endpoints.isEmpty()) { + return + } + val valueCopy = valueCodec.copy(value) pushBacklog(key) { @@ -655,6 +709,8 @@ class FieldSynchronizer { notifyEndpoints(this@Map) isDirty = true } + + hasChanges = true } override fun onValueRemoved(key: K, value: V) { @@ -662,6 +718,10 @@ class FieldSynchronizer { return } + if (endpoints.isEmpty()) { + return + } + val keyCopy = keyCodec.copy(key) pushBacklog(key) { @@ -675,6 +735,8 @@ class FieldSynchronizer { notifyEndpoints(this@Map) isDirty = true } + + hasChanges = true } } diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/network/WorldNetworkChannel.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/network/WorldNetworkChannel.kt index d7fb296c9..eefe58861 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/network/WorldNetworkChannel.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/network/WorldNetworkChannel.kt @@ -1,15 +1,48 @@ package ru.dbotthepony.mc.otm.network +import it.unimi.dsi.fastutil.io.FastByteArrayInputStream +import net.minecraft.core.BlockPos +import net.minecraft.network.FriendlyByteBuf import net.minecraftforge.network.NetworkDirection +import net.minecraftforge.network.NetworkEvent import ru.dbotthepony.mc.otm.android.feature.ItemEntityDataPacket -import ru.dbotthepony.mc.otm.block.entity.EnergyCounterPacket +import ru.dbotthepony.mc.otm.block.entity.MatteryBlockEntity +import ru.dbotthepony.mc.otm.client.minecraft +import java.util.function.Supplier + +class BlockEntitySyncPacket(val position: BlockPos, val buffer: ByteArray, val validBytes: Int) : MatteryPacket { + override fun write(buff: FriendlyByteBuf) { + buff.writeBlockPos(position) + buff.writeBytes(buffer, 0, validBytes) + } + + override fun play(context: Supplier) { + context.packetHandled = true + + context.enqueueWork { + val level = minecraft.player?.level ?: return@enqueueWork + val blockEntity = level.getBlockEntity(position) as? MatteryBlockEntity ?: return@enqueueWork + blockEntity.synchronizer.applyNetworkPayload(FastByteArrayInputStream(buffer, 0, validBytes)) + } + } + + companion object { + fun read(buff: FriendlyByteBuf): BlockEntitySyncPacket { + val position = buff.readBlockPos() + val size = buff.readableBytes() + val array = ByteArray(size) + buff.readBytes(array) + return BlockEntitySyncPacket(position, array, size) + } + } +} object WorldNetworkChannel : MatteryNetworkChannel( - version = "1", + version = "2", name = "world" ) { fun register() { - add(EnergyCounterPacket::class.java, EnergyCounterPacket.Companion::read, NetworkDirection.PLAY_TO_CLIENT) + add(BlockEntitySyncPacket::class.java, BlockEntitySyncPacket.Companion::read, NetworkDirection.PLAY_TO_CLIENT) add(ItemEntityDataPacket::class.java, ItemEntityDataPacket.Companion::read, NetworkDirection.PLAY_TO_CLIENT) } }