diff options
author | Nassim Jahnke <[email protected]> | 2024-04-25 14:23:55 +0200 |
---|---|---|
committer | Nassim Jahnke <[email protected]> | 2024-04-25 14:23:55 +0200 |
commit | 6da0d8cc91be3b8e1c84c0436f49654cdde6de2f (patch) | |
tree | e98a68395f98b4fb53be831513b00cfe4cabdb8d /patches/unapplied | |
parent | 2debcaff9dbd067a94385eb1b7d88110c2a4f231 (diff) | |
download | Paper-6da0d8cc91be3b8e1c84c0436f49654cdde6de2f.tar.gz Paper-6da0d8cc91be3b8e1c84c0436f49654cdde6de2f.zip |
(Almost) all patches applied
Diffstat (limited to 'patches/unapplied')
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()); |