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
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<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_) {
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
}

View File

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

View File

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

View File

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

View File

@ -73,6 +73,12 @@ class FieldSynchronizer {
private val fields = ArrayList<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(
value: Byte = 0,
getter: FieldGetter<Byte>? = 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<IField<*>>()
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 {
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 <K, V> getMapBacklog(map: Map<K, V>): LinkedList<Pair<Any?, (DataOutputStream) -> 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<LinkedList<Pair<Any?, (DataOutputStream) -> 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
}
}

View File

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