aboutsummaryrefslogtreecommitdiffhomepage
path: root/patches/unapplied
diff options
context:
space:
mode:
authorNassim Jahnke <[email protected]>2024-04-25 14:23:55 +0200
committerNassim Jahnke <[email protected]>2024-04-25 14:23:55 +0200
commit6da0d8cc91be3b8e1c84c0436f49654cdde6de2f (patch)
treee98a68395f98b4fb53be831513b00cfe4cabdb8d /patches/unapplied
parent2debcaff9dbd067a94385eb1b7d88110c2a4f231 (diff)
downloadPaper-6da0d8cc91be3b8e1c84c0436f49654cdde6de2f.tar.gz
Paper-6da0d8cc91be3b8e1c84c0436f49654cdde6de2f.zip
(Almost) all patches applied
Diffstat (limited to 'patches/unapplied')
-rw-r--r--patches/unapplied/server/1034-Improve-performance-of-mass-crafts.patch83
-rw-r--r--patches/unapplied/server/1035-Actually-optimise-explosions.patch521
-rw-r--r--patches/unapplied/server/1036-Optimise-chunk-tick-iteration.patch380
-rw-r--r--patches/unapplied/server/1037-Lag-compensation-ticks.patch129
-rw-r--r--patches/unapplied/server/1038-Optimise-nearby-player-retrieval.patch228
-rw-r--r--patches/unapplied/server/1039-Distance-manager-tick-timings.patch34
-rw-r--r--patches/unapplied/server/1040-Handle-Oversized-block-entities-in-chunks.patch64
-rw-r--r--patches/unapplied/server/1041-Send-full-pos-packets-for-hard-colliding-entities.patch23
8 files changed, 0 insertions, 1462 deletions
diff --git a/patches/unapplied/server/1034-Improve-performance-of-mass-crafts.patch b/patches/unapplied/server/1034-Improve-performance-of-mass-crafts.patch
deleted file mode 100644
index 3f69ae35b9..0000000000
--- a/patches/unapplied/server/1034-Improve-performance-of-mass-crafts.patch
+++ /dev/null
@@ -1,83 +0,0 @@
-From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
-From: Jake Potrebic <[email protected]>
-Date: Sun, 13 Aug 2023 15:41:52 -0700
-Subject: [PATCH] Improve performance of mass crafts
-
-When the server crafts all available items in CraftingMenu or InventoryMenu the game
-checks either 4 or 9 times for each individual craft for a matching recipe for that container.
-This check can be expensive if 64 total crafts are being performed with the recipe matching logic
-being run 64 * 9 + 64 times. A breakdown of those times is below. This patch caches the last matching
-recipe so that it is checked first and only if it doesn't match does the rest of the matching logic run.
-
-Shift-click crafts are processed one at a time, so shift clicking on an item in the result of a iron block craft
-where all the 9 inputs are full stacks of iron will run 64 iron block crafts. For each of those crafts, the
-'remaining' blocks are calculated. This is due to recipes that have leftover items like buckets. This is done
-for each craft, and done once to get the full 9 leftover items which are usually air. Then 1 item is removed
-from each of the 9 inputs and each time that happens, logic is triggered to update the result itemstack. So
-for each craft, that logic is run 9 times (hence the 64 * 9). The + 64 is from the 64 checks for remaining items.
-
-After this patch, the full iteration over all recipes checking for a match should run once for a full craft to find the
-initial recipe match. Then that recipe will be checked first for all future recipe match checks.
-
-diff --git a/src/main/java/net/minecraft/world/inventory/CraftingMenu.java b/src/main/java/net/minecraft/world/inventory/CraftingMenu.java
-index 7a0c1a55a211035bbca7b97293e94b04ae308bae..c3800bdd5096cb06e085e28f6bf0f65586ecf11e 100644
---- a/src/main/java/net/minecraft/world/inventory/CraftingMenu.java
-+++ b/src/main/java/net/minecraft/world/inventory/CraftingMenu.java
-@@ -76,7 +76,8 @@ public class CraftingMenu extends RecipeBookMenu<CraftingContainer> {
- if (!world.isClientSide) {
- ServerPlayer entityplayer = (ServerPlayer) player;
- ItemStack itemstack = ItemStack.EMPTY;
-- Optional<RecipeHolder<CraftingRecipe>> optional = world.getServer().getRecipeManager().getRecipeFor(RecipeType.CRAFTING, craftingInventory, world);
-+ final RecipeHolder<?> currentRecipe = craftingInventory.getCurrentRecipe(); // Paper - Perf: Improve mass crafting; check last recipe used first
-+ Optional<RecipeHolder<CraftingRecipe>> optional = currentRecipe == null ? world.getServer().getRecipeManager().getRecipeFor(RecipeType.CRAFTING, craftingInventory, world) : world.getServer().getRecipeManager().getRecipeFor(RecipeType.CRAFTING, craftingInventory, world, currentRecipe.id()).map(com.mojang.datafixers.util.Pair::getSecond); // Paper - Perf: Improve mass crafting; check last recipe used first
-
- if (optional.isPresent()) {
- RecipeHolder<CraftingRecipe> recipeholder = (RecipeHolder) optional.get();
-diff --git a/src/main/java/net/minecraft/world/inventory/ResultSlot.java b/src/main/java/net/minecraft/world/inventory/ResultSlot.java
-index 113460eff5121788fce44d6569ec07deb9701b20..accf752e7615f775483830f81bd0df30e40d3c7f 100644
---- a/src/main/java/net/minecraft/world/inventory/ResultSlot.java
-+++ b/src/main/java/net/minecraft/world/inventory/ResultSlot.java
-@@ -58,7 +58,7 @@ public class ResultSlot extends Slot {
- @Override
- public void onTake(Player player, ItemStack stack) {
- this.checkTakeAchievements(stack);
-- NonNullList<ItemStack> nonNullList = player.level().getRecipeManager().getRemainingItemsFor(RecipeType.CRAFTING, this.craftSlots, player.level());
-+ NonNullList<ItemStack> nonNullList = player.level().getRecipeManager().getRemainingItemsFor(RecipeType.CRAFTING, this.craftSlots, player.level(), this.craftSlots.getCurrentRecipe() != null ? this.craftSlots.getCurrentRecipe().id() : null); // Paper - Perf: Improve mass crafting; check last recipe used first
-
- for (int i = 0; i < nonNullList.size(); i++) {
- ItemStack itemStack = this.craftSlots.getItem(i);
-diff --git a/src/main/java/net/minecraft/world/item/crafting/RecipeManager.java b/src/main/java/net/minecraft/world/item/crafting/RecipeManager.java
-index a0ab3c55826af292d1cdac05648139b4d31f1376..d87124f5356180a37e581febc6141fdc5f1395a7 100644
---- a/src/main/java/net/minecraft/world/item/crafting/RecipeManager.java
-+++ b/src/main/java/net/minecraft/world/item/crafting/RecipeManager.java
-@@ -122,13 +122,16 @@ public class RecipeManager extends SimpleJsonResourceReloadListener {
- RecipeHolder<T> recipeholder = (RecipeHolder) map.get(id);
-
- if (recipeholder != null && recipeholder.value().matches(inventory, world)) {
-+ inventory.setCurrentRecipe(recipeholder); // Paper - Perf: Improve mass crafting
- return Optional.of(Pair.of(id, recipeholder));
- }
- }
-
-+ inventory.setCurrentRecipe(null); // Paper - Perf: Improve mass crafting;; clear before it might be set again
- return map.entrySet().stream().filter((entry) -> {
- return ((RecipeHolder) entry.getValue()).value().matches(inventory, world);
- }).findFirst().map((entry) -> {
-+ inventory.setCurrentRecipe(entry.getValue()); // Paper - Perf: Improve mass crafting
- return Pair.of((ResourceLocation) entry.getKey(), (RecipeHolder) entry.getValue());
- });
- }
-@@ -150,7 +153,12 @@ public class RecipeManager extends SimpleJsonResourceReloadListener {
- }
-
- public <C extends Container, T extends Recipe<C>> NonNullList<ItemStack> getRemainingItemsFor(RecipeType<T> type, C inventory, Level world) {
-- Optional<RecipeHolder<T>> optional = this.getRecipeFor(type, inventory, world);
-+ // Paper start - Perf: Improve mass crafting;; check last recipe used first
-+ return this.getRemainingItemsFor(type, inventory, world, null);
-+ }
-+ public <C extends Container, T extends Recipe<C>> NonNullList<ItemStack> getRemainingItemsFor(RecipeType<T> type, C inventory, Level world, @Nullable ResourceLocation firstToCheck) {
-+ Optional<RecipeHolder<T>> optional = firstToCheck == null ? this.getRecipeFor(type, inventory, world) : this.getRecipeFor(type, inventory, world, firstToCheck).map(Pair::getSecond);
-+ // Paper end - Perf: Improve mass crafting
-
- if (optional.isPresent()) {
- return ((RecipeHolder) optional.get()).value().getRemainingItems(inventory);
diff --git a/patches/unapplied/server/1035-Actually-optimise-explosions.patch b/patches/unapplied/server/1035-Actually-optimise-explosions.patch
deleted file mode 100644
index 7a35803ac0..0000000000
--- a/patches/unapplied/server/1035-Actually-optimise-explosions.patch
+++ /dev/null
@@ -1,521 +0,0 @@
-From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
-From: Spottedleaf <[email protected]>
-Date: Tue, 12 Sep 2023 06:50:16 -0700
-Subject: [PATCH] Actually optimise explosions
-
-The vast majority of blocks an explosion of power ~4 tries
-to destroy are duplicates. The core of the block destroying
-part of this patch is to cache the block state, resistance, and
-whether it should explode - as those will not change.
-
-The other part of this patch is to optimise the visibility
-percentage calculation. The new visibility calculation takes
-advantage of the block caching already done by the explosion logic.
-It continues to update the cache as the visibility calculation
-uses many rays which can overlap significantly.
-
-Effectively, the patch uses a lot of caching to eliminate
-redundant operations.
-
-Performance benchmarking explosions is challenging, as it varies
-depending on the power, the number of nearby entities, and the
-nearby terrain. This means that no benchmark can cover all the cases.
-I decided to test a giant block of TNT, as that's where the optimisations
-would be needed the most.
-
-I tested using a 50x10x50 block of TNT above ground
-and determined the following:
-
-Vanilla time per explosion: 2.27ms
-Lithium time per explosion: 1.07ms
-This patch time per explosion: 0.45ms
-
-The results indicate that this logic is 5 times faster than Vanilla
-and 2.3 times faster than Lithium.
-
-diff --git a/src/main/java/net/minecraft/world/level/Explosion.java b/src/main/java/net/minecraft/world/level/Explosion.java
-index 093c814d6835f20b1208236db96bb40b4611936c..b678da2cbb93cea7971bc3c4d324cfca18b0bc97 100644
---- a/src/main/java/net/minecraft/world/level/Explosion.java
-+++ b/src/main/java/net/minecraft/world/level/Explosion.java
-@@ -111,6 +111,271 @@ public class Explosion {
- this.yield = this.blockInteraction == Explosion.BlockInteraction.DESTROY_WITH_DECAY ? 1.0F / this.radius : 1.0F; // CraftBukkit
- }
-
-+ // Paper start - optimise collisions
-+ private static final double[] CACHED_RAYS;
-+ static {
-+ final it.unimi.dsi.fastutil.doubles.DoubleArrayList rayCoords = new it.unimi.dsi.fastutil.doubles.DoubleArrayList();
-+
-+ for (int x = 0; x <= 15; ++x) {
-+ for (int y = 0; y <= 15; ++y) {
-+ for (int z = 0; z <= 15; ++z) {
-+ if ((x == 0 || x == 15) || (y == 0 || y == 15) || (z == 0 || z == 15)) {
-+ double xDir = (double)((float)x / 15.0F * 2.0F - 1.0F);
-+ double yDir = (double)((float)y / 15.0F * 2.0F - 1.0F);
-+ double zDir = (double)((float)z / 15.0F * 2.0F - 1.0F);
-+
-+ double mag = Math.sqrt(
-+ xDir * xDir + yDir * yDir + zDir * zDir
-+ );
-+
-+ rayCoords.add((xDir / mag) * (double)0.3F);
-+ rayCoords.add((yDir / mag) * (double)0.3F);
-+ rayCoords.add((zDir / mag) * (double)0.3F);
-+ }
-+ }
-+ }
-+ }
-+
-+ CACHED_RAYS = rayCoords.toDoubleArray();
-+ }
-+
-+ private static final int CHUNK_CACHE_SHIFT = 2;
-+ private static final int CHUNK_CACHE_MASK = (1 << CHUNK_CACHE_SHIFT) - 1;
-+ private static final int CHUNK_CACHE_WIDTH = 1 << CHUNK_CACHE_SHIFT;
-+
-+ private static final int BLOCK_EXPLOSION_CACHE_SHIFT = 3;
-+ private static final int BLOCK_EXPLOSION_CACHE_MASK = (1 << BLOCK_EXPLOSION_CACHE_SHIFT) - 1;
-+ private static final int BLOCK_EXPLOSION_CACHE_WIDTH = 1 << BLOCK_EXPLOSION_CACHE_SHIFT;
-+
-+ // resistance = (res + 0.3F) * 0.3F;
-+ // so for resistance = 0, we need res = -0.3F
-+ private static final Float ZERO_RESISTANCE = Float.valueOf(-0.3f);
-+ private it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap<ExplosionBlockCache> blockCache = null;
-+
-+ public static final class ExplosionBlockCache {
-+
-+ public final long key;
-+ public final BlockPos immutablePos;
-+ public final BlockState blockState;
-+ public final FluidState fluidState;
-+ public final float resistance;
-+ public final boolean outOfWorld;
-+ public Boolean shouldExplode; // null -> not called yet
-+ public net.minecraft.world.phys.shapes.VoxelShape cachedCollisionShape;
-+
-+ public ExplosionBlockCache(long key, BlockPos immutablePos, BlockState blockState, FluidState fluidState, float resistance,
-+ boolean outOfWorld) {
-+ this.key = key;
-+ this.immutablePos = immutablePos;
-+ this.blockState = blockState;
-+ this.fluidState = fluidState;
-+ this.resistance = resistance;
-+ this.outOfWorld = outOfWorld;
-+ }
-+ }
-+
-+ private long[] chunkPosCache = null;
-+ private net.minecraft.world.level.chunk.LevelChunk[] chunkCache = null;
-+
-+ private ExplosionBlockCache getOrCacheExplosionBlock(final int x, final int y, final int z,
-+ final long key, final boolean calculateResistance) {
-+ ExplosionBlockCache ret = this.blockCache.get(key);
-+ if (ret != null) {
-+ return ret;
-+ }
-+
-+ BlockPos pos = new BlockPos(x, y, z);
-+
-+ if (!this.level.isInWorldBounds(pos)) {
-+ ret = new ExplosionBlockCache(key, pos, null, null, 0.0f, true);
-+ } else {
-+ net.minecraft.world.level.chunk.LevelChunk chunk;
-+ long chunkKey = io.papermc.paper.util.CoordinateUtils.getChunkKey(x >> 4, z >> 4);
-+ int chunkCacheKey = ((x >> 4) & CHUNK_CACHE_MASK) | (((z >> 4) << CHUNK_CACHE_SHIFT) & (CHUNK_CACHE_MASK << CHUNK_CACHE_SHIFT));
-+ if (this.chunkPosCache[chunkCacheKey] == chunkKey) {
-+ chunk = this.chunkCache[chunkCacheKey];
-+ } else {
-+ this.chunkPosCache[chunkCacheKey] = chunkKey;
-+ this.chunkCache[chunkCacheKey] = chunk = this.level.getChunk(x >> 4, z >> 4);
-+ }
-+
-+ BlockState blockState = chunk.getBlockStateFinal(x, y, z);
-+ FluidState fluidState = blockState.getFluidState();
-+
-+ Optional<Float> resistance = !calculateResistance ? Optional.empty() : this.damageCalculator.getBlockExplosionResistance((Explosion)(Object)this, this.level, pos, blockState, fluidState);
-+
-+ ret = new ExplosionBlockCache(
-+ key, pos, blockState, fluidState,
-+ (resistance.orElse(ZERO_RESISTANCE).floatValue() + 0.3f) * 0.3f,
-+ false
-+ );
-+ }
-+
-+ this.blockCache.put(key, ret);
-+
-+ return ret;
-+ }
-+
-+ private boolean clipsAnything(final Vec3 from, final Vec3 to,
-+ final io.papermc.paper.util.CollisionUtil.LazyEntityCollisionContext context,
-+ final ExplosionBlockCache[] blockCache,
-+ final BlockPos.MutableBlockPos currPos) {
-+ // assume that context.delegated = false
-+ final double adjX = io.papermc.paper.util.CollisionUtil.COLLISION_EPSILON * (from.x - to.x);
-+ final double adjY = io.papermc.paper.util.CollisionUtil.COLLISION_EPSILON * (from.y - to.y);
-+ final double adjZ = io.papermc.paper.util.CollisionUtil.COLLISION_EPSILON * (from.z - to.z);
-+
-+ if (adjX == 0.0 && adjY == 0.0 && adjZ == 0.0) {
-+ return false;
-+ }
-+
-+ final double toXAdj = to.x - adjX;
-+ final double toYAdj = to.y - adjY;
-+ final double toZAdj = to.z - adjZ;
-+ final double fromXAdj = from.x + adjX;
-+ final double fromYAdj = from.y + adjY;
-+ final double fromZAdj = from.z + adjZ;
-+
-+ int currX = Mth.floor(fromXAdj);
-+ int currY = Mth.floor(fromYAdj);
-+ int currZ = Mth.floor(fromZAdj);
-+
-+ final double diffX = toXAdj - fromXAdj;
-+ final double diffY = toYAdj - fromYAdj;
-+ final double diffZ = toZAdj - fromZAdj;
-+
-+ final double dxDouble = Math.signum(diffX);
-+ final double dyDouble = Math.signum(diffY);
-+ final double dzDouble = Math.signum(diffZ);
-+
-+ final int dx = (int)dxDouble;
-+ final int dy = (int)dyDouble;
-+ final int dz = (int)dzDouble;
-+
-+ final double normalizedDiffX = diffX == 0.0 ? Double.MAX_VALUE : dxDouble / diffX;
-+ final double normalizedDiffY = diffY == 0.0 ? Double.MAX_VALUE : dyDouble / diffY;
-+ final double normalizedDiffZ = diffZ == 0.0 ? Double.MAX_VALUE : dzDouble / diffZ;
-+
-+ double normalizedCurrX = normalizedDiffX * (diffX > 0.0 ? (1.0 - Mth.frac(fromXAdj)) : Mth.frac(fromXAdj));
-+ double normalizedCurrY = normalizedDiffY * (diffY > 0.0 ? (1.0 - Mth.frac(fromYAdj)) : Mth.frac(fromYAdj));
-+ double normalizedCurrZ = normalizedDiffZ * (diffZ > 0.0 ? (1.0 - Mth.frac(fromZAdj)) : Mth.frac(fromZAdj));
-+
-+ for (;;) {
-+ currPos.set(currX, currY, currZ);
-+
-+ // ClipContext.Block.COLLIDER -> BlockBehaviour.BlockStateBase::getCollisionShape
-+ // ClipContext.Fluid.NONE -> ignore fluids
-+
-+ // read block from cache
-+ final long key = BlockPos.asLong(currX, currY, currZ);
-+
-+ final int cacheKey =
-+ (currX & BLOCK_EXPLOSION_CACHE_MASK) |
-+ (currY & BLOCK_EXPLOSION_CACHE_MASK) << (BLOCK_EXPLOSION_CACHE_SHIFT) |
-+ (currZ & BLOCK_EXPLOSION_CACHE_MASK) << (BLOCK_EXPLOSION_CACHE_SHIFT + BLOCK_EXPLOSION_CACHE_SHIFT);
-+ ExplosionBlockCache cachedBlock = blockCache[cacheKey];
-+ if (cachedBlock == null || cachedBlock.key != key) {
-+ blockCache[cacheKey] = cachedBlock = this.getOrCacheExplosionBlock(currX, currY, currZ, key, false);
-+ }
-+
-+ final BlockState blockState = cachedBlock.blockState;
-+ if (blockState != null && !blockState.emptyCollisionShape()) {
-+ net.minecraft.world.phys.shapes.VoxelShape collision = cachedBlock.cachedCollisionShape;
-+ if (collision == null) {
-+ collision = blockState.getConstantCollisionShape();
-+ if (collision == null) {
-+ collision = blockState.getCollisionShape(this.level, currPos, context);
-+ if (!context.isDelegated()) {
-+ // if it was not delegated during this call, assume that for any future ones it will not be delegated
-+ // again, and cache the result
-+ cachedBlock.cachedCollisionShape = collision;
-+ }
-+ } else {
-+ cachedBlock.cachedCollisionShape = collision;
-+ }
-+ }
-+
-+ if (!collision.isEmpty() && collision.clip(from, to, currPos) != null) {
-+ return true;
-+ }
-+ }
-+
-+ if (normalizedCurrX > 1.0 && normalizedCurrY > 1.0 && normalizedCurrZ > 1.0) {
-+ return false;
-+ }
-+
-+ // inc the smallest normalized coordinate
-+
-+ if (normalizedCurrX < normalizedCurrY) {
-+ if (normalizedCurrX < normalizedCurrZ) {
-+ currX += dx;
-+ normalizedCurrX += normalizedDiffX;
-+ } else {
-+ // x < y && x >= z <--> z < y && z <= x
-+ currZ += dz;
-+ normalizedCurrZ += normalizedDiffZ;
-+ }
-+ } else if (normalizedCurrY < normalizedCurrZ) {
-+ // y <= x && y < z
-+ currY += dy;
-+ normalizedCurrY += normalizedDiffY;
-+ } else {
-+ // y <= x && z <= y <--> z <= y && z <= x
-+ currZ += dz;
-+ normalizedCurrZ += normalizedDiffZ;
-+ }
-+ }
-+ }
-+
-+ private float getSeenFraction(final Vec3 source, final Entity target,
-+ final ExplosionBlockCache[] blockCache,
-+ final BlockPos.MutableBlockPos blockPos) {
-+ final AABB boundingBox = target.getBoundingBox();
-+ final double diffX = boundingBox.maxX - boundingBox.minX;
-+ final double diffY = boundingBox.maxY - boundingBox.minY;
-+ final double diffZ = boundingBox.maxZ - boundingBox.minZ;
-+
-+ final double incX = 1.0 / (diffX * 2.0 + 1.0);
-+ final double incY = 1.0 / (diffY * 2.0 + 1.0);
-+ final double incZ = 1.0 / (diffZ * 2.0 + 1.0);
-+
-+ if (incX < 0.0 || incY < 0.0 || incZ < 0.0) {
-+ return 0.0f;
-+ }
-+
-+ final double offX = (1.0 - Math.floor(1.0 / incX) * incX) * 0.5 + boundingBox.minX;
-+ final double offY = boundingBox.minY;
-+ final double offZ = (1.0 - Math.floor(1.0 / incZ) * incZ) * 0.5 + boundingBox.minZ;
-+
-+ final io.papermc.paper.util.CollisionUtil.LazyEntityCollisionContext context = new io.papermc.paper.util.CollisionUtil.LazyEntityCollisionContext(target);
-+
-+ int totalRays = 0;
-+ int missedRays = 0;
-+
-+ for (double dx = 0.0; dx <= 1.0; dx += incX) {
-+ final double fromX = Math.fma(dx, diffX, offX);
-+ for (double dy = 0.0; dy <= 1.0; dy += incY) {
-+ final double fromY = Math.fma(dy, diffY, offY);
-+ for (double dz = 0.0; dz <= 1.0; dz += incZ) {
-+ ++totalRays;
-+
-+ final Vec3 from = new Vec3(
-+ fromX,
-+ fromY,
-+ Math.fma(dz, diffZ, offZ)
-+ );
-+
-+ if (!this.clipsAnything(from, source, context, blockCache, blockPos)) {
-+ ++missedRays;
-+ }
-+ }
-+ }
-+ }
-+
-+ return (float)missedRays / (float)totalRays;
-+ }
-+ // Paper end - optimise collisions
-+
- private ExplosionDamageCalculator makeDamageCalculator(@Nullable Entity entity) {
- return (ExplosionDamageCalculator) (entity == null ? Explosion.EXPLOSION_DAMAGE_CALCULATOR : new EntityBasedExplosionDamageCalculator(entity));
- }
-@@ -171,40 +436,88 @@ public class Explosion {
- int i;
- int j;
-
-- for (int k = 0; k < 16; ++k) {
-- for (i = 0; i < 16; ++i) {
-- for (j = 0; j < 16; ++j) {
-- if (k == 0 || k == 15 || i == 0 || i == 15 || j == 0 || j == 15) {
-- double d0 = (double) ((float) k / 15.0F * 2.0F - 1.0F);
-- double d1 = (double) ((float) i / 15.0F * 2.0F - 1.0F);
-- double d2 = (double) ((float) j / 15.0F * 2.0F - 1.0F);
-- double d3 = Math.sqrt(d0 * d0 + d1 * d1 + d2 * d2);
--
-- d0 /= d3;
-- d1 /= d3;
-- d2 /= d3;
-+ // Paper start - optimise explosions
-+ this.blockCache = new it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap<>();
-+
-+ this.chunkPosCache = new long[CHUNK_CACHE_WIDTH * CHUNK_CACHE_WIDTH];
-+ java.util.Arrays.fill(this.chunkPosCache, ChunkPos.INVALID_CHUNK_POS);
-+
-+ this.chunkCache = new net.minecraft.world.level.chunk.LevelChunk[CHUNK_CACHE_WIDTH * CHUNK_CACHE_WIDTH];
-+
-+ final ExplosionBlockCache[] blockCache = new ExplosionBlockCache[BLOCK_EXPLOSION_CACHE_WIDTH * BLOCK_EXPLOSION_CACHE_WIDTH * BLOCK_EXPLOSION_CACHE_WIDTH];
-+ // use initial cache value that is most likely to be used: the source position
-+ final ExplosionBlockCache initialCache;
-+ {
-+ final int blockX = Mth.floor(this.x);
-+ final int blockY = Mth.floor(this.y);
-+ final int blockZ = Mth.floor(this.z);
-+
-+ final long key = BlockPos.asLong(blockX, blockY, blockZ);
-+
-+ initialCache = this.getOrCacheExplosionBlock(blockX, blockY, blockZ, key, true);
-+ }
-+ // only ~1/3rd of the loop iterations in vanilla will result in a ray, as it is iterating the perimeter of
-+ // a 16x16x16 cube
-+ // we can cache the rays and their normals as well, so that we eliminate the excess iterations / checks and
-+ // calculations in one go
-+ // additional aggressive caching of block retrieval is very significant, as at low power (i.e tnt) most
-+ // block retrievals are not unique
-+ for (int ray = 0, len = CACHED_RAYS.length; ray < len;) {
-+ {
-+ {
-+ {
-+ ExplosionBlockCache cachedBlock = initialCache;
-+
-+ double d0 = CACHED_RAYS[ray];
-+ double d1 = CACHED_RAYS[ray + 1];
-+ double d2 = CACHED_RAYS[ray + 2];
-+ ray += 3;
-+ // Paper end - optimise explosions
- float f = this.radius * (0.7F + this.level.random.nextFloat() * 0.6F);
- double d4 = this.x;
- double d5 = this.y;
- double d6 = this.z;
-
- for (float f1 = 0.3F; f > 0.0F; f -= 0.22500001F) {
-- BlockPos blockposition = BlockPos.containing(d4, d5, d6);
-- BlockState iblockdata = this.level.getBlockState(blockposition);
-- if (!iblockdata.isDestroyable()) continue; // Paper - Protect Bedrock and End Portal/Frames from being destroyed
-- FluidState fluid = iblockdata.getFluidState(); // Paper - Perf: Optimize call to getFluid for explosions
-+ // Paper start - optimise explosions
-+ final int blockX = Mth.floor(d4);
-+ final int blockY = Mth.floor(d5);
-+ final int blockZ = Mth.floor(d6);
-+
-+ final long key = BlockPos.asLong(blockX, blockY, blockZ);
-+
-+ if (cachedBlock.key != key) {
-+ final int cacheKey =
-+ (blockX & BLOCK_EXPLOSION_CACHE_MASK) |
-+ (blockY & BLOCK_EXPLOSION_CACHE_MASK) << (BLOCK_EXPLOSION_CACHE_SHIFT) |
-+ (blockZ & BLOCK_EXPLOSION_CACHE_MASK) << (BLOCK_EXPLOSION_CACHE_SHIFT + BLOCK_EXPLOSION_CACHE_SHIFT);
-+ cachedBlock = blockCache[cacheKey];
-+ if (cachedBlock == null || cachedBlock.key != key) {
-+ blockCache[cacheKey] = cachedBlock = this.getOrCacheExplosionBlock(blockX, blockY, blockZ, key, true);
-+ }
-+ }
-
-- if (!this.level.isInWorldBounds(blockposition)) {
-+ if (cachedBlock.outOfWorld) {
- break;
- }
-
-- Optional<Float> optional = this.damageCalculator.getBlockExplosionResistance(this, this.level, blockposition, iblockdata, fluid);
-+ BlockPos blockposition = cachedBlock.immutablePos;
-+ BlockState iblockdata = cachedBlock.blockState;
-+ // Paper end - optimise explosions
-
-- if (optional.isPresent()) {
-- f -= ((Float) optional.get() + 0.3F) * 0.3F;
-- }
-+ if (!iblockdata.isDestroyable()) continue; // Paper
-+ // Paper - optimise explosions
-
-- if (f > 0.0F && this.damageCalculator.shouldBlockExplode(this, this.level, blockposition, iblockdata, f)) {
-+ f -= cachedBlock.resistance; // Paper - optimise explosions
-+
-+ if (f > 0.0F && cachedBlock.shouldExplode == null) { // Paper - optimise explosions
-+ // Paper start - optimise explosions
-+ // note: we expect shouldBlockExplode to be pure with respect to power, as Vanilla currently is.
-+ // basically, it is unused, which allows us to cache the result
-+ final boolean shouldExplode = this.damageCalculator.shouldBlockExplode(this, this.level, cachedBlock.immutablePos, cachedBlock.blockState, f);
-+ cachedBlock.shouldExplode = shouldExplode ? Boolean.TRUE : Boolean.FALSE;
-+ if (shouldExplode && (this.fire || !cachedBlock.blockState.isAir())) {
-+ // Paper end - optimise explosions
- set.add(blockposition);
- // Paper start - prevent headless pistons from forming
- if (!io.papermc.paper.configuration.GlobalConfiguration.get().unsupportedSettings.allowHeadlessPistons && iblockdata.getBlock() == Blocks.MOVING_PISTON) {
-@@ -215,11 +528,12 @@ public class Explosion {
- }
- }
- // Paper end - prevent headless pistons from forming
-+ } // Paper - optimise explosions
- }
-
-- d4 += d0 * 0.30000001192092896D;
-- d5 += d1 * 0.30000001192092896D;
-- d6 += d2 * 0.30000001192092896D;
-+ d4 += d0; // Paper - optimise explosions
-+ d5 += d1; // Paper - optimise explosions
-+ d6 += d2; // Paper - optimise explosions
- }
- }
- }
-@@ -239,6 +553,8 @@ public class Explosion {
- Vec3 vec3d = new Vec3(this.x, this.y, this.z);
- Iterator iterator = list.iterator();
-
-+ final BlockPos.MutableBlockPos blockPos = new BlockPos.MutableBlockPos(); // Paper - optimise explosions
-+
- while (iterator.hasNext()) {
- Entity entity = (Entity) iterator.next();
-
-@@ -274,11 +590,11 @@ public class Explosion {
- for (EnderDragonPart entityComplexPart : ((EnderDragon) entity).subEntities) {
- // Calculate damage separately for each EntityComplexPart
- if (list.contains(entityComplexPart)) {
-- entityComplexPart.hurt(this.damageSource, this.damageCalculator.getEntityDamageAmount(this, entity));
-+ entityComplexPart.hurt(this.damageSource, this.damageCalculator.getEntityDamageAmount(this, entityComplexPart, getSeenFraction(vec3d, entityComplexPart, blockCache, blockPos))); // Paper - actually optimise explosions and use the right entity to calculate the damage
- }
- }
- } else {
-- entity.hurt(this.damageSource, this.damageCalculator.getEntityDamageAmount(this, entity));
-+ entity.hurt(this.damageSource, this.damageCalculator.getEntityDamageAmount(this, entity, getSeenFraction(vec3d, entity, blockCache, blockPos))); // Paper - actually optimise explosions
- }
-
- if (entity.lastDamageCancelled) { // SPIGOT-5339, SPIGOT-6252, SPIGOT-6777: Skip entity if damage event was cancelled
-@@ -287,7 +603,7 @@ public class Explosion {
- // CraftBukkit end
- }
-
-- double d12 = (1.0D - d7) * this.getBlockDensity(vec3d, entity); // Paper - Optimize explosions
-+ double d12 = (1.0D - d7) * this.getBlockDensity(vec3d, entity, blockCache, blockPos); // Paper - Optimize explosions
- double d13;
-
- if (entity instanceof LivingEntity) {
-@@ -334,6 +650,9 @@ public class Explosion {
- }
- }
-
-+ this.blockCache = null; // Paper - optimise explosions
-+ this.chunkPosCache = null; // Paper - optimise explosions
-+ this.chunkCache = null; // Paper - optimise explosions
- }
-
- public void finalizeExplosion(boolean particles) {
-@@ -547,14 +866,14 @@ public class Explosion {
- private BlockInteraction() {}
- }
- // Paper start - Optimize explosions
-- private float getBlockDensity(Vec3 vec3d, Entity entity) {
-+ private float getBlockDensity(Vec3 vec3d, Entity entity, ExplosionBlockCache[] blockCache, BlockPos.MutableBlockPos blockPos) { // Paper - optimise explosions
- if (!this.level.paperConfig().environment.optimizeExplosions) {
-- return getSeenPercent(vec3d, entity);
-+ return this.getSeenFraction(vec3d, entity, blockCache, blockPos); // Paper - optimise explosions
- }
- CacheKey key = new CacheKey(this, entity.getBoundingBox());
- Float blockDensity = this.level.explosionDensityCache.get(key);
- if (blockDensity == null) {
-- blockDensity = getSeenPercent(vec3d, entity);
-+ blockDensity = this.getSeenFraction(vec3d, entity, blockCache, blockPos); // Paper - optimise explosions;
- this.level.explosionDensityCache.put(key, blockDensity);
- }
-
-diff --git a/src/main/java/net/minecraft/world/level/ExplosionDamageCalculator.java b/src/main/java/net/minecraft/world/level/ExplosionDamageCalculator.java
-index 4085949accf23728de9a2ff14249cd6ca9b71f8a..5b93c038331c1750260a42726f5bfb97998d93a9 100644
---- a/src/main/java/net/minecraft/world/level/ExplosionDamageCalculator.java
-+++ b/src/main/java/net/minecraft/world/level/ExplosionDamageCalculator.java
-@@ -22,11 +22,17 @@ public class ExplosionDamageCalculator {
- return true;
- }
-
-+ @io.papermc.paper.annotation.DoNotUse @Deprecated // Paper
- public float getEntityDamageAmount(Explosion explosion, Entity entity) {
-+ // Paper start - actually optimise explosions
-+ return this.getEntityDamageAmount(explosion, entity, Explosion.getSeenPercent(explosion.center(), entity));
-+ }
-+ public float getEntityDamageAmount(Explosion explosion, Entity entity, double seenPercent) {
-+ // Paper end - actually optimise explosions
- float f = explosion.radius() * 2.0F;
- Vec3 vec3 = explosion.center();
- double d = Math.sqrt(entity.distanceToSqr(vec3)) / (double)f;
-- double e = (1.0 - d) * (double)Explosion.getSeenPercent(vec3, entity);
-+ double e = (1.0 - d) * seenPercent; // Paper - actually optimise explosions
- return (float)((e * e + e) / 2.0 * 7.0 * (double)f + 1.0);
- }
- }
diff --git a/patches/unapplied/server/1036-Optimise-chunk-tick-iteration.patch b/patches/unapplied/server/1036-Optimise-chunk-tick-iteration.patch
deleted file mode 100644
index da9130527c..0000000000
--- a/patches/unapplied/server/1036-Optimise-chunk-tick-iteration.patch
+++ /dev/null
@@ -1,380 +0,0 @@
-From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
-From: Spottedleaf <[email protected]>
-Date: Sat, 23 Sep 2023 21:36:36 -0700
-Subject: [PATCH] Optimise chunk tick iteration
-
-When per-player mob spawning is enabled we do not need to randomly
-shuffle the chunk list. Additionally, we can use the NearbyPlayers
-class to quickly retrieve nearby players instead of possible
-searching all players on the server.
-
-diff --git a/src/main/java/io/papermc/paper/util/player/NearbyPlayers.java b/src/main/java/io/papermc/paper/util/player/NearbyPlayers.java
-index c3ce8a42dddd76b7189ad5685b23f9d9f8ccadb3..f164256d59b761264876ca0c85f812d101bfd5de 100644
---- a/src/main/java/io/papermc/paper/util/player/NearbyPlayers.java
-+++ b/src/main/java/io/papermc/paper/util/player/NearbyPlayers.java
-@@ -17,7 +17,8 @@ public final class NearbyPlayers {
- GENERAL_SMALL,
- GENERAL_REALLY_SMALL,
- TICK_VIEW_DISTANCE,
-- VIEW_DISTANCE;
-+ VIEW_DISTANCE, // Paper - optimise chunk iteration
-+ SPAWN_RANGE, // Paper - optimise chunk iteration
- }
-
- private static final NearbyMapType[] MOB_TYPES = NearbyMapType.values();
-@@ -26,10 +27,12 @@ public final class NearbyPlayers {
- private static final int GENERAL_AREA_VIEW_DISTANCE = 33;
- private static final int GENERAL_SMALL_VIEW_DISTANCE = 10;
- private static final int GENERAL_REALLY_SMALL_VIEW_DISTANCE = 3;
-+ private static final int SPAWN_RANGE_VIEW_DISTANCE = net.minecraft.server.level.DistanceManager.MOB_SPAWN_RANGE; // Paper - optimise chunk iteration
-
- public static final int GENERAL_AREA_VIEW_DISTANCE_BLOCKS = (GENERAL_AREA_VIEW_DISTANCE << 4);
- public static final int GENERAL_SMALL_AREA_VIEW_DISTANCE_BLOCKS = (GENERAL_SMALL_VIEW_DISTANCE << 4);
- public static final int GENERAL_REALLY_SMALL_AREA_VIEW_DISTANCE_BLOCKS = (GENERAL_REALLY_SMALL_VIEW_DISTANCE << 4);
-+ public static final int SPAWN_RANGE_VIEW_DISTANCE_BLOCKS = (SPAWN_RANGE_VIEW_DISTANCE << 4); // Paper - optimise chunk iteration
-
- private final ServerLevel world;
- private final Reference2ReferenceOpenHashMap<ServerPlayer, TrackedPlayer[]> players = new Reference2ReferenceOpenHashMap<>();
-@@ -80,6 +83,7 @@ public final class NearbyPlayers {
- players[NearbyMapType.GENERAL_REALLY_SMALL.ordinal()].update(chunk.x, chunk.z, GENERAL_REALLY_SMALL_VIEW_DISTANCE);
- players[NearbyMapType.TICK_VIEW_DISTANCE.ordinal()].update(chunk.x, chunk.z, ChunkSystem.getTickViewDistance(player));
- players[NearbyMapType.VIEW_DISTANCE.ordinal()].update(chunk.x, chunk.z, ChunkSystem.getLoadViewDistance(player));
-+ players[NearbyMapType.SPAWN_RANGE.ordinal()].update(chunk.x, chunk.z, SPAWN_RANGE_VIEW_DISTANCE); // Paper - optimise chunk iteration
- }
-
- public TrackedChunk getChunk(final ChunkPos pos) {
-diff --git a/src/main/java/net/minecraft/server/level/ChunkHolder.java b/src/main/java/net/minecraft/server/level/ChunkHolder.java
-index 2b998bdbe49bf8211b755e0eb7c1bf13ac280eab..627a88ec8c3b215b19b55a6d461c8754b4fcd1e8 100644
---- a/src/main/java/net/minecraft/server/level/ChunkHolder.java
-+++ b/src/main/java/net/minecraft/server/level/ChunkHolder.java
-@@ -79,11 +79,19 @@ public class ChunkHolder {
-
- // Paper start
- public void onChunkAdd() {
--
-+ // Paper start - optimise chunk tick iteration
-+ if (this.needsBroadcastChanges()) {
-+ this.chunkMap.needsChangeBroadcasting.add(this);
-+ }
-+ // Paper end - optimise chunk tick iteration
- }
-
- public void onChunkRemove() {
--
-+ // Paper start - optimise chunk tick iteration
-+ if (this.needsBroadcastChanges()) {
-+ this.chunkMap.needsChangeBroadcasting.remove(this);
-+ }
-+ // Paper end - optimise chunk tick iteration
- }
- // Paper end
-
-@@ -230,7 +238,7 @@ public class ChunkHolder {
-
- if (i < 0 || i >= this.changedBlocksPerSection.length) return; // CraftBukkit - SPIGOT-6086, SPIGOT-6296
- if (this.changedBlocksPerSection[i] == null) {
-- this.hasChangedSections = true;
-+ this.hasChangedSections = true; this.addToBroadcastMap(); // Paper - optimise chunk tick iteration
- this.changedBlocksPerSection[i] = new ShortOpenHashSet();
- }
-
-@@ -254,6 +262,7 @@ public class ChunkHolder {
- int k = this.lightEngine.getMaxLightSection();
-
- if (y >= j && y <= k) {
-+ this.addToBroadcastMap(); // Paper - optimise chunk tick iteration
- int l = y - j;
-
- if (lightType == LightLayer.SKY) {
-@@ -268,8 +277,19 @@ public class ChunkHolder {
- }
- }
-
-+ // Paper start - optimise chunk tick iteration
-+ public final boolean needsBroadcastChanges() {
-+ return this.hasChangedSections || !this.skyChangedLightSectionFilter.isEmpty() || !this.blockChangedLightSectionFilter.isEmpty();
-+ }
-+
-+ private void addToBroadcastMap() {
-+ io.papermc.paper.util.TickThread.ensureTickThread(this.chunkMap.level, this.pos, "Asynchronous ChunkHolder update is not allowed");
-+ this.chunkMap.needsChangeBroadcasting.add(this);
-+ }
-+ // Paper end - optimise chunk tick iteration
-+
- public void broadcastChanges(LevelChunk chunk) {
-- if (this.hasChangedSections || !this.skyChangedLightSectionFilter.isEmpty() || !this.blockChangedLightSectionFilter.isEmpty()) {
-+ if (this.needsBroadcastChanges()) { // Paper - optimise chunk tick iteration; moved into above, other logic needs to call
- Level world = chunk.getLevel();
- List list;
-
-diff --git a/src/main/java/net/minecraft/server/level/ChunkMap.java b/src/main/java/net/minecraft/server/level/ChunkMap.java
-index 284f9548d62f9230c668bb1adb8cb8084b7cef7c..12109446fc76a39faee6cda042ca48b3fd3809f4 100644
---- a/src/main/java/net/minecraft/server/level/ChunkMap.java
-+++ b/src/main/java/net/minecraft/server/level/ChunkMap.java
-@@ -191,6 +191,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
- this.playerEntityTrackerTrackMaps[i].remove(player);
- }
- // Paper end - use distance map to optimise tracker
-+ this.playerMobSpawnMap.remove(player); // Paper - optimise chunk tick iteration
- }
-
- void updateMaps(ServerPlayer player) {
-@@ -240,6 +241,10 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
- }
- public final io.papermc.paper.util.player.NearbyPlayers nearbyPlayers;
- // Paper end
-+ // Paper start - optimise chunk tick iteration
-+ public final it.unimi.dsi.fastutil.objects.ReferenceOpenHashSet<ChunkHolder> needsChangeBroadcasting = new it.unimi.dsi.fastutil.objects.ReferenceOpenHashSet<>();
-+ public final com.destroystokyo.paper.util.misc.PlayerAreaMap playerMobSpawnMap = new com.destroystokyo.paper.util.misc.PlayerAreaMap(this.pooledLinkedPlayerHashSets);
-+ // Paper end - optimise chunk tick iteration
-
- public ChunkMap(ServerLevel world, LevelStorageSource.LevelStorageAccess session, DataFixer dataFixer, StructureTemplateManager structureTemplateManager, Executor executor, BlockableEventLoop<Runnable> mainThreadExecutor, LightChunkGetter chunkProvider, ChunkGenerator chunkGenerator, ChunkProgressListener worldGenerationProgressListener, ChunkStatusUpdateListener chunkStatusChangeListener, Supplier<DimensionDataStorage> persistentStateManagerFactory, int viewDistance, boolean dsync) {
- super(session.getDimensionPath(world.dimension()).resolve("region"), dataFixer, dsync);
-@@ -408,7 +413,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
- }
- // Paper end - Optional per player mob spawns
-
-- private static double euclideanDistanceSquared(ChunkPos pos, Entity entity) {
-+ public static double euclideanDistanceSquared(ChunkPos pos, Entity entity) { // Paper - optimise chunk iteration; public
- double d0 = (double) SectionPos.sectionToBlockCoord(pos.x, 8);
- double d1 = (double) SectionPos.sectionToBlockCoord(pos.z, 8);
- double d2 = d0 - entity.getX();
-diff --git a/src/main/java/net/minecraft/server/level/DistanceManager.java b/src/main/java/net/minecraft/server/level/DistanceManager.java
-index c80a625f7289e3bb33c6851d2072957e153ca1fb..7c425ac50c83757b66a2178bc19d4c920b82f12f 100644
---- a/src/main/java/net/minecraft/server/level/DistanceManager.java
-+++ b/src/main/java/net/minecraft/server/level/DistanceManager.java
-@@ -50,7 +50,7 @@ public abstract class DistanceManager {
- private static final int INITIAL_TICKET_LIST_CAPACITY = 4;
- final Long2ObjectMap<ObjectSet<ServerPlayer>> playersPerChunk = new Long2ObjectOpenHashMap();
- // Paper - rewrite chunk system
-- private final DistanceManager.FixedPlayerDistanceChunkTracker naturalSpawnChunkCounter = new DistanceManager.FixedPlayerDistanceChunkTracker(8);
-+ public static final int MOB_SPAWN_RANGE = 8; //private final DistanceManager.FixedPlayerDistanceChunkTracker naturalSpawnChunkCounter = new DistanceManager.FixedPlayerDistanceChunkTracker(8); // Paper - optimise chunk tick iteration
- // Paper - rewrite chunk system
- private final ChunkMap chunkMap; // Paper
-
-@@ -135,7 +135,7 @@ public abstract class DistanceManager {
- long i = chunkcoordintpair.toLong();
-
- // Paper - no longer used
-- this.naturalSpawnChunkCounter.update(i, 0, true);
-+ //this.naturalSpawnChunkCounter.update(i, 0, true); // Paper - optimise chunk tick iteration
- //this.playerTicketManager.update(i, 0, true); // Paper - no longer used
- //this.tickingTicketsTracker.addTicket(TicketType.PLAYER, chunkcoordintpair, this.getPlayerTicketLevel(), chunkcoordintpair); // Paper - no longer used
- }
-@@ -149,7 +149,7 @@ public abstract class DistanceManager {
- if (objectset != null) objectset.remove(player); // Paper - some state corruption happens here, don't crash, clean up gracefully
- if (objectset == null || objectset.isEmpty()) { // Paper
- this.playersPerChunk.remove(i);
-- this.naturalSpawnChunkCounter.update(i, Integer.MAX_VALUE, false);
-+ //this.naturalSpawnChunkCounter.update(i, Integer.MAX_VALUE, false); // Paper - optimise chunk tick iteration
- //this.playerTicketManager.update(i, Integer.MAX_VALUE, false); // Paper - no longer used
- //this.tickingTicketsTracker.removeTicket(TicketType.PLAYER, chunkcoordintpair, this.getPlayerTicketLevel(), chunkcoordintpair); // Paper - no longer used
- }
-@@ -191,13 +191,11 @@ public abstract class DistanceManager {
- }
-
- public int getNaturalSpawnChunkCount() {
-- this.naturalSpawnChunkCounter.runAllUpdates();
-- return this.naturalSpawnChunkCounter.chunks.size();
-+ return this.chunkMap.playerMobSpawnMap.size(); // Paper - optimise chunk tick iteration
- }
-
- public boolean hasPlayersNearby(long chunkPos) {
-- this.naturalSpawnChunkCounter.runAllUpdates();
-- return this.naturalSpawnChunkCounter.chunks.containsKey(chunkPos);
-+ return this.chunkMap.playerMobSpawnMap.getObjectsInRange(chunkPos) != null; // Paper - optimise chunk tick iteration
- }
-
- public String getDebugStatus() {
-diff --git a/src/main/java/net/minecraft/server/level/ServerChunkCache.java b/src/main/java/net/minecraft/server/level/ServerChunkCache.java
-index 2b33a3d8fdb86024acb2a3ee9d0a4a7dd4989c98..366c0c9b45a819f7f94ebe3e49b8ab7f9edf9ce7 100644
---- a/src/main/java/net/minecraft/server/level/ServerChunkCache.java
-+++ b/src/main/java/net/minecraft/server/level/ServerChunkCache.java
-@@ -508,18 +508,10 @@ public class ServerChunkCache extends ChunkSource {
-
- gameprofilerfiller.push("pollingChunks");
- gameprofilerfiller.push("filteringLoadedChunks");
-- List<ServerChunkCache.ChunkAndHolder> list = Lists.newArrayListWithCapacity(this.chunkMap.size());
-- Iterator iterator = this.chunkMap.getChunks().iterator();
-+ // Paper - optimise chunk tick iteration
- if (this.level.getServer().tickRateManager().runsNormally()) this.level.timings.chunkTicks.startTiming(); // Paper
-
-- while (iterator.hasNext()) {
-- ChunkHolder playerchunk = (ChunkHolder) iterator.next();
-- LevelChunk chunk = playerchunk.getTickingChunk();
--
-- if (chunk != null) {
-- list.add(new ServerChunkCache.ChunkAndHolder(chunk, playerchunk));
-- }
-- }
-+ // Paper - optimise chunk tick iteration
-
- if (this.level.getServer().tickRateManager().runsNormally()) {
- gameprofilerfiller.popPush("naturalSpawnCount");
-@@ -554,38 +546,109 @@ public class ServerChunkCache extends ChunkSource {
- gameprofilerfiller.popPush("spawnAndTick");
- boolean flag = this.level.getGameRules().getBoolean(GameRules.RULE_DOMOBSPAWNING) && !this.level.players().isEmpty(); // CraftBukkit
-
-- Util.shuffle(list, this.level.random);
-- // Paper start - PlayerNaturallySpawnCreaturesEvent
-- int chunkRange = level.spigotConfig.mobSpawnRange;
-- chunkRange = (chunkRange > level.spigotConfig.viewDistance) ? (byte) level.spigotConfig.viewDistance : chunkRange;
-- chunkRange = Math.min(chunkRange, 8);
-- for (ServerPlayer entityPlayer : this.level.players()) {
-- entityPlayer.playerNaturallySpawnedEvent = new com.destroystokyo.paper.event.entity.PlayerNaturallySpawnCreaturesEvent(entityPlayer.getBukkitEntity(), (byte) chunkRange);
-- entityPlayer.playerNaturallySpawnedEvent.callEvent();
-+ // Paper start - optimise chunk tick iteration
-+ ChunkMap playerChunkMap = this.chunkMap;
-+ for (ServerPlayer player : this.level.players) {
-+ if (!player.affectsSpawning || player.isSpectator()) {
-+ playerChunkMap.playerMobSpawnMap.remove(player);
-+ player.playerNaturallySpawnedEvent = null;
-+ player.lastEntitySpawnRadiusSquared = -1.0;
-+ continue;
-+ }
-+
-+ int viewDistance = io.papermc.paper.chunk.system.ChunkSystem.getTickViewDistance(player);
-+
-+ // copied and modified from isOutisdeRange
-+ int chunkRange = (int)level.spigotConfig.mobSpawnRange;
-+ chunkRange = (chunkRange > viewDistance) ? viewDistance : chunkRange;
-+ chunkRange = (chunkRange > DistanceManager.MOB_SPAWN_RANGE) ? DistanceManager.MOB_SPAWN_RANGE : chunkRange;
-+
-+ com.destroystokyo.paper.event.entity.PlayerNaturallySpawnCreaturesEvent event = new com.destroystokyo.paper.event.entity.PlayerNaturallySpawnCreaturesEvent(player.getBukkitEntity(), (byte)chunkRange);
-+ event.callEvent();
-+ if (event.isCancelled() || event.getSpawnRadius() < 0) {
-+ playerChunkMap.playerMobSpawnMap.remove(player);
-+ player.playerNaturallySpawnedEvent = null;
-+ player.lastEntitySpawnRadiusSquared = -1.0;
-+ continue;
-+ }
-+
-+ int range = Math.min(event.getSpawnRadius(), DistanceManager.MOB_SPAWN_RANGE); // limit to max spawn range
-+ int chunkX = io.papermc.paper.util.CoordinateUtils.getChunkCoordinate(player.getX());
-+ int chunkZ = io.papermc.paper.util.CoordinateUtils.getChunkCoordinate(player.getZ());
-+
-+ playerChunkMap.playerMobSpawnMap.addOrUpdate(player, chunkX, chunkZ, range);
-+ player.lastEntitySpawnRadiusSquared = (double)((range << 4) * (range << 4)); // used in anyPlayerCloseEnoughForSpawning
-+ player.playerNaturallySpawnedEvent = event;
- }
-- // Paper end - PlayerNaturallySpawnCreaturesEvent
-+ // Paper end - optimise chunk tick iteration
- int l = this.level.getGameRules().getInt(GameRules.RULE_RANDOMTICKING);
- boolean flag1 = this.level.ticksPerSpawnCategory.getLong(org.bukkit.entity.SpawnCategory.ANIMAL) != 0L && this.level.getLevelData().getGameTime() % this.level.ticksPerSpawnCategory.getLong(org.bukkit.entity.SpawnCategory.ANIMAL) == 0L; // CraftBukkit
-- Iterator iterator1 = list.iterator();
-+ // Paper - optimise chunk tick iteration
-
- int chunksTicked = 0; // Paper
-- while (iterator1.hasNext()) {
-- ServerChunkCache.ChunkAndHolder chunkproviderserver_a = (ServerChunkCache.ChunkAndHolder) iterator1.next();
-- LevelChunk chunk1 = chunkproviderserver_a.chunk;
-+ // Paper start - optimise chunk tick iteration
-+ io.papermc.paper.util.player.NearbyPlayers nearbyPlayers = this.chunkMap.getNearbyPlayers(); // Paper - optimise chunk tick iteration
-+ Iterator<LevelChunk> chunkIterator;
-+ if (this.level.paperConfig().entities.spawning.perPlayerMobSpawns) {
-+ chunkIterator = this.tickingChunks.iterator();
-+ } else {
-+ chunkIterator = this.tickingChunks.unsafeIterator();
-+ List<LevelChunk> shuffled = Lists.newArrayListWithCapacity(this.tickingChunks.size());
-+ while (chunkIterator.hasNext()) {
-+ shuffled.add(chunkIterator.next());
-+ }
-+ Util.shuffle(shuffled, this.level.random);
-+ chunkIterator = shuffled.iterator();
-+ }
-+ try {
-+ // Paper end - optimise chunk tick iteration
-+ while (chunkIterator.hasNext()) {
-+ LevelChunk chunk1 = chunkIterator.next();
-+ // Paper end - optimise chunk tick iteration
- ChunkPos chunkcoordintpair = chunk1.getPos();
-
-- if (this.level.isNaturalSpawningAllowed(chunkcoordintpair) && this.chunkMap.anyPlayerCloseEnoughForSpawning(chunkcoordintpair)) {
-+ // Paper start - optimise chunk tick iteration
-+ com.destroystokyo.paper.util.maplist.ReferenceList<ServerPlayer> playersNearby
-+ = nearbyPlayers.getPlayers(chunkcoordintpair, io.papermc.paper.util.player.NearbyPlayers.NearbyMapType.SPAWN_RANGE);
-+ if (playersNearby == null) {
-+ continue;
-+ }
-+ Object[] rawData = playersNearby.getRawData();
-+ boolean spawn = false;
-+ boolean tick = false;
-+ for (int itr = 0, len = playersNearby.size(); itr < len; ++itr) {
-+ ServerPlayer player = (ServerPlayer)rawData[itr];
-+ if (player.isSpectator()) {
-+ continue;
-+ }
-+
-+ double distance = ChunkMap.euclideanDistanceSquared(chunkcoordintpair, player);
-+ spawn |= player.lastEntitySpawnRadiusSquared >= distance;
-+ tick |= ((double)io.papermc.paper.util.player.NearbyPlayers.SPAWN_RANGE_VIEW_DISTANCE_BLOCKS) * ((double)io.papermc.paper.util.player.NearbyPlayers.SPAWN_RANGE_VIEW_DISTANCE_BLOCKS) >= distance;
-+ if (spawn & tick) {
-+ break;
-+ }
-+ }
-+ if (tick && chunk1.chunkStatus.isOrAfter(net.minecraft.server.level.FullChunkStatus.ENTITY_TICKING)) {
-+ // Paper end - optimise chunk tick iteration
- chunk1.incrementInhabitedTime(j);
-- if (flag && (this.spawnEnemies || this.spawnFriendlies) && this.level.getWorldBorder().isWithinBounds(chunkcoordintpair) && this.chunkMap.anyPlayerCloseEnoughForSpawning(chunkcoordintpair, true)) { // Spigot
-+ if (spawn && flag && (this.spawnEnemies || this.spawnFriendlies) && this.level.getWorldBorder().isWithinBounds(chunkcoordintpair)) { // Spigot // Paper - optimise chunk tick iteration
- NaturalSpawner.spawnForChunk(this.level, chunk1, spawnercreature_d, this.spawnFriendlies, this.spawnEnemies, flag1);
- }
-
-- if (this.level.shouldTickBlocksAt(chunkcoordintpair.toLong())) {
-+ if (true || this.level.shouldTickBlocksAt(chunkcoordintpair.toLong())) { // Paper - optimise chunk tick iteration
- this.level.tickChunk(chunk1, l);
- if ((chunksTicked++ & 1) == 0) net.minecraft.server.MinecraftServer.getServer().executeMidTickTasks(); // Paper
- }
- }
- }
-+ // Paper start - optimise chunk tick iteration
-+ } finally {
-+ if (chunkIterator instanceof io.papermc.paper.util.maplist.IteratorSafeOrderedReferenceSet.Iterator safeIterator) {
-+ safeIterator.finishedIterating();
-+ }
-+ }
-+ // Paper end - optimise chunk tick iteration
- this.level.timings.chunkTicks.stopTiming(); // Paper
-
- gameprofilerfiller.popPush("customSpawners");
-@@ -597,11 +660,23 @@ public class ServerChunkCache extends ChunkSource {
- }
-
- gameprofilerfiller.popPush("broadcast");
-- list.forEach((chunkproviderserver_a1) -> {
-+ // Paper - optimise chunk tick iteration
- this.level.timings.broadcastChunkUpdates.startTiming(); // Paper - timing
-- chunkproviderserver_a1.holder.broadcastChanges(chunkproviderserver_a1.chunk);
-+ // Paper start - optimise chunk tick iteration
-+ if (!this.chunkMap.needsChangeBroadcasting.isEmpty()) {
-+ it.unimi.dsi.fastutil.objects.ReferenceOpenHashSet<ChunkHolder> copy = this.chunkMap.needsChangeBroadcasting.clone();
-+ this.chunkMap.needsChangeBroadcasting.clear();
-+ for (ChunkHolder holder : copy) {
-+ holder.broadcastChanges(holder.getFullChunkNowUnchecked()); // LevelChunks are NEVER unloaded
-+ if (holder.needsBroadcastChanges()) {
-+ // I DON'T want to KNOW what DUMB plugins might be doing.
-+ this.chunkMap.needsChangeBroadcasting.add(holder);
-+ }
-+ }
-+ }
-+ // Paper end - optimise chunk tick iteration
- this.level.timings.broadcastChunkUpdates.stopTiming(); // Paper - timing
-- });
-+ // Paper - optimise chunk tick iteration
- gameprofilerfiller.pop();
- gameprofilerfiller.pop();
- }
-diff --git a/src/main/java/net/minecraft/server/level/ServerPlayer.java b/src/main/java/net/minecraft/server/level/ServerPlayer.java
-index 17a6d43685f35a6978c2d941876a1f8a9a2c8b42..b3781efbd3edcf102fe1bda5d6149915dc1127c6 100644
---- a/src/main/java/net/minecraft/server/level/ServerPlayer.java
-+++ b/src/main/java/net/minecraft/server/level/ServerPlayer.java
-@@ -325,6 +325,9 @@ public class ServerPlayer extends Player {
- });
- }
- // Paper end - replace player chunk loader
-+ // Paper start - optimise chunk tick iteration
-+ public double lastEntitySpawnRadiusSquared = -1.0;
-+ // Paper end - optimise chunk tick iteration
-
- public ServerPlayer(MinecraftServer server, ServerLevel world, GameProfile profile, ClientInformation clientOptions) {
- super(world, world.getSharedSpawnPos(), world.getSharedSpawnAngle(), profile);
diff --git a/patches/unapplied/server/1037-Lag-compensation-ticks.patch b/patches/unapplied/server/1037-Lag-compensation-ticks.patch
deleted file mode 100644
index 40fbaa1e15..0000000000
--- a/patches/unapplied/server/1037-Lag-compensation-ticks.patch
+++ /dev/null
@@ -1,129 +0,0 @@
-From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
-From: Spottedleaf <[email protected]>
-Date: Sat, 23 Sep 2023 22:05:35 -0700
-Subject: [PATCH] Lag compensation ticks
-
-Areas affected by lag comepnsation:
- - Block breaking and destroying
- - Eating food items
-
-diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java
-index 2f263ef5120982b3167ab008a0e22b8cbc9b9fdd..f03f6922d15541c5491e5b37a3efa7ef0abef211 100644
---- a/src/main/java/net/minecraft/server/MinecraftServer.java
-+++ b/src/main/java/net/minecraft/server/MinecraftServer.java
-@@ -311,6 +311,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
-
- public volatile Thread shutdownThread; // Paper
- public volatile boolean abnormalExit = false; // Paper
-+ public static final long SERVER_INIT = System.nanoTime(); // Paper - Lag compensation
-
- public static <S extends MinecraftServer> S spin(Function<Thread, S> serverFactory) {
- AtomicReference<S> atomicreference = new AtomicReference();
-@@ -1693,6 +1694,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
- worldserver.hasPhysicsEvent = org.bukkit.event.block.BlockPhysicsEvent.getHandlerList().getRegisteredListeners().length > 0; // Paper - BlockPhysicsEvent
- worldserver.hasEntityMoveEvent = io.papermc.paper.event.entity.EntityMoveEvent.getHandlerList().getRegisteredListeners().length > 0; // Paper - Add EntityMoveEvent
- net.minecraft.world.level.block.entity.HopperBlockEntity.skipHopperEvents = worldserver.paperConfig().hopper.disableMoveEvent || org.bukkit.event.inventory.InventoryMoveItemEvent.getHandlerList().getRegisteredListeners().length == 0; // Paper - Perf: Optimize Hoppers
-+ worldserver.updateLagCompensationTick(); // Paper - lag compensation
-
- this.profiler.push(() -> {
- return worldserver + " " + worldserver.dimension().location();
-diff --git a/src/main/java/net/minecraft/server/level/ServerLevel.java b/src/main/java/net/minecraft/server/level/ServerLevel.java
-index c9405cbea1202e5603dde42637cf2a78592b92e1..8a5abc320137d045acba0c87cef9f2912d78b6fb 100644
---- a/src/main/java/net/minecraft/server/level/ServerLevel.java
-+++ b/src/main/java/net/minecraft/server/level/ServerLevel.java
-@@ -565,6 +565,17 @@ public class ServerLevel extends Level implements WorldGenLevel {
- return player != null && player.level() == this ? player : null;
- }
- // Paper end - optimise getPlayerByUUID
-+ // Paper start - lag compensation
-+ private long lagCompensationTick = net.minecraft.server.MinecraftServer.SERVER_INIT;
-+
-+ public long getLagCompensationTick() {
-+ return this.lagCompensationTick;
-+ }
-+
-+ public void updateLagCompensationTick() {
-+ this.lagCompensationTick = (System.nanoTime() - net.minecraft.server.MinecraftServer.SERVER_INIT) / (java.util.concurrent.TimeUnit.MILLISECONDS.toNanos(50L));
-+ }
-+ // Paper end - lag compensation
-
- // Add env and gen to constructor, IWorldDataServer -> WorldDataServer
- public ServerLevel(MinecraftServer minecraftserver, Executor executor, LevelStorageSource.LevelStorageAccess convertable_conversionsession, PrimaryLevelData iworlddataserver, ResourceKey<Level> resourcekey, LevelStem worlddimension, ChunkProgressListener worldloadlistener, boolean flag, long i, List<CustomSpawner> list, boolean flag1, @Nullable RandomSequences randomsequences, org.bukkit.World.Environment env, org.bukkit.generator.ChunkGenerator gen, org.bukkit.generator.BiomeProvider biomeProvider) {
-diff --git a/src/main/java/net/minecraft/server/level/ServerPlayerGameMode.java b/src/main/java/net/minecraft/server/level/ServerPlayerGameMode.java
-index ef3048a4748113538a0ee0af5b526b2cd51d5c29..a7b217ddbcbf92513bd38101fdfca2075505e267 100644
---- a/src/main/java/net/minecraft/server/level/ServerPlayerGameMode.java
-+++ b/src/main/java/net/minecraft/server/level/ServerPlayerGameMode.java
-@@ -124,7 +124,7 @@ public class ServerPlayerGameMode {
- }
-
- public void tick() {
-- this.gameTicks = MinecraftServer.currentTick; // CraftBukkit;
-+ this.gameTicks = (int)this.level.getLagCompensationTick(); // CraftBukkit; // Paper - lag compensation
- BlockState iblockdata;
-
- if (this.hasDelayedDestroy) {
-diff --git a/src/main/java/net/minecraft/world/entity/LivingEntity.java b/src/main/java/net/minecraft/world/entity/LivingEntity.java
-index 28b4e9ebc35058c3e094c1f8bd87130e288cea33..e9bb7feb591032904516d1b9374f486d8a7d066c 100644
---- a/src/main/java/net/minecraft/world/entity/LivingEntity.java
-+++ b/src/main/java/net/minecraft/world/entity/LivingEntity.java
-@@ -3849,6 +3849,10 @@ public abstract class LivingEntity extends Entity implements Attackable {
- this.getEntityData().resendPossiblyDesyncedDataValues(java.util.List.of(DATA_LIVING_ENTITY_FLAGS), serverPlayer);
- }
- // Paper end - Properly cancel usable items
-+ // Paper start - lag compensate eating
-+ protected long eatStartTime;
-+ protected int totalEatTimeTicks;
-+ // Paper end - lag compensate eating
- private void updatingUsingItem() {
- if (this.isUsingItem()) {
- if (ItemStack.isSameItem(this.getItemInHand(this.getUsedItemHand()), this.useItem)) {
-@@ -3867,7 +3871,12 @@ public abstract class LivingEntity extends Entity implements Attackable {
- this.triggerItemUseEffects(stack, 5);
- }
-
-- if (--this.useItemRemaining == 0 && !this.level().isClientSide && !stack.useOnRelease()) {
-+ // Paper start - lag compensate eating
-+ // we add 1 to the expected time to avoid lag compensating when we should not
-+ boolean shouldLagCompensate = this.useItem.getItem().isEdible() && this.eatStartTime != -1 && (System.nanoTime() - this.eatStartTime) > ((1 + this.totalEatTimeTicks) * 50 * (1000 * 1000));
-+ if ((--this.useItemRemaining == 0 || shouldLagCompensate) && !this.level().isClientSide && !stack.useOnRelease()) {
-+ this.useItemRemaining = 0;
-+ // Paper end - lag compensate eating
- this.completeUsingItem();
- }
-
-@@ -3915,7 +3924,10 @@ public abstract class LivingEntity extends Entity implements Attackable {
-
- if (!itemstack.isEmpty() && !this.isUsingItem() || forceUpdate) { // Paper - Prevent consuming the wrong itemstack
- this.useItem = itemstack;
-- this.useItemRemaining = itemstack.getUseDuration();
-+ // Paper start - lag compensate eating
-+ this.useItemRemaining = this.totalEatTimeTicks = itemstack.getUseDuration();
-+ this.eatStartTime = System.nanoTime();
-+ // Paper end - lag compensate eating
- if (!this.level().isClientSide) {
- this.setLivingEntityFlag(1, true);
- this.setLivingEntityFlag(2, hand == InteractionHand.OFF_HAND);
-@@ -3940,7 +3952,10 @@ public abstract class LivingEntity extends Entity implements Attackable {
- }
- } else if (!this.isUsingItem() && !this.useItem.isEmpty()) {
- this.useItem = ItemStack.EMPTY;
-- this.useItemRemaining = 0;
-+ // Paper start - lag compensate eating
-+ this.useItemRemaining = this.totalEatTimeTicks = 0;
-+ this.eatStartTime = -1L;
-+ // Paper end - lag compensate eating
- }
- }
-
-@@ -4075,7 +4090,10 @@ public abstract class LivingEntity extends Entity implements Attackable {
- }
-
- this.useItem = ItemStack.EMPTY;
-- this.useItemRemaining = 0;
-+ // Paper start - lag compensate eating
-+ this.useItemRemaining = this.totalEatTimeTicks = 0;
-+ this.eatStartTime = -1L;
-+ // Paper end - lag compensate eating
- }
-
- public boolean isBlocking() {
diff --git a/patches/unapplied/server/1038-Optimise-nearby-player-retrieval.patch b/patches/unapplied/server/1038-Optimise-nearby-player-retrieval.patch
deleted file mode 100644
index 06b8d42fec..0000000000
--- a/patches/unapplied/server/1038-Optimise-nearby-player-retrieval.patch
+++ /dev/null
@@ -1,228 +0,0 @@
-From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
-From: Spottedleaf <[email protected]>
-Date: Sat, 23 Sep 2023 23:15:52 -0700
-Subject: [PATCH] Optimise nearby player retrieval
-
-Instead of searching/testing every player online on the server,
-we can instead use the nearby player tracking system to reduce
-the number of tests per search.
-
-diff --git a/src/main/java/net/minecraft/server/level/ServerLevel.java b/src/main/java/net/minecraft/server/level/ServerLevel.java
-index 8a5abc320137d045acba0c87cef9f2912d78b6fb..6907d1be36fbdf0856c0e11983218d2fd1f9cb46 100644
---- a/src/main/java/net/minecraft/server/level/ServerLevel.java
-+++ b/src/main/java/net/minecraft/server/level/ServerLevel.java
-@@ -576,6 +576,115 @@ public class ServerLevel extends Level implements WorldGenLevel {
- this.lagCompensationTick = (System.nanoTime() - net.minecraft.server.MinecraftServer.SERVER_INIT) / (java.util.concurrent.TimeUnit.MILLISECONDS.toNanos(50L));
- }
- // Paper end - lag compensation
-+ // Paper start - optimise nearby player retrieval
-+ @Override
-+ public java.util.List<net.minecraft.world.entity.player.Player> getNearbyPlayers(net.minecraft.world.entity.ai.targeting.TargetingConditions targetPredicate,
-+ net.minecraft.world.entity.LivingEntity entity,
-+ net.minecraft.world.phys.AABB box) {
-+ return this.getNearbyEntities(Player.class, targetPredicate, entity, box);
-+ }
-+
-+ @Override
-+ public Player getNearestPlayer(double x, double y, double z, double maxDistance, @Nullable Predicate<Entity> targetPredicate) {
-+ if (maxDistance > 0.0D) {
-+ io.papermc.paper.util.player.NearbyPlayers players = this.chunkSource.chunkMap.getNearbyPlayers();
-+
-+ com.destroystokyo.paper.util.maplist.ReferenceList<ServerPlayer> nearby = players.getPlayersByBlock(
-+ io.papermc.paper.util.CoordinateUtils.getBlockCoordinate(x),
-+ io.papermc.paper.util.CoordinateUtils.getBlockCoordinate(z),
-+ io.papermc.paper.util.player.NearbyPlayers.NearbyMapType.GENERAL
-+ );
-+
-+ if (nearby == null) {
-+ return null;
-+ }
-+
-+ ServerPlayer nearest = null;
-+ double nearestDist = maxDistance * maxDistance;
-+ Object[] rawData = nearby.getRawData();
-+ for (int i = 0, len = nearby.size(); i < len; ++i) {
-+ ServerPlayer player = (ServerPlayer)rawData[i];
-+ double dist = player.distanceToSqr(x, y, z);
-+ if (dist >= nearestDist) {
-+ continue;
-+ }
-+
-+ if (targetPredicate == null || targetPredicate.test(player)) {
-+ nearest = player;
-+ nearestDist = dist;
-+ }
-+ }
-+
-+ return nearest;
-+ } else {
-+ ServerPlayer nearest = null;
-+ double nearestDist = Double.MAX_VALUE;
-+
-+ for (ServerPlayer player : this.players()) {
-+ double dist = player.distanceToSqr(x, y, z);
-+ if (dist >= nearestDist) {
-+ continue;
-+ }
-+
-+ if (targetPredicate == null || targetPredicate.test(player)) {
-+ nearest = player;
-+ nearestDist = dist;
-+ }
-+ }
-+
-+ return nearest;
-+ }
-+ }
-+
-+ @Override
-+ public Player getNearestPlayer(net.minecraft.world.entity.ai.targeting.TargetingConditions targetPredicate, LivingEntity entity) {
-+ return this.getNearestPlayer(targetPredicate, entity, entity.getX(), entity.getY(), entity.getZ());
-+ }
-+
-+ @Override
-+ public Player getNearestPlayer(net.minecraft.world.entity.ai.targeting.TargetingConditions targetPredicate, LivingEntity entity,
-+ double x, double y, double z) {
-+ double range = targetPredicate.range;
-+ if (range > 0.0D) {
-+ io.papermc.paper.util.player.NearbyPlayers players = this.chunkSource.chunkMap.getNearbyPlayers();
-+
-+ com.destroystokyo.paper.util.maplist.ReferenceList<ServerPlayer> nearby = players.getPlayersByBlock(
-+ io.papermc.paper.util.CoordinateUtils.getBlockCoordinate(x),
-+ io.papermc.paper.util.CoordinateUtils.getBlockCoordinate(z),
-+ io.papermc.paper.util.player.NearbyPlayers.NearbyMapType.GENERAL
-+ );
-+
-+ if (nearby == null) {
-+ return null;
-+ }
-+
-+ ServerPlayer nearest = null;
-+ double nearestDist = Double.MAX_VALUE;
-+ Object[] rawData = nearby.getRawData();
-+ for (int i = 0, len = nearby.size(); i < len; ++i) {
-+ ServerPlayer player = (ServerPlayer)rawData[i];
-+ double dist = player.distanceToSqr(x, y, z);
-+ if (dist >= nearestDist) {
-+ continue;
-+ }
-+
-+ if (targetPredicate.test(entity, player)) {
-+ nearest = player;
-+ nearestDist = dist;
-+ }
-+ }
-+
-+ return nearest;
-+ } else {
-+ return this.getNearestEntity(this.players(), targetPredicate, entity, x, y, z);
-+ }
-+ }
-+
-+ @Nullable
-+ public Player getNearestPlayer(net.minecraft.world.entity.ai.targeting.TargetingConditions targetPredicate, double x, double y, double z) {
-+ return this.getNearestPlayer(targetPredicate, null, x, y, z);
-+ }
-+ // Paper end - optimise nearby player retrieval
-
- // Add env and gen to constructor, IWorldDataServer -> WorldDataServer
- public ServerLevel(MinecraftServer minecraftserver, Executor executor, LevelStorageSource.LevelStorageAccess convertable_conversionsession, PrimaryLevelData iworlddataserver, ResourceKey<Level> resourcekey, LevelStem worlddimension, ChunkProgressListener worldloadlistener, boolean flag, long i, List<CustomSpawner> list, boolean flag1, @Nullable RandomSequences randomsequences, org.bukkit.World.Environment env, org.bukkit.generator.ChunkGenerator gen, org.bukkit.generator.BiomeProvider biomeProvider) {
-diff --git a/src/main/java/net/minecraft/world/entity/ai/sensing/PlayerSensor.java b/src/main/java/net/minecraft/world/entity/ai/sensing/PlayerSensor.java
-index 2e887e426dcd79e2dda401f127d0e01ca537e80e..65cd42ce9f553e0aa5bf248bdbf902f9d1f55460 100644
---- a/src/main/java/net/minecraft/world/entity/ai/sensing/PlayerSensor.java
-+++ b/src/main/java/net/minecraft/world/entity/ai/sensing/PlayerSensor.java
-@@ -21,17 +21,50 @@ public class PlayerSensor extends Sensor<LivingEntity> {
-
- @Override
- protected void doTick(ServerLevel world, LivingEntity entity) {
-- List<Player> list = world.players()
-- .stream()
-- .filter(EntitySelector.NO_SPECTATORS)
-- .filter(player -> entity.closerThan(player, 16.0))
-- .sorted(Comparator.comparingDouble(entity::distanceToSqr))
-- .collect(Collectors.toList());
-+ // Paper start - Perf: optimise nearby player retrieval & remove streams from hot code
-+ io.papermc.paper.util.player.NearbyPlayers nearbyPlayers = world.chunkSource.chunkMap.getNearbyPlayers();
-+ net.minecraft.world.phys.Vec3 entityPos = entity.position();
-+ com.destroystokyo.paper.util.maplist.ReferenceList<net.minecraft.server.level.ServerPlayer> nearby = nearbyPlayers.getPlayersByChunk(
-+ entity.chunkPosition().x,
-+ entity.chunkPosition().z,
-+ io.papermc.paper.util.player.NearbyPlayers.NearbyMapType.GENERAL_REALLY_SMALL
-+ );
-+
-+ List<Player> players = new java.util.ArrayList<>(nearby == null ? 0 : nearby.size());
-+ if (nearby != null) {
-+ Object[] rawData = nearby.getRawData();
-+ for (int index = 0, len = nearby.size(); index < len; ++index) {
-+ net.minecraft.server.level.ServerPlayer player = (net.minecraft.server.level.ServerPlayer) rawData[index];
-+ if (player.isSpectator()) {
-+ continue;
-+ }
-+ if (player.distanceToSqr(entityPos.x, entityPos.y, entityPos.z) >= (16.0 * 16.0)) {
-+ continue;
-+ }
-+ players.add(player);
-+ }
-+ }
-+ players.sort(Comparator.comparingDouble(entity::distanceToSqr));
- Brain<?> brain = entity.getBrain();
-- brain.setMemory(MemoryModuleType.NEAREST_PLAYERS, list);
-- List<Player> list2 = list.stream().filter(player -> isEntityTargetable(entity, player)).collect(Collectors.toList());
-- brain.setMemory(MemoryModuleType.NEAREST_VISIBLE_PLAYER, list2.isEmpty() ? null : list2.get(0));
-- Optional<Player> optional = list2.stream().filter(player -> isEntityAttackable(entity, player)).findFirst();
-- brain.setMemory(MemoryModuleType.NEAREST_VISIBLE_ATTACKABLE_PLAYER, optional);
-+
-+ brain.setMemory(MemoryModuleType.NEAREST_PLAYERS, players);
-+
-+ Player firstTargetable = null;
-+ Player firstAttackable = null;
-+ for (Player player : players) {
-+ if (firstTargetable == null && Sensor.isEntityTargetable(entity, player)) {
-+ firstTargetable = player;
-+ }
-+ if (firstAttackable == null && Sensor.isEntityAttackable(entity, player)) {
-+ firstAttackable = player;
-+ }
-+
-+ if (firstAttackable != null && firstTargetable != null) {
-+ break;
-+ }
-+ }
-+ brain.setMemory(MemoryModuleType.NEAREST_VISIBLE_PLAYER, firstTargetable);
-+ brain.setMemory(MemoryModuleType.NEAREST_VISIBLE_ATTACKABLE_PLAYER, Optional.ofNullable(firstAttackable));
-+ // Paper end - Perf: optimise nearby player retrieval & remove streams from hot code
- }
- }
-diff --git a/src/main/java/net/minecraft/world/entity/ai/targeting/TargetingConditions.java b/src/main/java/net/minecraft/world/entity/ai/targeting/TargetingConditions.java
-index aecb0ad814586bfc5e56755ee14379a69388b38c..d2f0c3b26d4beedb49d86e0242d843590d469d02 100644
---- a/src/main/java/net/minecraft/world/entity/ai/targeting/TargetingConditions.java
-+++ b/src/main/java/net/minecraft/world/entity/ai/targeting/TargetingConditions.java
-@@ -10,7 +10,7 @@ public class TargetingConditions {
- public static final TargetingConditions DEFAULT = forCombat();
- private static final double MIN_VISIBILITY_DISTANCE_FOR_INVISIBLE_TARGET = 2.0;
- private final boolean isCombat;
-- private double range = -1.0;
-+ public double range = -1.0; // Paper - public
- private boolean checkLineOfSight = true;
- private boolean testInvisible = true;
- @Nullable
-diff --git a/src/main/java/net/minecraft/world/level/EntityGetter.java b/src/main/java/net/minecraft/world/level/EntityGetter.java
-index 21843501355a0c0c8d594e3e5312e97861c9a777..ea0aee88c7d901034427db201c1b2430f8a1d522 100644
---- a/src/main/java/net/minecraft/world/level/EntityGetter.java
-+++ b/src/main/java/net/minecraft/world/level/EntityGetter.java
-@@ -233,9 +233,13 @@ public interface EntityGetter {
- T livingEntity = null;
-
- for (T livingEntity2 : entityList) {
-+ // Paper start - optimise nearby player retrieval; move up
-+ // don't check entities outside closest range
-+ double e = livingEntity2.distanceToSqr(x, y, z);
-+ if (d == -1.0 || e < d) {
-+ // Paper end - move up
- if (targetPredicate.test(entity, livingEntity2)) {
-- double e = livingEntity2.distanceToSqr(x, y, z);
-- if (d == -1.0 || e < d) {
-+ // Paper - optimise nearby player retrieval; move up
- d = e;
- livingEntity = livingEntity2;
- }
diff --git a/patches/unapplied/server/1039-Distance-manager-tick-timings.patch b/patches/unapplied/server/1039-Distance-manager-tick-timings.patch
deleted file mode 100644
index f946d92596..0000000000
--- a/patches/unapplied/server/1039-Distance-manager-tick-timings.patch
+++ /dev/null
@@ -1,34 +0,0 @@
-From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
-From: Spottedleaf <[email protected]>
-Date: Sat, 18 Jul 2020 16:03:57 -0700
-Subject: [PATCH] Distance manager tick timings
-
-Recently this has been taking up more time, so add a timings to
-really figure out how much.
-
-diff --git a/src/main/java/co/aikar/timings/MinecraftTimings.java b/src/main/java/co/aikar/timings/MinecraftTimings.java
-index 46449728f69ee7d4f78470f8da23c055acd53a3b..4b467f1af93452d13829f756d55dee18b8889d40 100644
---- a/src/main/java/co/aikar/timings/MinecraftTimings.java
-+++ b/src/main/java/co/aikar/timings/MinecraftTimings.java
-@@ -47,6 +47,7 @@ public final class MinecraftTimings {
- public static final Timing antiXrayUpdateTimer = Timings.ofSafe("anti-xray - update");
- public static final Timing antiXrayObfuscateTimer = Timings.ofSafe("anti-xray - obfuscate");
- public static final Timing scoreboardScoreSearch = Timings.ofSafe("Scoreboard score search"); // Paper - add timings for scoreboard search
-+ public static final Timing distanceManagerTick = Timings.ofSafe("Distance Manager Tick"); // Paper - add timings for distance manager
-
- public static final Timing midTickChunkTasks = Timings.ofSafe("Mid Tick Chunk Tasks");
-
-diff --git a/src/main/java/io/papermc/paper/chunk/system/scheduling/ChunkHolderManager.java b/src/main/java/io/papermc/paper/chunk/system/scheduling/ChunkHolderManager.java
-index 5b446e6ac151f99f64f0c442d0b40b5e251bc4c4..6bc7c6f16a1649fc9e24e7cf90fca401e5bd4875 100644
---- a/src/main/java/io/papermc/paper/chunk/system/scheduling/ChunkHolderManager.java
-+++ b/src/main/java/io/papermc/paper/chunk/system/scheduling/ChunkHolderManager.java
-@@ -1316,7 +1316,9 @@ public final class ChunkHolderManager {
- }
-
- public boolean processTicketUpdates() {
-+ co.aikar.timings.MinecraftTimings.distanceManagerTick.startTiming(); try { // Paper - add timings for distance manager
- return this.processTicketUpdates(true, true, null);
-+ } finally { co.aikar.timings.MinecraftTimings.distanceManagerTick.stopTiming(); } // Paper - add timings for distance manager
- }
-
- private static final ThreadLocal<List<ChunkProgressionTask>> CURRENT_TICKET_UPDATE_SCHEDULING = new ThreadLocal<>();
diff --git a/patches/unapplied/server/1040-Handle-Oversized-block-entities-in-chunks.patch b/patches/unapplied/server/1040-Handle-Oversized-block-entities-in-chunks.patch
deleted file mode 100644
index 27d881a58e..0000000000
--- a/patches/unapplied/server/1040-Handle-Oversized-block-entities-in-chunks.patch
+++ /dev/null
@@ -1,64 +0,0 @@
-From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
-From: Aikar <[email protected]>
-Date: Wed, 6 May 2020 05:00:57 -0400
-Subject: [PATCH] Handle Oversized block entities in chunks
-
-Splits out Extra Packets if too many TE's are encountered to prevent
-creating too large of a packet to sed.
-
-Co-authored-by: Spottedleaf <[email protected]>
-
-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 454bec4f8843e7e4e42cd8a8132b557ead292dcc..76bde683d193b37e563a67c1c7b9bdcf17d64524 100644
---- a/src/main/java/net/minecraft/network/protocol/game/ClientboundLevelChunkPacketData.java
-+++ b/src/main/java/net/minecraft/network/protocol/game/ClientboundLevelChunkPacketData.java
-@@ -24,6 +24,14 @@ public class ClientboundLevelChunkPacketData {
- private final CompoundTag heightmaps;
- private final byte[] buffer;
- private final List<ClientboundLevelChunkPacketData.BlockEntityInfo> blockEntitiesData;
-+ // Paper start - Handle oversized block entities in chunks
-+ private final java.util.List<net.minecraft.network.protocol.Packet<?>> extraPackets = new java.util.ArrayList<>();
-+ private static final int TE_LIMIT = Integer.getInteger("Paper.excessiveTELimit", 750);
-+
-+ public List<net.minecraft.network.protocol.Packet<?>> 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); }
-@@ -47,8 +55,18 @@ public class ClientboundLevelChunkPacketData {
- extractChunkData(new FriendlyByteBuf(this.getWriteBuffer()), chunk, chunkPacketInfo);
- // Paper end
- this.blockEntitiesData = Lists.newArrayList();
-+ int totalTileEntities = 0; // Paper - Handle oversized block entities in chunks
-
- for (Entry<BlockPos, BlockEntity> entry2 : chunk.getBlockEntities().entrySet()) {
-+ // Paper start - Handle oversized block entities in chunks
-+ if (++totalTileEntities > TE_LIMIT) {
-+ var packet = entry2.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()));
- }
- }
-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 6412dff5ed0505f62dd5b71ab9606257858a7317..fc230d835f9aa526a4b179d36d921f0fec348aa8 100644
---- a/src/main/java/net/minecraft/network/protocol/game/ClientboundLevelChunkWithLightPacket.java
-+++ b/src/main/java/net/minecraft/network/protocol/game/ClientboundLevelChunkWithLightPacket.java
-@@ -74,4 +74,11 @@ public class ClientboundLevelChunkWithLightPacket implements Packet<ClientGamePa
- public ClientboundLightUpdatePacketData getLightData() {
- return this.lightData;
- }
-+
-+ // Paper start - Handle oversized block entities in chunks
-+ @Override
-+ public java.util.List<Packet<?>> getExtraPackets() {
-+ return this.chunkData.getExtraPackets();
-+ }
-+ // Paper end - Handle oversized block entities in chunks
- }
diff --git a/patches/unapplied/server/1041-Send-full-pos-packets-for-hard-colliding-entities.patch b/patches/unapplied/server/1041-Send-full-pos-packets-for-hard-colliding-entities.patch
deleted file mode 100644
index 45738a19b2..0000000000
--- a/patches/unapplied/server/1041-Send-full-pos-packets-for-hard-colliding-entities.patch
+++ /dev/null
@@ -1,23 +0,0 @@
-From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
-From: Spottedleaf <[email protected]>
-Date: Tue, 16 Feb 2021 00:16:56 -0800
-Subject: [PATCH] Send full pos packets for hard colliding entities
-
-Prevent collision problems due to desync (i.e boats)
-
-Configurable under
-`send-full-pos-for-hard-colliding-entities`
-
-diff --git a/src/main/java/net/minecraft/server/level/ServerEntity.java b/src/main/java/net/minecraft/server/level/ServerEntity.java
-index 9154af6523f4eaab1636e0bad30f743244e47471..529ab44baaf573b97cf7e89560c548642733188f 100644
---- a/src/main/java/net/minecraft/server/level/ServerEntity.java
-+++ b/src/main/java/net/minecraft/server/level/ServerEntity.java
-@@ -183,7 +183,7 @@ public class ServerEntity {
- long i1 = this.positionCodec.encodeZ(vec3d);
- boolean flag6 = k < -32768L || k > 32767L || l < -32768L || l > 32767L || i1 < -32768L || i1 > 32767L;
-
-- if (!flag6 && this.teleportDelay <= 400 && !this.wasRiding && this.wasOnGround == this.entity.onGround()) {
-+ if (!flag6 && this.teleportDelay <= 400 && !this.wasRiding && this.wasOnGround == this.entity.onGround()&& !(io.papermc.paper.configuration.GlobalConfiguration.get().collisions.sendFullPosForHardCollidingEntities && this.entity.hardCollides())) { // Paper - send full pos for hard colliding entities to prevent collision problems due to desync
- if ((!flag2 || !flag3) && !(this.entity instanceof AbstractArrow)) {
- if (flag2) {
- packet1 = new ClientboundMoveEntityPacket.Pos(this.entity.getId(), (short) ((int) k), (short) ((int) l), (short) ((int) i1), this.entity.onGround());