Move energy counter synchronization to field synchronizer

This commit is contained in:
DBotThePony 2022-09-29 20:18:03 +07:00
parent 7ae82eebfc
commit 284e4d404a
Signed by: DBot
GPG Key ID: DCC23B5715498507
6 changed files with 162 additions and 65 deletions

View File

@ -1,12 +1,10 @@
package ru.dbotthepony.mc.otm.block.entity package ru.dbotthepony.mc.otm.block.entity
import net.minecraft.client.Minecraft
import net.minecraft.core.BlockPos import net.minecraft.core.BlockPos
import net.minecraft.core.Direction import net.minecraft.core.Direction
import net.minecraft.nbt.CompoundTag import net.minecraft.nbt.CompoundTag
import net.minecraft.nbt.IntTag import net.minecraft.nbt.IntTag
import net.minecraft.nbt.ListTag import net.minecraft.nbt.ListTag
import net.minecraft.network.FriendlyByteBuf
import net.minecraft.network.chat.Component import net.minecraft.network.chat.Component
import net.minecraft.server.level.ServerLevel import net.minecraft.server.level.ServerLevel
import net.minecraft.world.entity.player.Inventory 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.capabilities.ForgeCapabilities
import net.minecraftforge.common.util.LazyOptional import net.minecraftforge.common.util.LazyOptional
import net.minecraftforge.energy.IEnergyStorage import net.minecraftforge.energy.IEnergyStorage
import net.minecraftforge.network.NetworkEvent
import net.minecraftforge.network.PacketDistributor import net.minecraftforge.network.PacketDistributor
import ru.dbotthepony.mc.otm.* import ru.dbotthepony.mc.otm.*
import ru.dbotthepony.mc.otm.block.EnergyCounterBlock 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.compat.mekanism.Mattery2MekanismEnergyWrapper
import ru.dbotthepony.mc.otm.core.* import ru.dbotthepony.mc.otm.core.*
import ru.dbotthepony.mc.otm.menu.EnergyCounterMenu 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 ru.dbotthepony.mc.otm.registry.MBlockEntities
import java.lang.ref.WeakReference import java.lang.ref.WeakReference
import java.util.* 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<NetworkEvent.Context>) {
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_) { 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 } private val history = Array(10 * 20) { ImpreciseFraction.ZERO }
internal var historyTick = 0 internal var historyTick = 0
fun size() = history.size var lastTick by synchronizer.fraction()
operator fun get(i: Int) = history[i]
internal operator fun set(i: Int, value: ImpreciseFraction) {
history[i] = value
}
var lastTick = ImpreciseFraction.ZERO
internal set internal set
var ioLimit: ImpreciseFraction? = null var ioLimit: ImpreciseFraction? = null
@ -438,24 +390,16 @@ class EnergyCounterBlockEntity(p_155229_: BlockPos, p_155230_: BlockState) : Mat
return super.getCapability(cap, side) 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() { fun tick() {
lastTick = history[historyTick] lastTick = history[historyTick]
val index = historyTick
val accumulated = history[historyTick]
historyTick = (historyTick + 1) % history.size historyTick = (historyTick + 1) % history.size
history[historyTick] = ImpreciseFraction.ZERO history[historyTick] = ImpreciseFraction.ZERO
synchronizeToPlayers()
WorldNetworkChannel.send(PacketDistributor.NEAR.with(this::distributor), EnergyCounterPacket(blockPos, lastTick, passed, index, accumulated))
} }
fun clientTick() { fun clientTick() {
historyTick = (historyTick + 1) % history.size
history[historyTick] = lastTick
passed += lastTick passed += lastTick
} }

View File

