/* * Decompiled with CFR 0.152. * * Could not load the following classes: * com.google.common.collect.Comparators * com.mojang.logging.LogUtils * it.unimi.dsi.fastutil.longs.LongOpenHashSet * it.unimi.dsi.fastutil.longs.LongSet * org.slf4j.Logger */ package net.minecraft.server.network; import com.google.common.collect.Comparators; import com.mojang.logging.LogUtils; import it.unimi.dsi.fastutil.longs.LongOpenHashSet; import it.unimi.dsi.fastutil.longs.LongSet; import java.lang.invoke.LambdaMetafactory; import java.util.Comparator; import java.util.List; import java.util.function.LongFunction; import java.util.function.Predicate; import java.util.function.ToIntFunction; import java.util.function.ToLongFunction; import net.minecraft.SharedConstants; import net.minecraft.network.protocol.game.ClientboundChunkBatchFinishedPacket; import net.minecraft.network.protocol.game.ClientboundChunkBatchStartPacket; import net.minecraft.network.protocol.game.ClientboundForgetLevelChunkPacket; import net.minecraft.network.protocol.game.ClientboundLevelChunkWithLightPacket; import net.minecraft.server.level.ChunkMap; import net.minecraft.server.level.ServerLevel; import net.minecraft.server.level.ServerPlayer; import net.minecraft.server.network.ServerGamePacketListenerImpl; import net.minecraft.util.Mth; import net.minecraft.world.level.ChunkPos; import net.minecraft.world.level.chunk.LevelChunk; import org.slf4j.Logger; public class PlayerChunkSender { private static final Logger LOGGER = LogUtils.getLogger(); public static final float MIN_CHUNKS_PER_TICK = 0.01f; public static final float MAX_CHUNKS_PER_TICK = 64.0f; private static final float START_CHUNKS_PER_TICK = 9.0f; private static final int MAX_UNACKNOWLEDGED_BATCHES = 10; private final LongSet pendingChunks = new LongOpenHashSet(); private final boolean memoryConnection; private float desiredChunksPerTick = 9.0f; private float batchQuota; private int unacknowledgedBatches; private int maxUnacknowledgedBatches = 1; public PlayerChunkSender(boolean memoryConnection) { this.memoryConnection = memoryConnection; } public void markChunkPendingToSend(LevelChunk chunk) { this.pendingChunks.add(chunk.getPos().toLong()); } public void dropChunk(ServerPlayer player, ChunkPos pos) { if (!this.pendingChunks.remove(pos.toLong()) && player.isAlive()) { player.connection.send(new ClientboundForgetLevelChunkPacket(pos)); } } public void sendNextChunks(ServerPlayer player) { if (this.unacknowledgedBatches >= this.maxUnacknowledgedBatches) { return; } float maxBatchSize = Math.max(1.0f, this.desiredChunksPerTick); this.batchQuota = Math.min(this.batchQuota + this.desiredChunksPerTick, maxBatchSize); if (this.batchQuota < 1.0f) { return; } if (this.pendingChunks.isEmpty()) { return; } ServerLevel level = player.level(); ChunkMap chunkMap = level.getChunkSource().chunkMap; List chunksToSend = this.collectChunksToSend(chunkMap, player.chunkPosition()); if (chunksToSend.isEmpty()) { return; } ServerGamePacketListenerImpl connection = player.connection; ++this.unacknowledgedBatches; connection.send(ClientboundChunkBatchStartPacket.INSTANCE); for (LevelChunk chunk : chunksToSend) { PlayerChunkSender.sendChunk(connection, level, chunk); } connection.send(new ClientboundChunkBatchFinishedPacket(chunksToSend.size())); this.batchQuota -= (float)chunksToSend.size(); } private static void sendChunk(ServerGamePacketListenerImpl connection, ServerLevel level, LevelChunk chunk) { connection.send(new ClientboundLevelChunkWithLightPacket(chunk, level.getLightEngine(), null, null)); ChunkPos pos = chunk.getPos(); if (SharedConstants.DEBUG_VERBOSE_SERVER_EVENTS) { LOGGER.debug("SEN {}", (Object)pos); } level.debugSynchronizers().startTrackingChunk(connection.player, chunk.getPos()); } /* * Unable to fully structure code */ private List collectChunksToSend(ChunkMap chunkMap, ChunkPos playerPos) { maxBatchSize = Mth.floor(this.batchQuota); if (this.memoryConnection) ** GOTO lbl7 if (this.pendingChunks.size() <= maxBatchSize) { lbl7: // 2 sources chunks = this.pendingChunks.longStream().mapToObj((LongFunction)LambdaMetafactory.metafactory(null, null, null, (J)Ljava/lang/Object;, getChunkToSend(long ), (J)Lnet/minecraft/world/level/chunk/LevelChunk;)((ChunkMap)chunkMap)).filter((Predicate)LambdaMetafactory.metafactory(null, null, null, (Ljava/lang/Object;)Z, nonNull(java.lang.Object ), (Lnet/minecraft/world/level/chunk/LevelChunk;)Z)()).sorted(Comparator.comparingInt((ToIntFunction)LambdaMetafactory.metafactory(null, null, null, (Ljava/lang/Object;)I, lambda$collectChunksToSend$0(net.minecraft.world.level.ChunkPos net.minecraft.world.level.chunk.LevelChunk ), (Lnet/minecraft/world/level/chunk/LevelChunk;)I)((ChunkPos)playerPos))).toList(); } else { chunks = ((List)this.pendingChunks.stream().collect(Comparators.least((int)maxBatchSize, Comparator.comparingInt((ToIntFunction)LambdaMetafactory.metafactory(null, null, null, (Ljava/lang/Object;)I, distanceSquared(long ), (Ljava/lang/Long;)I)((ChunkPos)playerPos))))).stream().mapToLong((ToLongFunction)LambdaMetafactory.metafactory(null, null, null, (Ljava/lang/Object;)J, longValue(), (Ljava/lang/Long;)J)()).mapToObj((LongFunction)LambdaMetafactory.metafactory(null, null, null, (J)Ljava/lang/Object;, getChunkToSend(long ), (J)Lnet/minecraft/world/level/chunk/LevelChunk;)((ChunkMap)chunkMap)).filter((Predicate)LambdaMetafactory.metafactory(null, null, null, (Ljava/lang/Object;)Z, nonNull(java.lang.Object ), (Lnet/minecraft/world/level/chunk/LevelChunk;)Z)()).toList(); } for (LevelChunk chunk : chunks) { this.pendingChunks.remove(chunk.getPos().toLong()); } return chunks; } public void onChunkBatchReceivedByClient(float desiredChunksPerTick) { --this.unacknowledgedBatches; float f = this.desiredChunksPerTick = Double.isNaN(desiredChunksPerTick) ? 0.01f : Mth.clamp(desiredChunksPerTick, 0.01f, 64.0f); if (this.unacknowledgedBatches == 0) { this.batchQuota = 1.0f; } this.maxUnacknowledgedBatches = 10; } public boolean isPending(long pos) { return this.pendingChunks.contains(pos); } private static /* synthetic */ int lambda$collectChunksToSend$0(ChunkPos playerPos, LevelChunk chunk) { return playerPos.distanceSquared(chunk.getPos()); } }