parent
1b0fc4bf2e
commit
8f6af0061a
@ -141,7 +141,6 @@ public final class OverdriveThatMatters {
|
||||
EVENT_BUS.addListener(EventPriority.NORMAL, AndroidResearchManager.INSTANCE::syncEvent);
|
||||
|
||||
EVENT_BUS.addListener(EventPriority.NORMAL, SynchronizedBlockEntity.Companion::onServerStopping);
|
||||
EVENT_BUS.addListener(EventPriority.NORMAL, SynchronizedBlockEntity.Companion::onServerStarting);
|
||||
EVENT_BUS.addListener(EventPriority.NORMAL, SynchronizedBlockEntity.Companion::onWatch);
|
||||
EVENT_BUS.addListener(EventPriority.NORMAL, SynchronizedBlockEntity.Companion::onForget);
|
||||
EVENT_BUS.addListener(EventPriority.NORMAL, SynchronizedBlockEntity.Companion::playerDisconnected);
|
||||
|
@ -301,8 +301,6 @@ class BatteryBankBlockEntity(p_155229_: BlockPos, p_155230_: BlockState) : Matte
|
||||
}
|
||||
|
||||
fun tick() {
|
||||
synchronizeToPlayers()
|
||||
|
||||
if (isBlockedByRedstone)
|
||||
return
|
||||
|
||||
|
@ -394,7 +394,6 @@ class EnergyCounterBlockEntity(p_155229_: BlockPos, p_155230_: BlockState) : Mat
|
||||
lastTick = history[historyTick]
|
||||
historyTick = (historyTick + 1) % history.size
|
||||
history[historyTick] = ImpreciseFraction.ZERO
|
||||
synchronizeToPlayers()
|
||||
}
|
||||
|
||||
fun clientTick() {
|
||||
|
@ -353,7 +353,6 @@ abstract class MatteryWorkerBlockEntity<JobType : MatteryWorkerBlockEntity.Job>(
|
||||
fun basicTicker() {
|
||||
batteryChargeLoop()
|
||||
workerLoop()
|
||||
synchronizeToPlayers()
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
@ -19,6 +19,8 @@ import net.minecraftforge.event.level.ChunkWatchEvent
|
||||
import net.minecraftforge.event.server.ServerStartingEvent
|
||||
import net.minecraftforge.event.server.ServerStoppingEvent
|
||||
import org.apache.logging.log4j.LogManager
|
||||
import ru.dbotthepony.mc.otm.NULLABLE_MINECRAFT_SERVER
|
||||
import ru.dbotthepony.mc.otm.SERVER_IS_LIVE
|
||||
import ru.dbotthepony.mc.otm.core.component1
|
||||
import ru.dbotthepony.mc.otm.core.component2
|
||||
import ru.dbotthepony.mc.otm.core.component3
|
||||
@ -26,12 +28,13 @@ import ru.dbotthepony.mc.otm.core.position
|
||||
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.onceServer
|
||||
import java.lang.ref.WeakReference
|
||||
import java.util.LinkedList
|
||||
import java.util.WeakHashMap
|
||||
|
||||
private data class Subscribers(
|
||||
val blockEntities: LinkedList<WeakReference<BlockEntity>> = LinkedList(),
|
||||
val blockEntities: LinkedList<WeakReference<SynchronizedBlockEntity>> = LinkedList(),
|
||||
val players: LinkedList<ServerPlayer> = LinkedList(),
|
||||
val level: WeakReference<Level>,
|
||||
val chunkPos: Long,
|
||||
@ -47,18 +50,28 @@ private data class Subscribers(
|
||||
}
|
||||
}
|
||||
|
||||
fun subscribe(blockEntity: BlockEntity) {
|
||||
for (value in blockEntities) {
|
||||
if (value.get() === blockEntity) {
|
||||
fun subscribe(blockEntity: SynchronizedBlockEntity) {
|
||||
val iterator = blockEntities.listIterator()
|
||||
|
||||
for (value in iterator) {
|
||||
val ref = value.get()
|
||||
|
||||
if (ref === blockEntity) {
|
||||
return
|
||||
} else if (ref == null) {
|
||||
iterator.remove()
|
||||
}
|
||||
}
|
||||
|
||||
blockEntities.add(WeakReference(blockEntity))
|
||||
changeset++
|
||||
|
||||
onceServer {
|
||||
blockEntity.synchronizeToPlayers()
|
||||
}
|
||||
}
|
||||
|
||||
fun unsubscribe(blockEntity: BlockEntity): Boolean {
|
||||
fun unsubscribe(blockEntity: SynchronizedBlockEntity): Boolean {
|
||||
val listIterator = blockEntities.listIterator()
|
||||
|
||||
for (value in listIterator) {
|
||||
@ -88,18 +101,39 @@ private data class Subscribers(
|
||||
}
|
||||
|
||||
abstract class SynchronizedBlockEntity(p_155228_: BlockEntityType<*>, p_155229_: BlockPos, p_155230_: BlockState) : BlockEntity(p_155228_, p_155229_, p_155230_) {
|
||||
val synchronizer = FieldSynchronizer()
|
||||
val synchronizer = FieldSynchronizer {
|
||||
if (!isRemoved && level?.isClientSide == false) {
|
||||
onceServer {
|
||||
synchronizeToPlayers(true)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
init {
|
||||
synchronizer.defaultEndpoint.markUnused()
|
||||
}
|
||||
|
||||
open val synchronizationRadius: Double = 32.0
|
||||
|
||||
override fun setLevel(p_155231_: Level) {
|
||||
super.setLevel(p_155231_)
|
||||
unsubscribe()
|
||||
_subCache = null
|
||||
|
||||
if (!p_155231_.isClientSide) {
|
||||
subscribe()
|
||||
}
|
||||
}
|
||||
|
||||
override fun setRemoved() {
|
||||
super.setRemoved()
|
||||
unsubscribe()
|
||||
}
|
||||
|
||||
override fun clearRemoved() {
|
||||
super.clearRemoved()
|
||||
|
||||
if (level?.isClientSide == false) {
|
||||
subscribe()
|
||||
}
|
||||
}
|
||||
|
||||
private var _subCache: Subscribers? = null
|
||||
@ -119,6 +153,8 @@ abstract class SynchronizedBlockEntity(p_155228_: BlockEntityType<*>, p_155229_:
|
||||
}
|
||||
|
||||
private fun subscribe(): Subscribers {
|
||||
val level = level
|
||||
check(level is ServerLevel) { "Invalid realm" }
|
||||
unsubscribe()
|
||||
val subs = playerMap.computeIfAbsent(level) { Long2ObjectAVLTreeMap() }.computeIfAbsent(ChunkPos(blockPos).toLong(), Long2ObjectFunction { Subscribers(level = WeakReference(level), chunkPos = ChunkPos(blockPos).toLong()) })
|
||||
subs.subscribe(this)
|
||||
@ -130,7 +166,12 @@ abstract class SynchronizedBlockEntity(p_155228_: BlockEntityType<*>, p_155229_:
|
||||
private var lastSubscriptionChangeset = -1
|
||||
|
||||
fun synchronizeToPlayers() {
|
||||
if (synchronizationRadius <= 0.0 || synchronizer.isEmpty) {
|
||||
synchronizeToPlayers(false)
|
||||
}
|
||||
|
||||
protected fun synchronizeToPlayers(callbedBySynchronizer: Boolean) {
|
||||
if (synchronizer.isEmpty || isRemoved) {
|
||||
if (callbedBySynchronizer) synchronizer.markClean()
|
||||
return
|
||||
}
|
||||
|
||||
@ -141,35 +182,21 @@ abstract class SynchronizedBlockEntity(p_155228_: BlockEntityType<*>, p_155229_:
|
||||
val subscription = subscription
|
||||
|
||||
if (subscription.players.isEmpty() || lastSubscriptionChangeset == subscription.changeset && !synchronizer.hasChanges) {
|
||||
if (callbedBySynchronizer) synchronizer.markClean()
|
||||
return
|
||||
}
|
||||
|
||||
lastSubscriptionChangeset = subscription.changeset
|
||||
|
||||
val (xi, yi, zi) = blockPos
|
||||
val bx = xi + 0.5
|
||||
val by = yi + 0.5
|
||||
val bz = zi + 0.5
|
||||
|
||||
val double = synchronizationRadius * synchronizationRadius
|
||||
|
||||
subscription.players.stream().filter {
|
||||
if (!it.isAlive) {
|
||||
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()
|
||||
for (player in subscription.players) {
|
||||
val payload = synchronizer.computeEndpointFor(player).collectNetworkPayload()
|
||||
|
||||
if (payload != null) {
|
||||
WorldNetworkChannel.send(it, BlockEntitySyncPacket(blockPos, payload.array, payload.length))
|
||||
WorldNetworkChannel.send(player, BlockEntitySyncPacket(blockPos, payload.array, payload.length))
|
||||
}
|
||||
}
|
||||
|
||||
synchronizer.markClean()
|
||||
}
|
||||
|
||||
/**
|
||||
@ -187,16 +214,12 @@ abstract class SynchronizedBlockEntity(p_155228_: BlockEntityType<*>, p_155229_:
|
||||
* [net.minecraft.server.level.ChunkMap.getPlayers], which is not something we want
|
||||
*/
|
||||
companion object {
|
||||
private val playerMap = WeakHashMap<Level, Long2ObjectAVLTreeMap<Subscribers>>()
|
||||
private val playerMap = WeakHashMap<ServerLevel, Long2ObjectAVLTreeMap<Subscribers>>()
|
||||
|
||||
fun onServerStopping(event: ServerStoppingEvent) {
|
||||
playerMap.clear()
|
||||
}
|
||||
|
||||
fun onServerStarting(event: ServerStartingEvent) {
|
||||
playerMap.clear()
|
||||
}
|
||||
|
||||
fun onWatch(event: ChunkWatchEvent.Watch) {
|
||||
playerMap.computeIfAbsent(event.level) { Long2ObjectAVLTreeMap() }.computeIfAbsent(event.pos.toLong(), Long2ObjectFunction { Subscribers(level = WeakReference(event.level), chunkPos = event.pos.toLong()) }).let {
|
||||
val (blocks, players) = it
|
||||
@ -204,6 +227,22 @@ abstract class SynchronizedBlockEntity(p_155228_: BlockEntityType<*>, p_155229_:
|
||||
if (event.player !in players) {
|
||||
players.add(event.player)
|
||||
it.changeset++
|
||||
|
||||
onceServer(20) {
|
||||
if (!event.player.hasDisconnected()) {
|
||||
val iterator = blocks.listIterator()
|
||||
|
||||
for (block in iterator) {
|
||||
val deref = block.get()
|
||||
|
||||
if (deref == null) {
|
||||
iterator.remove()
|
||||
} else {
|
||||
deref.synchronizeToPlayers(false)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -216,6 +255,18 @@ abstract class SynchronizedBlockEntity(p_155228_: BlockEntityType<*>, p_155229_:
|
||||
playerMap.get(event.level)?.remove(event.pos.toLong())
|
||||
} else {
|
||||
subs.changeset++
|
||||
|
||||
val iterator = subs.blockEntities.listIterator()
|
||||
|
||||
for (block in iterator) {
|
||||
val deref = block.get()
|
||||
|
||||
if (deref == null) {
|
||||
iterator.remove()
|
||||
} else {
|
||||
deref.synchronizer.removeEndpointFor(event.player)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -167,8 +167,6 @@ class BlackHoleBlockEntity(p_155229_: BlockPos, p_155230_: BlockState) : Synchro
|
||||
nbt["spin_direction"] = spinDirection
|
||||
}
|
||||
|
||||
override val synchronizationRadius: Double = 256.0
|
||||
|
||||
override fun load(tag: CompoundTag) {
|
||||
super.load(tag)
|
||||
mass = tag.mapIf("mass", ImpreciseFraction::deserializeNBT) ?: BASELINE_MASS
|
||||
@ -223,8 +221,6 @@ class BlackHoleBlockEntity(p_155229_: BlockPos, p_155230_: BlockState) : Synchro
|
||||
}
|
||||
|
||||
fun tick() {
|
||||
synchronizeToPlayers()
|
||||
|
||||
sleepTicks--
|
||||
if (sleepTicks > 0) return
|
||||
val level = level as? ServerLevel ?: return
|
||||
|
@ -27,14 +27,6 @@ class MatterCapacitorBankBlock : RotatableMatteryBlock(), EntityBlock {
|
||||
return MatterCapacitorBankBlockEntity(blockPos, blockState)
|
||||
}
|
||||
|
||||
override fun <T : BlockEntity> getTicker(
|
||||
p_153212_: Level,
|
||||
p_153213_: BlockState,
|
||||
p_153214_: BlockEntityType<T>
|
||||
): BlockEntityTicker<T>? {
|
||||
return SynchronizedBlockEntity::synchronizeToPlayers.blockServerTicker(p_153212_, MBlockEntities.MATTER_CAPACITOR_BANK, p_153214_)
|
||||
}
|
||||
|
||||
override fun getStateForPlacement(context: BlockPlaceContext): BlockState? {
|
||||
var state = super.getStateForPlacement(context) ?: return null
|
||||
|
||||
|
@ -69,7 +69,10 @@ enum class MapAction {
|
||||
CLEAR, ADD, REMOVE
|
||||
}
|
||||
|
||||
class FieldSynchronizer {
|
||||
class FieldSynchronizer(private val callback: Runnable, private val alwaysCallCallback: Boolean) {
|
||||
constructor() : this(Runnable {}, false)
|
||||
constructor(callback: Runnable) : this(callback, false)
|
||||
|
||||
private val fields = ArrayList<IField<*>>()
|
||||
private val observers = LinkedList<IField<*>>()
|
||||
|
||||
@ -77,7 +80,23 @@ class FieldSynchronizer {
|
||||
val isNotEmpty: Boolean get() = fields.isNotEmpty()
|
||||
|
||||
var hasChanges: Boolean = false
|
||||
private set
|
||||
private set(value) {
|
||||
if (value != field) {
|
||||
field = value
|
||||
|
||||
if (value && !alwaysCallCallback) {
|
||||
callback.run()
|
||||
}
|
||||
}
|
||||
|
||||
if (alwaysCallCallback && value) {
|
||||
callback.run()
|
||||
}
|
||||
}
|
||||
|
||||
fun markClean() {
|
||||
hasChanges = false
|
||||
}
|
||||
|
||||
fun byte(
|
||||
value: Byte = 0,
|
||||
@ -345,6 +364,10 @@ class FieldSynchronizer {
|
||||
return boundEndpoints.computeIfAbsent(obj) { Endpoint() }
|
||||
}
|
||||
|
||||
fun removeEndpointFor(obj: Any): Endpoint? {
|
||||
return boundEndpoints.remove(obj)
|
||||
}
|
||||
|
||||
fun endpointFor(obj: Any): Endpoint? {
|
||||
return boundEndpoints[obj]
|
||||
}
|
||||
@ -820,8 +843,11 @@ class FieldSynchronizer {
|
||||
* [defaultEndpoint]#collectNetworkPayload
|
||||
*/
|
||||
fun collectNetworkPayload(): FastByteArrayOutputStream? {
|
||||
check(!defaultEndpoint.unused) { "Default endpoint is not used" }
|
||||
observe()
|
||||
return defaultEndpoint.collectNetworkPayload()
|
||||
val values = defaultEndpoint.collectNetworkPayload()
|
||||
markClean()
|
||||
return values
|
||||
}
|
||||
|
||||
fun applyNetworkPayload(stream: DataInputStream): Int {
|
||||
|
Loading…
Reference in New Issue
Block a user