From 901cf13d01285a572ad1a23f4745de2e2674b542 Mon Sep 17 00:00:00 2001 From: Nassim Jahnke Date: Mon, 16 Dec 2024 10:43:57 +0100 Subject: [PATCH] Update more feature patches --- ...041-Allow-Saving-of-Oversized-Chunks.patch | 101 ++++++++---------- ...e-Oversized-block-entities-in-chunks.patch | 40 +++---- ...heck-distance-in-entity-interactions.patch | 80 +++++++------- ...056-optimize-dirt-and-snow-spreading.patch | 84 +++++++-------- ...e-getChunkAt-calls-for-loaded-chunks.patch | 51 +++++---- 5 files changed, 170 insertions(+), 186 deletions(-) diff --git a/feature-patches/1041-Allow-Saving-of-Oversized-Chunks.patch b/feature-patches/1041-Allow-Saving-of-Oversized-Chunks.patch index 6c06181f47..9c998777de 100644 --- a/feature-patches/1041-Allow-Saving-of-Oversized-Chunks.patch +++ b/feature-patches/1041-Allow-Saving-of-Oversized-Chunks.patch @@ -30,36 +30,23 @@ This fix also maintains compatability if someone switches server jars to one wit this fix, as the data will remain in the oversized file. Once the server returns to a jar with this fix, the data will be restored. -diff --git a/src/main/java/net/minecraft/world/level/chunk/storage/RegionFile.java b/src/main/java/net/minecraft/world/level/chunk/storage/RegionFile.java -index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 ---- a/src/main/java/net/minecraft/world/level/chunk/storage/RegionFile.java -+++ b/src/main/java/net/minecraft/world/level/chunk/storage/RegionFile.java -@@ -0,0 +0,0 @@ import java.nio.file.LinkOption; - import java.nio.file.Path; - import java.nio.file.StandardCopyOption; - import java.nio.file.StandardOpenOption; -+import java.util.zip.InflaterInputStream; // Paper - import javax.annotation.Nullable; - import net.minecraft.Util; - import net.minecraft.resources.ResourceLocation; - import net.minecraft.util.profiling.jfr.JvmProfiler; -+import net.minecraft.nbt.CompoundTag; // Paper -+import net.minecraft.nbt.NbtIo; // Paper - import net.minecraft.world.level.ChunkPos; - import org.slf4j.Logger; - -@@ -0,0 +0,0 @@ public class RegionFile implements AutoCloseable { - this.usedSectors = new RegionBitmap(); - this.info = storageKey; +diff --git a/net/minecraft/world/level/chunk/storage/RegionFile.java b/net/minecraft/world/level/chunk/storage/RegionFile.java +index 2b4fa89f15a42ddd627983fbd1377fb7c9864896..7491644233d52dc56d83de5ad3373f6a9a455378 100644 +--- a/net/minecraft/world/level/chunk/storage/RegionFile.java ++++ b/net/minecraft/world/level/chunk/storage/RegionFile.java +@@ -54,6 +54,7 @@ public class RegionFile implements AutoCloseable { + this.info = info; this.path = path; -+ initOversizedState(); // Paper - this.version = compressionFormat; - if (!Files.isDirectory(directory, new LinkOption[0])) { - throw new IllegalArgumentException("Expected directory, got " + String.valueOf(directory.toAbsolutePath())); -@@ -0,0 +0,0 @@ public class RegionFile implements AutoCloseable { - + this.version = version; ++ this.initOversizedState(); // Paper + if (!Files.isDirectory(externalFileDir)) { + throw new IllegalArgumentException("Expected directory, got " + externalFileDir.toAbsolutePath()); + } else { +@@ -424,4 +425,75 @@ public class RegionFile implements AutoCloseable { + interface CommitOp { + void run() throws IOException; } - ++ + // Paper start + private final byte[] oversized = new byte[1024]; + private int oversizedCount; @@ -78,9 +65,11 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 + private static int getChunkIndex(int x, int z) { + return (x & 31) + (z & 31) * 32; + } ++ + synchronized boolean isOversized(int x, int z) { + return this.oversized[getChunkIndex(x, z)] == 1; + } ++ + synchronized void setOversized(int x, int z, boolean oversized) throws IOException { + final int offset = getChunkIndex(x, z); + boolean previous = this.oversized[offset] == 1; @@ -120,22 +109,20 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 + return this.path.getParent().resolve(this.path.getFileName().toString().replaceAll("\\.mca$", "") + "_oversized_" + x + "_" + z + ".nbt"); + } + -+ synchronized CompoundTag getOversizedData(int x, int z) throws IOException { ++ synchronized net.minecraft.nbt.CompoundTag getOversizedData(int x, int z) throws IOException { + Path file = getOversizedFile(x, z); -+ try (DataInputStream out = new DataInputStream(new java.io.BufferedInputStream(new InflaterInputStream(Files.newInputStream(file))))) { -+ return NbtIo.read((java.io.DataInput) out); ++ try (DataInputStream out = new DataInputStream(new java.io.BufferedInputStream(new java.util.zip.InflaterInputStream(Files.newInputStream(file))))) { ++ return net.minecraft.nbt.NbtIo.read((java.io.DataInput) out); + } + + } + // Paper end - private class ChunkBuffer extends ByteArrayOutputStream { - - private final ChunkPos pos; -diff --git a/src/main/java/net/minecraft/world/level/chunk/storage/RegionFileStorage.java b/src/main/java/net/minecraft/world/level/chunk/storage/RegionFileStorage.java -index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 ---- a/src/main/java/net/minecraft/world/level/chunk/storage/RegionFileStorage.java -+++ b/src/main/java/net/minecraft/world/level/chunk/storage/RegionFileStorage.java -@@ -0,0 +0,0 @@ public final class RegionFileStorage implements AutoCloseable { + } +diff --git a/net/minecraft/world/level/chunk/storage/RegionFileStorage.java b/net/minecraft/world/level/chunk/storage/RegionFileStorage.java +index 5ac84d6b47e7fdc16e1c09b739829de3d316bf5b..51bf310423013d0ae9d3202d66e36a053a767197 100644 +--- a/net/minecraft/world/level/chunk/storage/RegionFileStorage.java ++++ b/net/minecraft/world/level/chunk/storage/RegionFileStorage.java +@@ -47,6 +47,43 @@ public final class RegionFileStorage implements AutoCloseable { } } @@ -148,7 +135,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 + synchronized (regionfile) { + try (DataInputStream datainputstream = regionfile.getChunkDataInputStream(chunkCoordinate)) { + CompoundTag oversizedData = regionfile.getOversizedData(chunkCoordinate.x, chunkCoordinate.z); -+ CompoundTag chunk = NbtIo.read((DataInput) datainputstream); ++ CompoundTag chunk = NbtIo.read(datainputstream); + if (oversizedData == null) { + return chunk; + } @@ -177,26 +164,26 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 + // Paper end + @Nullable - public CompoundTag read(ChunkPos pos) throws IOException { + public CompoundTag read(ChunkPos chunkPos) throws IOException { // CraftBukkit start - SPIGOT-5680: There's no good reason to preemptively create files on read, save that for writing -@@ -0,0 +0,0 @@ public final class RegionFileStorage implements AutoCloseable { +@@ -55,6 +92,12 @@ public final class RegionFileStorage implements AutoCloseable { + return null; + } // CraftBukkit end - DataInputStream datainputstream = regionfile.getChunkDataInputStream(pos); - + // Paper start -+ if (regionfile.isOversized(pos.x, pos.z)) { -+ printOversizedLog("Loading Oversized Chunk!", regionfile.getPath(), pos.x, pos.z); -+ return readOversizedChunk(regionfile, pos); ++ if (regionFile.isOversized(chunkPos.x, chunkPos.z)) { ++ printOversizedLog("Loading Oversized Chunk!", regionFile.getPath(), chunkPos.x, chunkPos.z); ++ return readOversizedChunk(regionFile, chunkPos); + } + // Paper end - CompoundTag nbttagcompound; - label43: - { -@@ -0,0 +0,0 @@ public final class RegionFileStorage implements AutoCloseable { - try { - NbtIo.write(nbt, (DataOutput) dataoutputstream); -+ regionfile.setOversized(pos.x, pos.z, false); // Paper - We don't do this anymore, mojang stores differently, but clear old meta flag if it exists to get rid of our own meta file once last oversized is gone - } catch (Throwable throwable) { - if (dataoutputstream != null) { - try { + CompoundTag var4; + try (DataInputStream chunkDataInputStream = regionFile.getChunkDataInputStream(chunkPos)) { +@@ -90,6 +133,7 @@ public final class RegionFileStorage implements AutoCloseable { + } else { + try (DataOutputStream chunkDataOutputStream = regionFile.getChunkDataOutputStream(chunkPos)) { + NbtIo.write(chunkData, chunkDataOutputStream); ++ regionFile.setOversized(chunkPos.x, chunkPos.z, false); // Paper - We don't do this anymore, mojang stores differently, but clear old meta flag if it exists to get rid of our own meta file once last oversized is gone + } + } + } diff --git a/feature-patches/1054-Handle-Oversized-block-entities-in-chunks.patch b/feature-patches/1054-Handle-Oversized-block-entities-in-chunks.patch index da2af1a0ea..127068aa94 100644 --- a/feature-patches/1054-Handle-Oversized-block-entities-in-chunks.patch +++ b/feature-patches/1054-Handle-Oversized-block-entities-in-chunks.patch @@ -8,49 +8,49 @@ creating too large of a packet to sed. Co-authored-by: Spottedleaf -diff --git a/src/main/java/net/minecraft/network/protocol/game/ClientboundLevelChunkPacketData.java b/src/main/java/net/minecraft/network/protocol/game/ClientboundLevelChunkPacketData.java -index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 ---- a/src/main/java/net/minecraft/network/protocol/game/ClientboundLevelChunkPacketData.java -+++ b/src/main/java/net/minecraft/network/protocol/game/ClientboundLevelChunkPacketData.java -@@ -0,0 +0,0 @@ public class ClientboundLevelChunkPacketData { +diff --git a/net/minecraft/network/protocol/game/ClientboundLevelChunkPacketData.java b/net/minecraft/network/protocol/game/ClientboundLevelChunkPacketData.java +index 5d1943d37dfad0c12e77179f0866851532d983e9..0a7e6c639d5125a135a43476bbb2ef2d682f743c 100644 +--- a/net/minecraft/network/protocol/game/ClientboundLevelChunkPacketData.java ++++ b/net/minecraft/network/protocol/game/ClientboundLevelChunkPacketData.java +@@ -27,6 +27,14 @@ public class ClientboundLevelChunkPacketData { private final CompoundTag heightmaps; private final byte[] buffer; private final List blockEntitiesData; + // Paper start - Handle oversized block entities in chunks + private final java.util.List> extraPackets = new java.util.ArrayList<>(); -+ private static final int TE_LIMIT = Integer.getInteger("Paper.excessiveTELimit", 750); ++ private static final int BLOCK_ENTITY_LIMIT = Integer.getInteger("Paper.excessiveTELimit", 750); + + public List> getExtraPackets() { + return this.extraPackets; + } + // Paper end - Handle oversized block entities in chunks - // Paper start - Anti-Xray - Add chunk packet info - @Deprecated @io.papermc.paper.annotation.DoNotUse public ClientboundLevelChunkPacketData(LevelChunk chunk) { this(chunk, null); } -@@ -0,0 +0,0 @@ public class ClientboundLevelChunkPacketData { - extractChunkData(new FriendlyByteBuf(this.getWriteBuffer()), chunk, chunkPacketInfo); - // Paper end + public ClientboundLevelChunkPacketData(LevelChunk levelChunk) { + this.heightmaps = new CompoundTag(); +@@ -40,8 +48,18 @@ public class ClientboundLevelChunkPacketData { + this.buffer = new byte[calculateChunkSize(levelChunk)]; + extractChunkData(new FriendlyByteBuf(this.getWriteBuffer()), levelChunk); this.blockEntitiesData = Lists.newArrayList(); + int totalTileEntities = 0; // Paper - Handle oversized block entities in chunks - for (Entry entry2 : chunk.getBlockEntities().entrySet()) { + for (Entry entryx : levelChunk.getBlockEntities().entrySet()) { + // Paper start - Handle oversized block entities in chunks -+ if (++totalTileEntities > TE_LIMIT) { -+ var packet = entry2.getValue().getUpdatePacket(); ++ if (++totalTileEntities > BLOCK_ENTITY_LIMIT) { ++ net.minecraft.network.protocol.Packet packet = entryx.getValue().getUpdatePacket(); + if (packet != null) { + this.extraPackets.add(packet); + continue; + } + } + // Paper end - Handle oversized block entities in chunks - this.blockEntitiesData.add(ClientboundLevelChunkPacketData.BlockEntityInfo.create(entry2.getValue())); + this.blockEntitiesData.add(ClientboundLevelChunkPacketData.BlockEntityInfo.create(entryx.getValue())); } } -diff --git a/src/main/java/net/minecraft/network/protocol/game/ClientboundLevelChunkWithLightPacket.java b/src/main/java/net/minecraft/network/protocol/game/ClientboundLevelChunkWithLightPacket.java -index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 ---- a/src/main/java/net/minecraft/network/protocol/game/ClientboundLevelChunkWithLightPacket.java -+++ b/src/main/java/net/minecraft/network/protocol/game/ClientboundLevelChunkWithLightPacket.java -@@ -0,0 +0,0 @@ public class ClientboundLevelChunkWithLightPacket implements Packet fileSystemProvider.getScheme().equalsIgnoreCase("jar")) +diff --git a/net/minecraft/Util.java b/net/minecraft/Util.java +index 60952bd49a89b8d6247d0c8bac837e5b3d586a76..fe84fe69a2a9ed95ec45a9e5af6e6f5a5a74edda 100644 +--- a/net/minecraft/Util.java ++++ b/net/minecraft/Util.java +@@ -130,6 +130,7 @@ public class Util { .findFirst() .orElseThrow(() -> new IllegalStateException("No jar file system provider found")); + private static Consumer thePauser = string -> {}; + public static final double COLLISION_EPSILON = 1.0E-7; // Paper - Check distance in entity interactions - private static Consumer thePauser = message -> { - }; -diff --git a/src/main/java/net/minecraft/world/entity/LivingEntity.java b/src/main/java/net/minecraft/world/entity/LivingEntity.java -index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 ---- a/src/main/java/net/minecraft/world/entity/LivingEntity.java -+++ b/src/main/java/net/minecraft/world/entity/LivingEntity.java -@@ -0,0 +0,0 @@ public abstract class LivingEntity extends Entity implements Attackable { - if (!source.is(DamageTypeTags.IS_PROJECTILE)) { - Entity entity = source.getDirectEntity(); + public static Collector, ?, Map> toMap() { + return Collectors.toMap(Entry::getKey, Entry::getValue); +diff --git a/net/minecraft/world/entity/LivingEntity.java b/net/minecraft/world/entity/LivingEntity.java +index 88f8d462728231627c3ee7557518a2e04b4fd199..e118fd567427064c6ad6637f874ed146e67b2ee8 100644 +--- a/net/minecraft/world/entity/LivingEntity.java ++++ b/net/minecraft/world/entity/LivingEntity.java +@@ -1392,7 +1392,7 @@ public abstract class LivingEntity extends Entity implements Attackable { + this.hurtCurrentlyUsedShield(amount); + f1 = amount; + amount = 0.0F; +- if (!damageSource.is(DamageTypeTags.IS_PROJECTILE) && damageSource.getDirectEntity() instanceof LivingEntity livingEntity) { ++ if (!damageSource.is(DamageTypeTags.IS_PROJECTILE) && damageSource.getDirectEntity() instanceof LivingEntity livingEntity && livingEntity.distanceToSqr(this) <= (200.0D * 200.0D)) { // Paper - Check distance in entity interactions + this.blockUsingShield(livingEntity); + } -- if (entity instanceof LivingEntity) { -+ if (entity instanceof LivingEntity && entity.distanceToSqr(this) <= (200.0D * 200.0D)) { // Paper - Check distance in entity interactions - LivingEntity entityliving = (LivingEntity) entity; - - this.blockUsingShield(entityliving); -@@ -0,0 +0,0 @@ public abstract class LivingEntity extends Entity implements Attackable { - d0 = source.getSourcePosition().x() - this.getX(); - d1 = source.getSourcePosition().z() - this.getZ(); +@@ -1477,6 +1477,14 @@ public abstract class LivingEntity extends Entity implements Attackable { + d = damageSource.getSourcePosition().x() - this.getX(); + d1 = damageSource.getSourcePosition().z() - this.getZ(); } + // Paper start - Check distance in entity interactions; see for loop in knockback method -+ if (Math.abs(d0) > 200) { -+ d0 = Math.random() - Math.random(); ++ if (Math.abs(d) > 200) { ++ d = Math.random() - Math.random(); + } + if (Math.abs(d1) > 200) { + d1 = Math.random() - Math.random(); + } + // Paper end - Check distance in entity interactions - this.knockback(0.4000000059604645D, d0, d1, entity1, entity1 == null ? io.papermc.paper.event.entity.EntityKnockbackEvent.Cause.DAMAGE : io.papermc.paper.event.entity.EntityKnockbackEvent.Cause.ENTITY_ATTACK); // CraftBukkit // Paper - knockback events + this.knockback(0.4F, d, d1, damageSource.getDirectEntity(), damageSource.getDirectEntity() == null ? io.papermc.paper.event.entity.EntityKnockbackEvent.Cause.DAMAGE : io.papermc.paper.event.entity.EntityKnockbackEvent.Cause.ENTITY_ATTACK); // CraftBukkit // Paper - knockback events if (!flag) { -@@ -0,0 +0,0 @@ public abstract class LivingEntity extends Entity implements Attackable { +@@ -2352,7 +2360,7 @@ public abstract class LivingEntity extends Entity implements Attackable { this.hurtCurrentlyUsedShield((float) -event.getDamage(DamageModifier.BLOCKING)); - Entity entity = damagesource.getDirectEntity(); + Entity entity = damageSource.getDirectEntity(); -- if (!damagesource.is(DamageTypeTags.IS_PROJECTILE) && entity instanceof LivingEntity) { // Paper - Fix shield disable inconsistency -+ if (!damagesource.is(DamageTypeTags.IS_PROJECTILE) && entity instanceof LivingEntity && entity.distanceToSqr(this) <= (200.0D * 200.0D)) { // Paper - Fix shield disable inconsistency & Check distance in entity interactions +- if (!damageSource.is(DamageTypeTags.IS_PROJECTILE) && entity instanceof LivingEntity) { // Paper - Fix shield disable inconsistency ++ if (!damageSource.is(DamageTypeTags.IS_PROJECTILE) && entity instanceof LivingEntity && entity.distanceToSqr(this) <= (200.0D * 200.0D)) { // Paper - Fix shield disable inconsistency & Check distance in entity interactions this.blockUsingShield((LivingEntity) entity); } } -diff --git a/src/main/java/net/minecraft/world/entity/vehicle/AbstractBoat.java b/src/main/java/net/minecraft/world/entity/vehicle/AbstractBoat.java -index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 ---- a/src/main/java/net/minecraft/world/entity/vehicle/AbstractBoat.java -+++ b/src/main/java/net/minecraft/world/entity/vehicle/AbstractBoat.java -@@ -0,0 +0,0 @@ public abstract class AbstractBoat extends VehicleEntity implements Leashable { - double d2 = (double) (this.getWaterLevelAbove() - this.getBbHeight()) + 0.101D; - - if (this.level().noCollision(this, this.getBoundingBox().move(0.0D, d2 - this.getY(), 0.0D))) { +diff --git a/net/minecraft/world/entity/vehicle/AbstractBoat.java b/net/minecraft/world/entity/vehicle/AbstractBoat.java +index 5cd65e94ac7830aaa2a64057fc2a81478b55ea41..b9cb86717d7e6c05eb97f3b1bbf1d0111a0ba6ed 100644 +--- a/net/minecraft/world/entity/vehicle/AbstractBoat.java ++++ b/net/minecraft/world/entity/vehicle/AbstractBoat.java +@@ -641,7 +641,7 @@ public abstract class AbstractBoat extends VehicleEntity implements Leashable { + this.waterLevel = this.getY(1.0); + double d2 = this.getWaterLevelAbove() - this.getBbHeight() + 0.101; + if (this.level().noCollision(this, this.getBoundingBox().move(0.0, d2 - this.getY(), 0.0))) { - this.setPos(this.getX(), d2, this.getZ()); + this.move(MoverType.SELF, new Vec3(0.0D, d2 - this.getY(), 0.0D)); // Paper - Check distance in entity interactions // TODO Still needed?? - this.setDeltaMovement(this.getDeltaMovement().multiply(1.0D, 0.0D, 1.0D)); - this.lastYd = 0.0D; + this.setDeltaMovement(this.getDeltaMovement().multiply(1.0, 0.0, 1.0)); + this.lastYd = 0.0; } diff --git a/feature-patches/1056-optimize-dirt-and-snow-spreading.patch b/feature-patches/1056-optimize-dirt-and-snow-spreading.patch index c13eaa6f09..2423e0adc9 100644 --- a/feature-patches/1056-optimize-dirt-and-snow-spreading.patch +++ b/feature-patches/1056-optimize-dirt-and-snow-spreading.patch @@ -4,75 +4,75 @@ Date: Fri, 22 Jan 2021 21:50:18 +0100 Subject: [PATCH] optimize dirt and snow spreading -diff --git a/src/main/java/net/minecraft/world/level/block/SpreadingSnowyDirtBlock.java b/src/main/java/net/minecraft/world/level/block/SpreadingSnowyDirtBlock.java -index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 ---- a/src/main/java/net/minecraft/world/level/block/SpreadingSnowyDirtBlock.java -+++ b/src/main/java/net/minecraft/world/level/block/SpreadingSnowyDirtBlock.java -@@ -0,0 +0,0 @@ public abstract class SpreadingSnowyDirtBlock extends SnowyDirtBlock { +diff --git a/net/minecraft/world/level/block/SpreadingSnowyDirtBlock.java b/net/minecraft/world/level/block/SpreadingSnowyDirtBlock.java +index 11937aa74efe08bdbd66a619c7a825f91d971afd..722f2b9a24679e0fc67aae2cd27051f96f962efe 100644 +--- a/net/minecraft/world/level/block/SpreadingSnowyDirtBlock.java ++++ b/net/minecraft/world/level/block/SpreadingSnowyDirtBlock.java +@@ -17,8 +17,13 @@ public abstract class SpreadingSnowyDirtBlock extends SnowyDirtBlock { } - private static boolean canBeGrass(BlockState state, LevelReader world, BlockPos pos) { -+ // Paper start - Perf: optimize dirt and snow spreading -+ return canBeGrass(world.getChunk(pos), state, world, pos); + private static boolean canBeGrass(BlockState state, LevelReader levelReader, BlockPos pos) { ++ // Paper start - Perf: optimize dirt and snow spreading ++ return canBeGrass(levelReader.getChunk(pos), state, levelReader, pos); + } -+ private static boolean canBeGrass(net.minecraft.world.level.chunk.ChunkAccess chunk, BlockState state, LevelReader world, BlockPos pos) { -+ // Paper end - Perf: optimize dirt and snow spreading - BlockPos blockposition1 = pos.above(); -- BlockState iblockdata1 = world.getBlockState(blockposition1); -+ BlockState iblockdata1 = chunk.getBlockState(blockposition1); // Paper - Perf: optimize dirt and snow spreading - - if (iblockdata1.is(Blocks.SNOW) && (Integer) iblockdata1.getValue(SnowLayerBlock.LAYERS) == 1) { ++ private static boolean canBeGrass(net.minecraft.world.level.chunk.ChunkAccess chunk, BlockState state, LevelReader levelReader, BlockPos pos) { ++ // Paper end - Perf: optimize dirt and snow spreading + BlockPos blockPos = pos.above(); +- BlockState blockState = levelReader.getBlockState(blockPos); ++ BlockState blockState = chunk.getBlockState(blockPos); // Paper - Perf: optimize dirt and snow spreading + if (blockState.is(Blocks.SNOW) && blockState.getValue(SnowLayerBlock.LAYERS) == 1) { return true; -@@ -0,0 +0,0 @@ public abstract class SpreadingSnowyDirtBlock extends SnowyDirtBlock { + } else if (blockState.getFluidState().getAmount() == 8) { +@@ -33,14 +38,27 @@ public abstract class SpreadingSnowyDirtBlock extends SnowyDirtBlock { protected abstract MapCodec codec(); - private static boolean canPropagate(BlockState state, LevelReader world, BlockPos pos) { -+ // Paper start - Perf: optimize dirt and snow spreading -+ return canPropagate(world.getChunk(pos), state, world, pos); + private static boolean canPropagate(BlockState state, LevelReader level, BlockPos pos) { ++ // Paper start - Perf: optimize dirt and snow spreading ++ return canPropagate(level.getChunk(pos), state, level, pos); + } + -+ private static boolean canPropagate(net.minecraft.world.level.chunk.ChunkAccess chunk, BlockState state, LevelReader world, BlockPos pos) { -+ // Paper end - Perf: optimize dirt and snow spreading - BlockPos blockposition1 = pos.above(); - -- return SpreadingSnowyDirtBlock.canBeGrass(state, world, pos) && !world.getFluidState(blockposition1).is(FluidTags.WATER); -+ return SpreadingSnowyDirtBlock.canBeGrass(chunk, state, world, pos) && !chunk.getFluidState(blockposition1).is(FluidTags.WATER); // Paper - Perf: optimize dirt and snow spreading ++ private static boolean canPropagate(net.minecraft.world.level.chunk.ChunkAccess chunk, BlockState state, LevelReader level, BlockPos pos) { ++ // Paper end - Perf: optimize dirt and snow spreading + BlockPos blockPos = pos.above(); +- return canBeGrass(state, level, pos) && !level.getFluidState(blockPos).is(FluidTags.WATER); ++ return canBeGrass(chunk, state, level, pos) && !chunk.getFluidState(blockPos).is(FluidTags.WATER); // Paper - Perf: optimize dirt and snow spreading } @Override - protected void randomTick(BlockState state, ServerLevel world, BlockPos pos, RandomSource random) { - if (this instanceof GrassBlock && world.paperConfig().tickRates.grassSpread != 1 && (world.paperConfig().tickRates.grassSpread < 1 || (net.minecraft.server.MinecraftServer.currentTick + pos.hashCode()) % world.paperConfig().tickRates.grassSpread != 0)) { return; } // Paper - Configurable random tick rates for blocks -- if (!SpreadingSnowyDirtBlock.canBeGrass(state, world, pos)) { + protected void randomTick(BlockState state, ServerLevel level, BlockPos pos, RandomSource random) { + if (this instanceof GrassBlock && level.paperConfig().tickRates.grassSpread != 1 && (level.paperConfig().tickRates.grassSpread < 1 || (net.minecraft.server.MinecraftServer.currentTick + pos.hashCode()) % level.paperConfig().tickRates.grassSpread != 0)) { return; } // Paper - Configurable random tick rates for blocks +- if (!canBeGrass(state, level, pos)) { + // Paper start - Perf: optimize dirt and snow spreading -+ final net.minecraft.world.level.chunk.ChunkAccess cachedBlockChunk = world.getChunkIfLoaded(pos); ++ final net.minecraft.world.level.chunk.ChunkAccess cachedBlockChunk = level.getChunkIfLoaded(pos); + if (cachedBlockChunk == null) { // Is this needed? + return; + } -+ if (!SpreadingSnowyDirtBlock.canBeGrass(cachedBlockChunk, state, world, pos)) { ++ ++ if (!canBeGrass(cachedBlockChunk, state, level, pos)) { + // Paper end - Perf: optimize dirt and snow spreading // CraftBukkit start - if (org.bukkit.craftbukkit.event.CraftEventFactory.callBlockFadeEvent(world, pos, Blocks.DIRT.defaultBlockState()).isCancelled()) { + if (org.bukkit.craftbukkit.event.CraftEventFactory.callBlockFadeEvent(level, pos, Blocks.DIRT.defaultBlockState()).isCancelled()) { return; -@@ -0,0 +0,0 @@ public abstract class SpreadingSnowyDirtBlock extends SnowyDirtBlock { - for (int i = 0; i < 4; ++i) { - BlockPos blockposition1 = pos.offset(random.nextInt(3) - 1, random.nextInt(5) - 3, random.nextInt(3) - 1); +@@ -53,8 +71,20 @@ public abstract class SpreadingSnowyDirtBlock extends SnowyDirtBlock { -- if (world.getBlockState(blockposition1).is(Blocks.DIRT) && SpreadingSnowyDirtBlock.canPropagate(iblockdata1, world, blockposition1)) { -- org.bukkit.craftbukkit.event.CraftEventFactory.handleBlockSpreadEvent(world, pos, blockposition1, (BlockState) iblockdata1.setValue(SpreadingSnowyDirtBlock.SNOWY, isSnowySetting(world.getBlockState(blockposition1.above())))); // CraftBukkit + for (int i = 0; i < 4; i++) { + BlockPos blockPos = pos.offset(random.nextInt(3) - 1, random.nextInt(5) - 3, random.nextInt(3) - 1); +- if (level.getBlockState(blockPos).is(Blocks.DIRT) && canPropagate(blockState, level, blockPos)) { +- org.bukkit.craftbukkit.event.CraftEventFactory.handleBlockSpreadEvent(level, pos, blockPos, blockState.setValue(SNOWY, Boolean.valueOf(isSnowySetting(level.getBlockState(blockPos.above()))))); // CraftBukkit + // Paper start - Perf: optimize dirt and snow spreading -+ if (pos.getX() == blockposition1.getX() && pos.getY() == blockposition1.getY() && pos.getZ() == blockposition1.getZ()) { ++ if (pos.getX() == blockPos.getX() && pos.getY() == blockPos.getY() && pos.getZ() == blockPos.getZ()) { + continue; + } + + final net.minecraft.world.level.chunk.ChunkAccess access; -+ if (cachedBlockChunk.locX == blockposition1.getX() >> 4 && cachedBlockChunk.locZ == blockposition1.getZ() >> 4) { ++ if (cachedBlockChunk.locX == blockPos.getX() >> 4 && cachedBlockChunk.locZ == blockPos.getZ() >> 4) { + access = cachedBlockChunk; + } else { -+ access = world.getChunkAt(blockposition1); ++ access = level.getChunkAt(blockPos); + } -+ if (access.getBlockState(blockposition1).is(Blocks.DIRT) && SpreadingSnowyDirtBlock.canPropagate(access, iblockdata1, world, blockposition1)) { -+ org.bukkit.craftbukkit.event.CraftEventFactory.handleBlockSpreadEvent(world, pos, blockposition1, (BlockState) iblockdata1.setValue(SpreadingSnowyDirtBlock.SNOWY, isSnowySetting(access.getBlockState(blockposition1.above())))); // CraftBukkit -+ // Paper end - Perf: optimize dirt and snow spreading ++ if (access.getBlockState(blockPos).is(Blocks.DIRT) && SpreadingSnowyDirtBlock.canPropagate(access, blockState, level, blockPos)) { ++ org.bukkit.craftbukkit.event.CraftEventFactory.handleBlockSpreadEvent(level, pos, blockPos, (BlockState) blockState.setValue(SpreadingSnowyDirtBlock.SNOWY, isSnowySetting(access.getBlockState(blockPos.above())))); // CraftBukkit ++ // Paper end - Perf: optimize dirt and snow spreading } } } diff --git a/feature-patches/1057-Optimise-getChunkAt-calls-for-loaded-chunks.patch b/feature-patches/1057-Optimise-getChunkAt-calls-for-loaded-chunks.patch index 9f549ec79e..1138fb6f90 100644 --- a/feature-patches/1057-Optimise-getChunkAt-calls-for-loaded-chunks.patch +++ b/feature-patches/1057-Optimise-getChunkAt-calls-for-loaded-chunks.patch @@ -6,13 +6,13 @@ Subject: [PATCH] Optimise getChunkAt calls for loaded chunks bypass the need to get a player chunk, then get the either, then unwrap it... -diff --git a/src/main/java/net/minecraft/server/level/ServerChunkCache.java b/src/main/java/net/minecraft/server/level/ServerChunkCache.java -index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 ---- a/src/main/java/net/minecraft/server/level/ServerChunkCache.java -+++ b/src/main/java/net/minecraft/server/level/ServerChunkCache.java -@@ -0,0 +0,0 @@ public class ServerChunkCache extends ChunkSource { - return this.getChunk(x, z, leastStatus, create); - }, this.mainThreadProcessor).join(); +diff --git a/net/minecraft/server/level/ServerChunkCache.java b/net/minecraft/server/level/ServerChunkCache.java +index d310e7489fc4ecede8deef59241444769d87b0a1..f268808cb250e374f2d5652b20eece011e87dcfb 100644 +--- a/net/minecraft/server/level/ServerChunkCache.java ++++ b/net/minecraft/server/level/ServerChunkCache.java +@@ -218,6 +218,12 @@ public class ServerChunkCache extends ChunkSource { + if (Thread.currentThread() != this.mainThread) { + return CompletableFuture.supplyAsync(() -> this.getChunk(x, z, chunkStatus, requireChunk), this.mainThreadProcessor).join(); } else { + // Paper start - Perf: Optimise getChunkAt calls for loaded chunks + LevelChunk ifLoaded = this.getChunkAtIfLoadedMainThread(x, z); @@ -20,41 +20,38 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 + return ifLoaded; + } + // Paper end - Perf: Optimise getChunkAt calls for loaded chunks - ProfilerFiller gameprofilerfiller = Profiler.get(); - - gameprofilerfiller.incrementCounter("getChunk"); -@@ -0,0 +0,0 @@ public class ServerChunkCache extends ChunkSource { + ProfilerFiller profilerFiller = Profiler.get(); + profilerFiller.incrementCounter("getChunk"); + long packedChunkPos = ChunkPos.asLong(x, z); +@@ -252,30 +258,7 @@ public class ServerChunkCache extends ChunkSource { if (Thread.currentThread() != this.mainThread) { return null; } else { - Profiler.get().incrementCounter("getChunkNow"); -- long k = ChunkPos.asLong(chunkX, chunkZ); +- long packedChunkPos = ChunkPos.asLong(chunkX, chunkZ); - -- ChunkAccess ichunkaccess; -- -- for (int l = 0; l < 4; ++l) { -- if (k == this.lastChunkPos[l] && this.lastChunkStatus[l] == ChunkStatus.FULL) { -- ichunkaccess = this.lastChunk[l]; -- return ichunkaccess instanceof LevelChunk ? (LevelChunk) ichunkaccess : null; +- for (int i = 0; i < 4; i++) { +- if (packedChunkPos == this.lastChunkPos[i] && this.lastChunkStatus[i] == ChunkStatus.FULL) { +- ChunkAccess chunkAccess = this.lastChunk[i]; +- return chunkAccess instanceof LevelChunk ? (LevelChunk)chunkAccess : null; - } - } - -- ChunkHolder playerchunk = this.getVisibleChunkIfPresent(k); -- -- if (playerchunk == null) { +- ChunkHolder visibleChunkIfPresent = this.getVisibleChunkIfPresent(packedChunkPos); +- if (visibleChunkIfPresent == null) { - return null; - } else { -- ichunkaccess = playerchunk.getChunkIfPresent(ChunkStatus.FULL); -- if (ichunkaccess != null) { -- this.storeInCache(k, ichunkaccess, ChunkStatus.FULL); -- if (ichunkaccess instanceof LevelChunk) { -- return (LevelChunk) ichunkaccess; +- ChunkAccess chunkAccess = visibleChunkIfPresent.getChunkIfPresent(ChunkStatus.FULL); +- if (chunkAccess != null) { +- this.storeInCache(packedChunkPos, chunkAccess, ChunkStatus.FULL); +- if (chunkAccess instanceof LevelChunk) { +- return (LevelChunk)chunkAccess; - } - } - - return null; - } -+ return this.getChunkAtIfLoadedMainThread(chunkX, chunkZ); // Paper - Perf: Optimise getChunkAt calls for loaded chunks ++ return this.getChunkAtIfLoadedMainThread(chunkX, chunkZ); // Paper - Perf: Optimise getChunkAt calls for loaded chunk } }