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 b135c3f94..30a9e1273 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/network/WorldNetworkChannel.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/network/WorldNetworkChannel.kt @@ -1,14 +1,20 @@ package ru.dbotthepony.mc.otm.network import it.unimi.dsi.fastutil.io.FastByteArrayInputStream +import it.unimi.dsi.fastutil.objects.Object2ObjectFunction +import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap import net.minecraft.core.BlockPos import net.minecraft.network.FriendlyByteBuf +import net.minecraft.world.level.Level import net.minecraftforge.network.NetworkDirection import net.minecraftforge.network.NetworkEvent import org.apache.logging.log4j.LogManager import ru.dbotthepony.mc.otm.android.feature.ItemEntityDataPacket import ru.dbotthepony.mc.otm.block.entity.MatteryBlockEntity import ru.dbotthepony.mc.otm.client.minecraft +import ru.dbotthepony.mc.otm.client.onceClient +import java.lang.ref.SoftReference +import java.util.WeakHashMap import java.util.function.Supplier class BlockEntitySyncPacket(val position: BlockPos, val buffer: ByteArray, val validBytes: Int) : MatteryPacket { @@ -17,23 +23,63 @@ class BlockEntitySyncPacket(val position: BlockPos, val buffer: ByteArray, val v buff.writeBytes(buffer, 0, validBytes) } + private fun execute() { + val level = minecraft.player?.level + + if (level == null) { + LOGGER.error("Received BlockEntitySyncPacket before we are in valid level.") + + onceClient { + execute() + } + + return + } + + val blockEntity = level.getBlockEntity(position) + + if (blockEntity == null) { + LOGGER.warn("Putting BlockEntitySyncPacket received for $position into backlog because there is literally no block entity there!") + LOGGER.warn("This is CERTAINLY a bug in one of optimizing mods you have or server has installed!") + LOGGER.warn("This can cause memory leak.") + + backlog.computeIfAbsent(level) { Object2ObjectOpenHashMap() } + .computeIfAbsent(position, Object2ObjectFunction { ArrayList() }) + .add(this) + + return + } else if (blockEntity !is MatteryBlockEntity) { + LOGGER.warn("Dropping BlockEntitySyncPacket received for $position, because there is $blockEntity which is not MatteryBlockEntity!") + backlog[level]?.remove(position) + return + } + + val packets = backlog[level]?.remove(position) + + try { + if (packets != null) { + for (packet in packets) { + blockEntity.synchronizer.read(FastByteArrayInputStream(packet.buffer, 0, packet.validBytes)) + } + } + + blockEntity.synchronizer.read(FastByteArrayInputStream(buffer, 0, validBytes)) + } catch(err: Throwable) { + LOGGER.error("Exception while reading synchronized BlockEntity data!\nPosition: $position\nBlock: ${level.getBlockState(position)}\nBlock entity: $blockEntity", err) + } + } + 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 - - try { - blockEntity.synchronizer.read(FastByteArrayInputStream(buffer, 0, validBytes)) - } catch(err: Throwable) { - LOGGER.error("Exception while reading synchronized BlockEntity data!\nPosition: $position\nBlock: ${level.getBlockState(position)}\nBlock entity: $blockEntity", err) - throw err - } + execute() } } companion object { + private val backlog = WeakHashMap>>() + fun read(buff: FriendlyByteBuf): BlockEntitySyncPacket { val position = buff.readBlockPos() val size = buff.readableBytes() @@ -47,7 +93,7 @@ class BlockEntitySyncPacket(val position: BlockPos, val buffer: ByteArray, val v } object WorldNetworkChannel : MatteryNetworkChannel( - version = "2", + version = "3", name = "world" ) { fun register() {