@ -23,8 +23,15 @@ import net.minecraft.world.level.Level
import net.minecraftforge.common.capabilities.Capability import net.minecraftforge.common.capabilities.Capability
import ru.dbotthepony.mc.otm.core.TextComponent import ru.dbotthepony.mc.otm.core.TextComponent
import ru.dbotthepony.mc.otm.core.TranslatableComponent 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.ifHas
import ru.dbotthepony.mc.otm.core.position
import ru.dbotthepony.mc.otm.core.set 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 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 { 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 protected open val defaultDisplayName: Component
get() = level?.getBlockState(blockPos)?.block?.name ?: TextComponent("null at $blockPos") get() = level?.getBlockState(blockPos)?.block?.name ?: TextComponent("null at $blockPos")

View File

@ -353,6 +353,7 @@ abstract class MatteryWorkerBlockEntity<JobType : MatteryWorkerBlockEntity.Job>(
fun basicTicker() { fun basicTicker() {
batteryChargeLoop() batteryChargeLoop()
workerLoop() workerLoop()
synchronizeToPlayers()
} }
companion object { companion object {

View File

@ -70,6 +70,10 @@ operator fun Vector3f.component1() = x()
operator fun Vector3f.component2() = y() operator fun Vector3f.component2() = y()
operator fun Vector3f.component3() = z() 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.plus(direction: Vector): Vector = this.add(direction)
operator fun Vector.minus(direction: Vector): Vector = this.subtract(direction) operator fun Vector.minus(direction: Vector): Vector = this.subtract(direction)
operator fun Vector.unaryMinus(): Vector = Vector(-x, -y, -z) operator fun Vector.unaryMinus(): Vector = Vector(-x, -y, -z)

View File

@ -73,6 +73,12 @@ class FieldSynchronizer {
private val fields = ArrayList<IField<*>>() private val fields = ArrayList<IField<*>>()
private val observers = LinkedList<IField<*>>() private val observers = LinkedList<IField<*>>()
val isEmpty: Boolean get() = fields.isEmpty()
val isNotEmpty: Boolean get() = fields.isNotEmpty()
var hasChanges: Boolean = false
private set
fun byte( fun byte(
value: Byte = 0, value: Byte = 0,
getter: FieldGetter<Byte>? = null, getter: FieldGetter<Byte>? = null,
@ -222,6 +228,8 @@ class FieldSynchronizer {
private var lastEndpointsCleanup = System.nanoTime() private var lastEndpointsCleanup = System.nanoTime()
private fun notifyEndpoints(dirtyField: IField<*>) { private fun notifyEndpoints(dirtyField: IField<*>) {
hasChanges = true
forEachEndpoint { forEachEndpoint {
it.addDirtyField(dirtyField) it.addDirtyField(dirtyField)
} }
@ -267,6 +275,25 @@ class FieldSynchronizer {
private val dirtyFields = LinkedList<IField<*>>() private val dirtyFields = LinkedList<IField<*>>()
private val mapBacklogs = Reference2ObjectOpenHashMap<Map<*, *>, LinkedList<Pair<Any?, (DataOutputStream) -> Unit>>>() private val mapBacklogs = Reference2ObjectOpenHashMap<Map<*, *>, LinkedList<Pair<Any?, (DataOutputStream) -> 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 { init {
for (field in fields) { for (field in fields) {
field.markDirty(this) field.markDirty(this)
@ -274,19 +301,27 @@ class FieldSynchronizer {
} }
internal fun addDirtyField(field: IField<*>) { internal fun addDirtyField(field: IField<*>) {
if (unused) {
return
}
if (field !in dirtyFields) { if (field !in dirtyFields) {
dirtyFields.addLast(field) dirtyFields.addLast(field)
} }
} }
internal fun <K, V> getMapBacklog(map: Map<K, V>): LinkedList<Pair<Any?, (DataOutputStream) -> Unit>> { internal fun <K, V> getMapBacklog(map: Map<K, V>): LinkedList<Pair<Any?, (DataOutputStream) -> Unit>> {
if (unused) {
return LinkedList()
}
return mapBacklogs.computeIfAbsent(map, Reference2ObjectFunction { return mapBacklogs.computeIfAbsent(map, Reference2ObjectFunction {
LinkedList() LinkedList()
}) })
} }
fun collectNetworkPayload(): FastByteArrayOutputStream? { fun collectNetworkPayload(): FastByteArrayOutputStream? {
if (dirtyFields.isEmpty()) { if (unused || dirtyFields.isEmpty()) {
return null return null
} }
@ -566,6 +601,15 @@ class FieldSynchronizer {
return return
} }
if (endpoints.isEmpty()) {
val observingBackingMap = observingBackingMap ?: return
for ((key, value) in backingMap)
observingBackingMap[key] = valueCodec.copy(value)
return
}
isDirty = true isDirty = true
val backlogs = LinkedList<LinkedList<Pair<Any?, (DataOutputStream) -> Unit>>>() val backlogs = LinkedList<LinkedList<Pair<Any?, (DataOutputStream) -> Unit>>>()
@ -621,6 +665,10 @@ class FieldSynchronizer {
return return
} }
if (endpoints.isEmpty()) {
return
}
observingBackingMap?.clear() observingBackingMap?.clear()
forEachEndpoint { forEachEndpoint {
@ -634,6 +682,8 @@ class FieldSynchronizer {
notifyEndpoints(this@Map) notifyEndpoints(this@Map)
isDirty = true isDirty = true
} }
hasChanges = true
} }
override fun onValueAdded(key: K, value: V) { override fun onValueAdded(key: K, value: V) {
@ -641,6 +691,10 @@ class FieldSynchronizer {
return return
} }
if (endpoints.isEmpty()) {
return
}
val valueCopy = valueCodec.copy(value) val valueCopy = valueCodec.copy(value)
pushBacklog(key) { pushBacklog(key) {
@ -655,6 +709,8 @@ class FieldSynchronizer {
notifyEndpoints(this@Map) notifyEndpoints(this@Map)
isDirty = true isDirty = true
} }
hasChanges = true
} }
override fun onValueRemoved(key: K, value: V) { override fun onValueRemoved(key: K, value: V) {
@ -662,6 +718,10 @@ class FieldSynchronizer {
return return
} }
if (endpoints.isEmpty()) {
return
}
val keyCopy = keyCodec.copy(key) val keyCopy = keyCodec.copy(key)
pushBacklog(key) { pushBacklog(key) {
@ -675,6 +735,8 @@ class FieldSynchronizer {
notifyEndpoints(this@Map) notifyEndpoints(this@Map)
isDirty = true isDirty = true
} }
hasChanges = true
} }
} }

View File

@ -1,15 +1,48 @@
package ru.dbotthepony.mc.otm.network 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.NetworkDirection
import net.minecraftforge.network.NetworkEvent
import ru.dbotthepony.mc.otm.android.feature.ItemEntityDataPacket 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<NetworkEvent.Context>) {
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( object WorldNetworkChannel : MatteryNetworkChannel(
version = "1", version = "2",
name = "world" name = "world"
) { ) {
fun register() { 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) add(ItemEntityDataPacket::class.java, ItemEntityDataPacket.Companion::read, NetworkDirection.PLAY_TO_CLIENT)
} }
} }