aboutsummaryrefslogtreecommitdiffhomepage
path: root/patches/unapplied
diff options
context:
space:
mode:
authorNassim Jahnke <[email protected]>2024-04-25 11:42:10 +0200
committerNassim Jahnke <[email protected]>2024-04-25 11:42:10 +0200
commitf6ea3736a7e4466785d2884de71f3763bf4f34cc (patch)
tree3e11fb67228ca4c661bf77ae08d7beab845179e4 /patches/unapplied
parente9eec78fc7a69478481496b04511f047f875c318 (diff)
downloadPaper-f6ea3736a7e4466785d2884de71f3763bf4f34cc.tar.gz
Paper-f6ea3736a7e4466785d2884de71f3763bf4f34cc.zip
Patches
Diffstat (limited to 'patches/unapplied')
-rw-r--r--patches/unapplied/server/0988-Fix-and-optimise-world-force-upgrading.patch (renamed from patches/unapplied/server/1007-Fix-and-optimise-world-force-upgrading.patch)49
-rw-r--r--patches/unapplied/server/0992-incremental-chunk-and-player-saving.patch167
-rw-r--r--patches/unapplied/server/0993-Optimize-isInWorldBounds-and-getBlockState-for-inlin.patch99
-rw-r--r--patches/unapplied/server/0994-Improve-Maps-in-item-frames-performance-and-bug-fixe.patch128
-rw-r--r--patches/unapplied/server/0995-Strip-raytracing-for-EntityLiving-hasLineOfSight.patch158
-rw-r--r--patches/unapplied/server/0996-Optimize-Network-Manager-and-add-advanced-packet-sup.patch394
-rw-r--r--patches/unapplied/server/0997-Allow-Saving-of-Oversized-Chunks.patch211
-rw-r--r--patches/unapplied/server/0998-Fix-World-isChunkGenerated-calls.patch244
-rw-r--r--patches/unapplied/server/0999-Flat-bedrock-generator-settings.patch288
-rw-r--r--patches/unapplied/server/1000-Entity-Activation-Range-2.0.patch812
-rw-r--r--patches/unapplied/server/1001-Optional-per-player-mob-spawns.patch255
-rw-r--r--patches/unapplied/server/1002-Anti-Xray.patch1641
-rw-r--r--patches/unapplied/server/1003-Improve-cancelling-PreCreatureSpawnEvent-with-per-pl.patch89
-rw-r--r--patches/unapplied/server/1004-Optimize-Collision-to-not-load-chunks.patch109
-rw-r--r--patches/unapplied/server/1005-Optimize-GoalSelector-Goal.Flag-Set-operations.patch179
-rw-r--r--patches/unapplied/server/1006-Entity-load-save-limit-per-chunk.patch58
16 files changed, 26 insertions, 4855 deletions
diff --git a/patches/unapplied/server/1007-Fix-and-optimise-world-force-upgrading.patch b/patches/unapplied/server/0988-Fix-and-optimise-world-force-upgrading.patch
index 828506da0e..de599d49f8 100644
--- a/patches/unapplied/server/1007-Fix-and-optimise-world-force-upgrading.patch
+++ b/patches/unapplied/server/0988-Fix-and-optimise-world-force-upgrading.patch
@@ -33,10 +33,10 @@ public net.minecraft.util.worldupdate.WorldUpgrader REGEX
diff --git a/src/main/java/io/papermc/paper/world/ThreadedWorldUpgrader.java b/src/main/java/io/papermc/paper/world/ThreadedWorldUpgrader.java
new file mode 100644
-index 0000000000000000000000000000000000000000..513833c2ea23df5b079d157bc5cb89d5c9754c0b
+index 0000000000000000000000000000000000000000..90498d9a4f5aee0f6c8a202b5580b4260a28b378
--- /dev/null
+++ b/src/main/java/io/papermc/paper/world/ThreadedWorldUpgrader.java
-@@ -0,0 +1,209 @@
+@@ -0,0 +1,212 @@
+package io.papermc.paper.world;
+
+import com.mojang.datafixers.DataFixer;
@@ -79,9 +79,11 @@ index 0000000000000000000000000000000000000000..513833c2ea23df5b079d157bc5cb89d5
+ private final DataFixer dataFixer;
+ private final Optional<ResourceKey<Codec<? extends ChunkGenerator>>> generatorKey;
+ private final boolean removeCaches;
++ private final boolean recreateRegionFiles; // TODO
+
+ public ThreadedWorldUpgrader(final ResourceKey<LevelStem> dimensionType, final String worldName, final File worldDir, final int threads,
-+ final DataFixer dataFixer, final Optional<ResourceKey<Codec<? extends ChunkGenerator>>> generatorKey, final boolean removeCaches) {
++ final DataFixer dataFixer, final Optional<ResourceKey<Codec<? extends ChunkGenerator>>> generatorKey,
++ final boolean removeCaches, final boolean recreateRegionFiles) {
+ this.dimensionType = dimensionType;
+ this.worldName = worldName;
+ this.worldDir = worldDir;
@@ -103,6 +105,7 @@ index 0000000000000000000000000000000000000000..513833c2ea23df5b079d157bc5cb89d5
+ this.dataFixer = dataFixer;
+ this.generatorKey = generatorKey;
+ this.removeCaches = removeCaches;
++ this.recreateRegionFiles = recreateRegionFiles;
+ }
+
+ public void convert() {
@@ -247,50 +250,50 @@ index 0000000000000000000000000000000000000000..513833c2ea23df5b079d157bc5cb89d5
+ }
+}
diff --git a/src/main/java/net/minecraft/server/Main.java b/src/main/java/net/minecraft/server/Main.java
-index 329471af4f40e0a74612707cce96bb00819e6cf2..bc391d27399d8c22e78735ca39aa8ab45efb6413 100644
+index 244a19ecd0234fa1d7a6ecfea20751595688605d..8893aa1fe4b033fdc25ebe6f3a3606af1f9b5ea7 100644
--- a/src/main/java/net/minecraft/server/Main.java
+++ b/src/main/java/net/minecraft/server/Main.java
-@@ -388,6 +388,15 @@ public class Main {
+@@ -392,6 +392,15 @@ public class Main {
return new WorldLoader.InitConfig(worldloader_d, Commands.CommandSelection.DEDICATED, serverPropertiesHandler.functionPermissionLevel);
}
+ // Paper start - fix and optimise world upgrading
+ public static void convertWorldButItWorks(net.minecraft.resources.ResourceKey<net.minecraft.world.level.dimension.LevelStem> dimensionType, net.minecraft.world.level.storage.LevelStorageSource.LevelStorageAccess worldSession,
-+ DataFixer dataFixer, Optional<net.minecraft.resources.ResourceKey<com.mojang.serialization.Codec<? extends net.minecraft.world.level.chunk.ChunkGenerator>>> generatorKey, boolean removeCaches) {
++ DataFixer dataFixer, Optional<net.minecraft.resources.ResourceKey<com.mojang.serialization.Codec<? extends net.minecraft.world.level.chunk.ChunkGenerator>>> generatorKey, boolean removeCaches, boolean recreateRegionFiles) {
+ int threads = Runtime.getRuntime().availableProcessors() * 3 / 8;
-+ final io.papermc.paper.world.ThreadedWorldUpgrader worldUpgrader = new io.papermc.paper.world.ThreadedWorldUpgrader(dimensionType, worldSession.getLevelId(), worldSession.levelDirectory.path().toFile(), threads, dataFixer, generatorKey, removeCaches);
++ final io.papermc.paper.world.ThreadedWorldUpgrader worldUpgrader = new io.papermc.paper.world.ThreadedWorldUpgrader(dimensionType, worldSession.getLevelId(), worldSession.levelDirectory.path().toFile(), threads, dataFixer, generatorKey, removeCaches, recreateRegionFiles);
+ worldUpgrader.convert();
+ }
+ // Paper end - fix and optimise world upgrading
+
- public static void forceUpgrade(LevelStorageSource.LevelStorageAccess session, DataFixer dataFixer, boolean eraseCache, BooleanSupplier continueCheck, Registry<LevelStem> dimensionOptionsRegistry) {
+ public static void forceUpgrade(LevelStorageSource.LevelStorageAccess session, DataFixer dataFixer, boolean eraseCache, BooleanSupplier continueCheck, RegistryAccess dynamicRegistryManager, boolean recreateRegionFiles) {
Main.LOGGER.info("Forcing world upgrade! {}", session.getLevelId()); // CraftBukkit
- WorldUpgrader worldupgrader = new WorldUpgrader(session, dataFixer, dimensionOptionsRegistry, eraseCache);
+ WorldUpgrader worldupgrader = new WorldUpgrader(session, dataFixer, dynamicRegistryManager, eraseCache, recreateRegionFiles);
diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java
-index 54d6e197bd6357bf2d31d8d5d1cb3707d22ef03e..884c0e4f58f0e374c910bc0d8b5d3ab1b8ade226 100644
+index 0f3601f2f1a7ac53425129df6498ed0df302dec8..3cb64c6c870454ee3090d6c88bede8b58d9d3361 100644
--- a/src/main/java/net/minecraft/server/MinecraftServer.java
+++ b/src/main/java/net/minecraft/server/MinecraftServer.java
-@@ -584,11 +584,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
+@@ -595,11 +595,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
worlddata = new PrimaryLevelData(worldsettings, worldoptions, worlddimensions_b.specialWorldProperty(), lifecycle);
}
worlddata.checkName(name); // CraftBukkit - Migration did not rewrite the level.dat; This forces 1.8 to take the last loaded world as respawn (in this case the end)
- if (this.options.has("forceUpgrade")) {
- net.minecraft.server.Main.forceUpgrade(worldSession, DataFixers.getDataFixer(), this.options.has("eraseCache"), () -> {
- return true;
-- }, dimensions);
+- }, iregistrycustom_dimension, this.options.has("recreateRegionFiles"));
- }
+ // Paper - fix and optimise world upgrading; move down
PrimaryLevelData iworlddataserver = worlddata;
boolean flag = worlddata.isDebugWorld();
-@@ -603,6 +599,13 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
+@@ -614,6 +610,13 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
biomeProvider = gen.getDefaultBiomeProvider(worldInfo);
}
+ // Paper start - fix and optimise world upgrading
+ if (options.has("forceUpgrade")) {
+ net.minecraft.server.Main.convertWorldButItWorks(
-+ dimensionKey, worldSession, DataFixers.getDataFixer(), worlddimension.generator().getTypeNameForDataFixer(), options.has("eraseCache")
++ dimensionKey, worldSession, DataFixers.getDataFixer(), worlddimension.generator().getTypeNameForDataFixer(), options.has("eraseCache"), console.has("recreateRegionFiles")
+ );
+ }
+ // Paper end - fix and optimise world upgrading
@@ -298,10 +301,10 @@ index 54d6e197bd6357bf2d31d8d5d1cb3707d22ef03e..884c0e4f58f0e374c910bc0d8b5d3ab1
if (dimensionKey == LevelStem.OVERWORLD) {
diff --git a/src/main/java/net/minecraft/world/level/Level.java b/src/main/java/net/minecraft/world/level/Level.java
-index a2f8d2858c4a43ff642761fd86512a5f0666a0d2..16e0b4bcc903e6decbdf66ac4fb3d0dc261dbded 100644
+index a137b4a3be01a0333e5fdc1585098fafeeb4f725..ab502b4384d977ac78b05eaa7dd14eaf811f57b5 100644
--- a/src/main/java/net/minecraft/world/level/Level.java
+++ b/src/main/java/net/minecraft/world/level/Level.java
-@@ -181,6 +181,15 @@ public abstract class Level implements LevelAccessor, AutoCloseable {
+@@ -178,6 +178,15 @@ public abstract class Level implements LevelAccessor, AutoCloseable {
public final Map<Explosion.CacheKey, Float> explosionDensityCache = new HashMap<>(); // Paper - Optimize explosions
public java.util.ArrayDeque<net.minecraft.world.level.block.RedstoneTorchBlock.Toggle> redstoneUpdateInfos; // Paper - Faster redstone torch rapid clock removal; Move from Map in BlockRedstoneTorch to here
@@ -318,10 +321,10 @@ index a2f8d2858c4a43ff642761fd86512a5f0666a0d2..16e0b4bcc903e6decbdf66ac4fb3d0dc
return this.world;
}
diff --git a/src/main/java/net/minecraft/world/level/chunk/storage/RegionFileStorage.java b/src/main/java/net/minecraft/world/level/chunk/storage/RegionFileStorage.java
-index 0db8ee3b640e6d1268e9c1cccda85459bd447105..42d37bee3a459adcd46408596ccf93abbcff51fe 100644
+index a73a37320da2c141fc2db9d1d61233a34ce0c906..9607e38e39daf8196f1b728c0019a283d730b885 100644
--- a/src/main/java/net/minecraft/world/level/chunk/storage/RegionFileStorage.java
+++ b/src/main/java/net/minecraft/world/level/chunk/storage/RegionFileStorage.java
-@@ -60,6 +60,29 @@ public class RegionFileStorage implements AutoCloseable {
+@@ -62,6 +62,29 @@ public class RegionFileStorage implements AutoCloseable {
}
// Paper start
@@ -352,28 +355,28 @@ index 0db8ee3b640e6d1268e9c1cccda85459bd447105..42d37bee3a459adcd46408596ccf93ab
return this.regionCache.getAndMoveToFirst(ChunkPos.asLong(chunkcoordintpair.getRegionX(), chunkcoordintpair.getRegionZ()));
}
diff --git a/src/main/java/org/bukkit/craftbukkit/CraftServer.java b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
-index c9137151f0d2978adb432c40da68689465d2325d..ab7bc27e870227e6746b77a7b5e109e2cf198b5f 100644
+index f6b7ae6c00150d2bc33c42a1f8432478d979d38b..554096a467ed9e1c8ab393fdaa8b9c31e1f7bbd4 100644
--- a/src/main/java/org/bukkit/craftbukkit/CraftServer.java
+++ b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
-@@ -1357,9 +1357,7 @@ public final class CraftServer implements Server {
+@@ -1365,9 +1365,7 @@ public final class CraftServer implements Server {
worlddata.checkName(name);
worlddata.setModdedInfo(this.console.getServerModName(), this.console.getModdedStatus().shouldReportAsModified());
- if (this.console.options.has("forceUpgrade")) {
-- net.minecraft.server.Main.forceUpgrade(worldSession, DataFixers.getDataFixer(), this.console.options.has("eraseCache"), () -> true, iregistry);
+- net.minecraft.server.Main.forceUpgrade(worldSession, DataFixers.getDataFixer(), this.console.options.has("eraseCache"), () -> true, iregistrycustom_dimension, this.console.options.has("recreateRegionFiles"));
- }
+ // Paper - fix and optimise world upgrading; move down
long j = BiomeManager.obfuscateSeed(worlddata.worldGenOptions().seed()); // Paper - use world seed
List<CustomSpawner> list = ImmutableList.of(new PhantomSpawner(), new PatrolSpawner(), new CatSpawner(), new VillageSiege(), new WanderingTraderSpawner(worlddata));
-@@ -1370,6 +1368,13 @@ public final class CraftServer implements Server {
+@@ -1378,6 +1376,13 @@ public final class CraftServer implements Server {
biomeProvider = generator.getDefaultBiomeProvider(worldInfo);
}
+ // Paper start - fix and optimise world upgrading
+ if (this.console.options.has("forceUpgrade")) {
+ net.minecraft.server.Main.convertWorldButItWorks(
-+ actualDimension, worldSession, DataFixers.getDataFixer(), worlddimension.generator().getTypeNameForDataFixer(), this.console.options.has("eraseCache")
++ actualDimension, worldSession, DataFixers.getDataFixer(), worlddimension.generator().getTypeNameForDataFixer(), this.console.options.has("eraseCache"), this.console.options.has("recreateRegionFiles")
+ );
+ }
+ // Paper end - fix and optimise world upgrading
diff --git a/patches/unapplied/server/0992-incremental-chunk-and-player-saving.patch b/patches/unapplied/server/0992-incremental-chunk-and-player-saving.patch
deleted file mode 100644
index 33d6eb0a5f..0000000000
--- a/patches/unapplied/server/0992-incremental-chunk-and-player-saving.patch
+++ /dev/null
@@ -1,167 +0,0 @@
-From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
-From: Shane Freeder <[email protected]>
-Date: Sun, 9 Jun 2019 03:53:22 +0100
-Subject: [PATCH] incremental chunk and player saving
-
-
-diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java
-index 03d566c3e6d7541487ea79ed868aa7334793df3b..54d6e197bd6357bf2d31d8d5d1cb3707d22ef03e 100644
---- a/src/main/java/net/minecraft/server/MinecraftServer.java
-+++ b/src/main/java/net/minecraft/server/MinecraftServer.java
-@@ -908,7 +908,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
-
- try {
- this.isSaving = true;
-- this.getPlayerList().saveAll();
-+ this.getPlayerList().saveAll(); // Paper - Incremental chunk and player saving; diff on change
- flag3 = this.saveAllChunks(suppressLogs, flush, force);
- } finally {
- this.isSaving = false;
-@@ -1388,16 +1388,28 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
- }
-
- --this.ticksUntilAutosave;
-- // CraftBukkit start
-- if (this.autosavePeriod > 0 && this.ticksUntilAutosave <= 0) {
-- this.ticksUntilAutosave = this.autosavePeriod;
-- // CraftBukkit end
-- MinecraftServer.LOGGER.debug("Autosave started");
-- this.profiler.push("save");
-- this.saveEverything(true, false, false);
-- this.profiler.pop();
-- MinecraftServer.LOGGER.debug("Autosave finished");
-+ // Paper start - Incremental chunk and player saving
-+ int playerSaveInterval = io.papermc.paper.configuration.GlobalConfiguration.get().playerAutoSave.rate;
-+ if (playerSaveInterval < 0) {
-+ playerSaveInterval = autosavePeriod;
- }
-+ this.profiler.push("save");
-+ final boolean fullSave = autosavePeriod > 0 && this.tickCount % autosavePeriod == 0;
-+ try {
-+ this.isSaving = true;
-+ if (playerSaveInterval > 0) {
-+ this.playerList.saveAll(playerSaveInterval);
-+ }
-+ for (ServerLevel level : this.getAllLevels()) {
-+ if (level.paperConfig().chunks.autoSaveInterval.value() > 0) {
-+ level.saveIncrementally(fullSave);
-+ }
-+ }
-+ } finally {
-+ this.isSaving = false;
-+ }
-+ this.profiler.pop();
-+ // Paper end - Incremental chunk and player saving
- io.papermc.paper.util.CachedLists.reset(); // Paper
- // Paper start - move executeAll() into full server tick timing
- try (co.aikar.timings.Timing ignored = MinecraftTimings.processTasksTimer.startTiming()) {
-diff --git a/src/main/java/net/minecraft/server/level/ServerChunkCache.java b/src/main/java/net/minecraft/server/level/ServerChunkCache.java
-index 9bb4223fbb665211df11dc89fcd13cb7a92cd5dd..20cdfd2bbd5dc71fd37ccedaf3a8d06b45553c9b 100644
---- a/src/main/java/net/minecraft/server/level/ServerChunkCache.java
-+++ b/src/main/java/net/minecraft/server/level/ServerChunkCache.java
-@@ -435,6 +435,15 @@ public class ServerChunkCache extends ChunkSource {
- } // Paper - Timings
- }
-
-+ // Paper start - Incremental chunk and player saving; duplicate save, but call incremental
-+ public void saveIncrementally() {
-+ this.runDistanceManagerUpdates();
-+ try (co.aikar.timings.Timing timed = level.timings.chunkSaveData.startTiming()) { // Paper - Timings
-+ this.chunkMap.saveIncrementally();
-+ } // Paper - Timings
-+ }
-+ // Paper end - Incremental chunk and player saving
-+
- @Override
- public void close() throws IOException {
- // CraftBukkit start
-diff --git a/src/main/java/net/minecraft/server/level/ServerLevel.java b/src/main/java/net/minecraft/server/level/ServerLevel.java
-index a3881964bad0cab8f480eda634216d73dfbf7bb0..b6407dd3e5b87782503988f898bbf054e3f4e611 100644
---- a/src/main/java/net/minecraft/server/level/ServerLevel.java
-+++ b/src/main/java/net/minecraft/server/level/ServerLevel.java
-@@ -1290,6 +1290,37 @@ public class ServerLevel extends Level implements WorldGenLevel {
- return !this.server.isUnderSpawnProtection(this, pos, player) && this.getWorldBorder().isWithinBounds(pos);
- }
-
-+ // Paper start - Incremental chunk and player saving
-+ public void saveIncrementally(boolean doFull) {
-+ ServerChunkCache chunkproviderserver = this.getChunkSource();
-+
-+ if (doFull) {
-+ org.bukkit.Bukkit.getPluginManager().callEvent(new org.bukkit.event.world.WorldSaveEvent(getWorld()));
-+ }
-+
-+ try (co.aikar.timings.Timing ignored = this.timings.worldSave.startTiming()) {
-+ if (doFull) {
-+ this.saveLevelData();
-+ }
-+
-+ this.timings.worldSaveChunks.startTiming(); // Paper
-+ if (!this.noSave()) chunkproviderserver.saveIncrementally();
-+ this.timings.worldSaveChunks.stopTiming(); // Paper
-+
-+ // Copied from save()
-+ // CraftBukkit start - moved from MinecraftServer.saveChunks
-+ if (doFull) { // Paper
-+ ServerLevel worldserver1 = this;
-+
-+ this.serverLevelData.setWorldBorder(worldserver1.getWorldBorder().createSettings());
-+ this.serverLevelData.setCustomBossEvents(this.server.getCustomBossEvents().save());
-+ this.convertable.saveDataTag(this.server.registryAccess(), this.serverLevelData, this.server.getPlayerList().getSingleplayerData());
-+ }
-+ // CraftBukkit end
-+ }
-+ }
-+ // Paper end - Incremental chunk and player saving
-+
- public void save(@Nullable ProgressListener progressListener, boolean flush, boolean savingDisabled) {
- // Paper start - rewrite chunk system - add close param
- this.save(progressListener, flush, savingDisabled, false);
-diff --git a/src/main/java/net/minecraft/server/level/ServerPlayer.java b/src/main/java/net/minecraft/server/level/ServerPlayer.java
-index 18356db5d998dccb9e645a9ee0bebc5cbbfa5f7a..4a62c2937460dca9d938c40da47529e106503cad 100644
---- a/src/main/java/net/minecraft/server/level/ServerPlayer.java
-+++ b/src/main/java/net/minecraft/server/level/ServerPlayer.java
-@@ -191,6 +191,7 @@ import org.bukkit.inventory.MainHand;
- public class ServerPlayer extends Player {
-
- private static final Logger LOGGER = LogUtils.getLogger();
-+ public long lastSave = MinecraftServer.currentTick; // Paper - Incremental chunk and player saving
- private static final int NEUTRAL_MOB_DEATH_NOTIFICATION_RADII_XZ = 32;
- private static final int NEUTRAL_MOB_DEATH_NOTIFICATION_RADII_Y = 10;
- private static final int FLY_STAT_RECORDING_SPEED = 25;
-diff --git a/src/main/java/net/minecraft/server/players/PlayerList.java b/src/main/java/net/minecraft/server/players/PlayerList.java
-index 4a569bf782bfdd870f32fe0ab5c3b8b86a07f218..8a5f245fc98514b66216dde234650bfc0adc24b4 100644
---- a/src/main/java/net/minecraft/server/players/PlayerList.java
-+++ b/src/main/java/net/minecraft/server/players/PlayerList.java
-@@ -577,6 +577,7 @@ public abstract class PlayerList {
-
- protected void save(ServerPlayer player) {
- if (!player.getBukkitEntity().isPersistent()) return; // CraftBukkit
-+ player.lastSave = MinecraftServer.currentTick; // Paper - Incremental chunk and player saving
- this.playerIo.save(player);
- ServerStatsCounter serverstatisticmanager = (ServerStatsCounter) player.getStats(); // CraftBukkit
-
-@@ -1228,10 +1229,22 @@ public abstract class PlayerList {
- }
-
- public void saveAll() {
-+ // Paper start - Incremental chunk and player saving
-+ this.saveAll(-1);
-+ }
-+
-+ public void saveAll(int interval) {
- io.papermc.paper.util.MCUtil.ensureMain("Save Players" , () -> { // Paper - Ensure main
- MinecraftTimings.savePlayers.startTiming(); // Paper
-+ int numSaved = 0;
-+ long now = MinecraftServer.currentTick;
- for (int i = 0; i < this.players.size(); ++i) {
-- this.save(this.players.get(i));
-+ ServerPlayer entityplayer = this.players.get(i);
-+ if (interval == -1 || now - entityplayer.lastSave >= interval) {
-+ this.save(entityplayer);
-+ if (interval != -1 && ++numSaved >= io.papermc.paper.configuration.GlobalConfiguration.get().playerAutoSave.maxPerTick()) { break; }
-+ }
-+ // Paper end - Incremental chunk and player saving
- }
- MinecraftTimings.savePlayers.stopTiming(); // Paper
- return null; }); // Paper - ensure main
diff --git a/patches/unapplied/server/0993-Optimize-isInWorldBounds-and-getBlockState-for-inlin.patch b/patches/unapplied/server/0993-Optimize-isInWorldBounds-and-getBlockState-for-inlin.patch
deleted file mode 100644
index bb73216915..0000000000
--- a/patches/unapplied/server/0993-Optimize-isInWorldBounds-and-getBlockState-for-inlin.patch
+++ /dev/null
@@ -1,99 +0,0 @@
-From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
-From: Aikar <[email protected]>
-Date: Thu, 3 Mar 2016 02:07:55 -0600
-Subject: [PATCH] Optimize isInWorldBounds and getBlockState for inlining
-
-Hot methods, so reduce # of instructions for the method.
-
-Move is valid location test to the BlockPosition class so that it can access local variables.
-
-Replace all calls to the new place to the unnecessary forward.
-
-Optimize getType and getBlockData to manually inline and optimize the calls
-
-diff --git a/src/main/java/net/minecraft/core/Vec3i.java b/src/main/java/net/minecraft/core/Vec3i.java
-index 21387401c7958414fa6f3fd530488481d92a6eca..17bb8fb0353a818d946a0831781918781314c0ce 100644
---- a/src/main/java/net/minecraft/core/Vec3i.java
-+++ b/src/main/java/net/minecraft/core/Vec3i.java
-@@ -30,6 +30,12 @@ public class Vec3i implements Comparable<Vec3i> {
- );
- }
-
-+ // Paper start
-+ public final boolean isInsideBuildHeightAndWorldBoundsHorizontal(net.minecraft.world.level.LevelHeightAccessor levelHeightAccessor) {
-+ return getX() >= -30000000 && getZ() >= -30000000 && getX() < 30000000 && getZ() < 30000000 && !levelHeightAccessor.isOutsideBuildHeight(getY());
-+ }
-+ // Paper end
-+
- public Vec3i(int x, int y, int z) {
- this.x = x;
- this.y = y;
-diff --git a/src/main/java/net/minecraft/world/level/Level.java b/src/main/java/net/minecraft/world/level/Level.java
-index 9c743c980697a14d7348554fb77f242d5eaa152e..6c4af5e5e26930a7b0d140f8058d798355e4d53b 100644
---- a/src/main/java/net/minecraft/world/level/Level.java
-+++ b/src/main/java/net/minecraft/world/level/Level.java
-@@ -337,7 +337,7 @@ public abstract class Level implements LevelAccessor, AutoCloseable {
- // Paper end
-
- public boolean isInWorldBounds(BlockPos pos) {
-- return !this.isOutsideBuildHeight(pos) && Level.isInWorldBoundsHorizontal(pos);
-+ return pos.isInsideBuildHeightAndWorldBoundsHorizontal(this); // Paper - use better/optimized check
- }
-
- public static boolean isInSpawnableBounds(BlockPos pos) {
-diff --git a/src/main/java/net/minecraft/world/level/chunk/ChunkAccess.java b/src/main/java/net/minecraft/world/level/chunk/ChunkAccess.java
-index 3e5addb60ae8f466dad09edb3ae1fc88fe2681e9..5e8d2e4245757a0889645ea79ee68afb53f7dde4 100644
---- a/src/main/java/net/minecraft/world/level/chunk/ChunkAccess.java
-+++ b/src/main/java/net/minecraft/world/level/chunk/ChunkAccess.java
-@@ -172,6 +172,7 @@ public abstract class ChunkAccess implements BlockGetter, BiomeManager.NoiseBiom
- return GameEventListenerRegistry.NOOP;
- }
-
-+ public abstract BlockState getBlockState(final int x, final int y, final int z); // Paper
- @Nullable
- public abstract BlockState setBlockState(BlockPos pos, BlockState state, boolean moved);
-
-diff --git a/src/main/java/net/minecraft/world/level/chunk/ImposterProtoChunk.java b/src/main/java/net/minecraft/world/level/chunk/ImposterProtoChunk.java
-index 030fafe3b0bcad9e95251f5824ac2ef58640c705..b4e05ce176dfc6a2e66b294ed461c32020adf203 100644
---- a/src/main/java/net/minecraft/world/level/chunk/ImposterProtoChunk.java
-+++ b/src/main/java/net/minecraft/world/level/chunk/ImposterProtoChunk.java
-@@ -97,6 +97,12 @@ public class ImposterProtoChunk extends ProtoChunk {
- public BlockState getBlockState(BlockPos pos) {
- return this.wrapped.getBlockState(pos);
- }
-+ // Paper start
-+ @Override
-+ public final BlockState getBlockState(final int x, final int y, final int z) {
-+ return this.wrapped.getBlockStateFinal(x, y, z);
-+ }
-+ // Paper end
-
- @Override
- public FluidState getFluidState(BlockPos pos) {
-diff --git a/src/main/java/net/minecraft/world/level/chunk/ProtoChunk.java b/src/main/java/net/minecraft/world/level/chunk/ProtoChunk.java
-index 1036ff2ac8ba0967a36eb7977ed49500a05a33e6..a1f6274c9b1ab02ee55f1ae6011fc2dbb6e4824f 100644
---- a/src/main/java/net/minecraft/world/level/chunk/ProtoChunk.java
-+++ b/src/main/java/net/minecraft/world/level/chunk/ProtoChunk.java
-@@ -95,14 +95,18 @@ public class ProtoChunk extends ChunkAccess {
-
- @Override
- public BlockState getBlockState(BlockPos pos) {
-- int i = pos.getY();
-- if (this.isOutsideBuildHeight(i)) {
-+ // Paper start
-+ return getBlockState(pos.getX(), pos.getY(), pos.getZ());
-+ }
-+ public BlockState getBlockState(final int x, final int y, final int z) {
-+ if (this.isOutsideBuildHeight(y)) {
- return Blocks.VOID_AIR.defaultBlockState();
- } else {
-- LevelChunkSection levelChunkSection = this.getSection(this.getSectionIndex(i));
-- return levelChunkSection.hasOnlyAir() ? Blocks.AIR.defaultBlockState() : levelChunkSection.getBlockState(pos.getX() & 15, i & 15, pos.getZ() & 15);
-+ LevelChunkSection levelChunkSection = this.getSections()[this.getSectionIndex(y)];
-+ return levelChunkSection.hasOnlyAir() ? Blocks.AIR.defaultBlockState() : levelChunkSection.getBlockState(x & 15, y & 15, z & 15);
- }
- }
-+ // Paper end
-
- @Override
- public FluidState getFluidState(BlockPos pos) {
diff --git a/patches/unapplied/server/0994-Improve-Maps-in-item-frames-performance-and-bug-fixe.patch b/patches/unapplied/server/0994-Improve-Maps-in-item-frames-performance-and-bug-fixe.patch
deleted file mode 100644
index 811439226f..0000000000
--- a/patches/unapplied/server/0994-Improve-Maps-in-item-frames-performance-and-bug-fixe.patch
+++ /dev/null
@@ -1,128 +0,0 @@
-From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
-From: Aikar <[email protected]>
-Date: Fri, 29 Apr 2016 20:02:00 -0400
-Subject: [PATCH] Improve Maps (in item frames) performance and bug fixes
-
-Maps used a modified version of rendering to support plugin controlled
-imaging on maps. The Craft Map Renderer is much slower than Vanilla,
-causing maps in item frames to cause a noticeable hit on server performance.
-
-This updates the map system to not use the Craft system if we detect that no
-custom renderers are in use, defaulting to the much simpler Vanilla system.
-
-Additionally, numerous issues to player position tracking on maps has been fixed.
-
-diff --git a/src/main/java/net/minecraft/server/level/ServerLevel.java b/src/main/java/net/minecraft/server/level/ServerLevel.java
-index b6407dd3e5b87782503988f898bbf054e3f4e611..bd8c96e914b156284bdbb960f168e63e1f122920 100644
---- a/src/main/java/net/minecraft/server/level/ServerLevel.java
-+++ b/src/main/java/net/minecraft/server/level/ServerLevel.java
-@@ -2620,6 +2620,7 @@ public class ServerLevel extends Level implements WorldGenLevel {
- {
- if ( iter.next().player == entity )
- {
-+ map.decorations.remove(entity.getName().getString()); // Paper
- iter.remove();
- }
- }
-diff --git a/src/main/java/net/minecraft/world/entity/player/Player.java b/src/main/java/net/minecraft/world/entity/player/Player.java
-index 3e9c6e7e356ac08ec41736eaabf38714a8841d18..567704f61034363e48ef2a5b5566ebdc91682297 100644
---- a/src/main/java/net/minecraft/world/entity/player/Player.java
-+++ b/src/main/java/net/minecraft/world/entity/player/Player.java
-@@ -790,6 +790,14 @@ public abstract class Player extends LivingEntity {
- return null;
- }
- // CraftBukkit end
-+ // Paper start - remove player from map on drop
-+ if (itemstack.getItem() == Items.FILLED_MAP) {
-+ net.minecraft.world.level.saveddata.maps.MapItemSavedData worldmap = net.minecraft.world.item.MapItem.getSavedData(itemstack, this.level());
-+ if (worldmap != null) {
-+ worldmap.tickCarriedBy(this, itemstack);
-+ }
-+ }
-+ // Paper end
-
- return entityitem;
- }
-diff --git a/src/main/java/net/minecraft/world/level/saveddata/maps/MapItemSavedData.java b/src/main/java/net/minecraft/world/level/saveddata/maps/MapItemSavedData.java
-index ed57ce12d4d1cc632431a654cad648a8015402b1..45269115e63cfc3bd7dc740a5694e2cc7c35bcb1 100644
---- a/src/main/java/net/minecraft/world/level/saveddata/maps/MapItemSavedData.java
-+++ b/src/main/java/net/minecraft/world/level/saveddata/maps/MapItemSavedData.java
-@@ -66,6 +66,7 @@ public class MapItemSavedData extends SavedData {
- public final Map<String, MapDecoration> decorations = Maps.newLinkedHashMap();
- private final Map<String, MapFrame> frameMarkers = Maps.newHashMap();
- private int trackedDecorationCount;
-+ private org.bukkit.craftbukkit.map.RenderData vanillaRender = new org.bukkit.craftbukkit.map.RenderData(); // Paper
-
- // CraftBukkit start
- public final CraftMapView mapView;
-@@ -92,6 +93,7 @@ public class MapItemSavedData extends SavedData {
- // CraftBukkit start
- this.mapView = new CraftMapView(this);
- this.server = (CraftServer) org.bukkit.Bukkit.getServer();
-+ this.vanillaRender.buffer = colors; // Paper
- // CraftBukkit end
- }
-
-@@ -166,6 +168,7 @@ public class MapItemSavedData extends SavedData {
- if (abyte.length == 16384) {
- worldmap.colors = abyte;
- }
-+ worldmap.vanillaRender.buffer = abyte; // Paper
-
- ListTag nbttaglist = nbt.getList("banners", 10);
-
-@@ -578,6 +581,21 @@ public class MapItemSavedData extends SavedData {
-
- public class HoldingPlayer {
-
-+ // Paper start
-+ private void addSeenPlayers(java.util.Collection<MapDecoration> icons) {
-+ org.bukkit.entity.Player player = (org.bukkit.entity.Player) this.player.getBukkitEntity();
-+ MapItemSavedData.this.decorations.forEach((name, mapIcon) -> {
-+ // If this cursor is for a player check visibility with vanish system
-+ org.bukkit.entity.Player other = org.bukkit.Bukkit.getPlayerExact(name); // Spigot
-+ if (other == null || player.canSee(other)) {
-+ icons.add(mapIcon);
-+ }
-+ });
-+ }
-+ private boolean shouldUseVanillaMap() {
-+ return mapView.getRenderers().size() == 1 && mapView.getRenderers().get(0).getClass() == org.bukkit.craftbukkit.map.CraftMapRenderer.class;
-+ }
-+ // Paper end
- public final Player player;
- private boolean dirtyData = true;
- private int minDirtyX;
-@@ -611,7 +629,9 @@ public class MapItemSavedData extends SavedData {
- @Nullable
- Packet<?> nextUpdatePacket(int mapId) {
- MapItemSavedData.MapPatch worldmap_b;
-- org.bukkit.craftbukkit.map.RenderData render = MapItemSavedData.this.mapView.render((org.bukkit.craftbukkit.entity.CraftPlayer) this.player.getBukkitEntity()); // CraftBukkit
-+ if (!this.dirtyData && this.tick % 5 != 0) { this.tick++; return null; } // Paper - this won't end up sending, so don't render it!
-+ boolean vanillaMaps = shouldUseVanillaMap(); // Paper
-+ org.bukkit.craftbukkit.map.RenderData render = !vanillaMaps ? MapItemSavedData.this.mapView.render((org.bukkit.craftbukkit.entity.CraftPlayer) this.player.getBukkitEntity()) : MapItemSavedData.this.vanillaRender; // CraftBukkit // Paper
-
- if (this.dirtyData) {
- this.dirtyData = false;
-@@ -627,6 +647,8 @@ public class MapItemSavedData extends SavedData {
- // CraftBukkit start
- java.util.Collection<MapDecoration> icons = new java.util.ArrayList<MapDecoration>();
-
-+ if (vanillaMaps) addSeenPlayers(icons); // Paper
-+
- for (org.bukkit.map.MapCursor cursor : render.cursors) {
- if (cursor.isVisible()) {
- icons.add(new MapDecoration(MapDecoration.Type.byIcon(cursor.getRawType()), cursor.getX(), cursor.getY(), cursor.getDirection(), PaperAdventure.asVanilla(cursor.caption()))); // Paper - Adventure
-diff --git a/src/main/java/org/bukkit/craftbukkit/map/RenderData.java b/src/main/java/org/bukkit/craftbukkit/map/RenderData.java
-index 256a131781721c86dd6cdbc329335964570cbe8c..5768cd512ec166f1e8d1f4a28792015347297c3f 100644
---- a/src/main/java/org/bukkit/craftbukkit/map/RenderData.java
-+++ b/src/main/java/org/bukkit/craftbukkit/map/RenderData.java
-@@ -5,7 +5,7 @@ import org.bukkit.map.MapCursor;
-
- public class RenderData {
-
-- public final byte[] buffer;
-+ public byte[] buffer; // Paper
- public final ArrayList<MapCursor> cursors;
-
- public RenderData() {
diff --git a/patches/unapplied/server/0995-Strip-raytracing-for-EntityLiving-hasLineOfSight.patch b/patches/unapplied/server/0995-Strip-raytracing-for-EntityLiving-hasLineOfSight.patch
deleted file mode 100644
index 46e1b7e745..0000000000
--- a/patches/unapplied/server/0995-Strip-raytracing-for-EntityLiving-hasLineOfSight.patch
+++ /dev/null
@@ -1,158 +0,0 @@
-From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
-From: Paul Sauve <[email protected]>
-Date: Sat, 31 Oct 2020 18:43:02 -0500
-Subject: [PATCH] Strip raytracing for EntityLiving#hasLineOfSight
-
-The BlockGetter#clip method is very wasteful in both allocations,
-and in logic. While EntityLiving#hasLineOfSight provides static
-parameters for collisions with blocks and fluids, the method still does
-a lot of dynamic checks for both of these, which result in extra work.
-As well, since the fluid collision option is set to NONE, the entire
-fluid collision system is completely unneeded, yet used anyways.
-
-Copyright (C) 2020 Technove LLC
-
-This program is free software: you can redistribute it and/or modify
-it under the terms of the GNU General Public License as published by
-the Free Software Foundation, either version 3 of the License, or
-(at your option) any later version.
-
-This program is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-GNU General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with this program. If not, see <http://www.gnu.org/licenses/>.
-
-diff --git a/src/main/java/net/minecraft/world/entity/LivingEntity.java b/src/main/java/net/minecraft/world/entity/LivingEntity.java
-index 40e21effc948b02874a6ed1d1c340c4dc87579d6..aa261d3ad04bc3a19b3200214214650d9a9ac2af 100644
---- a/src/main/java/net/minecraft/world/entity/LivingEntity.java
-+++ b/src/main/java/net/minecraft/world/entity/LivingEntity.java
-@@ -3746,7 +3746,7 @@ public abstract class LivingEntity extends Entity implements Attackable {
- Vec3 vec3d1 = new Vec3(entity.getX(), entity.getEyeY(), entity.getZ());
-
- // Paper - diff on change - used in CraftLivingEntity#hasLineOfSight(Location) and CraftWorld#lineOfSightExists
-- return vec3d1.distanceToSqr(vec3d) > 128.0D * 128.0D ? false : this.level().clip(new ClipContext(vec3d, vec3d1, ClipContext.Block.COLLIDER, ClipContext.Fluid.NONE, this)).getType() == HitResult.Type.MISS; // Paper - Perf: Use distance squared
-+ return vec3d1.distanceToSqr(vec3d) > 128.0D * 128.0D ? false : this.level().clipDirect(vec3d, vec3d1, net.minecraft.world.phys.shapes.CollisionContext.of(this)) == HitResult.Type.MISS; // Paper - Perf: Use distance squared & strip raytracing
- }
- }
-
-diff --git a/src/main/java/net/minecraft/world/level/BlockGetter.java b/src/main/java/net/minecraft/world/level/BlockGetter.java
-index bb8e962e63c7a2d931f9bd7f7c002aa35cfa5fd3..0fa131a6c98adb498fc8d534e0e39647e80c6923 100644
---- a/src/main/java/net/minecraft/world/level/BlockGetter.java
-+++ b/src/main/java/net/minecraft/world/level/BlockGetter.java
-@@ -68,6 +68,18 @@ public interface BlockGetter extends LevelHeightAccessor {
- });
- }
-
-+ // Paper start - Broken down variant of the method below, used by Level#clipDirect
-+ @Nullable
-+ default BlockHitResult.Type clipDirect(Vec3 start, Vec3 end, BlockPos pos, BlockState state, net.minecraft.world.phys.shapes.CollisionContext collisionContext) {
-+ if (state.isAir()) {
-+ return null;
-+ }
-+
-+ final VoxelShape voxelshape = ClipContext.Block.COLLIDER.get(state, this, pos, collisionContext);
-+ final BlockHitResult hitResult = this.clipWithInteractionOverride(start, end, pos, voxelshape, state);
-+ return hitResult == null ? null : hitResult.getType();
-+ }
-+ // Paper end
- // CraftBukkit start - moved block handling into separate method for use by Block#rayTrace
- default BlockHitResult clip(ClipContext raytrace1, BlockPos blockposition) {
- // Paper start - Add predicate for blocks when raytracing
-diff --git a/src/main/java/net/minecraft/world/level/Level.java b/src/main/java/net/minecraft/world/level/Level.java
-index 6c4af5e5e26930a7b0d140f8058d798355e4d53b..5c209e323a5559480231c6d99357ba8b89edb027 100644
---- a/src/main/java/net/minecraft/world/level/Level.java
-+++ b/src/main/java/net/minecraft/world/level/Level.java
-@@ -329,10 +329,87 @@ public abstract class Level implements LevelAccessor, AutoCloseable {
- return null;
- }
-
-- // Paper start
-+ // Paper start - Broken down method of raytracing for EntityLiving#hasLineOfSight, replaces BlockGetter#clip(CollisionContext)
- public net.minecraft.world.phys.BlockHitResult.Type clipDirect(Vec3 start, Vec3 end, net.minecraft.world.phys.shapes.CollisionContext context) {
-- // To be patched over
-- return this.clip(new ClipContext(start, end, ClipContext.Block.COLLIDER, ClipContext.Fluid.NONE, context)).getType();
-+ // most of this code comes from BlockGetter#clip(CollisionContext, BiFunction, Function), but removes the needless functions
-+ if (start.equals(end)) {
-+ return net.minecraft.world.phys.BlockHitResult.Type.MISS;
-+ }
-+
-+ final double endX = Mth.lerp(-1.0E-7D, end.x, start.x);
-+ final double endY = Mth.lerp(-1.0E-7D, end.y, start.y);
-+ final double endZ = Mth.lerp(-1.0E-7D, end.z, start.z);
-+
-+ final double startX = Mth.lerp(-1.0E-7D, start.x, end.x);
-+ final double startY = Mth.lerp(-1.0E-7D, start.y, end.y);
-+ final double startZ = Mth.lerp(-1.0E-7D, start.z, end.z);
-+
-+ int currentX = Mth.floor(startX);
-+ int currentY = Mth.floor(startY);
-+ int currentZ = Mth.floor(startZ);
-+
-+ final BlockPos.MutableBlockPos currentBlock = new BlockPos.MutableBlockPos(currentX, currentY, currentZ);
-+
-+ LevelChunk chunk = this.getChunkIfLoaded(currentBlock);
-+ if (chunk == null) {
-+ return net.minecraft.world.phys.BlockHitResult.Type.MISS;
-+ }
-+
-+ final net.minecraft.world.phys.BlockHitResult.Type initialCheck = this.clipDirect(start, end, currentBlock, chunk.getBlockState(currentBlock), context);
-+ if (initialCheck != null) {
-+ return initialCheck;
-+ }
-+
-+ final double diffX = endX - startX;
-+ final double diffY = endY - startY;
-+ final double diffZ = endZ - startZ;
-+
-+ final int xDirection = Mth.sign(diffX);
-+ final int yDirection = Mth.sign(diffY);
-+ final int zDirection = Mth.sign(diffZ);
-+
-+ final double normalizedX = xDirection == 0 ? Double.MAX_VALUE : (double) xDirection / diffX;
-+ final double normalizedY = yDirection == 0 ? Double.MAX_VALUE : (double) yDirection / diffY;
-+ final double normalizedZ = zDirection == 0 ? Double.MAX_VALUE : (double) zDirection / diffZ;
-+
-+ double normalizedXDirection = normalizedX * (xDirection > 0 ? 1.0D - Mth.frac(startX) : Mth.frac(startX));
-+ double normalizedYDirection = normalizedY * (yDirection > 0 ? 1.0D - Mth.frac(startY) : Mth.frac(startY));
-+ double normalizedZDirection = normalizedZ * (zDirection > 0 ? 1.0D - Mth.frac(startZ) : Mth.frac(startZ));
-+
-+ net.minecraft.world.phys.BlockHitResult.Type result;
-+
-+ do {
-+ if (normalizedXDirection > 1.0D && normalizedYDirection > 1.0D && normalizedZDirection > 1.0D) {
-+ return net.minecraft.world.phys.BlockHitResult.Type.MISS;
-+ }
-+
-+ if (normalizedXDirection < normalizedYDirection) {
-+ if (normalizedXDirection < normalizedZDirection) {
-+ currentX += xDirection;
-+ normalizedXDirection += normalizedX;
-+ } else {
-+ currentZ += zDirection;
-+ normalizedZDirection += normalizedZ;
-+ }
-+ } else if (normalizedYDirection < normalizedZDirection) {
-+ currentY += yDirection;
-+ normalizedYDirection += normalizedY;
-+ } else {
-+ currentZ += zDirection;
-+ normalizedZDirection += normalizedZ;
-+ }
-+
-+ currentBlock.set(currentX, currentY, currentZ);
-+ if (chunk.getPos().x != currentBlock.getX() >> 4 || chunk.getPos().z != currentBlock.getZ() >> 4) {
-+ chunk = this.getChunkIfLoaded(currentBlock);
-+ if (chunk == null) {
-+ return net.minecraft.world.phys.BlockHitResult.Type.MISS;
-+ }
-+ }
-+ result = this.clipDirect(start, end, currentBlock, chunk.getBlockState(currentBlock), context);
-+ } while (result == null);
-+
-+ return result;
- }
- // Paper end
-
diff --git a/patches/unapplied/server/0996-Optimize-Network-Manager-and-add-advanced-packet-sup.patch b/patches/unapplied/server/0996-Optimize-Network-Manager-and-add-advanced-packet-sup.patch
deleted file mode 100644
index 2e8076eb2e..0000000000
--- a/patches/unapplied/server/0996-Optimize-Network-Manager-and-add-advanced-packet-sup.patch
+++ /dev/null
@@ -1,394 +0,0 @@
-From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
-From: Aikar <[email protected]>
-Date: Wed, 6 May 2020 04:53:35 -0400
-Subject: [PATCH] Optimize Network Manager and add advanced packet support
-
-Adds ability for 1 packet to bundle other packets to follow it
-Adds ability for a packet to delay sending more packets until a state is ready.
-
-Removes synchronization from sending packets
-Removes processing packet queue off of main thread
- - for the few cases where it is allowed, order is not necessary nor
- should it even be happening concurrently in first place (handshaking/login/status)
-
-Ensures packets sent asynchronously are dispatched on main thread
-
-This helps ensure safety for ProtocolLib as packet listeners
-are commonly accessing world state. This will allow you to schedule
-a packet to be sent async, but itll be dispatched sync for packet
-listeners to process.
-
-This should solve some deadlock risks
-
-Also adds Netty Channel Flush Consolidation to reduce the amount of flushing
-
-Also avoids spamming closed channel exception by rechecking closed state in dispatch
-and then catch exceptions and close if they fire.
-
-Part of this commit was authored by: Spottedleaf, sandtechnology
-
-diff --git a/src/main/java/net/minecraft/network/Connection.java b/src/main/java/net/minecraft/network/Connection.java
-index c399625a342ffd61102bb96a97ac24b0669e8e17..16eb94eb1f40485daef2713f740f6e0beeb1463f 100644
---- a/src/main/java/net/minecraft/network/Connection.java
-+++ b/src/main/java/net/minecraft/network/Connection.java
-@@ -84,7 +84,7 @@ public class Connection extends SimpleChannelInboundHandler<Packet<?>> {
- return new DefaultEventLoopGroup(0, (new ThreadFactoryBuilder()).setNameFormat("Netty Local Client IO #%d").setDaemon(true).setUncaughtExceptionHandler(new net.minecraft.DefaultUncaughtExceptionHandlerWithName(LOGGER)).build()); // Paper
- });
- private final PacketFlow receiving;
-- private final Queue<Consumer<Connection>> pendingActions = Queues.newConcurrentLinkedQueue();
-+ private final Queue<WrappedConsumer> pendingActions = Queues.newConcurrentLinkedQueue();
- public Channel channel;
- public SocketAddress address;
- // Spigot Start
-@@ -116,6 +116,10 @@ public class Connection extends SimpleChannelInboundHandler<Packet<?>> {
- public java.net.InetSocketAddress virtualHost;
- private static boolean enableExplicitFlush = Boolean.getBoolean("paper.explicit-flush"); // Paper - Disable explicit network manager flushing
- // Paper end
-+ // Paper start - Optimize network
-+ public boolean isPending = true;
-+ public boolean queueImmunity;
-+ // Paper end - Optimize network
-
- // Paper start - add utility methods
- public final net.minecraft.server.level.ServerPlayer getPlayer() {
-@@ -376,15 +380,39 @@ public class Connection extends SimpleChannelInboundHandler<Packet<?>> {
- }
-
- public void send(Packet<?> packet, @Nullable PacketSendListener callbacks, boolean flush) {
-- if (this.isConnected()) {
-- this.flushQueue();
-+ // Paper start - Optimize network: Handle oversized packets better
-+ final boolean connected = this.isConnected();
-+ if (!connected && !this.preparing) {
-+ return;
-+ }
-+
-+ packet.onPacketDispatch(this.getPlayer());
-+ if (connected && (InnerUtil.canSendImmediate(this, packet)
-+ || (io.papermc.paper.util.MCUtil.isMainThread() && packet.isReady() && this.pendingActions.isEmpty()
-+ && (packet.getExtraPackets() == null || packet.getExtraPackets().isEmpty())))) {
- this.sendPacket(packet, callbacks, flush);
- } else {
-- this.pendingActions.add((networkmanager) -> {
-- networkmanager.sendPacket(packet, callbacks, flush);
-- });
-- }
-+ // Write the packets to the queue, then flush - antixray hooks there already
-+ final java.util.List<Packet<?>> extraPackets = InnerUtil.buildExtraPackets(packet);
-+ final boolean hasExtraPackets = extraPackets != null && !extraPackets.isEmpty();
-+ if (!hasExtraPackets) {
-+ this.pendingActions.add(new PacketSendAction(packet, callbacks, flush));
-+ } else {
-+ final java.util.List<PacketSendAction> actions = new java.util.ArrayList<>(1 + extraPackets.size());
-+ actions.add(new PacketSendAction(packet, null, false)); // Delay the future listener until the end of the extra packets
-
-+ for (int i = 0, len = extraPackets.size(); i < len;) {
-+ final Packet<?> extraPacket = extraPackets.get(i);
-+ final boolean end = ++i == len;
-+ actions.add(new PacketSendAction(extraPacket, end ? callbacks : null, end)); // Append listener to the end
-+ }
-+
-+ this.pendingActions.addAll(actions);
-+ }
-+
-+ this.flushQueue();
-+ // Paper end - Optimize network
-+ }
- }
-
- public void runOnceConnected(Consumer<Connection> task) {
-@@ -392,7 +420,7 @@ public class Connection extends SimpleChannelInboundHandler<Packet<?>> {
- this.flushQueue();
- task.accept(this);
- } else {
-- this.pendingActions.add(task);
-+ this.pendingActions.add(new WrappedConsumer(task)); // Paper - Optimize network
- }
-
- }
-@@ -410,6 +438,14 @@ public class Connection extends SimpleChannelInboundHandler<Packet<?>> {
- }
-
- private void doSendPacket(Packet<?> packet, @Nullable PacketSendListener callbacks, boolean flush) {
-+ // Paper start - Optimize network
-+ final net.minecraft.server.level.ServerPlayer player = this.getPlayer();
-+ if (!this.isConnected()) {
-+ packet.onPacketDispatchFinish(player, null);
-+ return;
-+ }
-+ try {
-+ // Paper end - Optimize network
- ChannelFuture channelfuture = flush ? this.channel.writeAndFlush(packet) : this.channel.write(packet);
-
- if (callbacks != null) {
-@@ -429,14 +465,24 @@ public class Connection extends SimpleChannelInboundHandler<Packet<?>> {
- });
- }
-
-+ // Paper start - Optimize network
-+ if (packet.hasFinishListener()) {
-+ channelfuture.addListener((ChannelFutureListener) channelFuture -> packet.onPacketDispatchFinish(player, channelFuture));
-+ }
- channelfuture.addListener(ChannelFutureListener.FIRE_EXCEPTION_ON_FAILURE);
-+ } catch (final Exception e) {
-+ LOGGER.error("NetworkException: {}", player, e);
-+ this.disconnect(Component.translatable("disconnect.genericReason", "Internal Exception: " + e.getMessage()));
-+ packet.onPacketDispatchFinish(player, null);
-+ }
-+ // Paper end - Optimize network
- }
-
- public void flushChannel() {
- if (this.isConnected()) {
- this.flush();
- } else {
-- this.pendingActions.add(Connection::flush);
-+ this.pendingActions.add(new WrappedConsumer(Connection::flush)); // Paper - Optimize network
- }
-
- }
-@@ -469,20 +515,57 @@ public class Connection extends SimpleChannelInboundHandler<Packet<?>> {
- return attributekey;
- }
-
-- private void flushQueue() {
-- if (this.channel != null && this.channel.isOpen()) {
-- Queue queue = this.pendingActions;
--
-+ // Paper start - Optimize network: Rewrite this to be safer if ran off main thread
-+ private boolean flushQueue() {
-+ if (!this.isConnected()) {
-+ return true;
-+ }
-+ if (io.papermc.paper.util.MCUtil.isMainThread()) {
-+ return this.processQueue();
-+ } else if (this.isPending) {
-+ // Should only happen during login/status stages
- synchronized (this.pendingActions) {
-- Consumer consumer;
-+ return this.processQueue();
-+ }
-+ }
-+ return false;
-+ }
-+
-+ private boolean processQueue() {
-+ if (this.pendingActions.isEmpty()) {
-+ return true;
-+ }
-
-- while ((consumer = (Consumer) this.pendingActions.poll()) != null) {
-- consumer.accept(this);
-+ // If we are on main, we are safe here in that nothing else should be processing queue off main anymore
-+ // But if we are not on main due to login/status, the parent is synchronized on packetQueue
-+ final java.util.Iterator<WrappedConsumer> iterator = this.pendingActions.iterator();
-+ while (iterator.hasNext()) {
-+ final WrappedConsumer queued = iterator.next(); // poll -> peek
-+
-+ // Fix NPE (Spigot bug caused by handleDisconnection())
-+ if (queued == null) {
-+ return true;
-+ }
-+
-+ if (queued.isConsumed()) {
-+ continue;
-+ }
-+
-+ if (queued instanceof PacketSendAction packetSendAction) {
-+ final Packet<?> packet = packetSendAction.packet;
-+ if (!packet.isReady()) {
-+ return false;
- }
-+ }
-
-+ iterator.remove();
-+ if (queued.tryMarkConsumed()) {
-+ queued.accept(this);
- }
- }
-+ return true;
- }
-+ // Paper end - Optimize network
-
- private static final int MAX_PER_TICK = io.papermc.paper.configuration.GlobalConfiguration.get().misc.maxJoinsPerTick; // Paper - Buffer joins to world
- private static int joinAttemptsThisTick; // Paper - Buffer joins to world
-@@ -545,6 +628,7 @@ public class Connection extends SimpleChannelInboundHandler<Packet<?>> {
- public void disconnect(Component disconnectReason) {
- // Spigot Start
- this.preparing = false;
-+ this.clearPacketQueue(); // Paper - Optimize network
- // Spigot End
- if (this.channel == null) {
- this.delayedDisconnect = disconnectReason;
-@@ -716,7 +800,7 @@ public class Connection extends SimpleChannelInboundHandler<Packet<?>> {
- public void handleDisconnection() {
- if (this.channel != null && !this.channel.isOpen()) {
- if (this.disconnectionHandled) {
-- Connection.LOGGER.warn("handleDisconnection() called twice");
-+ // Connection.LOGGER.warn("handleDisconnection() called twice"); // Paper - Don't log useless message
- } else {
- this.disconnectionHandled = true;
- PacketListener packetlistener = this.getPacketListener();
-@@ -729,7 +813,7 @@ public class Connection extends SimpleChannelInboundHandler<Packet<?>> {
-
- packetlistener1.onDisconnect(ichatbasecomponent);
- }
-- this.pendingActions.clear(); // Free up packet queue.
-+ this.clearPacketQueue(); // Paper - Optimize network
- // Paper start - Add PlayerConnectionCloseEvent
- final PacketListener packetListener = this.getPacketListener();
- if (packetListener instanceof net.minecraft.server.network.ServerCommonPacketListenerImpl commonPacketListener) {
-@@ -766,4 +850,93 @@ public class Connection extends SimpleChannelInboundHandler<Packet<?>> {
- public void setBandwidthLogger(SampleLogger log) {
- this.bandwidthDebugMonitor = new BandwidthDebugMonitor(log);
- }
-+
-+ // Paper start - Optimize network
-+ public void clearPacketQueue() {
-+ final net.minecraft.server.level.ServerPlayer player = getPlayer();
-+ for (final Consumer<Connection> queuedAction : this.pendingActions) {
-+ if (queuedAction instanceof PacketSendAction packetSendAction) {
-+ final Packet<?> packet = packetSendAction.packet;
-+ if (packet.hasFinishListener()) {
-+ packet.onPacketDispatchFinish(player, null);
-+ }
-+ }
-+ }
-+ this.pendingActions.clear();
-+ }
-+
-+ private static class InnerUtil { // Attempt to hide these methods from ProtocolLib, so it doesn't accidently pick them up.
-+
-+ @Nullable
-+ private static java.util.List<Packet<?>> buildExtraPackets(final Packet<?> packet) {
-+ final java.util.List<Packet<?>> extra = packet.getExtraPackets();
-+ if (extra == null || extra.isEmpty()) {
-+ return null;
-+ }
-+
-+ final java.util.List<Packet<?>> ret = new java.util.ArrayList<>(1 + extra.size());
-+ buildExtraPackets0(extra, ret);
-+ return ret;
-+ }
-+
-+ private static void buildExtraPackets0(final java.util.List<Packet<?>> extraPackets, final java.util.List<Packet<?>> into) {
-+ for (final Packet<?> extra : extraPackets) {
-+ into.add(extra);
-+ final java.util.List<Packet<?>> extraExtra = extra.getExtraPackets();
-+ if (extraExtra != null && !extraExtra.isEmpty()) {
-+ buildExtraPackets0(extraExtra, into);
-+ }
-+ }
-+ }
-+
-+ private static boolean canSendImmediate(final Connection networkManager, final net.minecraft.network.protocol.Packet<?> packet) {
-+ return networkManager.isPending || networkManager.packetListener.protocol() != ConnectionProtocol.PLAY ||
-+ packet instanceof net.minecraft.network.protocol.common.ClientboundKeepAlivePacket ||
-+ packet instanceof net.minecraft.network.protocol.game.ClientboundPlayerChatPacket ||
-+ packet instanceof net.minecraft.network.protocol.game.ClientboundSystemChatPacket ||
-+ packet instanceof net.minecraft.network.protocol.game.ClientboundCommandSuggestionsPacket ||
-+ packet instanceof net.minecraft.network.protocol.game.ClientboundSetTitleTextPacket ||
-+ packet instanceof net.minecraft.network.protocol.game.ClientboundSetSubtitleTextPacket ||
-+ packet instanceof net.minecraft.network.protocol.game.ClientboundSetActionBarTextPacket ||
-+ packet instanceof net.minecraft.network.protocol.game.ClientboundSetTitlesAnimationPacket ||
-+ packet instanceof net.minecraft.network.protocol.game.ClientboundClearTitlesPacket ||
-+ packet instanceof net.minecraft.network.protocol.game.ClientboundSoundPacket ||
-+ packet instanceof net.minecraft.network.protocol.game.ClientboundSoundEntityPacket ||
-+ packet instanceof net.minecraft.network.protocol.game.ClientboundStopSoundPacket ||
-+ packet instanceof net.minecraft.network.protocol.game.ClientboundLevelParticlesPacket ||
-+ packet instanceof net.minecraft.network.protocol.game.ClientboundBossEventPacket;
-+ }
-+ }
-+
-+ private static class WrappedConsumer implements Consumer<Connection> {
-+ private final Consumer<Connection> delegate;
-+ private final java.util.concurrent.atomic.AtomicBoolean consumed = new java.util.concurrent.atomic.AtomicBoolean(false);
-+
-+ private WrappedConsumer(final Consumer<Connection> delegate) {
-+ this.delegate = delegate;
-+ }
-+
-+ @Override
-+ public void accept(final Connection connection) {
-+ this.delegate.accept(connection);
-+ }
-+
-+ public boolean tryMarkConsumed() {
-+ return consumed.compareAndSet(false, true);
-+ }
-+
-+ public boolean isConsumed() {
-+ return consumed.get();
-+ }
-+ }
-+
-+ private static final class PacketSendAction extends WrappedConsumer {
-+ private final Packet<?> packet;
-+
-+ private PacketSendAction(final Packet<?> packet, @Nullable final PacketSendListener packetSendListener, final boolean flush) {
-+ super(connection -> connection.sendPacket(packet, packetSendListener, flush));
-+ this.packet = packet;
-+ }
-+ }
-+ // Paper end - Optimize network
- }
-diff --git a/src/main/java/net/minecraft/network/protocol/Packet.java b/src/main/java/net/minecraft/network/protocol/Packet.java
-index cc658a61065d5c0021a4b88fa58b40211b94f8ec..da11266a0a23f446196e6facf2c358cfcc18070f 100644
---- a/src/main/java/net/minecraft/network/protocol/Packet.java
-+++ b/src/main/java/net/minecraft/network/protocol/Packet.java
-@@ -11,6 +11,30 @@ public interface Packet<T extends PacketListener> {
- void handle(T listener);
-
- // Paper start
-+ /**
-+ * @param player Null if not at PLAY stage yet
-+ */
-+ default void onPacketDispatch(@Nullable net.minecraft.server.level.ServerPlayer player) {
-+ }
-+
-+ /**
-+ * @param player Null if not at PLAY stage yet
-+ * @param future Can be null if packet was cancelled
-+ */
-+ default void onPacketDispatchFinish(@Nullable net.minecraft.server.level.ServerPlayer player, @Nullable io.netty.channel.ChannelFuture future) {}
-+
-+ default boolean hasFinishListener() {
-+ return false;
-+ }
-+
-+ default boolean isReady() {
-+ return true;
-+ }
-+
-+ @Nullable
-+ default java.util.List<Packet<?>> getExtraPackets() {
-+ return null;
-+ }
- default boolean packetTooLarge(net.minecraft.network.Connection manager) {
- return false;
- }
-diff --git a/src/main/java/net/minecraft/server/network/ServerConnectionListener.java b/src/main/java/net/minecraft/server/network/ServerConnectionListener.java
-index 4f330a44c77a7ec3237a86fda04921a8c4a1c00f..a4a29a7ea0035ecf4c61ee8547a9eb24acb667d0 100644
---- a/src/main/java/net/minecraft/server/network/ServerConnectionListener.java
-+++ b/src/main/java/net/minecraft/server/network/ServerConnectionListener.java
-@@ -63,10 +63,12 @@ public class ServerConnectionListener {
- final List<Connection> connections = Collections.synchronizedList(Lists.newArrayList());
- // Paper start - prevent blocking on adding a new connection while the server is ticking
- private final java.util.Queue<Connection> pending = new java.util.concurrent.ConcurrentLinkedQueue<>();
-+ private static final boolean disableFlushConsolidation = Boolean.getBoolean("Paper.disableFlushConsolidate"); // Paper - Optimize network
- private final void addPending() {
- Connection connection;
- while ((connection = pending.poll()) != null) {
- connections.add(connection);
-+ connection.isPending = false; // Paper - Optimize network
- }
- }
- // Paper end - prevent blocking on adding a new connection while the server is ticking
-@@ -114,6 +116,7 @@ public class ServerConnectionListener {
- ;
- }
-
-+ if (!disableFlushConsolidation) channel.pipeline().addFirst(new io.netty.handler.flush.FlushConsolidationHandler()); // Paper - Optimize network
- ChannelPipeline channelpipeline = channel.pipeline().addLast("timeout", new ReadTimeoutHandler(30)).addLast("legacy_query", new LegacyQueryHandler(ServerConnectionListener.this.getServer()));
-
- Connection.configureSerialization(channelpipeline, PacketFlow.SERVERBOUND, (BandwidthDebugMonitor) null);
diff --git a/patches/unapplied/server/0997-Allow-Saving-of-Oversized-Chunks.patch b/patches/unapplied/server/0997-Allow-Saving-of-Oversized-Chunks.patch
deleted file mode 100644
index f6a81ca166..0000000000
--- a/patches/unapplied/server/0997-Allow-Saving-of-Oversized-Chunks.patch
+++ /dev/null
@@ -1,211 +0,0 @@
-From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
-From: Aikar <[email protected]>
-Date: Fri, 15 Feb 2019 01:08:19 -0500
-Subject: [PATCH] Allow Saving of Oversized Chunks
-
-Note 1.17 update: With 1.17, Entities are no longer stored in chunk slices, so this needs updating!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
-
-The Minecraft World Region File format has a hard cap of 1MB per chunk.
-This is due to the fact that the header of the file format only allocates
-a single byte for sector count, meaning a maximum of 256 sectors, at 4k per sector.
-
-This limit can be reached fairly easily with books, resulting in the chunk being unable
-to save to the world. Worse off, is that nothing printed when this occured, and silently
-performed a chunk rollback on next load.
-
-This leads to security risk with duplication and is being actively exploited.
-
-This patch catches the too large scenario, falls back and moves any large Entity
-or Tile Entity into a new compound, and this compound is saved into a different file.
-
-On Chunk Load, we check for oversized status, and if so, we load the extra file and
-merge the Entities and Tile Entities from the oversized chunk back into the level to
-then be loaded as normal.
-
-Once a chunk is returned back to normal size, the oversized flag will clear, and no
-extra data file will exist.
-
-This fix maintains compatability with all existing Anvil Region Format tools as it
-does not alter the save format. They will just not know about the extra entities.
-
-This fix also maintains compatability if someone switches server jars to one without
-this fix, as the data will remain in the oversized file. Once the server returns
-to a jar with this fix, the data will be restored.
-
-diff --git a/src/main/java/net/minecraft/world/level/chunk/storage/RegionFile.java b/src/main/java/net/minecraft/world/level/chunk/storage/RegionFile.java
-index bc8038da65f834249c61a262fc1a5abb7cc91a63..6c89b92cac521808873e9e1eccc363695275cd7a 100644
---- a/src/main/java/net/minecraft/world/level/chunk/storage/RegionFile.java
-+++ b/src/main/java/net/minecraft/world/level/chunk/storage/RegionFile.java
-@@ -18,8 +18,11 @@ import java.nio.file.LinkOption;
- import java.nio.file.Path;
- import java.nio.file.StandardCopyOption;
- import java.nio.file.StandardOpenOption;
-+import java.util.zip.InflaterInputStream; // Paper
- import javax.annotation.Nullable;
- import net.minecraft.Util;
-+import net.minecraft.nbt.CompoundTag; // Paper
-+import net.minecraft.nbt.NbtIo; // Paper
- import net.minecraft.world.level.ChunkPos;
- import org.slf4j.Logger;
-
-@@ -45,6 +48,7 @@ public class RegionFile implements AutoCloseable {
- @VisibleForTesting
- protected final RegionBitmap usedSectors;
- public final java.util.concurrent.locks.ReentrantLock fileLock = new java.util.concurrent.locks.ReentrantLock(); // Paper
-+ public final Path regionFile; // Paper
-
- public RegionFile(Path file, Path directory, boolean dsync) throws IOException {
- this(file, directory, RegionFileVersion.getCompressionFormat(), dsync); // Paper - Configurable region compression format
-@@ -52,6 +56,8 @@ public class RegionFile implements AutoCloseable {
-
- public RegionFile(Path file, Path directory, RegionFileVersion outputChunkStreamVersion, boolean dsync) throws IOException {
- this.header = ByteBuffer.allocateDirect(8192);
-+ this.regionFile = file; // Paper
-+ initOversizedState(); // Paper
- this.usedSectors = new RegionBitmap();
- this.version = outputChunkStreamVersion;
- if (!Files.isDirectory(directory, new LinkOption[0])) {
-@@ -431,6 +437,74 @@ public class RegionFile implements AutoCloseable {
- }
-
- public static final int MAX_CHUNK_SIZE = 500 * 1024 * 1024; // Paper - don't write garbage data to disk if writing serialization fails
-+ // Paper start
-+ private final byte[] oversized = new byte[1024];
-+ private int oversizedCount;
-+
-+ private synchronized void initOversizedState() throws IOException {
-+ Path metaFile = getOversizedMetaFile();
-+ if (Files.exists(metaFile)) {
-+ final byte[] read = java.nio.file.Files.readAllBytes(metaFile);
-+ System.arraycopy(read, 0, oversized, 0, oversized.length);
-+ for (byte temp : oversized) {
-+ oversizedCount += temp;
-+ }
-+ }
-+ }
-+
-+ private static int getChunkIndex(int x, int z) {
-+ return (x & 31) + (z & 31) * 32;
-+ }
-+ synchronized boolean isOversized(int x, int z) {
-+ return this.oversized[getChunkIndex(x, z)] == 1;
-+ }
-+ synchronized void setOversized(int x, int z, boolean oversized) throws IOException {
-+ final int offset = getChunkIndex(x, z);
-+ boolean previous = this.oversized[offset] == 1;
-+ this.oversized[offset] = (byte) (oversized ? 1 : 0);
-+ if (!previous && oversized) {
-+ oversizedCount++;
-+ } else if (!oversized && previous) {
-+ oversizedCount--;
-+ }
-+ if (previous && !oversized) {
-+ Path oversizedFile = getOversizedFile(x, z);
-+ if (Files.exists(oversizedFile)) {
-+ Files.delete(oversizedFile);
-+ }
-+ }
-+ if (oversizedCount > 0) {
-+ if (previous != oversized) {
-+ writeOversizedMeta();
-+ }
-+ } else if (previous) {
-+ Path oversizedMetaFile = getOversizedMetaFile();
-+ if (Files.exists(oversizedMetaFile)) {
-+ Files.delete(oversizedMetaFile);
-+ }
-+ }
-+ }
-+
-+ private void writeOversizedMeta() throws IOException {
-+ java.nio.file.Files.write(getOversizedMetaFile(), oversized);
-+ }
-+
-+ private Path getOversizedMetaFile() {
-+ return this.regionFile.getParent().resolve(this.regionFile.getFileName().toString().replaceAll("\\.mca$", "") + ".oversized.nbt");
-+ }
-+
-+ private Path getOversizedFile(int x, int z) {
-+ return this.regionFile.getParent().resolve(this.regionFile.getFileName().toString().replaceAll("\\.mca$", "") + "_oversized_" + x + "_" + z + ".nbt");
-+ }
-+
-+ synchronized CompoundTag getOversizedData(int x, int z) throws IOException {
-+ Path file = getOversizedFile(x, z);
-+ try (DataInputStream out = new DataInputStream(new java.io.BufferedInputStream(new InflaterInputStream(Files.newInputStream(file))))) {
-+ return NbtIo.read((java.io.DataInput) out);
-+ }
-+
-+ }
-+ // Paper end
- private class ChunkBuffer extends ByteArrayOutputStream {
-
- private final ChunkPos pos;
-diff --git a/src/main/java/net/minecraft/world/level/chunk/storage/RegionFileStorage.java b/src/main/java/net/minecraft/world/level/chunk/storage/RegionFileStorage.java
-index f1ecc3832da094400ed9d45bfc60af10da682b4a..f27cf743bbc379520263909541d653dd38d1be58 100644
---- a/src/main/java/net/minecraft/world/level/chunk/storage/RegionFileStorage.java
-+++ b/src/main/java/net/minecraft/world/level/chunk/storage/RegionFileStorage.java
-@@ -121,6 +121,43 @@ public class RegionFileStorage implements AutoCloseable {
- }
- }
-
-+ // Paper start
-+ private static void printOversizedLog(String msg, Path file, int x, int z) {
-+ org.apache.logging.log4j.LogManager.getLogger().fatal(msg + " (" + file.toString().replaceAll(".+[\\\\/]", "") + " - " + x + "," + z + ") Go clean it up to remove this message. /minecraft:tp " + (x<<4)+" 128 "+(z<<4) + " - DO NOT REPORT THIS TO PAPER - You may ask for help on Discord, but do not file an issue. These error messages can not be removed.");
-+ }
-+
-+ private static CompoundTag readOversizedChunk(RegionFile regionfile, ChunkPos chunkCoordinate) throws IOException {
-+ synchronized (regionfile) {
-+ try (DataInputStream datainputstream = regionfile.getChunkDataInputStream(chunkCoordinate)) {
-+ CompoundTag oversizedData = regionfile.getOversizedData(chunkCoordinate.x, chunkCoordinate.z);
-+ CompoundTag chunk = NbtIo.read((DataInput) datainputstream);
-+ if (oversizedData == null) {
-+ return chunk;
-+ }
-+ CompoundTag oversizedLevel = oversizedData.getCompound("Level");
-+
-+ mergeChunkList(chunk.getCompound("Level"), oversizedLevel, "Entities", "Entities");
-+ mergeChunkList(chunk.getCompound("Level"), oversizedLevel, "TileEntities", "TileEntities");
-+
-+ return chunk;
-+ } catch (Throwable throwable) {
-+ throwable.printStackTrace();
-+ throw throwable;
-+ }
-+ }
-+ }
-+
-+ private static void mergeChunkList(CompoundTag level, CompoundTag oversizedLevel, String key, String oversizedKey) {
-+ net.minecraft.nbt.ListTag levelList = level.getList(key, net.minecraft.nbt.Tag.TAG_COMPOUND);
-+ net.minecraft.nbt.ListTag oversizedList = oversizedLevel.getList(oversizedKey, net.minecraft.nbt.Tag.TAG_COMPOUND);
-+
-+ if (!oversizedList.isEmpty()) {
-+ levelList.addAll(oversizedList);
-+ level.put(key, levelList);
-+ }
-+ }
-+ // Paper end
-+
- @Nullable
- public CompoundTag read(ChunkPos pos) throws IOException {
- // CraftBukkit start - SPIGOT-5680: There's no good reason to preemptively create files on read, save that for writing
-@@ -132,6 +169,12 @@ public class RegionFileStorage implements AutoCloseable {
- try { // Paper
- DataInputStream datainputstream = regionfile.getChunkDataInputStream(pos);
-
-+ // Paper start
-+ if (regionfile.isOversized(pos.x, pos.z)) {
-+ printOversizedLog("Loading Oversized Chunk!", regionfile.regionFile, pos.x, pos.z);
-+ return readOversizedChunk(regionfile, pos);
-+ }
-+ // Paper end
- CompoundTag nbttagcompound;
- label43:
- {
-@@ -223,6 +266,7 @@ public class RegionFileStorage implements AutoCloseable {
-
- try {
- NbtIo.write(nbt, (DataOutput) dataoutputstream);
-+ regionfile.setOversized(pos.x, pos.z, false); // Paper - We don't do this anymore, mojang stores differently, but clear old meta flag if it exists to get rid of our own meta file once last oversized is gone
- // Paper start - don't write garbage data to disk if writing serialization fails
- dataoutputstream.close(); // Only write if successful
- } catch (final RegionFileSizeException e) {
diff --git a/patches/unapplied/server/0998-Fix-World-isChunkGenerated-calls.patch b/patches/unapplied/server/0998-Fix-World-isChunkGenerated-calls.patch
deleted file mode 100644
index f6df1f113b..0000000000
--- a/patches/unapplied/server/0998-Fix-World-isChunkGenerated-calls.patch
+++ /dev/null
@@ -1,244 +0,0 @@
-From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
-From: Spottedleaf <[email protected]>
-Date: Sat, 15 Jun 2019 08:54:33 -0700
-Subject: [PATCH] Fix World#isChunkGenerated calls
-
-Optimize World#loadChunk() too
-This patch also adds a chunk status cache on region files (note that
-its only purpose is to cache the status on DISK)
-
-diff --git a/src/main/java/net/minecraft/server/level/ChunkMap.java b/src/main/java/net/minecraft/server/level/ChunkMap.java
-index 4549b32a3d848e4e84334e889dbc9c6b883fe621..07a9a11c7fa608e221c0f0e759c483b44de9fdd5 100644
---- a/src/main/java/net/minecraft/server/level/ChunkMap.java
-+++ b/src/main/java/net/minecraft/server/level/ChunkMap.java
-@@ -717,9 +717,13 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
- // Paper end
-
- private CompletableFuture<Optional<CompoundTag>> readChunk(ChunkPos chunkPos) {
-- return this.read(chunkPos).thenApplyAsync((optional) -> {
-- return optional.map((nbttagcompound) -> this.upgradeChunkTag(nbttagcompound, chunkPos)); // CraftBukkit
-- }, Util.backgroundExecutor());
-+ // Paper start - Cache chunk status on disk
-+ try {
-+ return CompletableFuture.completedFuture(Optional.ofNullable(this.readConvertChunkSync(chunkPos)));
-+ } catch (Throwable thr) {
-+ return CompletableFuture.failedFuture(thr);
-+ }
-+ // Paper end - Cache chunk status on disk
- }
-
- // CraftBukkit start
-@@ -728,6 +732,60 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
- // CraftBukkit end
- }
-
-+ // Paper start - Cache chunk status on disk
-+ @Nullable
-+ public CompoundTag readConvertChunkSync(ChunkPos pos) throws IOException {
-+ CompoundTag nbttagcompound = this.readSync(pos);
-+ if (nbttagcompound == null) {
-+ return null;
-+ }
-+
-+ nbttagcompound = this.upgradeChunkTag(nbttagcompound, pos); // CraftBukkit
-+ if (nbttagcompound == null) {
-+ return null;
-+ }
-+
-+ this.updateChunkStatusOnDisk(pos, nbttagcompound);
-+
-+ return nbttagcompound;
-+ }
-+
-+ public ChunkStatus getChunkStatusOnDiskIfCached(ChunkPos chunkPos) {
-+ net.minecraft.world.level.chunk.storage.RegionFile regionFile = regionFileCache.getRegionFileIfLoaded(chunkPos);
-+
-+ return regionFile == null ? null : regionFile.getStatusIfCached(chunkPos.x, chunkPos.z);
-+ }
-+
-+ public ChunkStatus getChunkStatusOnDisk(ChunkPos chunkPos) throws IOException {
-+ net.minecraft.world.level.chunk.storage.RegionFile regionFile = regionFileCache.getRegionFile(chunkPos, true);
-+
-+ if (regionFile == null || !regionFileCache.chunkExists(chunkPos)) {
-+ return null;
-+ }
-+
-+ ChunkStatus status = regionFile.getStatusIfCached(chunkPos.x, chunkPos.z);
-+
-+ if (status != null) {
-+ return status;
-+ }
-+
-+ this.readChunk(chunkPos);
-+
-+ return regionFile.getStatusIfCached(chunkPos.x, chunkPos.z);
-+ }
-+
-+ public void updateChunkStatusOnDisk(ChunkPos chunkPos, @Nullable CompoundTag compound) throws IOException {
-+ net.minecraft.world.level.chunk.storage.RegionFile regionFile = regionFileCache.getRegionFile(chunkPos, false);
-+
-+ regionFile.setStatus(chunkPos.x, chunkPos.z, ChunkSerializer.getStatus(compound));
-+ }
-+
-+ public ChunkAccess getUnloadingChunk(int chunkX, int chunkZ) {
-+ ChunkHolder chunkHolder = io.papermc.paper.chunk.system.ChunkSystem.getUnloadingChunkHolder(this.level, chunkX, chunkZ);
-+ return chunkHolder == null ? null : chunkHolder.getAvailableChunkNow();
-+ }
-+ // Paper end - Cache chunk status on disk
-+
- public boolean anyPlayerCloseEnoughForSpawning(ChunkPos pos) { // Paper - public
- // Spigot start
- return this.anyPlayerCloseEnoughForSpawning(pos, false);
-diff --git a/src/main/java/net/minecraft/world/level/chunk/storage/RegionFile.java b/src/main/java/net/minecraft/world/level/chunk/storage/RegionFile.java
-index 6c89b92cac521808873e9e1eccc363695275cd7a..92ba75254f6ffca40abd5485dbb4789de59edebd 100644
---- a/src/main/java/net/minecraft/world/level/chunk/storage/RegionFile.java
-+++ b/src/main/java/net/minecraft/world/level/chunk/storage/RegionFile.java
-@@ -50,6 +50,30 @@ public class RegionFile implements AutoCloseable {
- public final java.util.concurrent.locks.ReentrantLock fileLock = new java.util.concurrent.locks.ReentrantLock(); // Paper
- public final Path regionFile; // Paper
-
-+ // Paper start - Cache chunk status
-+ private final net.minecraft.world.level.chunk.ChunkStatus[] statuses = new net.minecraft.world.level.chunk.ChunkStatus[32 * 32];
-+
-+ private boolean closed;
-+
-+ // invoked on write/read
-+ public void setStatus(int x, int z, net.minecraft.world.level.chunk.ChunkStatus status) {
-+ if (this.closed) {
-+ // We've used an invalid region file.
-+ throw new IllegalStateException("RegionFile is closed");
-+ }
-+ this.statuses[getChunkLocation(x, z)] = status;
-+ }
-+
-+ public net.minecraft.world.level.chunk.ChunkStatus getStatusIfCached(int x, int z) {
-+ if (this.closed) {
-+ // We've used an invalid region file.
-+ throw new IllegalStateException("RegionFile is closed");
-+ }
-+ final int location = getChunkLocation(x, z);
-+ return this.statuses[location];
-+ }
-+ // Paper end - Cache chunk status
-+
- public RegionFile(Path file, Path directory, boolean dsync) throws IOException {
- this(file, directory, RegionFileVersion.getCompressionFormat(), dsync); // Paper - Configurable region compression format
- }
-@@ -397,6 +421,7 @@ public class RegionFile implements AutoCloseable {
- return this.getOffset(pos) != 0;
- }
-
-+ private static int getChunkLocation(int x, int z) { return (x & 31) + (z & 31) * 32; } // Paper - Cache chunk status; OBFHELPER - sort of, mirror of logic below
- private static int getOffsetIndex(ChunkPos pos) {
- return pos.getRegionLocalX() + pos.getRegionLocalZ() * 32;
- }
-@@ -407,6 +432,7 @@ public class RegionFile implements AutoCloseable {
- synchronized (this) {
- try {
- // Paper end
-+ this.closed = true; // Paper - Cache chunk status
- try {
- this.padToFullSector();
- } finally {
-diff --git a/src/main/java/net/minecraft/world/level/chunk/storage/RegionFileStorage.java b/src/main/java/net/minecraft/world/level/chunk/storage/RegionFileStorage.java
-index f27cf743bbc379520263909541d653dd38d1be58..0db8ee3b640e6d1268e9c1cccda85459bd447105 100644
---- a/src/main/java/net/minecraft/world/level/chunk/storage/RegionFileStorage.java
-+++ b/src/main/java/net/minecraft/world/level/chunk/storage/RegionFileStorage.java
-@@ -266,6 +266,7 @@ public class RegionFileStorage implements AutoCloseable {
-
- try {
- NbtIo.write(nbt, (DataOutput) dataoutputstream);
-+ regionfile.setStatus(pos.x, pos.z, ChunkSerializer.getStatus(nbt)); // Paper - Cache chunk status
- regionfile.setOversized(pos.x, pos.z, false); // Paper - We don't do this anymore, mojang stores differently, but clear old meta flag if it exists to get rid of our own meta file once last oversized is gone
- // Paper start - don't write garbage data to disk if writing serialization fails
- dataoutputstream.close(); // Only write if successful
-diff --git a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java
-index 75ddeceb3f6c381b95dca0a93643aaca69d418e3..18a2f8c956137b8b60b07e02df4b3a2350fc6e46 100644
---- a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java
-+++ b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java
-@@ -383,9 +383,23 @@ public class CraftWorld extends CraftRegionAccessor implements World {
-
- @Override
- public boolean isChunkGenerated(int x, int z) {
-+ // Paper start - Fix this method
-+ if (!Bukkit.isPrimaryThread()) {
-+ return java.util.concurrent.CompletableFuture.supplyAsync(() -> {
-+ return CraftWorld.this.isChunkGenerated(x, z);
-+ }, world.getChunkSource().mainThreadProcessor).join();
-+ }
-+ ChunkAccess chunk = world.getChunkSource().getChunkAtImmediately(x, z);
-+ if (chunk == null) {
-+ chunk = world.getChunkSource().chunkMap.getUnloadingChunk(x, z);
-+ }
-+ if (chunk != null) {
-+ return chunk instanceof ImposterProtoChunk || chunk instanceof net.minecraft.world.level.chunk.LevelChunk;
-+ }
- try {
-- return this.isChunkLoaded(x, z) || this.world.getChunkSource().chunkMap.read(new ChunkPos(x, z)).get().isPresent();
-- } catch (InterruptedException | ExecutionException ex) {
-+ return world.getChunkSource().chunkMap.getChunkStatusOnDisk(new ChunkPos(x, z)) == ChunkStatus.FULL;
-+ } catch (java.io.IOException ex) {
-+ // Paper end - Fix this method
- throw new RuntimeException(ex);
- }
- }
-@@ -537,20 +551,48 @@ public class CraftWorld extends CraftRegionAccessor implements World {
- public boolean loadChunk(int x, int z, boolean generate) {
- org.spigotmc.AsyncCatcher.catchOp("chunk load"); // Spigot
- warnUnsafeChunk("loading a faraway chunk", x, z); // Paper
-- ChunkAccess chunk = this.world.getChunkSource().getChunk(x, z, generate || isChunkGenerated(x, z) ? ChunkStatus.FULL : ChunkStatus.EMPTY, true); // Paper
--
-- // If generate = false, but the chunk already exists, we will get this back.
-- if (chunk instanceof ImposterProtoChunk) {
-- // We then cycle through again to get the full chunk immediately, rather than after the ticket addition
-- chunk = this.world.getChunkSource().getChunk(x, z, ChunkStatus.FULL, true);
-- }
--
-- if (chunk instanceof net.minecraft.world.level.chunk.LevelChunk) {
-- this.world.getChunkSource().addRegionTicket(TicketType.PLUGIN, new ChunkPos(x, z), 0, Unit.INSTANCE); // Paper
-+ // Paper start - Optimize this method
-+ ChunkPos chunkPos = new ChunkPos(x, z);
-+ ChunkAccess immediate = world.getChunkSource().getChunkAtIfLoadedImmediately(x, z);
-+ if (immediate != null) {
-+ // Plugins should use plugin tickets instead of this method to keep a chunk perpetually loaded
- return true;
- }
-
-- return false;
-+ if (!generate) {
-+ immediate = world.getChunkSource().chunkMap.getUnloadingChunk(x, z);
-+ if (immediate != null) {
-+ if (!(immediate instanceof ImposterProtoChunk) && !(immediate instanceof net.minecraft.world.level.chunk.LevelChunk)) {
-+ return false; // not full status
-+ }
-+ world.getChunkSource().addRegionTicket(TicketType.PLUGIN, chunkPos, 0, Unit.INSTANCE); // Paper
-+ world.getChunk(x, z); // make sure we're at ticket level 32 or lower
-+ return true;
-+ }
-+ net.minecraft.world.level.chunk.storage.RegionFile file;
-+ try {
-+ file = world.getChunkSource().chunkMap.regionFileCache.getRegionFile(chunkPos, false);
-+ } catch (java.io.IOException ex) {
-+ throw new RuntimeException(ex);
-+ }
-+
-+ ChunkStatus status = file.getStatusIfCached(x, z);
-+ if (!file.hasChunk(chunkPos) || (status != null && status != ChunkStatus.FULL)) {
-+ return false;
-+ }
-+
-+ ChunkAccess chunk = world.getChunkSource().getChunk(x, z, ChunkStatus.EMPTY, true);
-+ if (!(chunk instanceof ImposterProtoChunk) && !(chunk instanceof net.minecraft.world.level.chunk.LevelChunk)) {
-+ return false;
-+ }
-+
-+ // fall through to load
-+ // we do this so we do not re-read the chunk data on disk
-+ }
-+ world.getChunkSource().addRegionTicket(TicketType.PLUGIN, chunkPos, 0, Unit.INSTANCE); // Paper
-+ world.getChunkSource().getChunk(x, z, ChunkStatus.FULL, true);
-+ return true;
-+ // Paper end - Optimize this method
- }
-
- @Override
diff --git a/patches/unapplied/server/0999-Flat-bedrock-generator-settings.patch b/patches/unapplied/server/0999-Flat-bedrock-generator-settings.patch
deleted file mode 100644
index d1bbbdf7ef..0000000000
--- a/patches/unapplied/server/0999-Flat-bedrock-generator-settings.patch
+++ /dev/null
@@ -1,288 +0,0 @@
-From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
-From: Byteflux <[email protected]>
-Date: Wed, 2 Mar 2016 02:17:54 -0600
-Subject: [PATCH] Flat bedrock generator settings
-
-== AT ==
-public net.minecraft.world.level.levelgen.SurfaceRules$Condition
-public net.minecraft.world.level.levelgen.SurfaceRules$Context
-public net.minecraft.world.level.levelgen.SurfaceRules$Context blockX
-public net.minecraft.world.level.levelgen.SurfaceRules$Context blockY
-public net.minecraft.world.level.levelgen.SurfaceRules$Context blockZ
-public net.minecraft.world.level.levelgen.SurfaceRules$Context context
-public net.minecraft.world.level.levelgen.SurfaceRules$Context randomState
-public net.minecraft.world.level.levelgen.SurfaceRules$LazyYCondition
-public net.minecraft.world.level.levelgen.SurfaceRules$LazyCondition
-public net.minecraft.world.level.levelgen.SurfaceRules$VerticalGradientConditionSource
-public net.minecraft.world.level.levelgen.SurfaceRules$SurfaceRule
-
-Co-authored-by: Noah van der Aa <[email protected]>
-
-diff --git a/src/main/java/io/papermc/paper/world/worldgen/OptionallyFlatBedrockConditionSource.java b/src/main/java/io/papermc/paper/world/worldgen/OptionallyFlatBedrockConditionSource.java
-new file mode 100644
-index 0000000000000000000000000000000000000000..02d98ec591b676acf64460d14d608860d32a362a
---- /dev/null
-+++ b/src/main/java/io/papermc/paper/world/worldgen/OptionallyFlatBedrockConditionSource.java
-@@ -0,0 +1,76 @@
-+package io.papermc.paper.world.worldgen;
-+
-+import com.mojang.serialization.Codec;
-+import com.mojang.serialization.codecs.RecordCodecBuilder;
-+import net.minecraft.core.Registry;
-+import net.minecraft.core.registries.BuiltInRegistries;
-+import net.minecraft.core.registries.Registries;
-+import net.minecraft.resources.ResourceKey;
-+import net.minecraft.resources.ResourceLocation;
-+import net.minecraft.util.KeyDispatchDataCodec;
-+import net.minecraft.util.Mth;
-+import net.minecraft.util.RandomSource;
-+import net.minecraft.world.level.levelgen.PositionalRandomFactory;
-+import net.minecraft.world.level.levelgen.SurfaceRules;
-+import net.minecraft.world.level.levelgen.VerticalAnchor;
-+import org.checkerframework.checker.nullness.qual.NonNull;
-+import org.checkerframework.framework.qual.DefaultQualifier;
-+
-+// Modelled off of SurfaceRules$VerticalGradientConditionSource
-+@DefaultQualifier(NonNull.class)
-+public record OptionallyFlatBedrockConditionSource(ResourceLocation randomName, VerticalAnchor trueAtAndBelow, VerticalAnchor falseAtAndAbove, boolean isRoof) implements SurfaceRules.ConditionSource {
-+
-+ private static final ResourceKey<Codec<? extends SurfaceRules.ConditionSource>> CODEC_RESOURCE_KEY = ResourceKey.create(Registries.MATERIAL_CONDITION, new ResourceLocation(ResourceLocation.PAPER_NAMESPACE, "optionally_flat_bedrock_condition_source"));
-+ private static final KeyDispatchDataCodec<OptionallyFlatBedrockConditionSource> CODEC = KeyDispatchDataCodec.of(RecordCodecBuilder.mapCodec((instance) -> {
-+ return instance.group(
-+ ResourceLocation.CODEC.fieldOf("random_name").forGetter(OptionallyFlatBedrockConditionSource::randomName),
-+ VerticalAnchor.CODEC.fieldOf("true_at_and_below").forGetter(OptionallyFlatBedrockConditionSource::trueAtAndBelow),
-+ VerticalAnchor.CODEC.fieldOf("false_at_and_above").forGetter(OptionallyFlatBedrockConditionSource::falseAtAndAbove),
-+ Codec.BOOL.fieldOf("is_roof").forGetter(OptionallyFlatBedrockConditionSource::isRoof)
-+ ).apply(instance, OptionallyFlatBedrockConditionSource::new);
-+ }));
-+
-+ public static void bootstrap() {
-+ Registry.register(BuiltInRegistries.MATERIAL_CONDITION, CODEC_RESOURCE_KEY, CODEC.codec());
-+ }
-+
-+ @Override
-+ public KeyDispatchDataCodec<? extends SurfaceRules.ConditionSource> codec() {
-+ return CODEC;
-+ }
-+
-+ @Override
-+ public SurfaceRules.Condition apply(final SurfaceRules.Context context) {
-+ boolean hasFlatBedrock = context.context.getWorld().paperConfig().environment.generateFlatBedrock;
-+ int tempTrueAtAndBelowY = this.trueAtAndBelow().resolveY(context.context);
-+ int tempFalseAtAndAboveY = this.falseAtAndAbove().resolveY(context.context);
-+
-+ int flatYLevel = this.isRoof ? Math.max(tempFalseAtAndAboveY, tempTrueAtAndBelowY) - 1 : Math.min(tempFalseAtAndAboveY, tempTrueAtAndBelowY);
-+ final int trueAtAndBelowY = hasFlatBedrock ? flatYLevel : tempTrueAtAndBelowY;
-+ final int falseAtAndAboveY = hasFlatBedrock ? flatYLevel : tempFalseAtAndAboveY;
-+
-+ final PositionalRandomFactory positionalRandomFactory = context.randomState.getOrCreateRandomFactory(this.randomName());
-+
-+ class VerticalGradientCondition extends SurfaceRules.LazyYCondition {
-+ VerticalGradientCondition(SurfaceRules.Context context) {
-+ super(context);
-+ }
-+
-+ @Override
-+ protected boolean compute() {
-+ int blockY = this.context.blockY;
-+ if (blockY <= trueAtAndBelowY) {
-+ return true;
-+ } else if (blockY >= falseAtAndAboveY) {
-+ return false;
-+ } else {
-+ double d = Mth.map(blockY, trueAtAndBelowY, falseAtAndAboveY, 1.0D, 0.0D);
-+ RandomSource randomSource = positionalRandomFactory.at(this.context.blockX, blockY, this.context.blockZ);
-+ return (double)randomSource.nextFloat() < d;
-+ }
-+ }
-+ }
-+
-+ return new VerticalGradientCondition(context);
-+ }
-+}
-diff --git a/src/main/java/net/minecraft/server/Bootstrap.java b/src/main/java/net/minecraft/server/Bootstrap.java
-index c887d34171f89c731d76c4ca92c70be2b1edc1e6..438ae006a8e7da0e5124415b8350ebfd45ac6a10 100644
---- a/src/main/java/net/minecraft/server/Bootstrap.java
-+++ b/src/main/java/net/minecraft/server/Bootstrap.java
-@@ -78,6 +78,7 @@ public class Bootstrap {
- CauldronInteraction.bootStrap();
- // Paper start
- BuiltInRegistries.bootStrap(() -> {
-+ io.papermc.paper.world.worldgen.OptionallyFlatBedrockConditionSource.bootstrap(); // Paper - Flat bedrock generator settings
- io.papermc.paper.plugin.entrypoint.LaunchEntryPointHandler.enterBootstrappers(); // Paper - Entrypoint for bootstrapping
- });
- // Paper end
-diff --git a/src/main/java/net/minecraft/world/level/levelgen/NoiseBasedChunkGenerator.java b/src/main/java/net/minecraft/world/level/levelgen/NoiseBasedChunkGenerator.java
-index 902156477bdfc9917105f1229f760c26e5af302a..98c7f695093acbcf9382a5f07a7a89e373709763 100644
---- a/src/main/java/net/minecraft/world/level/levelgen/NoiseBasedChunkGenerator.java
-+++ b/src/main/java/net/minecraft/world/level/levelgen/NoiseBasedChunkGenerator.java
-@@ -207,7 +207,7 @@ public final class NoiseBasedChunkGenerator extends ChunkGenerator {
- @Override
- public void buildSurface(WorldGenRegion region, StructureManager structures, RandomState noiseConfig, ChunkAccess chunk) {
- if (!SharedConstants.debugVoidTerrain(chunk.getPos())) {
-- WorldGenerationContext worldgenerationcontext = new WorldGenerationContext(this, region);
-+ WorldGenerationContext worldgenerationcontext = new WorldGenerationContext(this, region, region.getMinecraftWorld()); // Paper - Flat bedrock generator settings
-
- this.buildSurface(chunk, worldgenerationcontext, noiseConfig, structures, region.getBiomeManager(), region.registryAccess().registryOrThrow(Registries.BIOME), Blender.of(region));
- }
-@@ -235,7 +235,7 @@ public final class NoiseBasedChunkGenerator extends ChunkGenerator {
- return this.createNoiseChunk(ichunkaccess1, structureAccessor, Blender.of(chunkRegion), noiseConfig);
- });
- Aquifer aquifer = noisechunk.aquifer();
-- CarvingContext carvingcontext = new CarvingContext(this, chunkRegion.registryAccess(), chunk.getHeightAccessorForGeneration(), noisechunk, noiseConfig, ((NoiseGeneratorSettings) this.settings.value()).surfaceRule());
-+ CarvingContext carvingcontext = new CarvingContext(this, chunkRegion.registryAccess(), chunk.getHeightAccessorForGeneration(), noisechunk, noiseConfig, ((NoiseGeneratorSettings) this.settings.value()).surfaceRule(), chunkRegion.getMinecraftWorld()); // Paper - Flat bedrock generator settings
- CarvingMask carvingmask = ((ProtoChunk) chunk).getOrCreateCarvingMask(carverStep);
-
- for (int j = -8; j <= 8; ++j) {
-diff --git a/src/main/java/net/minecraft/world/level/levelgen/WorldGenerationContext.java b/src/main/java/net/minecraft/world/level/levelgen/WorldGenerationContext.java
-index b99283c31193e2110f6e3f39c23dbfc2442bab2b..a34e53249668d917c9d77c6837b91360a2349bbc 100644
---- a/src/main/java/net/minecraft/world/level/levelgen/WorldGenerationContext.java
-+++ b/src/main/java/net/minecraft/world/level/levelgen/WorldGenerationContext.java
-@@ -6,10 +6,13 @@ import net.minecraft.world.level.chunk.ChunkGenerator;
- public class WorldGenerationContext {
- private final int minY;
- private final int height;
-+ private final @javax.annotation.Nullable net.minecraft.world.level.Level level; // Paper - Flat bedrock generator settings
-
-- public WorldGenerationContext(ChunkGenerator generator, LevelHeightAccessor world) {
-+ public WorldGenerationContext(ChunkGenerator generator, LevelHeightAccessor world) { this(generator, world, null); } // Paper - Flat bedrock generator settings
-+ public WorldGenerationContext(ChunkGenerator generator, LevelHeightAccessor world, @org.jetbrains.annotations.Nullable net.minecraft.world.level.Level level) { // Paper - Flat bedrock generator settings
- this.minY = Math.max(world.getMinBuildHeight(), generator.getMinY());
- this.height = Math.min(world.getHeight(), generator.getGenDepth());
-+ this.level = level; // Paper - Flat bedrock generator settings
- }
-
- public int getMinGenY() {
-@@ -19,4 +22,13 @@ public class WorldGenerationContext {
- public int getGenDepth() {
- return this.height;
- }
-+
-+ // Paper start - Flat bedrock generator settings
-+ public net.minecraft.world.level.Level getWorld() {
-+ if (this.level == null) {
-+ throw new NullPointerException("WorldGenerationContext was initialized without a Level, but WorldGenerationContext#getWorld was called");
-+ }
-+ return this.level;
-+ }
-+ // Paper end - Flat bedrock generator settings
- }
-diff --git a/src/main/java/net/minecraft/world/level/levelgen/carver/CarvingContext.java b/src/main/java/net/minecraft/world/level/levelgen/carver/CarvingContext.java
-index 390bcf9c302effd9db42d7a0e65b5433cc2eadd6..b9e919d31e442f49300744395af3cf9431e86c57 100644
---- a/src/main/java/net/minecraft/world/level/levelgen/carver/CarvingContext.java
-+++ b/src/main/java/net/minecraft/world/level/levelgen/carver/CarvingContext.java
-@@ -27,9 +27,9 @@ public class CarvingContext extends WorldGenerationContext {
- LevelHeightAccessor heightLimitView,
- NoiseChunk chunkNoiseSampler,
- RandomState noiseConfig,
-- SurfaceRules.RuleSource materialRule
-+ SurfaceRules.RuleSource materialRule, @javax.annotation.Nullable net.minecraft.world.level.Level level // Paper - Flat bedrock generator settings
- ) {
-- super(noiseChunkGenerator, heightLimitView);
-+ super(noiseChunkGenerator, heightLimitView, level); // Paper - Flat bedrock generator settings
- this.registryAccess = registryManager;
- this.noiseChunk = chunkNoiseSampler;
- this.randomState = noiseConfig;
-diff --git a/src/main/java/net/minecraft/world/level/levelgen/placement/PlacementContext.java b/src/main/java/net/minecraft/world/level/levelgen/placement/PlacementContext.java
-index 640c2683c842655bbaee8f293f1c2613ef44844e..c7dfd844c7846281ceff0d443c0160054fd36c5c 100644
---- a/src/main/java/net/minecraft/world/level/levelgen/placement/PlacementContext.java
-+++ b/src/main/java/net/minecraft/world/level/levelgen/placement/PlacementContext.java
-@@ -18,7 +18,7 @@ public class PlacementContext extends WorldGenerationContext {
- private final Optional<PlacedFeature> topFeature;
-
- public PlacementContext(WorldGenLevel world, ChunkGenerator generator, Optional<PlacedFeature> placedFeature) {
-- super(generator, world);
-+ super(generator, world, world.getLevel()); // Paper - Flat bedrock generator settings
- this.level = world;
- this.generator = generator;
- this.topFeature = placedFeature;
-diff --git a/src/main/resources/data/minecraft/worldgen/noise_settings/amplified.json b/src/main/resources/data/minecraft/worldgen/noise_settings/amplified.json
-index 3f61ea695aa6a91a0cacf1fa8dc0a9bdc6fa36e6..02d32bbdbc795db271205bef95e6987ac34b0136 100644
---- a/src/main/resources/data/minecraft/worldgen/noise_settings/amplified.json
-+++ b/src/main/resources/data/minecraft/worldgen/noise_settings/amplified.json
-@@ -389,7 +389,8 @@
- {
- "type": "minecraft:condition",
- "if_true": {
-- "type": "minecraft:vertical_gradient",
-+ "type": "paper:optionally_flat_bedrock_condition_source",
-+ "is_roof": false,
- "false_at_and_above": {
- "above_bottom": 5
- },
-diff --git a/src/main/resources/data/minecraft/worldgen/noise_settings/caves.json b/src/main/resources/data/minecraft/worldgen/noise_settings/caves.json
-index 1fe9ce904cba21ff4e6efb948a0bc274a22bcb0b..2e8c1aad10a2b0adbdee4180cfc1902984b6565b 100644
---- a/src/main/resources/data/minecraft/worldgen/noise_settings/caves.json
-+++ b/src/main/resources/data/minecraft/worldgen/noise_settings/caves.json
-@@ -110,7 +110,8 @@
- "if_true": {
- "type": "minecraft:not",
- "invert": {
-- "type": "minecraft:vertical_gradient",
-+ "type": "paper:optionally_flat_bedrock_condition_source",
-+ "is_roof": true,
- "false_at_and_above": {
- "below_top": 0
- },
-@@ -130,7 +131,8 @@
- {
- "type": "minecraft:condition",
- "if_true": {
-- "type": "minecraft:vertical_gradient",
-+ "type": "paper:optionally_flat_bedrock_condition_source",
-+ "is_roof": false,
- "false_at_and_above": {
- "above_bottom": 5
- },
-diff --git a/src/main/resources/data/minecraft/worldgen/noise_settings/large_biomes.json b/src/main/resources/data/minecraft/worldgen/noise_settings/large_biomes.json
-index f4c34de998d78a80bc026d8e0d423c9bed9bbf8a..fd5bd5474b8f931f2e04706997e71cd5145a5a82 100644
---- a/src/main/resources/data/minecraft/worldgen/noise_settings/large_biomes.json
-+++ b/src/main/resources/data/minecraft/worldgen/noise_settings/large_biomes.json
-@@ -389,7 +389,8 @@
- {
- "type": "minecraft:condition",
- "if_true": {
-- "type": "minecraft:vertical_gradient",
-+ "type": "paper:optionally_flat_bedrock_condition_source",
-+ "is_roof": false,
- "false_at_and_above": {
- "above_bottom": 5
- },
-diff --git a/src/main/resources/data/minecraft/worldgen/noise_settings/nether.json b/src/main/resources/data/minecraft/worldgen/noise_settings/nether.json
-index 6219d25fcdbc761debc1d1e357757d1b977dc0b0..1657179b8c3f7e549e3b8774ecb75f292ae79c38 100644
---- a/src/main/resources/data/minecraft/worldgen/noise_settings/nether.json
-+++ b/src/main/resources/data/minecraft/worldgen/noise_settings/nether.json
-@@ -108,7 +108,8 @@
- {
- "type": "minecraft:condition",
- "if_true": {
-- "type": "minecraft:vertical_gradient",
-+ "type": "paper:optionally_flat_bedrock_condition_source",
-+ "is_roof": false,
- "false_at_and_above": {
- "above_bottom": 5
- },
-@@ -129,7 +130,8 @@
- "if_true": {
- "type": "minecraft:not",
- "invert": {
-- "type": "minecraft:vertical_gradient",
-+ "type": "paper:optionally_flat_bedrock_condition_source",
-+ "is_roof": true,
- "false_at_and_above": {
- "below_top": 0
- },
-diff --git a/src/main/resources/data/minecraft/worldgen/noise_settings/overworld.json b/src/main/resources/data/minecraft/worldgen/noise_settings/overworld.json
-index da3bda6167859f4ddf7d76ec2053f40b2139c13e..70beb7665a43a06b4afcb3c77aa77f923cb444cb 100644
---- a/src/main/resources/data/minecraft/worldgen/noise_settings/overworld.json
-+++ b/src/main/resources/data/minecraft/worldgen/noise_settings/overworld.json
-@@ -389,7 +389,8 @@
- {
- "type": "minecraft:condition",
- "if_true": {
-- "type": "minecraft:vertical_gradient",
-+ "type": "paper:optionally_flat_bedrock_condition_source",
-+ "is_roof": false,
- "false_at_and_above": {
- "above_bottom": 5
- },
diff --git a/patches/unapplied/server/1000-Entity-Activation-Range-2.0.patch b/patches/unapplied/server/1000-Entity-Activation-Range-2.0.patch
deleted file mode 100644
index 82e80d8998..0000000000
--- a/patches/unapplied/server/1000-Entity-Activation-Range-2.0.patch
+++ /dev/null
@@ -1,812 +0,0 @@
-From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
-From: Aikar <[email protected]>
-Date: Fri, 13 May 2016 01:38:06 -0400
-Subject: [PATCH] Entity Activation Range 2.0
-
-Optimizes performance of Activation Range
-
-Adds many new configurations and a new wake up inactive system
-
-Fixes and adds new Immunities to improve gameplay behavior
-
-Adds water Mobs to activation range config and nerfs fish
-Adds flying monsters to control ghast and phantoms
-Adds villagers as separate config
-
-== AT ==
-public net.minecraft.world.entity.Entity isInsidePortal
-
-diff --git a/src/main/java/net/minecraft/server/level/ServerLevel.java b/src/main/java/net/minecraft/server/level/ServerLevel.java
-index bd8c96e914b156284bdbb960f168e63e1f122920..abb4c32e8b35de332fa517523e8c598ea3275def 100644
---- a/src/main/java/net/minecraft/server/level/ServerLevel.java
-+++ b/src/main/java/net/minecraft/server/level/ServerLevel.java
-@@ -2,7 +2,6 @@ package net.minecraft.server.level;
-
- import com.google.common.annotations.VisibleForTesting;
- import co.aikar.timings.TimingHistory; // Paper
--import co.aikar.timings.Timings; // Paper
- import com.google.common.collect.Lists;
- import com.mojang.datafixers.DataFixer;
- import com.mojang.datafixers.util.Pair;
-@@ -1222,17 +1221,17 @@ public class ServerLevel extends Level implements WorldGenLevel {
- ++TimingHistory.entityTicks; // Paper - timings
- // Spigot start
- co.aikar.timings.Timing timer; // Paper
-- if (!org.spigotmc.ActivationRange.checkIfActive(entity)) {
-+ /*if (!org.spigotmc.ActivationRange.checkIfActive(entity)) { // Paper - comment out - EAR 2, reimplement below
- entity.tickCount++;
- timer = entity.getType().inactiveTickTimer.startTiming(); try { // Paper - timings
- entity.inactiveTick();
- } finally { timer.stopTiming(); } // Paper
- return;
-- }
-+ }*/ // Paper - comment out EAR 2
- // Spigot end
- // Paper start- timings
-- TimingHistory.activatedEntityTicks++;
-- timer = entity.getVehicle() != null ? entity.getType().passengerTickTimer.startTiming() : entity.getType().tickTimer.startTiming();
-+ final boolean isActive = org.spigotmc.ActivationRange.checkIfActive(entity);
-+ timer = isActive ? entity.getType().tickTimer.startTiming() : entity.getType().inactiveTickTimer.startTiming(); // Paper
- try {
- // Paper end - timings
- entity.setOldPosAndRot();
-@@ -1243,9 +1242,13 @@ public class ServerLevel extends Level implements WorldGenLevel {
- return BuiltInRegistries.ENTITY_TYPE.getKey(entity.getType()).toString();
- });
- gameprofilerfiller.incrementCounter("tickNonPassenger");
-+ if (isActive) { // Paper - EAR 2
-+ TimingHistory.activatedEntityTicks++;
- entity.tick();
- entity.postTick(); // CraftBukkit
-+ } else { entity.inactiveTick(); } // Paper - EAR 2
- this.getProfiler().pop();
-+ } finally { timer.stopTiming(); } // Paper - timings
- Iterator iterator = entity.getPassengers().iterator();
-
- while (iterator.hasNext()) {
-@@ -1253,13 +1256,18 @@ public class ServerLevel extends Level implements WorldGenLevel {
-
- this.tickPassenger(entity, entity1);
- }
-- } finally { timer.stopTiming(); } // Paper - timings
-+ // } finally { timer.stopTiming(); } // Paper - timings - move up
-
- }
-
- private void tickPassenger(Entity vehicle, Entity passenger) {
- if (!passenger.isRemoved() && passenger.getVehicle() == vehicle) {
- if (passenger instanceof Player || this.entityTickList.contains(passenger)) {
-+ // Paper - EAR 2
-+ final boolean isActive = org.spigotmc.ActivationRange.checkIfActive(passenger);
-+ co.aikar.timings.Timing timer = isActive ? passenger.getType().passengerTickTimer.startTiming() : passenger.getType().passengerInactiveTickTimer.startTiming(); // Paper
-+ try {
-+ // Paper end
- passenger.setOldPosAndRot();
- ++passenger.tickCount;
- ProfilerFiller gameprofilerfiller = this.getProfiler();
-@@ -1268,8 +1276,17 @@ public class ServerLevel extends Level implements WorldGenLevel {
- return BuiltInRegistries.ENTITY_TYPE.getKey(passenger.getType()).toString();
- });
- gameprofilerfiller.incrementCounter("tickPassenger");
-+ // Paper start - EAR 2
-+ if (isActive) {
- passenger.rideTick();
- passenger.postTick(); // CraftBukkit
-+ } else {
-+ passenger.setDeltaMovement(Vec3.ZERO);
-+ passenger.inactiveTick();
-+ // copied from inside of if (isPassenger()) of passengerTick, but that ifPassenger is unnecessary
-+ vehicle.positionRider(passenger);
-+ }
-+ // Paper end - EAR 2
- gameprofilerfiller.pop();
- Iterator iterator = passenger.getPassengers().iterator();
-
-@@ -1279,6 +1296,7 @@ public class ServerLevel extends Level implements WorldGenLevel {
- this.tickPassenger(passenger, entity2);
- }
-
-+ } finally { timer.stopTiming(); }// Paper - EAR2 timings
- }
- } else {
- passenger.stopRiding();
-diff --git a/src/main/java/net/minecraft/world/entity/Entity.java b/src/main/java/net/minecraft/world/entity/Entity.java
-index b108f779abe3d9798c0bcbc983f41d48b33aa153..a66fe080ee73171090abec48352ad0bd457a2a6f 100644
---- a/src/main/java/net/minecraft/world/entity/Entity.java
-+++ b/src/main/java/net/minecraft/world/entity/Entity.java
-@@ -412,6 +412,8 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource, S
- // Spigot end
- protected int numCollisions = 0; // Paper - Cap entity collisions
- public boolean fromNetherPortal; // Paper - Add option to nerf pigmen from nether portals
-+ public long activatedImmunityTick = Integer.MIN_VALUE; // Paper - EAR
-+ public boolean isTemporarilyActive; // Paper - EAR
- public boolean spawnedViaMobSpawner; // Paper - Yes this name is similar to above, upstream took the better one
- // Paper start - Entity origin API
- @javax.annotation.Nullable
-@@ -1034,6 +1036,8 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource, S
- } else {
- this.wasOnFire = this.isOnFire();
- if (movementType == MoverType.PISTON) {
-+ this.activatedTick = Math.max(this.activatedTick, MinecraftServer.currentTick + 20); // Paper
-+ this.activatedImmunityTick = Math.max(this.activatedImmunityTick, MinecraftServer.currentTick + 20); // Paper
- movement = this.limitPistonMovement(movement);
- if (movement.equals(Vec3.ZERO)) {
- return;
-@@ -1046,6 +1050,13 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource, S
- this.stuckSpeedMultiplier = Vec3.ZERO;
- this.setDeltaMovement(Vec3.ZERO);
- }
-+ // Paper start - ignore movement changes while inactive.
-+ if (isTemporarilyActive && !(this instanceof ItemEntity) && movement == getDeltaMovement() && movementType == MoverType.SELF) {
-+ setDeltaMovement(Vec3.ZERO);
-+ this.level.getProfiler().pop();
-+ return;
-+ }
-+ // Paper end
-
- movement = this.maybeBackOffFromEdge(movement, movementType);
- Vec3 vec3d1 = this.collide(movement);
-diff --git a/src/main/java/net/minecraft/world/entity/Mob.java b/src/main/java/net/minecraft/world/entity/Mob.java
-index 11c933a662c2275e2ef239cb0b5dd2480cc55490..d2c92df28475f0a32a0134324eb0a5609a9afb99 100644
---- a/src/main/java/net/minecraft/world/entity/Mob.java
-+++ b/src/main/java/net/minecraft/world/entity/Mob.java
-@@ -222,6 +222,19 @@ public abstract class Mob extends LivingEntity implements Targeting {
- return this.lookControl;
- }
-
-+ // Paper start
-+ @Override
-+ public void inactiveTick() {
-+ super.inactiveTick();
-+ if (this.goalSelector.inactiveTick()) {
-+ this.goalSelector.tick();
-+ }
-+ if (this.targetSelector.inactiveTick()) {
-+ this.targetSelector.tick();
-+ }
-+ }
-+ // Paper end
-+
- public MoveControl getMoveControl() {
- Entity entity = this.getControlledVehicle();
-
-diff --git a/src/main/java/net/minecraft/world/entity/PathfinderMob.java b/src/main/java/net/minecraft/world/entity/PathfinderMob.java
-index d6393210cfee53685f83c8491bea8b9c13b01eea..3d95257d2203fe40bb1fab58ad2a1f9e815184a9 100644
---- a/src/main/java/net/minecraft/world/entity/PathfinderMob.java
-+++ b/src/main/java/net/minecraft/world/entity/PathfinderMob.java
-@@ -21,6 +21,7 @@ public abstract class PathfinderMob extends Mob {
- }
-
- public org.bukkit.craftbukkit.entity.CraftCreature getBukkitCreature() { return (org.bukkit.craftbukkit.entity.CraftCreature) super.getBukkitEntity(); } // Paper
-+ public BlockPos movingTarget = null; public BlockPos getMovingTarget() { return movingTarget; } // Paper
-
- public float getWalkTargetValue(BlockPos pos) {
- return this.getWalkTargetValue(pos, this.level());
-diff --git a/src/main/java/net/minecraft/world/entity/ai/goal/GoalSelector.java b/src/main/java/net/minecraft/world/entity/ai/goal/GoalSelector.java
-index 9be9a6a59666297e05a9fc19d9345ae7d5f3bf40..040d62effc651d14d3557f8ff582cb078b74ae1e 100644
---- a/src/main/java/net/minecraft/world/entity/ai/goal/GoalSelector.java
-+++ b/src/main/java/net/minecraft/world/entity/ai/goal/GoalSelector.java
-@@ -34,6 +34,7 @@ public class GoalSelector {
- private final EnumSet<Goal.Flag> disabledFlags = EnumSet.noneOf(Goal.Flag.class);
- private int tickCount;
- private int newGoalRate = 3;
-+ private int curRate;
-
- public GoalSelector(Supplier<ProfilerFiller> profiler) {
- this.profiler = profiler;
-@@ -48,6 +49,20 @@ public class GoalSelector {
- this.availableGoals.removeIf(goal -> predicate.test(goal.getGoal()));
- }
-
-+ // Paper start
-+ public boolean inactiveTick() {
-+ this.curRate++;
-+ return this.curRate % this.newGoalRate == 0;
-+ }
-+ public boolean hasTasks() {
-+ for (WrappedGoal task : this.availableGoals) {
-+ if (task.isRunning()) {
-+ return true;
-+ }
-+ }
-+ return false;
-+ }
-+ // Paper end
- public void removeGoal(Goal goal) {
- this.availableGoals.stream().filter(wrappedGoal -> wrappedGoal.getGoal() == goal).filter(WrappedGoal::isRunning).forEach(WrappedGoal::stop);
- this.availableGoals.removeIf(wrappedGoal -> wrappedGoal.getGoal() == goal);
-diff --git a/src/main/java/net/minecraft/world/entity/ai/goal/MoveToBlockGoal.java b/src/main/java/net/minecraft/world/entity/ai/goal/MoveToBlockGoal.java
-index 6d8ea05e5e86e9f6359b560043bb55a10784e952..aee0147649d458b87d92496eda0c1723ebe570d2 100644
---- a/src/main/java/net/minecraft/world/entity/ai/goal/MoveToBlockGoal.java
-+++ b/src/main/java/net/minecraft/world/entity/ai/goal/MoveToBlockGoal.java
-@@ -23,6 +23,14 @@ public abstract class MoveToBlockGoal extends Goal {
- public MoveToBlockGoal(PathfinderMob mob, double speed, int range) {
- this(mob, speed, range, 1);
- }
-+ // Paper start - activation range improvements
-+ @Override
-+ public void stop() {
-+ super.stop();
-+ this.blockPos = BlockPos.ZERO;
-+ this.mob.movingTarget = null;
-+ }
-+ // Paper end
-
- public MoveToBlockGoal(PathfinderMob mob, double speed, int range, int maxYDifference) {
- this.mob = mob;
-@@ -115,6 +123,7 @@ public abstract class MoveToBlockGoal extends Goal {
- mutableBlockPos.setWithOffset(blockPos, m, k - 1, n);
- if (this.mob.isWithinRestriction(mutableBlockPos) && this.isValidTarget(this.mob.level(), mutableBlockPos)) {
- this.blockPos = mutableBlockPos;
-+ this.mob.movingTarget = mutableBlockPos == BlockPos.ZERO ? null : mutableBlockPos.immutable(); // Paper
- return true;
- }
- }
-diff --git a/src/main/java/net/minecraft/world/entity/npc/Villager.java b/src/main/java/net/minecraft/world/entity/npc/Villager.java
-index fa2569fecefb3e3af3264928a3c7a347710deedf..24044795d8e0f1fb15a4f2f5401f44897092f2a3 100644
---- a/src/main/java/net/minecraft/world/entity/npc/Villager.java
-+++ b/src/main/java/net/minecraft/world/entity/npc/Villager.java
-@@ -228,17 +228,34 @@ public class Villager extends AbstractVillager implements ReputationEventHandler
- @Override
- public void inactiveTick() {
- // SPIGOT-3874, SPIGOT-3894, SPIGOT-3846, SPIGOT-5286 :(
-- if (this.level().spigotConfig.tickInactiveVillagers && this.isEffectiveAi()) {
-- this.customServerAiStep();
-+ // Paper start
-+ if (this.getUnhappyCounter() > 0) {
-+ this.setUnhappyCounter(this.getUnhappyCounter() - 1);
- }
-+ if (this.isEffectiveAi()) {
-+ if (this.level().spigotConfig.tickInactiveVillagers) {
-+ this.customServerAiStep();
-+ } else {
-+ this.customServerAiStep(true);
-+ }
-+ }
-+ maybeDecayGossip();
-+ // Paper end
-+
- super.inactiveTick();
- }
- // Spigot End
-
- @Override
-+ @Deprecated // Paper
- protected void customServerAiStep() {
-+ // Paper start
-+ this.customServerAiStep(false);
-+ }
-+ protected void customServerAiStep(final boolean inactive) {
-+ // Paper end
- this.level().getProfiler().push("villagerBrain");
-- this.getBrain().tick((ServerLevel) this.level(), this);
-+ if (!inactive) this.getBrain().tick((ServerLevel) this.level(), this); // Paper
- this.level().getProfiler().pop();
- if (this.assignProfessionWhenSpawned) {
- this.assignProfessionWhenSpawned = false;
-@@ -262,7 +279,7 @@ public class Villager extends AbstractVillager implements ReputationEventHandler
- this.lastTradedPlayer = null;
- }
-
-- if (!this.isNoAi() && this.random.nextInt(100) == 0) {
-+ if (!inactive && !this.isNoAi() && this.random.nextInt(100) == 0) { // Paper
- Raid raid = ((ServerLevel) this.level()).getRaidAt(this.blockPosition());
-
- if (raid != null && raid.isActive() && !raid.isOver()) {
-@@ -273,6 +290,7 @@ public class Villager extends AbstractVillager implements ReputationEventHandler
- if (this.getVillagerData().getProfession() == VillagerProfession.NONE && this.isTrading()) {
- this.stopTrading();
- }
-+ if (inactive) return; // Paper
-
- super.customServerAiStep();
- }
-diff --git a/src/main/java/net/minecraft/world/entity/vehicle/MinecartHopper.java b/src/main/java/net/minecraft/world/entity/vehicle/MinecartHopper.java
-index eaafb57c14ead01ffd2650fdec9ca75406201e41..761142374f793a1cd4228936b21a68d7a0458894 100644
---- a/src/main/java/net/minecraft/world/entity/vehicle/MinecartHopper.java
-+++ b/src/main/java/net/minecraft/world/entity/vehicle/MinecartHopper.java
-@@ -52,6 +52,7 @@ public class MinecartHopper extends AbstractMinecartContainer implements Hopper
- if (bl != this.isEnabled()) {
- this.setEnabled(bl);
- }
-+ this.immunize(); // Paper
- }
-
- public boolean isEnabled() {
-@@ -87,11 +88,13 @@ public class MinecartHopper extends AbstractMinecartContainer implements Hopper
-
- public boolean suckInItems() {
- if (HopperBlockEntity.suckInItems(this.level(), this)) {
-+ this.immunize(); // Paper
- return true;
- } else {
- for (ItemEntity itemEntity : this.level()
- .getEntitiesOfClass(ItemEntity.class, this.getBoundingBox().inflate(0.25, 0.0, 0.25), EntitySelector.ENTITY_STILL_ALIVE)) {
- if (HopperBlockEntity.addItem(this, itemEntity)) {
-+ this.immunize(); // Paper
- return true;
- }
- }
-@@ -121,4 +124,11 @@ public class MinecartHopper extends AbstractMinecartContainer implements Hopper
- public AbstractContainerMenu createMenu(int syncId, Inventory playerInventory) {
- return new HopperMenu(syncId, playerInventory, this);
- }
-+
-+ // Paper start
-+ public void immunize() {
-+ this.activatedImmunityTick = Math.max(this.activatedImmunityTick, net.minecraft.server.MinecraftServer.currentTick + 20);
-+ }
-+ // Paper end
-+
- }
-diff --git a/src/main/java/net/minecraft/world/level/Level.java b/src/main/java/net/minecraft/world/level/Level.java
-index 5c209e323a5559480231c6d99357ba8b89edb027..4bedd5801cc8ce14387f02dfb361a00ab2960855 100644
---- a/src/main/java/net/minecraft/world/level/Level.java
-+++ b/src/main/java/net/minecraft/world/level/Level.java
-@@ -157,6 +157,12 @@ public abstract class Level implements LevelAccessor, AutoCloseable {
- public Map<BlockPos, BlockEntity> capturedTileEntities = new java.util.LinkedHashMap<>(); // Paper - Retain block place order when capturing blockstates
- public List<ItemEntity> captureDrops;
- public final it.unimi.dsi.fastutil.objects.Object2LongOpenHashMap<SpawnCategory> ticksPerSpawnCategory = new it.unimi.dsi.fastutil.objects.Object2LongOpenHashMap<>();
-+ // Paper start
-+ public int wakeupInactiveRemainingAnimals;
-+ public int wakeupInactiveRemainingFlying;
-+ public int wakeupInactiveRemainingMonsters;
-+ public int wakeupInactiveRemainingVillagers;
-+ // Paper end
- public boolean populating;
- public final org.spigotmc.SpigotWorldConfig spigotConfig; // Spigot
- // Paper start - add paper world config
-diff --git a/src/main/java/net/minecraft/world/level/block/piston/PistonMovingBlockEntity.java b/src/main/java/net/minecraft/world/level/block/piston/PistonMovingBlockEntity.java
-index 0e15da7cae105196d444b924b8e0db190583ba30..9f45dda6ff45ac1ffb7ac99575b7d09bdc61c56a 100644
---- a/src/main/java/net/minecraft/world/level/block/piston/PistonMovingBlockEntity.java
-+++ b/src/main/java/net/minecraft/world/level/block/piston/PistonMovingBlockEntity.java
-@@ -147,6 +147,10 @@ public class PistonMovingBlockEntity extends BlockEntity {
- }
-
- entity.setDeltaMovement(e, g, h);
-+ // Paper - EAR items stuck in in slime pushed by a piston
-+ entity.activatedTick = Math.max(entity.activatedTick, net.minecraft.server.MinecraftServer.currentTick + 10);
-+ entity.activatedImmunityTick = Math.max(entity.activatedImmunityTick, net.minecraft.server.MinecraftServer.currentTick + 10);
-+ // Paper end
- break;
- }
- }
-diff --git a/src/main/java/org/spigotmc/ActivationRange.java b/src/main/java/org/spigotmc/ActivationRange.java
-index 9fb9fa62c32445ac3c3883a6433759c86dcfc428..3283ed99c35ffed6805567705e0518d9f84feedc 100644
---- a/src/main/java/org/spigotmc/ActivationRange.java
-+++ b/src/main/java/org/spigotmc/ActivationRange.java
-@@ -1,33 +1,43 @@
- package org.spigotmc;
-
-+import net.minecraft.core.BlockPos;
- import net.minecraft.server.MinecraftServer;
-+import net.minecraft.server.level.ServerChunkCache;
- import net.minecraft.world.entity.Entity;
- import net.minecraft.world.entity.ExperienceOrb;
-+import net.minecraft.world.entity.FlyingMob;
- import net.minecraft.world.entity.LightningBolt;
- import net.minecraft.world.entity.LivingEntity;
-+import net.minecraft.world.entity.Mob;
- import net.minecraft.world.entity.PathfinderMob;
-+import net.minecraft.world.entity.ai.Brain;
- import net.minecraft.world.entity.ambient.AmbientCreature;
- import net.minecraft.world.entity.animal.Animal;
-+import net.minecraft.world.entity.animal.Bee;
- import net.minecraft.world.entity.animal.Sheep;
-+import net.minecraft.world.entity.animal.WaterAnimal;
-+import net.minecraft.world.entity.animal.horse.Llama;
- import net.minecraft.world.entity.boss.EnderDragonPart;
- import net.minecraft.world.entity.boss.enderdragon.EndCrystal;
- import net.minecraft.world.entity.boss.enderdragon.EnderDragon;
- import net.minecraft.world.entity.boss.wither.WitherBoss;
- import net.minecraft.world.entity.item.PrimedTnt;
- import net.minecraft.world.entity.monster.Creeper;
--import net.minecraft.world.entity.monster.Monster;
--import net.minecraft.world.entity.monster.Slime;
-+import net.minecraft.world.entity.monster.Enemy;
-+import net.minecraft.world.entity.monster.Pillager;
- import net.minecraft.world.entity.npc.Villager;
- import net.minecraft.world.entity.player.Player;
- import net.minecraft.world.entity.projectile.AbstractArrow;
- import net.minecraft.world.entity.projectile.AbstractHurtingProjectile;
-+import net.minecraft.world.entity.projectile.EyeOfEnder;
- import net.minecraft.world.entity.projectile.FireworkRocketEntity;
- import net.minecraft.world.entity.projectile.ThrowableProjectile;
- import net.minecraft.world.entity.projectile.ThrownTrident;
- import net.minecraft.world.entity.raid.Raider;
-+import co.aikar.timings.MinecraftTimings;
-+import net.minecraft.world.entity.schedule.Activity;
- import net.minecraft.world.level.Level;
- import net.minecraft.world.phys.AABB;
--import co.aikar.timings.MinecraftTimings;
-
- public class ActivationRange
- {
-@@ -44,6 +54,43 @@ public class ActivationRange
-
- AABB boundingBox = new AABB( 0, 0, 0, 0, 0, 0 );
- }
-+ // Paper start
-+
-+ static Activity[] VILLAGER_PANIC_IMMUNITIES = {
-+ Activity.HIDE,
-+ Activity.PRE_RAID,
-+ Activity.RAID,
-+ Activity.PANIC
-+ };
-+
-+ private static int checkInactiveWakeup(Entity entity) {
-+ Level world = entity.level();
-+ SpigotWorldConfig config = world.spigotConfig;
-+ long inactiveFor = MinecraftServer.currentTick - entity.activatedTick;
-+ if (entity.activationType == ActivationType.VILLAGER) {
-+ if (inactiveFor > config.wakeUpInactiveVillagersEvery && world.wakeupInactiveRemainingVillagers > 0) {
-+ world.wakeupInactiveRemainingVillagers--;
-+ return config.wakeUpInactiveVillagersFor;
-+ }
-+ } else if (entity.activationType == ActivationType.ANIMAL) {
-+ if (inactiveFor > config.wakeUpInactiveAnimalsEvery && world.wakeupInactiveRemainingAnimals > 0) {
-+ world.wakeupInactiveRemainingAnimals--;
-+ return config.wakeUpInactiveAnimalsFor;
-+ }
-+ } else if (entity.activationType == ActivationType.FLYING_MONSTER) {
-+ if (inactiveFor > config.wakeUpInactiveFlyingEvery && world.wakeupInactiveRemainingFlying > 0) {
-+ world.wakeupInactiveRemainingFlying--;
-+ return config.wakeUpInactiveFlyingFor;
-+ }
-+ } else if (entity.activationType == ActivationType.MONSTER || entity.activationType == ActivationType.RAIDER) {
-+ if (inactiveFor > config.wakeUpInactiveMonstersEvery && world.wakeupInactiveRemainingMonsters > 0) {
-+ world.wakeupInactiveRemainingMonsters--;
-+ return config.wakeUpInactiveMonstersFor;
-+ }
-+ }
-+ return -1;
-+ }
-+ // Paper end
-
- static AABB maxBB = new AABB( 0, 0, 0, 0, 0, 0 );
-
-@@ -56,10 +103,13 @@ public class ActivationRange
- */
- public static ActivationType initializeEntityActivationType(Entity entity)
- {
-+ if (entity instanceof WaterAnimal) { return ActivationType.WATER; } // Paper
-+ else if (entity instanceof Villager) { return ActivationType.VILLAGER; } // Paper
-+ else if (entity instanceof FlyingMob && entity instanceof Enemy) { return ActivationType.FLYING_MONSTER; } // Paper - doing & Monster incase Flying no longer includes monster in future
- if ( entity instanceof Raider )
- {
- return ActivationType.RAIDER;
-- } else if ( entity instanceof Monster || entity instanceof Slime )
-+ } else if ( entity instanceof Enemy ) // Paper - correct monster check
- {
- return ActivationType.MONSTER;
- } else if ( entity instanceof PathfinderMob || entity instanceof AmbientCreature )
-@@ -80,10 +130,14 @@ public class ActivationRange
- */
- public static boolean initializeEntityActivationState(Entity entity, SpigotWorldConfig config)
- {
-- if ( ( entity.activationType == ActivationType.MISC && config.miscActivationRange == 0 )
-- || ( entity.activationType == ActivationType.RAIDER && config.raiderActivationRange == 0 )
-- || ( entity.activationType == ActivationType.ANIMAL && config.animalActivationRange == 0 )
-- || ( entity.activationType == ActivationType.MONSTER && config.monsterActivationRange == 0 )
-+ if ( ( entity.activationType == ActivationType.MISC && config.miscActivationRange <= 0 )
-+ || ( entity.activationType == ActivationType.RAIDER && config.raiderActivationRange <= 0 )
-+ || ( entity.activationType == ActivationType.ANIMAL && config.animalActivationRange <= 0 )
-+ || ( entity.activationType == ActivationType.MONSTER && config.monsterActivationRange <= 0 )
-+ || ( entity.activationType == ActivationType.VILLAGER && config.villagerActivationRange <= 0 ) // Paper
-+ || ( entity.activationType == ActivationType.WATER && config.waterActivationRange <= 0 ) // Paper
-+ || ( entity.activationType == ActivationType.FLYING_MONSTER && config.flyingMonsterActivationRange <= 0 ) // Paper
-+ || entity instanceof EyeOfEnder // Paper
- || entity instanceof Player
- || entity instanceof ThrowableProjectile
- || entity instanceof EnderDragon
-@@ -118,10 +172,25 @@ public class ActivationRange
- final int raiderActivationRange = world.spigotConfig.raiderActivationRange;
- final int animalActivationRange = world.spigotConfig.animalActivationRange;
- final int monsterActivationRange = world.spigotConfig.monsterActivationRange;
-+ // Paper start
-+ final int waterActivationRange = world.spigotConfig.waterActivationRange;
-+ final int flyingActivationRange = world.spigotConfig.flyingMonsterActivationRange;
-+ final int villagerActivationRange = world.spigotConfig.villagerActivationRange;
-+ world.wakeupInactiveRemainingAnimals = Math.min(world.wakeupInactiveRemainingAnimals + 1, world.spigotConfig.wakeUpInactiveAnimals);
-+ world.wakeupInactiveRemainingVillagers = Math.min(world.wakeupInactiveRemainingVillagers + 1, world.spigotConfig.wakeUpInactiveVillagers);
-+ world.wakeupInactiveRemainingMonsters = Math.min(world.wakeupInactiveRemainingMonsters + 1, world.spigotConfig.wakeUpInactiveMonsters);
-+ world.wakeupInactiveRemainingFlying = Math.min(world.wakeupInactiveRemainingFlying + 1, world.spigotConfig.wakeUpInactiveFlying);
-+ final ServerChunkCache chunkProvider = (ServerChunkCache) world.getChunkSource();
-+ // Paper end
-
- int maxRange = Math.max( monsterActivationRange, animalActivationRange );
- maxRange = Math.max( maxRange, raiderActivationRange );
- maxRange = Math.max( maxRange, miscActivationRange );
-+ // Paper start
-+ maxRange = Math.max( maxRange, flyingActivationRange );
-+ maxRange = Math.max( maxRange, waterActivationRange );
-+ maxRange = Math.max( maxRange, villagerActivationRange );
-+ // Paper end
- maxRange = Math.min( ( world.spigotConfig.simulationDistance << 4 ) - 8, maxRange );
-
- for ( Player player : world.players() )
-@@ -132,13 +201,30 @@ public class ActivationRange
- continue;
- }
-
-- ActivationRange.maxBB = player.getBoundingBox().inflate( maxRange, 256, maxRange );
-- ActivationType.MISC.boundingBox = player.getBoundingBox().inflate( miscActivationRange, 256, miscActivationRange );
-- ActivationType.RAIDER.boundingBox = player.getBoundingBox().inflate( raiderActivationRange, 256, raiderActivationRange );
-- ActivationType.ANIMAL.boundingBox = player.getBoundingBox().inflate( animalActivationRange, 256, animalActivationRange );
-- ActivationType.MONSTER.boundingBox = player.getBoundingBox().inflate( monsterActivationRange, 256, monsterActivationRange );
-+ // Paper start
-+ int worldHeight = world.getHeight();
-+ ActivationRange.maxBB = player.getBoundingBox().inflate( maxRange, worldHeight, maxRange );
-+ ActivationType.MISC.boundingBox = player.getBoundingBox().inflate( miscActivationRange, worldHeight, miscActivationRange );
-+ ActivationType.RAIDER.boundingBox = player.getBoundingBox().inflate( raiderActivationRange, worldHeight, raiderActivationRange );
-+ ActivationType.ANIMAL.boundingBox = player.getBoundingBox().inflate( animalActivationRange, worldHeight, animalActivationRange );
-+ ActivationType.MONSTER.boundingBox = player.getBoundingBox().inflate( monsterActivationRange, worldHeight, monsterActivationRange );
-+ ActivationType.WATER.boundingBox = player.getBoundingBox().inflate( waterActivationRange, worldHeight, waterActivationRange );
-+ ActivationType.FLYING_MONSTER.boundingBox = player.getBoundingBox().inflate( flyingActivationRange, worldHeight, flyingActivationRange );
-+ ActivationType.VILLAGER.boundingBox = player.getBoundingBox().inflate( villagerActivationRange, worldHeight, villagerActivationRange );
-+ // Paper end
-
-- world.getEntities().get(ActivationRange.maxBB, ActivationRange::activateEntity);
-+ // Paper start
-+ java.util.List<Entity> entities = world.getEntities((Entity)null, ActivationRange.maxBB, null);
-+ boolean tickMarkers = world.paperConfig().entities.markers.tick; // Paper - Configurable marker ticking
-+ for (Entity entity : entities) {
-+ // Paper start - Configurable marker ticking
-+ if (!tickMarkers && entity instanceof net.minecraft.world.entity.Marker) {
-+ continue;
-+ }
-+ // Paper end - Configurable marker ticking
-+ ActivationRange.activateEntity(entity);
-+ }
-+ // Paper end
- }
- MinecraftTimings.entityActivationCheckTimer.stopTiming();
- }
-@@ -171,60 +257,118 @@ public class ActivationRange
- * @param entity
- * @return
- */
-- public static boolean checkEntityImmunities(Entity entity)
-+ public static int checkEntityImmunities(Entity entity) // Paper - return # of ticks to get immunity
- {
-+ // Paper start
-+ SpigotWorldConfig config = entity.level().spigotConfig;
-+ int inactiveWakeUpImmunity = checkInactiveWakeup(entity);
-+ if (inactiveWakeUpImmunity > -1) {
-+ return inactiveWakeUpImmunity;
-+ }
-+ if (entity.getRemainingFireTicks() > 0) {
-+ return 2;
-+ }
-+ if (entity.activatedImmunityTick >= MinecraftServer.currentTick) {
-+ return 1;
-+ }
-+ long inactiveFor = MinecraftServer.currentTick - entity.activatedTick;
-+ // Paper end
- // quick checks.
-- if ( entity.wasTouchingWater || entity.getRemainingFireTicks() > 0 )
-+ if ( (entity.activationType != ActivationType.WATER && entity.wasTouchingWater && entity.isPushedByFluid()) ) // Paper
- {
-- return true;
-+ return 100; // Paper
-+ }
-+ // Paper start
-+ if ( !entity.onGround() || entity.getDeltaMovement().horizontalDistanceSqr() > 9.999999747378752E-6D )
-+ {
-+ return 100;
- }
-+ // Paper end
- if ( !( entity instanceof AbstractArrow ) )
- {
-- if ( !entity.onGround() || !entity.passengers.isEmpty() || entity.isPassenger() )
-+ if ( (!entity.onGround() && !(entity instanceof FlyingMob)) ) // Paper - remove passengers logic
- {
-- return true;
-+ return 10; // Paper
- }
- } else if ( !( (AbstractArrow) entity ).inGround )
- {
-- return true;
-+ return 1; // Paper
- }
- // special cases.
- if ( entity instanceof LivingEntity )
- {
- LivingEntity living = (LivingEntity) entity;
-- if ( /*TODO: Missed mapping? living.attackTicks > 0 || */ living.hurtTime > 0 || living.activeEffects.size() > 0 )
-+ if ( living.onClimbable() || living.jumping || living.hurtTime > 0 || living.activeEffects.size() > 0 || living.isFreezing()) // Paper
- {
-- return true;
-+ return 1; // Paper
- }
-- if ( entity instanceof PathfinderMob && ( (PathfinderMob) entity ).getTarget() != null )
-+ if ( entity instanceof Mob && ((Mob) entity ).getTarget() != null) // Paper
- {
-- return true;
-+ return 20; // Paper
-+ }
-+ // Paper start
-+ if (entity instanceof Bee) {
-+ Bee bee = (Bee)entity;
-+ BlockPos movingTarget = bee.getMovingTarget();
-+ if (bee.isAngry() ||
-+ (bee.getHivePos() != null && bee.getHivePos().equals(movingTarget)) ||
-+ (bee.getSavedFlowerPos() != null && bee.getSavedFlowerPos().equals(movingTarget))
-+ ) {
-+ return 20;
-+ }
- }
-- if ( entity instanceof Villager && ( (Villager) entity ).canBreed() )
-+ if ( entity instanceof Villager ) {
-+ Brain<Villager> behaviorController = ((Villager) entity).getBrain();
-+
-+ if (config.villagersActiveForPanic) {
-+ for (Activity activity : VILLAGER_PANIC_IMMUNITIES) {
-+ if (behaviorController.isActive(activity)) {
-+ return 20*5;
-+ }
-+ }
-+ }
-+
-+ if (config.villagersWorkImmunityAfter > 0 && inactiveFor >= config.villagersWorkImmunityAfter) {
-+ if (behaviorController.isActive(Activity.WORK)) {
-+ return config.villagersWorkImmunityFor;
-+ }
-+ }
-+ }
-+ if ( entity instanceof Llama && ( (Llama) entity ).inCaravan() )
- {
-- return true;
-+ return 1;
- }
-+ // Paper end
- if ( entity instanceof Animal )
- {
- Animal animal = (Animal) entity;
- if ( animal.isBaby() || animal.isInLove() )
- {
-- return true;
-+ return 5; // Paper
- }
- if ( entity instanceof Sheep && ( (Sheep) entity ).isSheared() )
- {
-- return true;
-+ return 1; // Paper
- }
- }
- if (entity instanceof Creeper && ((Creeper) entity).isIgnited()) { // isExplosive
-- return true;
-+ return 20; // Paper
-+ }
-+ // Paper start
-+ if (entity instanceof Mob && ((Mob) entity).targetSelector.hasTasks() ) {
-+ return 0;
- }
-+ if (entity instanceof Pillager) {
-+ Pillager pillager = (Pillager) entity;
-+ // TODO:?
-+ }
-+ // Paper end
- }
- // SPIGOT-6644: Otherwise the target refresh tick will be missed
- if (entity instanceof ExperienceOrb) {
-- return true;
-+ return 20; // Paper
- }
-- return false;
-+ return -1; // Paper
- }
-
- /**
-@@ -239,8 +383,19 @@ public class ActivationRange
- if ( entity instanceof FireworkRocketEntity ) {
- return true;
- }
-+ // Paper start - special case always immunities
-+ // immunize brand new entities, dead entities, and portal scenarios
-+ if (entity.defaultActivationState || entity.tickCount < 20*10 || !entity.isAlive() || entity.isInsidePortal || entity.portalCooldown > 0) {
-+ return true;
-+ }
-+ // immunize leashed entities
-+ if (entity instanceof Mob && ((Mob)entity).getLeashHolder() instanceof Player) {
-+ return true;
-+ }
-+ // Paper end
-
-- boolean isActive = entity.activatedTick >= MinecraftServer.currentTick || entity.defaultActivationState;
-+ boolean isActive = entity.activatedTick >= MinecraftServer.currentTick;
-+ entity.isTemporarilyActive = false; // Paper
-
- // Should this entity tick?
- if ( !isActive )
-@@ -248,15 +403,19 @@ public class ActivationRange
- if ( ( MinecraftServer.currentTick - entity.activatedTick - 1 ) % 20 == 0 )
- {
- // Check immunities every 20 ticks.
-- if ( ActivationRange.checkEntityImmunities( entity ) )
-- {
-- // Triggered some sort of immunity, give 20 full ticks before we check again.
-- entity.activatedTick = MinecraftServer.currentTick + 20;
-+ // Paper start
-+ int immunity = checkEntityImmunities(entity);
-+ if (immunity >= 0) {
-+ entity.activatedTick = MinecraftServer.currentTick + immunity;
-+ } else {
-+ entity.isTemporarilyActive = true;
- }
-+ // Paper end
- isActive = true;
-+
- }
- // Add a little performance juice to active entities. Skip 1/4 if not immune.
-- } else if ( !entity.defaultActivationState && (entity.tickCount + entity.getId()) % 4 == 0 && !ActivationRange.checkEntityImmunities( entity ) ) // Paper - Ensure checking item movement is offset from Spigot's entity activation range check
-+ } else if ( (entity.tickCount + entity.getId()) % 4 == 0 && ActivationRange.checkEntityImmunities( entity ) < 0 ) // Paper
- {
- isActive = false;
- }
-diff --git a/src/main/java/org/spigotmc/SpigotWorldConfig.java b/src/main/java/org/spigotmc/SpigotWorldConfig.java
-index 5485df1a1b59e81f4dcedd21dd972e1fd2759573..1cf6d4f854d89c515e48e1fb365eb95ff9340765 100644
---- a/src/main/java/org/spigotmc/SpigotWorldConfig.java
-+++ b/src/main/java/org/spigotmc/SpigotWorldConfig.java
-@@ -211,14 +211,60 @@ public class SpigotWorldConfig
- public int monsterActivationRange = 32;
- public int raiderActivationRange = 48;
- public int miscActivationRange = 16;
-+ // Paper start
-+ public int flyingMonsterActivationRange = 32;
-+ public int waterActivationRange = 16;
-+ public int villagerActivationRange = 32;
-+ public int wakeUpInactiveAnimals = 4;
-+ public int wakeUpInactiveAnimalsEvery = 60*20;
-+ public int wakeUpInactiveAnimalsFor = 5*20;
-+ public int wakeUpInactiveMonsters = 8;
-+ public int wakeUpInactiveMonstersEvery = 20*20;
-+ public int wakeUpInactiveMonstersFor = 5*20;
-+ public int wakeUpInactiveVillagers = 4;
-+ public int wakeUpInactiveVillagersEvery = 30*20;
-+ public int wakeUpInactiveVillagersFor = 5*20;
-+ public int wakeUpInactiveFlying = 8;
-+ public int wakeUpInactiveFlyingEvery = 10*20;
-+ public int wakeUpInactiveFlyingFor = 5*20;
-+ public int villagersWorkImmunityAfter = 5*20;
-+ public int villagersWorkImmunityFor = 20;
-+ public boolean villagersActiveForPanic = true;
-+ // Paper end
- public boolean tickInactiveVillagers = true;
- public boolean ignoreSpectatorActivation = false;
- private void activationRange()
- {
-+ boolean hasAnimalsConfig = config.getInt("entity-activation-range.animals", this.animalActivationRange) != this.animalActivationRange; // Paper
- this.animalActivationRange = this.getInt( "entity-activation-range.animals", this.animalActivationRange );
- this.monsterActivationRange = this.getInt( "entity-activation-range.monsters", this.monsterActivationRange );
- this.raiderActivationRange = this.getInt( "entity-activation-range.raiders", this.raiderActivationRange );
- this.miscActivationRange = this.getInt( "entity-activation-range.misc", this.miscActivationRange );
-+ // Paper start
-+ this.waterActivationRange = this.getInt( "entity-activation-range.water", this.waterActivationRange );
-+ this.villagerActivationRange = this.getInt( "entity-activation-range.villagers", hasAnimalsConfig ? this.animalActivationRange : this.villagerActivationRange );
-+ this.flyingMonsterActivationRange = this.getInt( "entity-activation-range.flying-monsters", this.flyingMonsterActivationRange );
-+
-+ this.wakeUpInactiveAnimals = this.getInt("entity-activation-range.wake-up-inactive.animals-max-per-tick", this.wakeUpInactiveAnimals);
-+ this.wakeUpInactiveAnimalsEvery = this.getInt("entity-activation-range.wake-up-inactive.animals-every", this.wakeUpInactiveAnimalsEvery);
-+ this.wakeUpInactiveAnimalsFor = this.getInt("entity-activation-range.wake-up-inactive.animals-for", this.wakeUpInactiveAnimalsFor);
-+
-+ this.wakeUpInactiveMonsters = this.getInt("entity-activation-range.wake-up-inactive.monsters-max-per-tick", this.wakeUpInactiveMonsters);
-+ this.wakeUpInactiveMonstersEvery = this.getInt("entity-activation-range.wake-up-inactive.monsters-every", this.wakeUpInactiveMonstersEvery);
-+ this.wakeUpInactiveMonstersFor = this.getInt("entity-activation-range.wake-up-inactive.monsters-for", this.wakeUpInactiveMonstersFor);
-+
-+ this.wakeUpInactiveVillagers = this.getInt("entity-activation-range.wake-up-inactive.villagers-max-per-tick", this.wakeUpInactiveVillagers);
-+ this.wakeUpInactiveVillagersEvery = this.getInt("entity-activation-range.wake-up-inactive.villagers-every", this.wakeUpInactiveVillagersEvery);
-+ this.wakeUpInactiveVillagersFor = this.getInt("entity-activation-range.wake-up-inactive.villagers-for", this.wakeUpInactiveVillagersFor);
-+
-+ this.wakeUpInactiveFlying = this.getInt("entity-activation-range.wake-up-inactive.flying-monsters-max-per-tick", this.wakeUpInactiveFlying);
-+ this.wakeUpInactiveFlyingEvery = this.getInt("entity-activation-range.wake-up-inactive.flying-monsters-every", this.wakeUpInactiveFlyingEvery);
-+ this.wakeUpInactiveFlyingFor = this.getInt("entity-activation-range.wake-up-inactive.flying-monsters-for", this.wakeUpInactiveFlyingFor);
-+
-+ this.villagersWorkImmunityAfter = this.getInt( "entity-activation-range.villagers-work-immunity-after", this.villagersWorkImmunityAfter );
-+ this.villagersWorkImmunityFor = this.getInt( "entity-activation-range.villagers-work-immunity-for", this.villagersWorkImmunityFor );
-+ this.villagersActiveForPanic = this.getBoolean( "entity-activation-range.villagers-active-for-panic", this.villagersActiveForPanic );
-+ // Paper end
- this.tickInactiveVillagers = this.getBoolean( "entity-activation-range.tick-inactive-villagers", this.tickInactiveVillagers );
- this.ignoreSpectatorActivation = this.getBoolean( "entity-activation-range.ignore-spectators", this.ignoreSpectatorActivation );
- this.log( "Entity Activation Range: An " + this.animalActivationRange + " / Mo " + this.monsterActivationRange + " / Ra " + this.raiderActivationRange + " / Mi " + this.miscActivationRange + " / Tiv " + this.tickInactiveVillagers + " / Isa " + this.ignoreSpectatorActivation );
diff --git a/patches/unapplied/server/1001-Optional-per-player-mob-spawns.patch b/patches/unapplied/server/1001-Optional-per-player-mob-spawns.patch
deleted file mode 100644
index dc4d88d053..0000000000
--- a/patches/unapplied/server/1001-Optional-per-player-mob-spawns.patch
+++ /dev/null
@@ -1,255 +0,0 @@
-From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
-From: kickash32 <[email protected]>
-Date: Mon, 19 Aug 2019 01:27:58 +0500
-Subject: [PATCH] Optional per player mob spawns
-
-
-diff --git a/src/main/java/net/minecraft/server/level/ChunkMap.java b/src/main/java/net/minecraft/server/level/ChunkMap.java
-index 07a9a11c7fa608e221c0f0e759c483b44de9fdd5..ccf0f7c7feaf47f451cec30ba02bea39ba192b3c 100644
---- a/src/main/java/net/minecraft/server/level/ChunkMap.java
-+++ b/src/main/java/net/minecraft/server/level/ChunkMap.java
-@@ -288,9 +288,28 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
- });
- }
-
-+ // Paper start - Optional per player mob spawns
-+ public void updatePlayerMobTypeMap(final Entity entity) {
-+ if (!this.level.paperConfig().entities.spawning.perPlayerMobSpawns) {
-+ return;
-+ }
-+ int index = entity.getType().getCategory().ordinal();
-+
-+ final com.destroystokyo.paper.util.maplist.ReferenceList<ServerPlayer> inRange =
-+ this.getNearbyPlayers().getPlayers(entity.chunkPosition(), io.papermc.paper.util.player.NearbyPlayers.NearbyMapType.TICK_VIEW_DISTANCE);
-+ if (inRange == null) {
-+ return;
-+ }
-+ final Object[] backingSet = inRange.getRawData();
-+ for (int i = 0, len = inRange.size(); i < len; i++) {
-+ ++((ServerPlayer)backingSet[i]).mobCounts[index];
-+ }
-+ }
-+
- public int getMobCountNear(final ServerPlayer player, final net.minecraft.world.entity.MobCategory mobCategory) {
-- return -1;
-+ return player.mobCounts[mobCategory.ordinal()];
- }
-+ // Paper end - Optional per player mob spawns
-
- private static double euclideanDistanceSquared(ChunkPos pos, Entity entity) {
- double d0 = (double) SectionPos.sectionToBlockCoord(pos.x, 8);
-diff --git a/src/main/java/net/minecraft/server/level/ServerChunkCache.java b/src/main/java/net/minecraft/server/level/ServerChunkCache.java
-index 20cdfd2bbd5dc71fd37ccedaf3a8d06b45553c9b..059ab637adf1be576fa1fff36a91b6c5f1b5f035 100644
---- a/src/main/java/net/minecraft/server/level/ServerChunkCache.java
-+++ b/src/main/java/net/minecraft/server/level/ServerChunkCache.java
-@@ -518,7 +518,19 @@ public class ServerChunkCache extends ChunkSource {
- gameprofilerfiller.popPush("naturalSpawnCount");
- this.level.timings.countNaturalMobs.startTiming(); // Paper - timings
- int k = this.distanceManager.getNaturalSpawnChunkCount();
-- NaturalSpawner.SpawnState spawnercreature_d = NaturalSpawner.createState(k, this.level.getAllEntities(), this::getFullChunk, new LocalMobCapCalculator(this.chunkMap));
-+ // Paper start - Optional per player mob spawns
-+ int naturalSpawnChunkCount = k;
-+ NaturalSpawner.SpawnState spawnercreature_d; // moved down
-+ if ((this.spawnFriendlies || this.spawnEnemies) && this.level.paperConfig().entities.spawning.perPlayerMobSpawns) { // don't count mobs when animals and monsters are disabled
-+ // re-set mob counts
-+ for (ServerPlayer player : this.level.players) {
-+ Arrays.fill(player.mobCounts, 0);
-+ }
-+ spawnercreature_d = NaturalSpawner.createState(naturalSpawnChunkCount, this.level.getAllEntities(), this::getFullChunk, null, true);
-+ } else {
-+ spawnercreature_d = NaturalSpawner.createState(naturalSpawnChunkCount, this.level.getAllEntities(), this::getFullChunk, !this.level.paperConfig().entities.spawning.perPlayerMobSpawns ? new LocalMobCapCalculator(this.chunkMap) : null, false);
-+ }
-+ // Paper end - Optional per player mob spawns
- this.level.timings.countNaturalMobs.stopTiming(); // Paper - timings
-
- this.lastSpawnState = spawnercreature_d;
-diff --git a/src/main/java/net/minecraft/server/level/ServerPlayer.java b/src/main/java/net/minecraft/server/level/ServerPlayer.java
-index 4a62c2937460dca9d938c40da47529e106503cad..7fb2c0f2576142423cd0e50b811ce4f55795e43d 100644
---- a/src/main/java/net/minecraft/server/level/ServerPlayer.java
-+++ b/src/main/java/net/minecraft/server/level/ServerPlayer.java
-@@ -256,6 +256,10 @@ public class ServerPlayer extends Player {
- public boolean queueHealthUpdatePacket;
- public net.minecraft.network.protocol.game.ClientboundSetHealthPacket queuedHealthUpdatePacket;
- // Paper end - cancellable death event
-+ // Paper start - Optional per player mob spawns
-+ public static final int MOBCATEGORY_TOTAL_ENUMS = net.minecraft.world.entity.MobCategory.values().length;
-+ public final int[] mobCounts = new int[MOBCATEGORY_TOTAL_ENUMS]; // Paper
-+ // Paper end - Optional per player mob spawns
-
- // CraftBukkit start
- public String displayName;
-diff --git a/src/main/java/net/minecraft/world/level/NaturalSpawner.java b/src/main/java/net/minecraft/world/level/NaturalSpawner.java
-index c44c10e15564af6ba0f6d60a1b5f38c6e874a43a..14f4ceb6c0be34d23b24c1695f966145c3aaee96 100644
---- a/src/main/java/net/minecraft/world/level/NaturalSpawner.java
-+++ b/src/main/java/net/minecraft/world/level/NaturalSpawner.java
-@@ -70,6 +70,12 @@ public final class NaturalSpawner {
- private NaturalSpawner() {}
-
- public static NaturalSpawner.SpawnState createState(int spawningChunkCount, Iterable<Entity> entities, NaturalSpawner.ChunkGetter chunkSource, LocalMobCapCalculator densityCapper) {
-+ // Paper start - Optional per player mob spawns
-+ return createState(spawningChunkCount, entities, chunkSource, densityCapper, false);
-+ }
-+
-+ public static NaturalSpawner.SpawnState createState(int spawningChunkCount, Iterable<Entity> entities, NaturalSpawner.ChunkGetter chunkSource, LocalMobCapCalculator densityCapper, boolean countMobs) {
-+ // Paper end - Optional per player mob spawns
- PotentialCalculator spawnercreatureprobabilities = new PotentialCalculator();
- Object2IntOpenHashMap<MobCategory> object2intopenhashmap = new Object2IntOpenHashMap();
- Iterator iterator = entities.iterator();
-@@ -104,11 +110,16 @@ public final class NaturalSpawner {
- spawnercreatureprobabilities.addCharge(entity.blockPosition(), biomesettingsmobs_b.charge());
- }
-
-- if (entity instanceof Mob) {
-+ if (densityCapper != null && entity instanceof Mob) { // Paper - Optional per player mob spawns
- densityCapper.addMob(chunk.getPos(), enumcreaturetype);
- }
-
- object2intopenhashmap.addTo(enumcreaturetype, 1);
-+ // Paper start - Optional per player mob spawns
-+ if (countMobs) {
-+ chunk.level.getChunkSource().chunkMap.updatePlayerMobTypeMap(entity);
-+ }
-+ // Paper end - Optional per player mob spawns
- });
- }
- }
-@@ -143,13 +154,35 @@ public final class NaturalSpawner {
- continue;
- }
-
-- if ((spawnAnimals || !enumcreaturetype.isFriendly()) && (spawnMonsters || enumcreaturetype.isFriendly()) && (rareSpawn || !enumcreaturetype.isPersistent()) && info.canSpawnForCategory(enumcreaturetype, chunk.getPos(), limit)) {
-+ // Paper start - Optional per player mob spawns; only allow spawns upto the limit per chunk and update count afterwards
-+ int currEntityCount = info.mobCategoryCounts.getInt(enumcreaturetype);
-+ int k1 = limit * info.getSpawnableChunkCount() / NaturalSpawner.MAGIC_NUMBER;
-+ int difference = k1 - currEntityCount;
-+
-+ if (world.paperConfig().entities.spawning.perPlayerMobSpawns) {
-+ int minDiff = Integer.MAX_VALUE;
-+ final com.destroystokyo.paper.util.maplist.ReferenceList<net.minecraft.server.level.ServerPlayer> inRange =
-+ world.chunkSource.chunkMap.getNearbyPlayers().getPlayers(chunk.getPos(), io.papermc.paper.util.player.NearbyPlayers.NearbyMapType.TICK_VIEW_DISTANCE);
-+ if (inRange != null) {
-+ final Object[] backingSet = inRange.getRawData();
-+ for (int k = 0, len = inRange.size(); k < len; k++) {
-+ minDiff = Math.min(limit - world.getChunkSource().chunkMap.getMobCountNear((net.minecraft.server.level.ServerPlayer)backingSet[k], enumcreaturetype), minDiff);
-+ }
-+ }
-+ difference = (minDiff == Integer.MAX_VALUE) ? 0 : minDiff;
-+ }
-+ if ((spawnAnimals || !enumcreaturetype.isFriendly()) && (spawnMonsters || enumcreaturetype.isFriendly()) && (rareSpawn || !enumcreaturetype.isPersistent()) && difference > 0) {
-+ // Paper end - Optional per player mob spawns
- // CraftBukkit end
- Objects.requireNonNull(info);
- NaturalSpawner.SpawnPredicate spawnercreature_c = info::canSpawn;
-
- Objects.requireNonNull(info);
-- NaturalSpawner.spawnCategoryForChunk(enumcreaturetype, world, chunk, spawnercreature_c, info::afterSpawn);
-+ // Paper start - Optional per player mob spawns
-+ int spawnCount = NaturalSpawner.spawnCategoryForChunk(enumcreaturetype, world, chunk, spawnercreature_c, info::afterSpawn,
-+ difference, world.paperConfig().entities.spawning.perPlayerMobSpawns ? world.getChunkSource().chunkMap::updatePlayerMobTypeMap : null);
-+ info.mobCategoryCounts.mergeInt(enumcreaturetype, spawnCount, Integer::sum);
-+ // Paper end - Optional per player mob spawns
- }
- }
-
-@@ -168,11 +201,17 @@ public final class NaturalSpawner {
- // Paper end - Add mobcaps commands
-
- public static void spawnCategoryForChunk(MobCategory group, ServerLevel world, LevelChunk chunk, NaturalSpawner.SpawnPredicate checker, NaturalSpawner.AfterSpawnCallback runner) {
-+ // Paper start - Optional per player mob spawns
-+ spawnCategoryForChunk(group, world, chunk, checker, runner, Integer.MAX_VALUE, null);
-+ }
-+ public static int spawnCategoryForChunk(MobCategory group, ServerLevel world, LevelChunk chunk, NaturalSpawner.SpawnPredicate checker, NaturalSpawner.AfterSpawnCallback runner, int maxSpawns, Consumer<Entity> trackEntity) {
-+ // Paper end - Optional per player mob spawns
- BlockPos blockposition = NaturalSpawner.getRandomPosWithin(world, chunk);
-
- if (blockposition.getY() >= world.getMinBuildHeight() + 1) {
-- NaturalSpawner.spawnCategoryForPosition(group, world, chunk, blockposition, checker, runner);
-+ return NaturalSpawner.spawnCategoryForPosition(group, world, chunk, blockposition, checker, runner, maxSpawns, trackEntity); // Paper - Optional per player mob spawns
- }
-+ return 0; // Paper - Optional per player mob spawns
- }
-
- @VisibleForDebug
-@@ -183,15 +222,21 @@ public final class NaturalSpawner {
- });
- }
-
-+ // Paper start - Optional per player mob spawns
- public static void spawnCategoryForPosition(MobCategory group, ServerLevel world, ChunkAccess chunk, BlockPos pos, NaturalSpawner.SpawnPredicate checker, NaturalSpawner.AfterSpawnCallback runner) {
-+ spawnCategoryForPosition(group, world,chunk, pos, checker, runner, Integer.MAX_VALUE, null);
-+ }
-+ public static int spawnCategoryForPosition(MobCategory group, ServerLevel world, ChunkAccess chunk, BlockPos pos, NaturalSpawner.SpawnPredicate checker, NaturalSpawner.AfterSpawnCallback runner, int maxSpawns, Consumer<Entity> trackEntity) {
-+ // Paper end - Optional per player mob spawns
- StructureManager structuremanager = world.structureManager();
- ChunkGenerator chunkgenerator = world.getChunkSource().getGenerator();
- int i = pos.getY();
- BlockState iblockdata = world.getBlockStateIfLoadedAndInBounds(pos); // Paper - don't load chunks for mob spawn
-+ int j = 0; // Paper - Optional per player mob spawns; moved up
-
- if (iblockdata != null && !iblockdata.isRedstoneConductor(chunk, pos)) { // Paper - don't load chunks for mob spawn
- BlockPos.MutableBlockPos blockposition_mutableblockposition = new BlockPos.MutableBlockPos();
-- int j = 0;
-+ //int j = 0; // Paper - Optional per player mob spawns; moved up
- int k = 0;
-
- while (k < 3) {
-@@ -233,14 +278,14 @@ public final class NaturalSpawner {
- // Paper start - PreCreatureSpawnEvent
- PreSpawnStatus doSpawning = isValidSpawnPostitionForType(world, group, structuremanager, chunkgenerator, biomesettingsmobs_c, blockposition_mutableblockposition, d2);
- if (doSpawning == PreSpawnStatus.ABORT) {
-- return;
-+ return j; // Paper - Optional per player mob spawns
- }
- if (doSpawning == PreSpawnStatus.SUCCESS && checker.test(biomesettingsmobs_c.type, blockposition_mutableblockposition, chunk)) {
- // Paper end - PreCreatureSpawnEvent
- Mob entityinsentient = NaturalSpawner.getMobForSpawn(world, biomesettingsmobs_c.type);
-
- if (entityinsentient == null) {
-- return;
-+ return j; // Paper - Optional per player mob spawns
- }
-
- entityinsentient.moveTo(d0, (double) i, d1, world.random.nextFloat() * 360.0F, 0.0F);
-@@ -253,10 +298,15 @@ public final class NaturalSpawner {
- ++j;
- ++k1;
- runner.run(entityinsentient, chunk);
-+ // Paper start - Optional per player mob spawns
-+ if (trackEntity != null) {
-+ trackEntity.accept(entityinsentient);
-+ }
-+ // Paper end - Optional per player mob spawns
- }
- // CraftBukkit end
-- if (j >= entityinsentient.getMaxSpawnClusterSize()) {
-- return;
-+ if (j >= entityinsentient.getMaxSpawnClusterSize() || j >= maxSpawns) { // Paper - Optional per player mob spawns
-+ return j; // Paper - Optional per player mob spawns
- }
-
- if (entityinsentient.isMaxGroupSizeReached(k1)) {
-@@ -278,6 +328,7 @@ public final class NaturalSpawner {
- }
-
- }
-+ return j; // Paper - Optional per player mob spawns
- }
-
- private static boolean isRightDistanceToPlayerAndSpawnPoint(ServerLevel world, ChunkAccess chunk, BlockPos.MutableBlockPos pos, double squaredDistance) {
-@@ -573,7 +624,7 @@ public final class NaturalSpawner {
- MobCategory enumcreaturetype = entitytypes.getCategory();
-
- this.mobCategoryCounts.addTo(enumcreaturetype, 1);
-- this.localMobCapCalculator.addMob(new ChunkPos(blockposition), enumcreaturetype);
-+ if (this.localMobCapCalculator != null) this.localMobCapCalculator.addMob(new ChunkPos(blockposition), enumcreaturetype); // Paper - Optional per player mob spawns
- }
-
- public int getSpawnableChunkCount() {
-@@ -589,6 +640,7 @@ public final class NaturalSpawner {
- int i = limit * this.spawnableChunkCount / NaturalSpawner.MAGIC_NUMBER;
- // CraftBukkit end
-
-+ if (this.localMobCapCalculator == null) return this.mobCategoryCounts.getInt(enumcreaturetype) < i; // Paper - Optional per player mob spawns
- return this.mobCategoryCounts.getInt(enumcreaturetype) >= i ? false : this.localMobCapCalculator.canSpawn(enumcreaturetype, chunkcoordintpair);
- }
- }
diff --git a/patches/unapplied/server/1002-Anti-Xray.patch b/patches/unapplied/server/1002-Anti-Xray.patch
deleted file mode 100644
index 0e19dabd18..0000000000
--- a/patches/unapplied/server/1002-Anti-Xray.patch
+++ /dev/null
@@ -1,1641 +0,0 @@
-From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
-From: stonar96 <[email protected]>
-Date: Thu, 25 Nov 2021 13:27:51 +0100
-Subject: [PATCH] Anti-Xray
-
-
-diff --git a/src/main/java/com/destroystokyo/paper/antixray/BitStorageReader.java b/src/main/java/com/destroystokyo/paper/antixray/BitStorageReader.java
-new file mode 100644
-index 0000000000000000000000000000000000000000..e448c26327b5f6189c3c52e698cff66c8f9ad81a
---- /dev/null
-+++ b/src/main/java/com/destroystokyo/paper/antixray/BitStorageReader.java
-@@ -0,0 +1,51 @@
-+package com.destroystokyo.paper.antixray;
-+
-+public final class BitStorageReader {
-+
-+ private byte[] buffer;
-+ private int bits;
-+ private int mask;
-+ private int longInBufferIndex;
-+ private int bitInLongIndex;
-+ private long current;
-+
-+ public void setBuffer(byte[] buffer) {
-+ this.buffer = buffer;
-+ }
-+
-+ public void setBits(int bits) {
-+ this.bits = bits;
-+ mask = (1 << bits) - 1;
-+ }
-+
-+ public void setIndex(int index) {
-+ longInBufferIndex = index;
-+ bitInLongIndex = 0;
-+ init();
-+ }
-+
-+ private void init() {
-+ if (buffer.length > longInBufferIndex + 7) {
-+ current = ((((long) buffer[longInBufferIndex]) << 56)
-+ | (((long) buffer[longInBufferIndex + 1] & 0xff) << 48)
-+ | (((long) buffer[longInBufferIndex + 2] & 0xff) << 40)
-+ | (((long) buffer[longInBufferIndex + 3] & 0xff) << 32)
-+ | (((long) buffer[longInBufferIndex + 4] & 0xff) << 24)
-+ | (((long) buffer[longInBufferIndex + 5] & 0xff) << 16)
-+ | (((long) buffer[longInBufferIndex + 6] & 0xff) << 8)
-+ | (((long) buffer[longInBufferIndex + 7] & 0xff)));
-+ }
-+ }
-+
-+ public int read() {
-+ if (bitInLongIndex + bits > 64) {
-+ bitInLongIndex = 0;
-+ longInBufferIndex += 8;
-+ init();
-+ }
-+
-+ int value = (int) (current >>> bitInLongIndex) & mask;
-+ bitInLongIndex += bits;
-+ return value;
-+ }
-+}
-diff --git a/src/main/java/com/destroystokyo/paper/antixray/BitStorageWriter.java b/src/main/java/com/destroystokyo/paper/antixray/BitStorageWriter.java
-new file mode 100644
-index 0000000000000000000000000000000000000000..e4540ea278f2dc871cb6a3cb8897559bfd65e134
---- /dev/null
-+++ b/src/main/java/com/destroystokyo/paper/antixray/BitStorageWriter.java
-@@ -0,0 +1,79 @@
-+package com.destroystokyo.paper.antixray;
-+
-+public final class BitStorageWriter {
-+
-+ private byte[] buffer;
-+ private int bits;
-+ private long mask;
-+ private int longInBufferIndex;
-+ private int bitInLongIndex;
-+ private long current;
-+ private boolean dirty;
-+
-+ public void setBuffer(byte[] buffer) {
-+ this.buffer = buffer;
-+ }
-+
-+ public void setBits(int bits) {
-+ this.bits = bits;
-+ mask = (1L << bits) - 1;
-+ }
-+
-+ public void setIndex(int index) {
-+ longInBufferIndex = index;
-+ bitInLongIndex = 0;
-+ init();
-+ }
-+
-+ private void init() {
-+ if (buffer.length > longInBufferIndex + 7) {
-+ current = ((((long) buffer[longInBufferIndex]) << 56)
-+ | (((long) buffer[longInBufferIndex + 1] & 0xff) << 48)
-+ | (((long) buffer[longInBufferIndex + 2] & 0xff) << 40)
-+ | (((long) buffer[longInBufferIndex + 3] & 0xff) << 32)
-+ | (((long) buffer[longInBufferIndex + 4] & 0xff) << 24)
-+ | (((long) buffer[longInBufferIndex + 5] & 0xff) << 16)
-+ | (((long) buffer[longInBufferIndex + 6] & 0xff) << 8)
-+ | (((long) buffer[longInBufferIndex + 7] & 0xff)));
-+ }
-+
-+ dirty = false;
-+ }
-+
-+ public void flush() {
-+ if (dirty && buffer.length > longInBufferIndex + 7) {
-+ buffer[longInBufferIndex] = (byte) (current >> 56 & 0xff);
-+ buffer[longInBufferIndex + 1] = (byte) (current >> 48 & 0xff);
-+ buffer[longInBufferIndex + 2] = (byte) (current >> 40 & 0xff);
-+ buffer[longInBufferIndex + 3] = (byte) (current >> 32 & 0xff);
-+ buffer[longInBufferIndex + 4] = (byte) (current >> 24 & 0xff);
-+ buffer[longInBufferIndex + 5] = (byte) (current >> 16 & 0xff);
-+ buffer[longInBufferIndex + 6] = (byte) (current >> 8 & 0xff);
-+ buffer[longInBufferIndex + 7] = (byte) (current & 0xff);
-+ }
-+ }
-+
-+ public void write(int value) {
-+ if (bitInLongIndex + bits > 64) {
-+ flush();
-+ bitInLongIndex = 0;
-+ longInBufferIndex += 8;
-+ init();
-+ }
-+
-+ current = current & ~(mask << bitInLongIndex) | (value & mask) << bitInLongIndex;
-+ dirty = true;
-+ bitInLongIndex += bits;
-+ }
-+
-+ public void skip() {
-+ bitInLongIndex += bits;
-+
-+ if (bitInLongIndex > 64) {
-+ flush();
-+ bitInLongIndex = bits;
-+ longInBufferIndex += 8;
-+ init();
-+ }
-+ }
-+}
-diff --git a/src/main/java/com/destroystokyo/paper/antixray/ChunkPacketBlockController.java b/src/main/java/com/destroystokyo/paper/antixray/ChunkPacketBlockController.java
-new file mode 100644
-index 0000000000000000000000000000000000000000..52d2e2b744f91914802506e52a07161729bbcf3a
---- /dev/null
-+++ b/src/main/java/com/destroystokyo/paper/antixray/ChunkPacketBlockController.java
-@@ -0,0 +1,45 @@
-+package com.destroystokyo.paper.antixray;
-+
-+import net.minecraft.core.BlockPos;
-+import net.minecraft.core.Direction;
-+import net.minecraft.network.protocol.game.ClientboundLevelChunkWithLightPacket;
-+import net.minecraft.network.protocol.game.ServerboundPlayerActionPacket;
-+import net.minecraft.server.level.ServerPlayer;
-+import net.minecraft.server.level.ServerPlayerGameMode;
-+import net.minecraft.world.level.ChunkPos;
-+import net.minecraft.world.level.Level;
-+import net.minecraft.world.level.block.state.BlockState;
-+import net.minecraft.world.level.chunk.LevelChunk;
-+
-+public class ChunkPacketBlockController {
-+
-+ public static final ChunkPacketBlockController NO_OPERATION_INSTANCE = new ChunkPacketBlockController();
-+
-+ protected ChunkPacketBlockController() {
-+
-+ }
-+
-+ public BlockState[] getPresetBlockStates(Level level, ChunkPos chunkPos, int chunkSectionY) {
-+ return null;
-+ }
-+
-+ public boolean shouldModify(ServerPlayer player, LevelChunk chunk) {
-+ return false;
-+ }
-+
-+ public ChunkPacketInfo<BlockState> getChunkPacketInfo(ClientboundLevelChunkWithLightPacket chunkPacket, LevelChunk chunk) {
-+ return null;
-+ }
-+
-+ public void modifyBlocks(ClientboundLevelChunkWithLightPacket chunkPacket, ChunkPacketInfo<BlockState> chunkPacketInfo) {
-+ chunkPacket.setReady(true);
-+ }
-+
-+ public void onBlockChange(Level level, BlockPos blockPos, BlockState newBlockState, BlockState oldBlockState, int flags, int maxUpdateDepth) {
-+
-+ }
-+
-+ public void onPlayerLeftClickBlock(ServerPlayerGameMode serverPlayerGameMode, BlockPos blockPos, ServerboundPlayerActionPacket.Action action, Direction direction, int worldHeight, int sequence) {
-+
-+ }
-+}
-diff --git a/src/main/java/com/destroystokyo/paper/antixray/ChunkPacketBlockControllerAntiXray.java b/src/main/java/com/destroystokyo/paper/antixray/ChunkPacketBlockControllerAntiXray.java
-new file mode 100644
-index 0000000000000000000000000000000000000000..e7fe98ea30ae6d0baea3ec1f9f98a89502a49a12
---- /dev/null
-+++ b/src/main/java/com/destroystokyo/paper/antixray/ChunkPacketBlockControllerAntiXray.java
-@@ -0,0 +1,676 @@
-+package com.destroystokyo.paper.antixray;
-+
-+import io.papermc.paper.configuration.WorldConfiguration;
-+import io.papermc.paper.configuration.type.EngineMode;
-+import java.util.ArrayList;
-+import java.util.LinkedHashSet;
-+import java.util.LinkedList;
-+import java.util.List;
-+import java.util.Set;
-+import java.util.concurrent.Executor;
-+import java.util.concurrent.ThreadLocalRandom;
-+import java.util.function.IntSupplier;
-+import net.minecraft.core.BlockPos;
-+import net.minecraft.core.Direction;
-+import net.minecraft.core.registries.Registries;
-+import net.minecraft.network.protocol.game.ClientboundLevelChunkWithLightPacket;
-+import net.minecraft.network.protocol.game.ServerboundPlayerActionPacket;
-+import net.minecraft.server.MinecraftServer;
-+import net.minecraft.server.level.ServerLevel;
-+import net.minecraft.server.level.ServerPlayer;
-+import net.minecraft.server.level.ServerPlayerGameMode;
-+import net.minecraft.world.level.ChunkPos;
-+import net.minecraft.world.level.Level;
-+import net.minecraft.world.level.biome.Biomes;
-+import net.minecraft.world.level.block.Block;
-+import net.minecraft.world.level.block.Blocks;
-+import net.minecraft.world.level.block.EntityBlock;
-+import net.minecraft.world.level.block.state.BlockState;
-+import net.minecraft.world.level.chunk.EmptyLevelChunk;
-+import net.minecraft.world.level.chunk.GlobalPalette;
-+import net.minecraft.world.level.chunk.LevelChunk;
-+import net.minecraft.world.level.chunk.LevelChunkSection;
-+import net.minecraft.world.level.chunk.MissingPaletteEntryException;
-+import net.minecraft.world.level.chunk.Palette;
-+import org.bukkit.Bukkit;
-+
-+public final class ChunkPacketBlockControllerAntiXray extends ChunkPacketBlockController {
-+
-+ private static final Palette<BlockState> GLOBAL_BLOCKSTATE_PALETTE = new GlobalPalette<>(Block.BLOCK_STATE_REGISTRY);
-+ private static final LevelChunkSection EMPTY_SECTION = null;
-+ private final Executor executor;
-+ private final EngineMode engineMode;
-+ private final int maxBlockHeight;
-+ private final int updateRadius;
-+ private final boolean usePermission;
-+ private final BlockState[] presetBlockStates;
-+ private final BlockState[] presetBlockStatesFull;
-+ private final BlockState[] presetBlockStatesStone;
-+ private final BlockState[] presetBlockStatesDeepslate;
-+ private final BlockState[] presetBlockStatesNetherrack;
-+ private final BlockState[] presetBlockStatesEndStone;
-+ private final int[] presetBlockStateBitsGlobal;
-+ private final int[] presetBlockStateBitsStoneGlobal;
-+ private final int[] presetBlockStateBitsDeepslateGlobal;
-+ private final int[] presetBlockStateBitsNetherrackGlobal;
-+ private final int[] presetBlockStateBitsEndStoneGlobal;
-+ private final boolean[] solidGlobal = new boolean[Block.BLOCK_STATE_REGISTRY.size()];
-+ private final boolean[] obfuscateGlobal = new boolean[Block.BLOCK_STATE_REGISTRY.size()];
-+ private final LevelChunkSection[] emptyNearbyChunkSections = {EMPTY_SECTION, EMPTY_SECTION, EMPTY_SECTION, EMPTY_SECTION};
-+ private final int maxBlockHeightUpdatePosition;
-+
-+ public ChunkPacketBlockControllerAntiXray(Level level, Executor executor) {
-+ this.executor = executor;
-+ WorldConfiguration.Anticheat.AntiXray paperWorldConfig = level.paperConfig().anticheat.antiXray;
-+ engineMode = paperWorldConfig.engineMode;
-+ maxBlockHeight = paperWorldConfig.maxBlockHeight >> 4 << 4;
-+ updateRadius = paperWorldConfig.updateRadius;
-+ usePermission = paperWorldConfig.usePermission;
-+ List<Block> toObfuscate;
-+
-+ if (engineMode == EngineMode.HIDE) {
-+ toObfuscate = paperWorldConfig.hiddenBlocks;
-+ presetBlockStates = null;
-+ presetBlockStatesFull = null;
-+ presetBlockStatesStone = new BlockState[]{Blocks.STONE.defaultBlockState()};
-+ presetBlockStatesDeepslate = new BlockState[]{Blocks.DEEPSLATE.defaultBlockState()};
-+ presetBlockStatesNetherrack = new BlockState[]{Blocks.NETHERRACK.defaultBlockState()};
-+ presetBlockStatesEndStone = new BlockState[]{Blocks.END_STONE.defaultBlockState()};
-+ presetBlockStateBitsGlobal = null;
-+ presetBlockStateBitsStoneGlobal = new int[]{GLOBAL_BLOCKSTATE_PALETTE.idFor(Blocks.STONE.defaultBlockState())};
-+ presetBlockStateBitsDeepslateGlobal = new int[]{GLOBAL_BLOCKSTATE_PALETTE.idFor(Blocks.DEEPSLATE.defaultBlockState())};
-+ presetBlockStateBitsNetherrackGlobal = new int[]{GLOBAL_BLOCKSTATE_PALETTE.idFor(Blocks.NETHERRACK.defaultBlockState())};
-+ presetBlockStateBitsEndStoneGlobal = new int[]{GLOBAL_BLOCKSTATE_PALETTE.idFor(Blocks.END_STONE.defaultBlockState())};
-+ } else {
-+ toObfuscate = new ArrayList<>(paperWorldConfig.replacementBlocks);
-+ List<BlockState> presetBlockStateList = new LinkedList<>();
-+
-+ for (Block block : paperWorldConfig.hiddenBlocks) {
-+
-+ if (!(block instanceof EntityBlock)) {
-+ toObfuscate.add(block);
-+ presetBlockStateList.add(block.defaultBlockState());
-+ }
-+ }
-+
-+ // The doc of the LinkedHashSet(Collection<? extends E>) constructor doesn't specify that the insertion order is the predictable iteration order of the specified Collection, although it is in the implementation
-+ Set<BlockState> presetBlockStateSet = new LinkedHashSet<>();
-+ // Therefore addAll(Collection<? extends E>) is used, which guarantees this order in the doc
-+ presetBlockStateSet.addAll(presetBlockStateList);
-+ presetBlockStates = presetBlockStateSet.isEmpty() ? new BlockState[]{Blocks.DIAMOND_ORE.defaultBlockState()} : presetBlockStateSet.toArray(new BlockState[0]);
-+ presetBlockStatesFull = presetBlockStateSet.isEmpty() ? new BlockState[]{Blocks.DIAMOND_ORE.defaultBlockState()} : presetBlockStateList.toArray(new BlockState[0]);
-+ presetBlockStatesStone = null;
-+ presetBlockStatesDeepslate = null;
-+ presetBlockStatesNetherrack = null;
-+ presetBlockStatesEndStone = null;
-+ presetBlockStateBitsGlobal = new int[presetBlockStatesFull.length];
-+
-+ for (int i = 0; i < presetBlockStatesFull.length; i++) {
-+ presetBlockStateBitsGlobal[i] = GLOBAL_BLOCKSTATE_PALETTE.idFor(presetBlockStatesFull[i]);
-+ }
-+
-+ presetBlockStateBitsStoneGlobal = null;
-+ presetBlockStateBitsDeepslateGlobal = null;
-+ presetBlockStateBitsNetherrackGlobal = null;
-+ presetBlockStateBitsEndStoneGlobal = null;
-+ }
-+
-+ for (Block block : toObfuscate) {
-+
-+ // Don't obfuscate air because air causes unnecessary block updates and causes block updates to fail in the void
-+ if (block != null && !block.defaultBlockState().isAir()) {
-+ // Replace all block states of a specified block
-+ for (BlockState blockState : block.getStateDefinition().getPossibleStates()) {
-+ obfuscateGlobal[GLOBAL_BLOCKSTATE_PALETTE.idFor(blockState)] = true;
-+ }
-+ }
-+ }
-+
-+ EmptyLevelChunk emptyChunk = new EmptyLevelChunk(level, new ChunkPos(0, 0), MinecraftServer.getServer().registryAccess().registryOrThrow(Registries.BIOME).getHolderOrThrow(Biomes.PLAINS));
-+ BlockPos zeroPos = new BlockPos(0, 0, 0);
-+
-+ for (int i = 0; i < solidGlobal.length; i++) {
-+ BlockState blockState = GLOBAL_BLOCKSTATE_PALETTE.valueFor(i);
-+
-+ if (blockState != null) {
-+ solidGlobal[i] = blockState.isRedstoneConductor(emptyChunk, zeroPos)
-+ && blockState.getBlock() != Blocks.SPAWNER && blockState.getBlock() != Blocks.BARRIER && blockState.getBlock() != Blocks.SHULKER_BOX && blockState.getBlock() != Blocks.SLIME_BLOCK && blockState.getBlock() != Blocks.MANGROVE_ROOTS || paperWorldConfig.lavaObscures && blockState == Blocks.LAVA.defaultBlockState();
-+ // Comparing blockState == Blocks.LAVA.defaultBlockState() instead of blockState.getBlock() == Blocks.LAVA ensures that only "stationary lava" is used
-+ // shulker box checks TE.
-+ }
-+ }
-+
-+ maxBlockHeightUpdatePosition = maxBlockHeight + updateRadius - 1;
-+ }
-+
-+ private int getPresetBlockStatesFullLength() {
-+ return engineMode == EngineMode.HIDE ? 1 : presetBlockStatesFull.length;
-+ }
-+
-+ @Override
-+ public BlockState[] getPresetBlockStates(Level level, ChunkPos chunkPos, int chunkSectionY) {
-+ // Return the block states to be added to the paletted containers so that they can be used for obfuscation
-+ int bottomBlockY = chunkSectionY << 4;
-+
-+ if (bottomBlockY < maxBlockHeight) {
-+ if (engineMode == EngineMode.HIDE) {
-+ return switch (level.getWorld().getEnvironment()) {
-+ case NETHER -> presetBlockStatesNetherrack;
-+ case THE_END -> presetBlockStatesEndStone;
-+ default -> bottomBlockY < 0 ? presetBlockStatesDeepslate : presetBlockStatesStone;
-+ };
-+ }
-+
-+ return presetBlockStates;
-+ }
-+
-+ return null;
-+ }
-+
-+ @Override
-+ public boolean shouldModify(ServerPlayer player, LevelChunk chunk) {
-+ return !usePermission || !player.getBukkitEntity().hasPermission("paper.antixray.bypass");
-+ }
-+
-+ @Override
-+ public ChunkPacketInfoAntiXray getChunkPacketInfo(ClientboundLevelChunkWithLightPacket chunkPacket, LevelChunk chunk) {
-+ // Return a new instance to collect data and objects in the right state while creating the chunk packet for thread safe access later
-+ return new ChunkPacketInfoAntiXray(chunkPacket, chunk, this);
-+ }
-+
-+ @Override
-+ public void modifyBlocks(ClientboundLevelChunkWithLightPacket chunkPacket, ChunkPacketInfo<BlockState> chunkPacketInfo) {
-+ if (!(chunkPacketInfo instanceof ChunkPacketInfoAntiXray)) {
-+ chunkPacket.setReady(true);
-+ return;
-+ }
-+
-+ if (!Bukkit.isPrimaryThread()) {
-+ // Plugins?
-+ MinecraftServer.getServer().scheduleOnMain(() -> modifyBlocks(chunkPacket, chunkPacketInfo));
-+ return;
-+ }
-+
-+ LevelChunk chunk = chunkPacketInfo.getChunk();
-+ int x = chunk.getPos().x;
-+ int z = chunk.getPos().z;
-+ Level level = chunk.getLevel();
-+ ((ChunkPacketInfoAntiXray) chunkPacketInfo).setNearbyChunks(level.getChunkIfLoaded(x - 1, z), level.getChunkIfLoaded(x + 1, z), level.getChunkIfLoaded(x, z - 1), level.getChunkIfLoaded(x, z + 1));
-+ executor.execute((Runnable) chunkPacketInfo);
-+ }
-+
-+ // Actually these fields should be variables inside the obfuscate method but in sync mode or with SingleThreadExecutor in async mode it's okay (even without ThreadLocal)
-+ // If an ExecutorService with multiple threads is used, ThreadLocal must be used here
-+ private final ThreadLocal<int[]> presetBlockStateBits = ThreadLocal.withInitial(() -> new int[getPresetBlockStatesFullLength()]);
-+ private static final ThreadLocal<boolean[]> SOLID = ThreadLocal.withInitial(() -> new boolean[Block.BLOCK_STATE_REGISTRY.size()]);
-+ private static final ThreadLocal<boolean[]> OBFUSCATE = ThreadLocal.withInitial(() -> new boolean[Block.BLOCK_STATE_REGISTRY.size()]);
-+ // These boolean arrays represent chunk layers, true means don't obfuscate, false means obfuscate
-+ private static final ThreadLocal<boolean[][]> CURRENT = ThreadLocal.withInitial(() -> new boolean[16][16]);
-+ private static final ThreadLocal<boolean[][]> NEXT = ThreadLocal.withInitial(() -> new boolean[16][16]);
-+ private static final ThreadLocal<boolean[][]> NEXT_NEXT = ThreadLocal.withInitial(() -> new boolean[16][16]);
-+
-+ public void obfuscate(ChunkPacketInfoAntiXray chunkPacketInfoAntiXray) {
-+ int[] presetBlockStateBits = this.presetBlockStateBits.get();
-+ boolean[] solid = SOLID.get();
-+ boolean[] obfuscate = OBFUSCATE.get();
-+ boolean[][] current = CURRENT.get();
-+ boolean[][] next = NEXT.get();
-+ boolean[][] nextNext = NEXT_NEXT.get();
-+ // bitStorageReader, bitStorageWriter and nearbyChunkSections could also be reused (with ThreadLocal if necessary) but it's not worth it
-+ BitStorageReader bitStorageReader = new BitStorageReader();
-+ BitStorageWriter bitStorageWriter = new BitStorageWriter();
-+ LevelChunkSection[] nearbyChunkSections = new LevelChunkSection[4];
-+ LevelChunk chunk = chunkPacketInfoAntiXray.getChunk();
-+ Level level = chunk.getLevel();
-+ int maxChunkSectionIndex = Math.min((maxBlockHeight >> 4) - chunk.getMinSection(), chunk.getSectionsCount()) - 1;
-+ boolean[] solidTemp = null;
-+ boolean[] obfuscateTemp = null;
-+ bitStorageReader.setBuffer(chunkPacketInfoAntiXray.getBuffer());
-+ bitStorageWriter.setBuffer(chunkPacketInfoAntiXray.getBuffer());
-+ int numberOfBlocks = presetBlockStateBits.length;
-+ // Keep the lambda expressions as simple as possible. They are used very frequently.
-+ LayeredIntSupplier random = numberOfBlocks == 1 ? (() -> 0) : engineMode == EngineMode.OBFUSCATE_LAYER ? new LayeredIntSupplier() {
-+ // engine-mode: 3
-+ private int state;
-+ private int next;
-+
-+ {
-+ while ((state = ThreadLocalRandom.current().nextInt()) == 0) ;
-+ }
-+
-+ @Override
-+ public void nextLayer() {
-+ // https://en.wikipedia.org/wiki/Xorshift
-+ state ^= state << 13;
-+ state ^= state >>> 17;
-+ state ^= state << 5;
-+ // https://www.pcg-random.org/posts/bounded-rands.html
-+ next = (int) ((Integer.toUnsignedLong(state) * numberOfBlocks) >>> 32);
-+ }
-+
-+ @Override
-+ public int getAsInt() {
-+ return next;
-+ }
-+ } : new LayeredIntSupplier() {
-+ // engine-mode: 2
-+ private int state;
-+
-+ {
-+ while ((state = ThreadLocalRandom.current().nextInt()) == 0) ;
-+ }
-+
-+ @Override
-+ public int getAsInt() {
-+ // https://en.wikipedia.org/wiki/Xorshift
-+ state ^= state << 13;
-+ state ^= state >>> 17;
-+ state ^= state << 5;
-+ // https://www.pcg-random.org/posts/bounded-rands.html
-+ return (int) ((Integer.toUnsignedLong(state) * numberOfBlocks) >>> 32);
-+ }
-+ };
-+
-+ for (int chunkSectionIndex = 0; chunkSectionIndex <= maxChunkSectionIndex; chunkSectionIndex++) {
-+ if (chunkPacketInfoAntiXray.isWritten(chunkSectionIndex) && chunkPacketInfoAntiXray.getPresetValues(chunkSectionIndex) != null) {
-+ int[] presetBlockStateBitsTemp;
-+
-+ if (chunkPacketInfoAntiXray.getPalette(chunkSectionIndex) instanceof GlobalPalette) {
-+ if (engineMode == EngineMode.HIDE) {
-+ presetBlockStateBitsTemp = switch (level.getWorld().getEnvironment()) {
-+ case NETHER -> presetBlockStateBitsNetherrackGlobal;
-+ case THE_END -> presetBlockStateBitsEndStoneGlobal;
-+ default -> chunkSectionIndex + chunk.getMinSection() < 0 ? presetBlockStateBitsDeepslateGlobal : presetBlockStateBitsStoneGlobal;
-+ };
-+ } else {
-+ presetBlockStateBitsTemp = presetBlockStateBitsGlobal;
-+ }
-+ } else {
-+ // If it's presetBlockStates, use this.presetBlockStatesFull instead
-+ BlockState[] presetBlockStatesFull = chunkPacketInfoAntiXray.getPresetValues(chunkSectionIndex) == presetBlockStates ? this.presetBlockStatesFull : chunkPacketInfoAntiXray.getPresetValues(chunkSectionIndex);
-+ presetBlockStateBitsTemp = presetBlockStateBits;
-+
-+ for (int i = 0; i < presetBlockStateBitsTemp.length; i++) {
-+ // This is thread safe because we only request IDs that are guaranteed to be in the palette and are visible
-+ // For more details see the comments in the readPalette method
-+ presetBlockStateBitsTemp[i] = chunkPacketInfoAntiXray.getPalette(chunkSectionIndex).idFor(presetBlockStatesFull[i]);
-+ }
-+ }
-+
-+ bitStorageWriter.setIndex(chunkPacketInfoAntiXray.getIndex(chunkSectionIndex));
-+
-+ // Check if the chunk section below was not obfuscated
-+ if (chunkSectionIndex == 0 || !chunkPacketInfoAntiXray.isWritten(chunkSectionIndex - 1) || chunkPacketInfoAntiXray.getPresetValues(chunkSectionIndex - 1) == null) {
-+ // If so, initialize some stuff
-+ bitStorageReader.setBits(chunkPacketInfoAntiXray.getBits(chunkSectionIndex));
-+ bitStorageReader.setIndex(chunkPacketInfoAntiXray.getIndex(chunkSectionIndex));
-+ solidTemp = readPalette(chunkPacketInfoAntiXray.getPalette(chunkSectionIndex), solid, solidGlobal);
-+ obfuscateTemp = readPalette(chunkPacketInfoAntiXray.getPalette(chunkSectionIndex), obfuscate, obfuscateGlobal);
-+ // Read the blocks of the upper layer of the chunk section below if it exists
-+ LevelChunkSection belowChunkSection = null;
-+ boolean skipFirstLayer = chunkSectionIndex == 0 || (belowChunkSection = chunk.getSections()[chunkSectionIndex - 1]) == EMPTY_SECTION;
-+
-+ for (int z = 0; z < 16; z++) {
-+ for (int x = 0; x < 16; x++) {
-+ current[z][x] = true;
-+ next[z][x] = skipFirstLayer || isTransparent(belowChunkSection, x, 15, z);
-+ }
-+ }
-+
-+ // Abuse the obfuscateLayer method to read the blocks of the first layer of the current chunk section
-+ bitStorageWriter.setBits(0);
-+ obfuscateLayer(-1, bitStorageReader, bitStorageWriter, solidTemp, obfuscateTemp, presetBlockStateBitsTemp, current, next, nextNext, emptyNearbyChunkSections, random);
-+ }
-+
-+ bitStorageWriter.setBits(chunkPacketInfoAntiXray.getBits(chunkSectionIndex));
-+ nearbyChunkSections[0] = chunkPacketInfoAntiXray.getNearbyChunks()[0] == null ? EMPTY_SECTION : chunkPacketInfoAntiXray.getNearbyChunks()[0].getSections()[chunkSectionIndex];
-+ nearbyChunkSections[1] = chunkPacketInfoAntiXray.getNearbyChunks()[1] == null ? EMPTY_SECTION : chunkPacketInfoAntiXray.getNearbyChunks()[1].getSections()[chunkSectionIndex];
-+ nearbyChunkSections[2] = chunkPacketInfoAntiXray.getNearbyChunks()[2] == null ? EMPTY_SECTION : chunkPacketInfoAntiXray.getNearbyChunks()[2].getSections()[chunkSectionIndex];
-+ nearbyChunkSections[3] = chunkPacketInfoAntiXray.getNearbyChunks()[3] == null ? EMPTY_SECTION : chunkPacketInfoAntiXray.getNearbyChunks()[3].getSections()[chunkSectionIndex];
-+
-+ // Obfuscate all layers of the current chunk section except the upper one
-+ for (int y = 0; y < 15; y++) {
-+ boolean[][] temp = current;
-+ current = next;
-+ next = nextNext;
-+ nextNext = temp;
-+ random.nextLayer();
-+ obfuscateLayer(y, bitStorageReader, bitStorageWriter, solidTemp, obfuscateTemp, presetBlockStateBitsTemp, current, next, nextNext, nearbyChunkSections, random);
-+ }
-+
-+ // Check if the chunk section above doesn't need obfuscation
-+ if (chunkSectionIndex == maxChunkSectionIndex || !chunkPacketInfoAntiXray.isWritten(chunkSectionIndex + 1) || chunkPacketInfoAntiXray.getPresetValues(chunkSectionIndex + 1) == null) {
-+ // If so, obfuscate the upper layer of the current chunk section by reading blocks of the first layer from the chunk section above if it exists
-+ LevelChunkSection aboveChunkSection;
-+
-+ if (chunkSectionIndex != chunk.getSectionsCount() - 1 && (aboveChunkSection = chunk.getSections()[chunkSectionIndex + 1]) != EMPTY_SECTION) {
-+ boolean[][] temp = current;
-+ current = next;
-+ next = nextNext;
-+ nextNext = temp;
-+
-+ for (int z = 0; z < 16; z++) {
-+ for (int x = 0; x < 16; x++) {
-+ if (isTransparent(aboveChunkSection, x, 0, z)) {
-+ current[z][x] = true;
-+ }
-+ }
-+ }
-+
-+ // There is nothing to read anymore
-+ bitStorageReader.setBits(0);
-+ solid[0] = true;
-+ random.nextLayer();
-+ obfuscateLayer(15, bitStorageReader, bitStorageWriter, solid, obfuscateTemp, presetBlockStateBitsTemp, current, next, nextNext, nearbyChunkSections, random);
-+ }
-+ } else {
-+ // If not, initialize the reader and other stuff for the chunk section above to obfuscate the upper layer of the current chunk section
-+ bitStorageReader.setBits(chunkPacketInfoAntiXray.getBits(chunkSectionIndex + 1));
-+ bitStorageReader.setIndex(chunkPacketInfoAntiXray.getIndex(chunkSectionIndex + 1));
-+ solidTemp = readPalette(chunkPacketInfoAntiXray.getPalette(chunkSectionIndex + 1), solid, solidGlobal);
-+ obfuscateTemp = readPalette(chunkPacketInfoAntiXray.getPalette(chunkSectionIndex + 1), obfuscate, obfuscateGlobal);
-+ boolean[][] temp = current;
-+ current = next;
-+ next = nextNext;
-+ nextNext = temp;
-+ random.nextLayer();
-+ obfuscateLayer(15, bitStorageReader, bitStorageWriter, solidTemp, obfuscateTemp, presetBlockStateBitsTemp, current, next, nextNext, nearbyChunkSections, random);
-+ }
-+
-+ bitStorageWriter.flush();
-+ }
-+ }
-+
-+ chunkPacketInfoAntiXray.getChunkPacket().setReady(true);
-+ }
-+
-+ private void obfuscateLayer(int y, BitStorageReader bitStorageReader, BitStorageWriter bitStorageWriter, boolean[] solid, boolean[] obfuscate, int[] presetBlockStateBits, boolean[][] current, boolean[][] next, boolean[][] nextNext, LevelChunkSection[] nearbyChunkSections, IntSupplier random) {
-+ // First block of first line
-+ int bits = bitStorageReader.read();
-+
-+ if (nextNext[0][0] = !solid[bits]) {
-+ bitStorageWriter.skip();
-+ next[0][1] = true;
-+ next[1][0] = true;
-+ } else {
-+ if (current[0][0] || isTransparent(nearbyChunkSections[2], 0, y, 15) || isTransparent(nearbyChunkSections[0], 15, y, 0)) {
-+ bitStorageWriter.skip();
-+ } else {
-+ bitStorageWriter.write(presetBlockStateBits[random.getAsInt()]);
-+ }
-+ }
-+
-+ if (!obfuscate[bits]) {
-+ next[0][0] = true;
-+ }
-+
-+ // First line
-+ for (int x = 1; x < 15; x++) {
-+ bits = bitStorageReader.read();
-+
-+ if (nextNext[0][x] = !solid[bits]) {
-+ bitStorageWriter.skip();
-+ next[0][x - 1] = true;
-+ next[0][x + 1] = true;
-+ next[1][x] = true;
-+ } else {
-+ if (current[0][x] || isTransparent(nearbyChunkSections[2], x, y, 15)) {
-+ bitStorageWriter.skip();
-+ } else {
-+ bitStorageWriter.write(presetBlockStateBits[random.getAsInt()]);
-+ }
-+ }
-+
-+ if (!obfuscate[bits]) {
-+ next[0][x] = true;
-+ }
-+ }
-+
-+ // Last block of first line
-+ bits = bitStorageReader.read();
-+
-+ if (nextNext[0][15] = !solid[bits]) {
-+ bitStorageWriter.skip();
-+ next[0][14] = true;
-+ next[1][15] = true;
-+ } else {
-+ if (current[0][15] || isTransparent(nearbyChunkSections[2], 15, y, 15) || isTransparent(nearbyChunkSections[1], 0, y, 0)) {
-+ bitStorageWriter.skip();
-+ } else {
-+ bitStorageWriter.write(presetBlockStateBits[random.getAsInt()]);
-+ }
-+ }
-+
-+ if (!obfuscate[bits]) {
-+ next[0][15] = true;
-+ }
-+
-+ // All inner lines
-+ for (int z = 1; z < 15; z++) {
-+ // First block
-+ bits = bitStorageReader.read();
-+
-+ if (nextNext[z][0] = !solid[bits]) {
-+ bitStorageWriter.skip();
-+ next[z][1] = true;
-+ next[z - 1][0] = true;
-+ next[z + 1][0] = true;
-+ } else {
-+ if (current[z][0] || isTransparent(nearbyChunkSections[0], 15, y, z)) {
-+ bitStorageWriter.skip();
-+ } else {
-+ bitStorageWriter.write(presetBlockStateBits[random.getAsInt()]);
-+ }
-+ }
-+
-+ if (!obfuscate[bits]) {
-+ next[z][0] = true;
-+ }
-+
-+ // All inner blocks
-+ for (int x = 1; x < 15; x++) {
-+ bits = bitStorageReader.read();
-+
-+ if (nextNext[z][x] = !solid[bits]) {
-+ bitStorageWriter.skip();
-+ next[z][x - 1] = true;
-+ next[z][x + 1] = true;
-+ next[z - 1][x] = true;
-+ next[z + 1][x] = true;
-+ } else {
-+ if (current[z][x]) {
-+ bitStorageWriter.skip();
-+ } else {
-+ bitStorageWriter.write(presetBlockStateBits[random.getAsInt()]);
-+ }
-+ }
-+
-+ if (!obfuscate[bits]) {
-+ next[z][x] = true;
-+ }
-+ }
-+
-+ // Last block
-+ bits = bitStorageReader.read();
-+
-+ if (nextNext[z][15] = !solid[bits]) {
-+ bitStorageWriter.skip();
-+ next[z][14] = true;
-+ next[z - 1][15] = true;
-+ next[z + 1][15] = true;
-+ } else {
-+ if (current[z][15] || isTransparent(nearbyChunkSections[1], 0, y, z)) {
-+ bitStorageWriter.skip();
-+ } else {
-+ bitStorageWriter.write(presetBlockStateBits[random.getAsInt()]);
-+ }
-+ }
-+
-+ if (!obfuscate[bits]) {
-+ next[z][15] = true;
-+ }
-+ }
-+
-+ // First block of last line
-+ bits = bitStorageReader.read();
-+
-+ if (nextNext[15][0] = !solid[bits]) {
-+ bitStorageWriter.skip();
-+ next[15][1] = true;
-+ next[14][0] = true;
-+ } else {
-+ if (current[15][0] || isTransparent(nearbyChunkSections[3], 0, y, 0) || isTransparent(nearbyChunkSections[0], 15, y, 15)) {
-+ bitStorageWriter.skip();
-+ } else {
-+ bitStorageWriter.write(presetBlockStateBits[random.getAsInt()]);
-+ }
-+ }
-+
-+ if (!obfuscate[bits]) {
-+ next[15][0] = true;
-+ }
-+
-+ // Last line
-+ for (int x = 1; x < 15; x++) {
-+ bits = bitStorageReader.read();
-+
-+ if (nextNext[15][x] = !solid[bits]) {
-+ bitStorageWriter.skip();
-+ next[15][x - 1] = true;
-+ next[15][x + 1] = true;
-+ next[14][x] = true;
-+ } else {
-+ if (current[15][x] || isTransparent(nearbyChunkSections[3], x, y, 0)) {
-+ bitStorageWriter.skip();
-+ } else {
-+ bitStorageWriter.write(presetBlockStateBits[random.getAsInt()]);
-+ }
-+ }
-+
-+ if (!obfuscate[bits]) {
-+ next[15][x] = true;
-+ }
-+ }
-+
-+ // Last block of last line
-+ bits = bitStorageReader.read();
-+
-+ if (nextNext[15][15] = !solid[bits]) {
-+ bitStorageWriter.skip();
-+ next[15][14] = true;
-+ next[14][15] = true;
-+ } else {
-+ if (current[15][15] || isTransparent(nearbyChunkSections[3], 15, y, 0) || isTransparent(nearbyChunkSections[1], 0, y, 15)) {
-+ bitStorageWriter.skip();
-+ } else {
-+ bitStorageWriter.write(presetBlockStateBits[random.getAsInt()]);
-+ }
-+ }
-+
-+ if (!obfuscate[bits]) {
-+ next[15][15] = true;
-+ }
-+ }
-+
-+ private boolean isTransparent(LevelChunkSection chunkSection, int x, int y, int z) {
-+ if (chunkSection == EMPTY_SECTION) {
-+ return true;
-+ }
-+
-+ try {
-+ return !solidGlobal[GLOBAL_BLOCKSTATE_PALETTE.idFor(chunkSection.getBlockState(x, y, z))];
-+ } catch (MissingPaletteEntryException e) {
-+ // Race condition / visibility issue / no happens-before relationship
-+ // We don't care and treat the block as transparent
-+ // Internal implementation details of PalettedContainer, LinearPalette, HashMapPalette, CrudeIncrementalIntIdentityHashBiMap, ... guarantee us that no (other) exceptions will occur
-+ return true;
-+ }
-+ }
-+
-+ private boolean[] readPalette(Palette<BlockState> palette, boolean[] temp, boolean[] global) {
-+ if (palette instanceof GlobalPalette) {
-+ return global;
-+ }
-+
-+ try {
-+ for (int i = 0; i < palette.getSize(); i++) {
-+ temp[i] = global[GLOBAL_BLOCKSTATE_PALETTE.idFor(palette.valueFor(i))];
-+ }
-+ } catch (MissingPaletteEntryException e) {
-+ // Race condition / visibility issue / no happens-before relationship
-+ // We don't care because we at least see the state as it was when the chunk packet was created
-+ // Internal implementation details of PalettedContainer, LinearPalette, HashMapPalette, CrudeIncrementalIntIdentityHashBiMap, ... guarantee us that no (other) exceptions will occur until we have all the data that we need here
-+ // Since all palettes have a fixed initial maximum size and there is no internal restructuring and no values are removed from palettes, we are also guaranteed to see the data
-+ }
-+
-+ return temp;
-+ }
-+
-+ @Override
-+ public void onBlockChange(Level level, BlockPos blockPos, BlockState newBlockState, BlockState oldBlockState, int flags, int maxUpdateDepth) {
-+ if (oldBlockState != null && solidGlobal[GLOBAL_BLOCKSTATE_PALETTE.idFor(oldBlockState)] && !solidGlobal[GLOBAL_BLOCKSTATE_PALETTE.idFor(newBlockState)] && blockPos.getY() <= maxBlockHeightUpdatePosition) {
-+ updateNearbyBlocks(level, blockPos);
-+ }
-+ }
-+
-+ @Override
-+ public void onPlayerLeftClickBlock(ServerPlayerGameMode serverPlayerGameMode, BlockPos blockPos, ServerboundPlayerActionPacket.Action action, Direction direction, int worldHeight, int sequence) {
-+ if (blockPos.getY() <= maxBlockHeightUpdatePosition) {
-+ updateNearbyBlocks(serverPlayerGameMode.level, blockPos);
-+ }
-+ }
-+
-+ private void updateNearbyBlocks(Level level, BlockPos blockPos) {
-+ if (updateRadius >= 2) {
-+ BlockPos temp = blockPos.west();
-+ updateBlock(level, temp);
-+ updateBlock(level, temp.west());
-+ updateBlock(level, temp.below());
-+ updateBlock(level, temp.above());
-+ updateBlock(level, temp.north());
-+ updateBlock(level, temp.south());
-+ updateBlock(level, temp = blockPos.east());
-+ updateBlock(level, temp.east());
-+ updateBlock(level, temp.below());
-+ updateBlock(level, temp.above());
-+ updateBlock(level, temp.north());
-+ updateBlock(level, temp.south());
-+ updateBlock(level, temp = blockPos.below());
-+ updateBlock(level, temp.below());
-+ updateBlock(level, temp.north());
-+ updateBlock(level, temp.south());
-+ updateBlock(level, temp = blockPos.above());
-+ updateBlock(level, temp.above());
-+ updateBlock(level, temp.north());
-+ updateBlock(level, temp.south());
-+ updateBlock(level, temp = blockPos.north());
-+ updateBlock(level, temp.north());
-+ updateBlock(level, temp = blockPos.south());
-+ updateBlock(level, temp.south());
-+ } else if (updateRadius == 1) {
-+ updateBlock(level, blockPos.west());
-+ updateBlock(level, blockPos.east());
-+ updateBlock(level, blockPos.below());
-+ updateBlock(level, blockPos.above());
-+ updateBlock(level, blockPos.north());
-+ updateBlock(level, blockPos.south());
-+ } else {
-+ // Do nothing if updateRadius <= 0 (test mode)
-+ }
-+ }
-+
-+ private void updateBlock(Level level, BlockPos blockPos) {
-+ BlockState blockState = level.getBlockStateIfLoaded(blockPos);
-+
-+ if (blockState != null && obfuscateGlobal[GLOBAL_BLOCKSTATE_PALETTE.idFor(blockState)]) {
-+ ((ServerLevel) level).getChunkSource().blockChanged(blockPos);
-+ }
-+ }
-+
-+ @FunctionalInterface
-+ private interface LayeredIntSupplier extends IntSupplier {
-+ default void nextLayer() {
-+
-+ }
-+ }
-+}
-diff --git a/src/main/java/com/destroystokyo/paper/antixray/ChunkPacketInfo.java b/src/main/java/com/destroystokyo/paper/antixray/ChunkPacketInfo.java
-new file mode 100644
-index 0000000000000000000000000000000000000000..d98a3f5c54c67a673eb7dc456dd039cd78f9c34d
---- /dev/null
-+++ b/src/main/java/com/destroystokyo/paper/antixray/ChunkPacketInfo.java
-@@ -0,0 +1,80 @@
-+package com.destroystokyo.paper.antixray;
-+
-+import net.minecraft.network.protocol.game.ClientboundLevelChunkWithLightPacket;
-+import net.minecraft.world.level.chunk.LevelChunk;
-+import net.minecraft.world.level.chunk.Palette;
-+
-+public class ChunkPacketInfo<T> {
-+
-+ private final ClientboundLevelChunkWithLightPacket chunkPacket;
-+ private final LevelChunk chunk;
-+ private final int[] bits;
-+ private final Object[] palettes;
-+ private final int[] indexes;
-+ private final Object[][] presetValues;
-+ private byte[] buffer;
-+
-+ public ChunkPacketInfo(ClientboundLevelChunkWithLightPacket chunkPacket, LevelChunk chunk) {
-+ this.chunkPacket = chunkPacket;
-+ this.chunk = chunk;
-+ int sections = chunk.getSectionsCount();
-+ bits = new int[sections];
-+ palettes = new Object[sections];
-+ indexes = new int[sections];
-+ presetValues = new Object[sections][];
-+ }
-+
-+ public ClientboundLevelChunkWithLightPacket getChunkPacket() {
-+ return chunkPacket;
-+ }
-+
-+ public LevelChunk getChunk() {
-+ return chunk;
-+ }
-+
-+ public byte[] getBuffer() {
-+ return buffer;
-+ }
-+
-+ public void setBuffer(byte[] buffer) {
-+ this.buffer = buffer;
-+ }
-+
-+ public int getBits(int chunkSectionIndex) {
-+ return bits[chunkSectionIndex];
-+ }
-+
-+ public void setBits(int chunkSectionIndex, int bits) {
-+ this.bits[chunkSectionIndex] = bits;
-+ }
-+
-+ @SuppressWarnings("unchecked")
-+ public Palette<T> getPalette(int chunkSectionIndex) {
-+ return (Palette<T>) palettes[chunkSectionIndex];
-+ }
-+
-+ public void setPalette(int chunkSectionIndex, Palette<T> palette) {
-+ palettes[chunkSectionIndex] = palette;
-+ }
-+
-+ public int getIndex(int chunkSectionIndex) {
-+ return indexes[chunkSectionIndex];
-+ }
-+
-+ public void setIndex(int chunkSectionIndex, int index) {
-+ indexes[chunkSectionIndex] = index;
-+ }
-+
-+ @SuppressWarnings("unchecked")
-+ public T[] getPresetValues(int chunkSectionIndex) {
-+ return (T[]) presetValues[chunkSectionIndex];
-+ }
-+
-+ public void setPresetValues(int chunkSectionIndex, T[] presetValues) {
-+ this.presetValues[chunkSectionIndex] = presetValues;
-+ }
-+
-+ public boolean isWritten(int chunkSectionIndex) {
-+ return bits[chunkSectionIndex] != 0;
-+ }
-+}
-diff --git a/src/main/java/com/destroystokyo/paper/antixray/ChunkPacketInfoAntiXray.java b/src/main/java/com/destroystokyo/paper/antixray/ChunkPacketInfoAntiXray.java
-new file mode 100644
-index 0000000000000000000000000000000000000000..80a2dfb266ae1221680a7b24fee2f7e2a8330b7d
---- /dev/null
-+++ b/src/main/java/com/destroystokyo/paper/antixray/ChunkPacketInfoAntiXray.java
-@@ -0,0 +1,29 @@
-+package com.destroystokyo.paper.antixray;
-+
-+import net.minecraft.network.protocol.game.ClientboundLevelChunkWithLightPacket;
-+import net.minecraft.world.level.block.state.BlockState;
-+import net.minecraft.world.level.chunk.LevelChunk;
-+
-+public final class ChunkPacketInfoAntiXray extends ChunkPacketInfo<BlockState> implements Runnable {
-+
-+ private final ChunkPacketBlockControllerAntiXray chunkPacketBlockControllerAntiXray;
-+ private LevelChunk[] nearbyChunks;
-+
-+ public ChunkPacketInfoAntiXray(ClientboundLevelChunkWithLightPacket chunkPacket, LevelChunk chunk, ChunkPacketBlockControllerAntiXray chunkPacketBlockControllerAntiXray) {
-+ super(chunkPacket, chunk);
-+ this.chunkPacketBlockControllerAntiXray = chunkPacketBlockControllerAntiXray;
-+ }
-+
-+ public LevelChunk[] getNearbyChunks() {
-+ return nearbyChunks;
-+ }
-+
-+ public void setNearbyChunks(LevelChunk... nearbyChunks) {
-+ this.nearbyChunks = nearbyChunks;
-+ }
-+
-+ @Override
-+ public void run() {
-+ chunkPacketBlockControllerAntiXray.obfuscate(this);
-+ }
-+}
-diff --git a/src/main/java/net/minecraft/network/protocol/game/ClientboundChunksBiomesPacket.java b/src/main/java/net/minecraft/network/protocol/game/ClientboundChunksBiomesPacket.java
-index 921bd11bd8d266c2dd5c78a44f4714a48417eb3e..99e6d6e97e2869d574ea04b7eccbd6c0f827b19b 100644
---- a/src/main/java/net/minecraft/network/protocol/game/ClientboundChunksBiomesPacket.java
-+++ b/src/main/java/net/minecraft/network/protocol/game/ClientboundChunksBiomesPacket.java
-@@ -61,8 +61,10 @@ public record ClientboundChunksBiomesPacket(List<ClientboundChunksBiomesPacket.C
- }
-
- public static void extractChunkData(FriendlyByteBuf buf, LevelChunk chunk) {
-+ int chunkSectionIndex = 0; // Paper - Anti-Xray
- for (LevelChunkSection levelChunkSection : chunk.getSections()) {
-- levelChunkSection.getBiomes().write(buf);
-+ levelChunkSection.getBiomes().write(buf, null, chunkSectionIndex); // Paper - Anti-Xray
-+ chunkSectionIndex++; // Paper - Anti-Xray
- }
- }
-
-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 03b6aa2e3bd871ae59e761866b53a10f72b3cdac..454bec4f8843e7e4e42cd8a8132b557ead292dcc 100644
---- a/src/main/java/net/minecraft/network/protocol/game/ClientboundLevelChunkPacketData.java
-+++ b/src/main/java/net/minecraft/network/protocol/game/ClientboundLevelChunkPacketData.java
-@@ -25,7 +25,10 @@ public class ClientboundLevelChunkPacketData {
- private final byte[] buffer;
- private final List<ClientboundLevelChunkPacketData.BlockEntityInfo> blockEntitiesData;
-
-- public ClientboundLevelChunkPacketData(LevelChunk chunk) {
-+ // Paper start - Anti-Xray - Add chunk packet info
-+ @Deprecated @io.papermc.paper.annotation.DoNotUse public ClientboundLevelChunkPacketData(LevelChunk chunk) { this(chunk, null); }
-+ public ClientboundLevelChunkPacketData(LevelChunk chunk, com.destroystokyo.paper.antixray.ChunkPacketInfo<net.minecraft.world.level.block.state.BlockState> chunkPacketInfo) {
-+ // Paper end
- this.heightmaps = new CompoundTag();
-
- for (Entry<Heightmap.Types, Heightmap> entry : chunk.getHeightmaps()) {
-@@ -35,7 +38,14 @@ public class ClientboundLevelChunkPacketData {
- }
-
- this.buffer = new byte[calculateChunkSize(chunk)];
-- extractChunkData(new FriendlyByteBuf(this.getWriteBuffer()), chunk);
-+
-+ // Paper start - Anti-Xray - Add chunk packet info
-+ if (chunkPacketInfo != null) {
-+ chunkPacketInfo.setBuffer(this.buffer);
-+ }
-+
-+ extractChunkData(new FriendlyByteBuf(this.getWriteBuffer()), chunk, chunkPacketInfo);
-+ // Paper end
- this.blockEntitiesData = Lists.newArrayList();
-
- for (Entry<BlockPos, BlockEntity> entry2 : chunk.getBlockEntities().entrySet()) {
-@@ -82,9 +92,15 @@ public class ClientboundLevelChunkPacketData {
- return byteBuf;
- }
-
-- public static void extractChunkData(FriendlyByteBuf buf, LevelChunk chunk) {
-+ // Paper start - Anti-Xray - Add chunk packet info
-+ @Deprecated @io.papermc.paper.annotation.DoNotUse public static void extractChunkData(FriendlyByteBuf buf, LevelChunk chunk) { ClientboundLevelChunkPacketData.extractChunkData(buf, chunk, null); }
-+ public static void extractChunkData(FriendlyByteBuf buf, LevelChunk chunk, com.destroystokyo.paper.antixray.ChunkPacketInfo<net.minecraft.world.level.block.state.BlockState> chunkPacketInfo) {
-+ int chunkSectionIndex = 0;
-+
- for (LevelChunkSection levelChunkSection : chunk.getSections()) {
-- levelChunkSection.write(buf);
-+ levelChunkSection.write(buf, chunkPacketInfo, chunkSectionIndex);
-+ chunkSectionIndex++;
-+ // Paper end
- }
- }
-
-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 26e46d751c8f8162c2bafe2fc109fc91dc4b7c0f..6412dff5ed0505f62dd5b71ab9606257858a7317 100644
---- a/src/main/java/net/minecraft/network/protocol/game/ClientboundLevelChunkWithLightPacket.java
-+++ b/src/main/java/net/minecraft/network/protocol/game/ClientboundLevelChunkWithLightPacket.java
-@@ -13,13 +13,30 @@ public class ClientboundLevelChunkWithLightPacket implements Packet<ClientGamePa
- private final int z;
- private final ClientboundLevelChunkPacketData chunkData;
- private final ClientboundLightUpdatePacketData lightData;
-+ // Paper start - Async-Anti-Xray - Ready flag for the connection
-+ private volatile boolean ready;
-
-- public ClientboundLevelChunkWithLightPacket(LevelChunk chunk, LevelLightEngine lightProvider, @Nullable BitSet skyBits, @Nullable BitSet blockBits) {
-+ @Override
-+ public boolean isReady() {
-+ return this.ready;
-+ }
-+
-+ public void setReady(boolean ready) {
-+ this.ready = ready;
-+ }
-+ // Paper end
-+
-+ // Paper start - Anti-Xray - Add chunk packet info
-+ @Deprecated @io.papermc.paper.annotation.DoNotUse public ClientboundLevelChunkWithLightPacket(LevelChunk chunk, LevelLightEngine lightProvider, @Nullable BitSet skyBits, @Nullable BitSet blockBits) { this(chunk, lightProvider, skyBits, blockBits, true); }
-+ public ClientboundLevelChunkWithLightPacket(LevelChunk chunk, LevelLightEngine lightProvider, @Nullable BitSet skyBits, @Nullable BitSet blockBits, boolean modifyBlocks) {
- ChunkPos chunkPos = chunk.getPos();
- this.x = chunkPos.x;
- this.z = chunkPos.z;
-- this.chunkData = new ClientboundLevelChunkPacketData(chunk);
-+ com.destroystokyo.paper.antixray.ChunkPacketInfo<net.minecraft.world.level.block.state.BlockState> chunkPacketInfo = modifyBlocks ? chunk.getLevel().chunkPacketBlockController.getChunkPacketInfo(this, chunk) : null;
-+ this.chunkData = new ClientboundLevelChunkPacketData(chunk, chunkPacketInfo);
-+ // Paper end
- this.lightData = new ClientboundLightUpdatePacketData(chunkPos, lightProvider, skyBits, blockBits);
-+ chunk.getLevel().chunkPacketBlockController.modifyBlocks(this, chunkPacketInfo); // Paper - Anti-Xray - Modify blocks
- }
-
- public ClientboundLevelChunkWithLightPacket(FriendlyByteBuf buf) {
-diff --git a/src/main/java/net/minecraft/server/level/ServerLevel.java b/src/main/java/net/minecraft/server/level/ServerLevel.java
-index abb4c32e8b35de332fa517523e8c598ea3275def..6f497b95fd870a32c56590c00b2b39f88c51ecb9 100644
---- a/src/main/java/net/minecraft/server/level/ServerLevel.java
-+++ b/src/main/java/net/minecraft/server/level/ServerLevel.java
-@@ -570,7 +570,7 @@ public class ServerLevel extends Level implements WorldGenLevel {
- // Holder holder = worlddimension.type(); // CraftBukkit - decompile error
-
- // Objects.requireNonNull(minecraftserver); // CraftBukkit - decompile error
-- super(iworlddataserver, resourcekey, minecraftserver.registryAccess(), worlddimension.type(), minecraftserver::getProfiler, false, flag, i, minecraftserver.getMaxChainedNeighborUpdates(), gen, biomeProvider, env, spigotConfig -> minecraftserver.paperConfigurations.createWorldConfig(io.papermc.paper.configuration.PaperConfigurations.createWorldContextMap(convertable_conversionsession.levelDirectory.path(), iworlddataserver.getLevelName(), resourcekey.location(), spigotConfig, minecraftserver.registryAccess()))); // Paper - create paper world configs
-+ super(iworlddataserver, resourcekey, minecraftserver.registryAccess(), worlddimension.type(), minecraftserver::getProfiler, false, flag, i, minecraftserver.getMaxChainedNeighborUpdates(), gen, biomeProvider, env, spigotConfig -> minecraftserver.paperConfigurations.createWorldConfig(io.papermc.paper.configuration.PaperConfigurations.createWorldContextMap(convertable_conversionsession.levelDirectory.path(), iworlddataserver.getLevelName(), resourcekey.location(), spigotConfig, minecraftserver.registryAccess())), executor); // Paper - create paper world configs; Async-Anti-Xray: Pass executor
- this.pvpMode = minecraftserver.isPvpAllowed();
- this.convertable = convertable_conversionsession;
- this.uuid = WorldUUID.getUUID(convertable_conversionsession.levelDirectory.path().toFile());
-diff --git a/src/main/java/net/minecraft/server/level/ServerPlayerGameMode.java b/src/main/java/net/minecraft/server/level/ServerPlayerGameMode.java
-index 5063eb6d4a24600262c32d2c9eb5fb5bf8fa354e..692a01b52a71e26887ee42cbd5fd64b0a81bfc99 100644
---- a/src/main/java/net/minecraft/server/level/ServerPlayerGameMode.java
-+++ b/src/main/java/net/minecraft/server/level/ServerPlayerGameMode.java
-@@ -49,7 +49,7 @@ import org.bukkit.event.player.PlayerInteractEvent;
- public class ServerPlayerGameMode {
-
- private static final Logger LOGGER = LogUtils.getLogger();
-- protected ServerLevel level;
-+ public ServerLevel level; // Paper - Anti-Xray - protected -> public
- protected final ServerPlayer player;
- private GameType gameModeForPlayer;
- @Nullable
-@@ -326,6 +326,8 @@ public class ServerPlayerGameMode {
- }
-
- }
-+
-+ this.level.chunkPacketBlockController.onPlayerLeftClickBlock(this, pos, action, direction, worldHeight, sequence); // Paper - Anti-Xray
- }
-
- public void destroyAndAck(BlockPos pos, int sequence, String reason) {
-diff --git a/src/main/java/net/minecraft/server/network/PlayerChunkSender.java b/src/main/java/net/minecraft/server/network/PlayerChunkSender.java
-index 9baae5af750b46ededbd660d15934da71befde72..8a360bf585041af8cd74dbb38d3c888c998289c5 100644
---- a/src/main/java/net/minecraft/server/network/PlayerChunkSender.java
-+++ b/src/main/java/net/minecraft/server/network/PlayerChunkSender.java
-@@ -87,7 +87,10 @@ public class PlayerChunkSender {
-
- public static void sendChunk(ServerGamePacketListenerImpl handler, ServerLevel world, LevelChunk chunk) { // Paper - rewrite chunk loader - public
- handler.player.serverLevel().chunkSource.chunkMap.getVisibleChunkIfPresent(chunk.getPos().toLong()).addPlayer(handler.player);
-- handler.send(new ClientboundLevelChunkWithLightPacket(chunk, world.getLightEngine(), null, null));
-+ // Paper start - Anti-Xray
-+ final boolean shouldModify = world.chunkPacketBlockController.shouldModify(handler.player, chunk);
-+ handler.send(new ClientboundLevelChunkWithLightPacket(chunk, world.getLightEngine(), null, null, shouldModify));
-+ // Paper end - Anti-Xray
- // Paper start - PlayerChunkLoadEvent
- if (io.papermc.paper.event.packet.PlayerChunkLoadEvent.getHandlerList().getRegisteredListeners().length > 0) {
- new io.papermc.paper.event.packet.PlayerChunkLoadEvent(new org.bukkit.craftbukkit.CraftChunk(chunk), handler.getPlayer().getBukkitEntity()).callEvent();
-diff --git a/src/main/java/net/minecraft/server/players/PlayerList.java b/src/main/java/net/minecraft/server/players/PlayerList.java
-index 8a5f245fc98514b66216dde234650bfc0adc24b4..461c27292af06a5150de8ec263d0c8527e8c5278 100644
---- a/src/main/java/net/minecraft/server/players/PlayerList.java
-+++ b/src/main/java/net/minecraft/server/players/PlayerList.java
-@@ -419,7 +419,7 @@ public abstract class PlayerList {
- .getHolderOrThrow(net.minecraft.world.level.biome.Biomes.PLAINS);
- player.connection.send(new net.minecraft.network.protocol.game.ClientboundLevelChunkWithLightPacket(
- new net.minecraft.world.level.chunk.EmptyLevelChunk(worldserver1, player.chunkPosition(), plains),
-- worldserver1.getLightEngine(), (java.util.BitSet)null, (java.util.BitSet) null)
-+ worldserver1.getLightEngine(), (java.util.BitSet)null, (java.util.BitSet) null, true)
- );
- }
- // Paper end - Send empty chunk
-diff --git a/src/main/java/net/minecraft/world/level/Level.java b/src/main/java/net/minecraft/world/level/Level.java
-index 4bedd5801cc8ce14387f02dfb361a00ab2960855..a2f8d2858c4a43ff642761fd86512a5f0666a0d2 100644
---- a/src/main/java/net/minecraft/world/level/Level.java
-+++ b/src/main/java/net/minecraft/world/level/Level.java
-@@ -172,6 +172,7 @@ public abstract class Level implements LevelAccessor, AutoCloseable {
- }
- // Paper end - add paper world config
-
-+ public final com.destroystokyo.paper.antixray.ChunkPacketBlockController chunkPacketBlockController; // Paper - Anti-Xray
- public final co.aikar.timings.WorldTimingsHandler timings; // Paper
- public static BlockPos lastPhysicsProblem; // Spigot
- private org.spigotmc.TickLimiter entityLimiter;
-@@ -197,7 +198,7 @@ public abstract class Level implements LevelAccessor, AutoCloseable {
-
- public abstract ResourceKey<LevelStem> getTypeKey();
-
-- protected Level(WritableLevelData worlddatamutable, ResourceKey<Level> resourcekey, RegistryAccess iregistrycustom, Holder<DimensionType> holder, Supplier<ProfilerFiller> supplier, boolean flag, boolean flag1, long i, int j, org.bukkit.generator.ChunkGenerator gen, org.bukkit.generator.BiomeProvider biomeProvider, org.bukkit.World.Environment env, java.util.function.Function<org.spigotmc.SpigotWorldConfig, io.papermc.paper.configuration.WorldConfiguration> paperWorldConfigCreator) { // Paper - create paper world config
-+ protected Level(WritableLevelData worlddatamutable, ResourceKey<Level> resourcekey, RegistryAccess iregistrycustom, Holder<DimensionType> holder, Supplier<ProfilerFiller> supplier, boolean flag, boolean flag1, long i, int j, org.bukkit.generator.ChunkGenerator gen, org.bukkit.generator.BiomeProvider biomeProvider, org.bukkit.World.Environment env, java.util.function.Function<org.spigotmc.SpigotWorldConfig, io.papermc.paper.configuration.WorldConfiguration> paperWorldConfigCreator, java.util.concurrent.Executor executor) { // Paper - create paper world config; Async-Anti-Xray: Pass executor
- this.spigotConfig = new org.spigotmc.SpigotWorldConfig(((net.minecraft.world.level.storage.PrimaryLevelData) worlddatamutable).getLevelName()); // Spigot
- this.paperConfig = paperWorldConfigCreator.apply(this.spigotConfig); // Paper - create paper world config
- this.generator = gen;
-@@ -283,6 +284,7 @@ public abstract class Level implements LevelAccessor, AutoCloseable {
- this.keepSpawnInMemory = this.paperConfig().spawn.keepSpawnLoaded; // Paper - Option to keep spawn chunks loaded
- this.entityLimiter = new org.spigotmc.TickLimiter(this.spigotConfig.entityMaxTickTime);
- this.tileLimiter = new org.spigotmc.TickLimiter(this.spigotConfig.tileMaxTickTime);
-+ this.chunkPacketBlockController = this.paperConfig().anticheat.antiXray.enabled ? new com.destroystokyo.paper.antixray.ChunkPacketBlockControllerAntiXray(this, executor) : com.destroystokyo.paper.antixray.ChunkPacketBlockController.NO_OPERATION_INSTANCE; // Paper - Anti-Xray
- }
-
- // Paper start - Cancel hit for vanished players
-@@ -558,6 +560,7 @@ public abstract class Level implements LevelAccessor, AutoCloseable {
- // CraftBukkit end
-
- BlockState iblockdata1 = chunk.setBlockState(pos, state, (flags & 64) != 0, (flags & 1024) == 0); // CraftBukkit custom NO_PLACE flag
-+ this.chunkPacketBlockController.onBlockChange(this, pos, state, iblockdata1, flags, maxUpdateDepth); // Paper - Anti-Xray
-
- if (iblockdata1 == null) {
- // CraftBukkit start - remove blockstate if failed (or the same)
-diff --git a/src/main/java/net/minecraft/world/level/chunk/ChunkAccess.java b/src/main/java/net/minecraft/world/level/chunk/ChunkAccess.java
-index 5e8d2e4245757a0889645ea79ee68afb53f7dde4..f7e5e016a7028a9196e689e950805b0d5b31fe38 100644
---- a/src/main/java/net/minecraft/world/level/chunk/ChunkAccess.java
-+++ b/src/main/java/net/minecraft/world/level/chunk/ChunkAccess.java
-@@ -152,17 +152,17 @@ public abstract class ChunkAccess implements BlockGetter, BiomeManager.NoiseBiom
- }
- }
-
-- ChunkAccess.replaceMissingSections(biomeRegistry, this.sections);
-+ this.replaceMissingSections(biomeRegistry, this.sections); // Paper - Anti-Xray - make it a non-static method
- // CraftBukkit start
- this.biomeRegistry = biomeRegistry;
- }
- public final Registry<Biome> biomeRegistry;
- // CraftBukkit end
-
-- private static void replaceMissingSections(Registry<Biome> biomeRegistry, LevelChunkSection[] sectionArray) {
-+ private void replaceMissingSections(Registry<Biome> biomeRegistry, LevelChunkSection[] sectionArray) { // Paper - Anti-Xray - static -> non-static
- for (int i = 0; i < sectionArray.length; ++i) {
- if (sectionArray[i] == null) {
-- sectionArray[i] = new LevelChunkSection(biomeRegistry);
-+ sectionArray[i] = new LevelChunkSection(biomeRegistry, this.levelHeightAccessor instanceof net.minecraft.world.level.Level ? (net.minecraft.world.level.Level) this.levelHeightAccessor : null, this.chunkPos, this.levelHeightAccessor.getSectionYFromSectionIndex(i)); // Paper start - Anti-Xray - Add parameters
- }
- }
-
-diff --git a/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java b/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java
-index 6a5756bd333d9b221e7770842e5114d295cb7f1d..2eeb0c78f2b717b59542b6b668371558ae2fcc25 100644
---- a/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java
-+++ b/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java
-@@ -91,7 +91,7 @@ public class LevelChunk extends ChunkAccess {
- }
-
- public LevelChunk(Level world, ChunkPos pos, UpgradeData upgradeData, LevelChunkTicks<Block> blockTickScheduler, LevelChunkTicks<Fluid> fluidTickScheduler, long inhabitedTime, @Nullable LevelChunkSection[] sectionArrayInitializer, @Nullable LevelChunk.PostLoadProcessor entityLoader, @Nullable BlendingData blendingData) {
-- super(pos, upgradeData, world, world.registryAccess().registryOrThrow(Registries.BIOME), inhabitedTime, sectionArrayInitializer, blendingData);
-+ super(pos, upgradeData, world, net.minecraft.server.MinecraftServer.getServer().registryAccess().registryOrThrow(Registries.BIOME), inhabitedTime, sectionArrayInitializer, blendingData); // Paper - Anti-Xray - The world isn't ready yet, use server singleton for registry
- this.tickersInLevel = Maps.newHashMap();
- this.level = (ServerLevel) world; // CraftBukkit - type
- this.gameEventListenerRegistrySections = new Int2ObjectOpenHashMap();
-diff --git a/src/main/java/net/minecraft/world/level/chunk/LevelChunkSection.java b/src/main/java/net/minecraft/world/level/chunk/LevelChunkSection.java
-index b606e33f8b64eaba28c008cc353d88aa45549e31..8852263cb6faec1b68326145aa30e5cd36d066e7 100644
---- a/src/main/java/net/minecraft/world/level/chunk/LevelChunkSection.java
-+++ b/src/main/java/net/minecraft/world/level/chunk/LevelChunkSection.java
-@@ -33,9 +33,12 @@ public class LevelChunkSection {
- this.recalcBlockCounts();
- }
-
-- public LevelChunkSection(Registry<Biome> biomeRegistry) {
-- this.states = new PalettedContainer<>(Block.BLOCK_STATE_REGISTRY, Blocks.AIR.defaultBlockState(), PalettedContainer.Strategy.SECTION_STATES);
-- this.biomes = new PalettedContainer<>(biomeRegistry.asHolderIdMap(), biomeRegistry.getHolderOrThrow(Biomes.PLAINS), PalettedContainer.Strategy.SECTION_BIOMES);
-+ // Paper start - Anti-Xray - Add parameters
-+ @Deprecated @io.papermc.paper.annotation.DoNotUse public LevelChunkSection(Registry<Biome> biomeRegistry) { this(biomeRegistry, null, null, 0); }
-+ public LevelChunkSection(Registry<Biome> biomeRegistry, net.minecraft.world.level.Level level, net.minecraft.world.level.ChunkPos chunkPos, int chunkSectionY) {
-+ // Paper end
-+ this.states = new PalettedContainer<>(Block.BLOCK_STATE_REGISTRY, Blocks.AIR.defaultBlockState(), PalettedContainer.Strategy.SECTION_STATES, level == null || level.chunkPacketBlockController == null ? null : level.chunkPacketBlockController.getPresetBlockStates(level, chunkPos, chunkSectionY)); // Paper - Anti-Xray - Add preset block states
-+ this.biomes = new PalettedContainer<>(biomeRegistry.asHolderIdMap(), biomeRegistry.getHolderOrThrow(Biomes.PLAINS), PalettedContainer.Strategy.SECTION_BIOMES, null); // Paper - Anti-Xray - Add preset biomes
- }
-
- public BlockState getBlockState(int x, int y, int z) {
-@@ -172,10 +175,13 @@ public class LevelChunkSection {
- this.biomes = datapaletteblock;
- }
-
-- public void write(FriendlyByteBuf buf) {
-+ // Paper start - Anti-Xray - Add chunk packet info
-+ @Deprecated @io.papermc.paper.annotation.DoNotUse public void write(FriendlyByteBuf buf) { this.write(buf, null, 0); }
-+ public void write(FriendlyByteBuf buf, com.destroystokyo.paper.antixray.ChunkPacketInfo<BlockState> chunkPacketInfo, int chunkSectionIndex) {
- buf.writeShort(this.nonEmptyBlockCount);
-- this.states.write(buf);
-- this.biomes.write(buf);
-+ this.states.write(buf, chunkPacketInfo, chunkSectionIndex);
-+ this.biomes.write(buf, null, chunkSectionIndex);
-+ // Paper end
- }
-
- public int getSerializedSize() {
-diff --git a/src/main/java/net/minecraft/world/level/chunk/PalettedContainer.java b/src/main/java/net/minecraft/world/level/chunk/PalettedContainer.java
-index 2d7c6f00d399c7607e653078d77103a54509d03b..d473637a705c1f11079fff08e334545779a99a84 100644
---- a/src/main/java/net/minecraft/world/level/chunk/PalettedContainer.java
-+++ b/src/main/java/net/minecraft/world/level/chunk/PalettedContainer.java
-@@ -28,6 +28,7 @@ public class PalettedContainer<T> implements PaletteResize<T>, PalettedContainer
- private static final int MIN_PALETTE_BITS = 0;
- private final PaletteResize<T> dummyPaletteResize = (newSize, added) -> 0;
- public final IdMap<T> registry;
-+ private final T @org.jetbrains.annotations.Nullable [] presetValues; // Paper - Anti-Xray - Add preset values
- private volatile PalettedContainer.Data<T> data;
- private final PalettedContainer.Strategy strategy;
- // private final ThreadingDetector threadingDetector = new ThreadingDetector("PalettedContainer"); // Paper - unused
-@@ -40,14 +41,19 @@ public class PalettedContainer<T> implements PaletteResize<T>, PalettedContainer
- // this.threadingDetector.checkAndUnlock(); // Paper - disable this
- }
-
-- public static <T> Codec<PalettedContainer<T>> codecRW(IdMap<T> idList, Codec<T> entryCodec, PalettedContainer.Strategy paletteProvider, T defaultValue) {
-- PalettedContainerRO.Unpacker<T, PalettedContainer<T>> unpacker = PalettedContainer::unpack;
-+ // Paper start - Anti-Xray - Add preset values
-+ @Deprecated @io.papermc.paper.annotation.DoNotUse public static <T> Codec<PalettedContainer<T>> codecRW(IdMap<T> idList, Codec<T> entryCodec, PalettedContainer.Strategy paletteProvider, T defaultValue) { return PalettedContainer.codecRW(idList, entryCodec, paletteProvider, defaultValue, null); }
-+ public static <T> Codec<PalettedContainer<T>> codecRW(IdMap<T> idList, Codec<T> entryCodec, PalettedContainer.Strategy paletteProvider, T defaultValue, T @org.jetbrains.annotations.Nullable [] presetValues) {
-+ PalettedContainerRO.Unpacker<T, PalettedContainer<T>> unpacker = (idListx, paletteProviderx, serialized) -> {
-+ return unpack(idListx, paletteProviderx, serialized, defaultValue, presetValues);
-+ };
-+ // Paper end
- return codec(idList, entryCodec, paletteProvider, defaultValue, unpacker);
- }
-
- public static <T> Codec<PalettedContainerRO<T>> codecRO(IdMap<T> idList, Codec<T> entryCodec, PalettedContainer.Strategy paletteProvider, T defaultValue) {
- PalettedContainerRO.Unpacker<T, PalettedContainerRO<T>> unpacker = (idListx, paletteProviderx, serialized) -> unpack(
-- idListx, paletteProviderx, serialized
-+ idListx, paletteProviderx, serialized, defaultValue, null // Paper - Anti-Xray - Add preset values
- )
- .map(result -> (PalettedContainerRO<T>)result);
- return codec(idList, entryCodec, paletteProvider, defaultValue, unpacker);
-@@ -71,25 +77,58 @@ public class PalettedContainer<T> implements PaletteResize<T>, PalettedContainer
- );
- }
-
-+ // Paper start - Anti-Xray - Add preset values
-+ @Deprecated @io.papermc.paper.annotation.DoNotUse public PalettedContainer(IdMap<T> idList, PalettedContainer.Strategy paletteProvider, PalettedContainer.Configuration<T> dataProvider, BitStorage storage, List<T> paletteEntries) { this(idList, paletteProvider, dataProvider, storage, paletteEntries, null, null); }
- public PalettedContainer(
- IdMap<T> idList,
- PalettedContainer.Strategy paletteProvider,
- PalettedContainer.Configuration<T> dataProvider,
- BitStorage storage,
-- List<T> paletteEntries
-+ List<T> paletteEntries, T defaultValue, T @org.jetbrains.annotations.Nullable [] presetValues
- ) {
-+ this.presetValues = presetValues;
- this.registry = idList;
- this.strategy = paletteProvider;
- this.data = new PalettedContainer.Data<>(dataProvider, storage, dataProvider.factory().create(dataProvider.bits(), idList, this, paletteEntries));
-+
-+ if (presetValues != null && (dataProvider.factory() == PalettedContainer.Strategy.SINGLE_VALUE_PALETTE_FACTORY ? this.data.palette.valueFor(0) != defaultValue : dataProvider.factory() != PalettedContainer.Strategy.GLOBAL_PALETTE_FACTORY)) {
-+ // In 1.18 Mojang unfortunately removed code that already handled possible resize operations on read from disk for us
-+ // We readd this here but in a smarter way than it was before
-+ int maxSize = 1 << dataProvider.bits();
-+
-+ for (T presetValue : presetValues) {
-+ if (this.data.palette.getSize() >= maxSize) {
-+ java.util.Set<T> allValues = new java.util.HashSet<>(paletteEntries);
-+ allValues.addAll(Arrays.asList(presetValues));
-+ int newBits = Mth.ceillog2(allValues.size());
-+
-+ if (newBits > dataProvider.bits()) {
-+ this.onResize(newBits, null);
-+ }
-+
-+ break;
-+ }
-+
-+ this.data.palette.idFor(presetValue);
-+ }
-+ }
-+ // Paper end
- }
-
-- private PalettedContainer(IdMap<T> idList, PalettedContainer.Strategy paletteProvider, PalettedContainer.Data<T> data) {
-+ // Paper start - Anti-Xray - Add preset values
-+ private PalettedContainer(IdMap<T> idList, PalettedContainer.Strategy paletteProvider, PalettedContainer.Data<T> data, T @org.jetbrains.annotations.Nullable [] presetValues) {
-+ this.presetValues = presetValues;
-+ // Paper end
- this.registry = idList;
- this.strategy = paletteProvider;
- this.data = data;
- }
-
-- public PalettedContainer(IdMap<T> idList, T object, PalettedContainer.Strategy paletteProvider) {
-+ // Paper start - Anti-Xray - Add preset values
-+ @Deprecated @io.papermc.paper.annotation.DoNotUse public PalettedContainer(IdMap<T> idList, T object, PalettedContainer.Strategy paletteProvider) { this(idList, object, paletteProvider, null); }
-+ public PalettedContainer(IdMap<T> idList, T object, PalettedContainer.Strategy paletteProvider, T @org.jetbrains.annotations.Nullable [] presetValues) {
-+ this.presetValues = presetValues;
-+ // Paper end
- this.strategy = paletteProvider;
- this.registry = idList;
- this.data = this.createOrReuseData(null, 0);
-@@ -106,11 +145,33 @@ public class PalettedContainer<T> implements PaletteResize<T>, PalettedContainer
- @Override
- public synchronized int onResize(int newBits, T object) { // Paper - synchronize
- PalettedContainer.Data<T> data = this.data;
-+
-+ // Paper start - Anti-Xray - Add preset values
-+ if (this.presetValues != null && object != null && data.configuration().factory() == PalettedContainer.Strategy.SINGLE_VALUE_PALETTE_FACTORY) {
-+ int duplicates = 0;
-+ List<T> presetValues = Arrays.asList(this.presetValues);
-+ duplicates += presetValues.contains(object) ? 1 : 0;
-+ duplicates += presetValues.contains(data.palette.valueFor(0)) ? 1 : 0;
-+ newBits = Mth.ceillog2((1 << this.strategy.calculateBitsForSerialization(this.registry, 1 << newBits)) + presetValues.size() - duplicates);
-+ }
-+
- PalettedContainer.Data<T> data2 = this.createOrReuseData(data, newBits);
- data2.copyFrom(data.palette, data.storage);
- this.data = data2;
-- return data2.palette.idFor(object);
-+ this.addPresetValues();
-+ return object == null ? -1 : data2.palette.idFor(object);
-+ // Paper end
-+ }
-+
-+ // Paper start - Anti-Xray - Add preset values
-+ private void addPresetValues() {
-+ if (this.presetValues != null && this.data.configuration().factory() != PalettedContainer.Strategy.GLOBAL_PALETTE_FACTORY) {
-+ for (T presetValue : this.presetValues) {
-+ this.data.palette.idFor(presetValue);
-+ }
-+ }
- }
-+ // Paper end
-
- public T getAndSet(int x, int y, int z, T value) {
- this.acquire();
-@@ -177,24 +238,33 @@ public class PalettedContainer<T> implements PaletteResize<T>, PalettedContainer
- data.palette.read(buf);
- buf.readLongArray(data.storage.getRaw());
- this.data = data;
-+ this.addPresetValues(); // Paper - Anti-Xray - Add preset values (inefficient, but this isn't used by the server)
- } finally {
- this.release();
- }
- }
-
-+ // Paper start - Anti-Xray; Add chunk packet info
-+ @Override
-+ @Deprecated @io.papermc.paper.annotation.DoNotUse public void write(FriendlyByteBuf buf) { this.write(buf, null, 0); }
- @Override
-- public synchronized void write(FriendlyByteBuf buf) { // Paper - synchronize
-+ public synchronized void write(FriendlyByteBuf buf, @Nullable com.destroystokyo.paper.antixray.ChunkPacketInfo<T> chunkPacketInfo, int chunkSectionIndex) { // Paper - Synchronize
- this.acquire();
-
- try {
-- this.data.write(buf);
-+ this.data.write(buf, chunkPacketInfo, chunkSectionIndex);
-+
-+ if (chunkPacketInfo != null) {
-+ chunkPacketInfo.setPresetValues(chunkSectionIndex, this.presetValues);
-+ }
-+ // Paper end
- } finally {
- this.release();
- }
- }
-
- private static <T> DataResult<PalettedContainer<T>> unpack(
-- IdMap<T> idList, PalettedContainer.Strategy paletteProvider, PalettedContainerRO.PackedData<T> serialized
-+ IdMap<T> idList, PalettedContainer.Strategy paletteProvider, PalettedContainerRO.PackedData<T> serialized, T defaultValue, T @org.jetbrains.annotations.Nullable [] presetValues // Paper - Anti-Xray - Add preset values
- ) {
- List<T> list = serialized.paletteEntries();
- int i = paletteProvider.size();
-@@ -227,7 +297,7 @@ public class PalettedContainer<T> implements PaletteResize<T>, PalettedContainer
- }
- }
-
-- return DataResult.success(new PalettedContainer<>(idList, paletteProvider, configuration, bitStorage, list));
-+ return DataResult.success(new PalettedContainer<>(idList, paletteProvider, configuration, bitStorage, list, defaultValue, presetValues)); // Paper - Anti-Xray - Add preset values
- }
-
- @Override
-@@ -284,12 +354,12 @@ public class PalettedContainer<T> implements PaletteResize<T>, PalettedContainer
- }
-
- public PalettedContainer<T> copy() {
-- return new PalettedContainer<>(this.registry, this.strategy, this.data.copy());
-+ return new PalettedContainer<>(this.registry, this.strategy, this.data.copy(), this.presetValues); // Paper - Anti-Xray - Add preset values
- }
-
- @Override
- public PalettedContainer<T> recreate() {
-- return new PalettedContainer<>(this.registry, this.data.palette.valueFor(0), this.strategy);
-+ return new PalettedContainer<>(this.registry, this.data.palette.valueFor(0), this.strategy, this.presetValues); // Paper - Anti-Xray - Add preset values
- }
-
- @Override
-@@ -328,9 +398,18 @@ public class PalettedContainer<T> implements PaletteResize<T>, PalettedContainer
- return 1 + this.palette.getSerializedSize() + VarInt.getByteSize(this.storage.getRaw().length) + this.storage.getRaw().length * 8;
- }
-
-- public void write(FriendlyByteBuf buf) {
-+ // Paper start - Anti-Xray - Add chunk packet info
-+ public void write(FriendlyByteBuf buf, @Nullable com.destroystokyo.paper.antixray.ChunkPacketInfo<T> chunkPacketInfo, int chunkSectionIndex) {
- buf.writeByte(this.storage.getBits());
- this.palette.write(buf);
-+
-+ if (chunkPacketInfo != null) {
-+ chunkPacketInfo.setBits(chunkSectionIndex, this.configuration.bits());
-+ chunkPacketInfo.setPalette(chunkSectionIndex, this.palette);
-+ chunkPacketInfo.setIndex(chunkSectionIndex, buf.writerIndex() + VarInt.getByteSize(this.storage.getRaw().length));
-+ }
-+ // Paper end
-+
- buf.writeLongArray(this.storage.getRaw());
- }
-
-diff --git a/src/main/java/net/minecraft/world/level/chunk/PalettedContainerRO.java b/src/main/java/net/minecraft/world/level/chunk/PalettedContainerRO.java
-index 9a2bf744abd8916d492e901be889223591bac3fd..1dd415c96d17eff8e7555c33d3c52e57f2559fa5 100644
---- a/src/main/java/net/minecraft/world/level/chunk/PalettedContainerRO.java
-+++ b/src/main/java/net/minecraft/world/level/chunk/PalettedContainerRO.java
-@@ -14,7 +14,10 @@ public interface PalettedContainerRO<T> {
-
- void getAll(Consumer<T> action);
-
-- void write(FriendlyByteBuf buf);
-+ // Paper start - Anti-Xray - Add chunk packet info
-+ @Deprecated @io.papermc.paper.annotation.DoNotUse void write(FriendlyByteBuf buf);
-+ void write(FriendlyByteBuf buf, @javax.annotation.Nullable com.destroystokyo.paper.antixray.ChunkPacketInfo<T> chunkPacketInfo, int chunkSectionIndex);
-+ // Paper end
-
- int getSerializedSize();
-
-diff --git a/src/main/java/net/minecraft/world/level/chunk/storage/ChunkSerializer.java b/src/main/java/net/minecraft/world/level/chunk/storage/ChunkSerializer.java
-index e67ebc8517a1afb0c7fe23f19a781942dded3241..539b36bde9cba3a44184eba36df9aa4c345a5b84 100644
---- a/src/main/java/net/minecraft/world/level/chunk/storage/ChunkSerializer.java
-+++ b/src/main/java/net/minecraft/world/level/chunk/storage/ChunkSerializer.java
-@@ -71,7 +71,7 @@ import org.slf4j.Logger;
-
- public class ChunkSerializer {
-
-- public static final Codec<PalettedContainer<BlockState>> BLOCK_STATE_CODEC = PalettedContainer.codecRW(Block.BLOCK_STATE_REGISTRY, BlockState.CODEC, PalettedContainer.Strategy.SECTION_STATES, Blocks.AIR.defaultBlockState());
-+ public static final Codec<PalettedContainer<BlockState>> BLOCK_STATE_CODEC = PalettedContainer.codecRW(Block.BLOCK_STATE_REGISTRY, BlockState.CODEC, PalettedContainer.Strategy.SECTION_STATES, Blocks.AIR.defaultBlockState(), null); // Paper - Anti-Xray - Add preset block states
- private static final Logger LOGGER = LogUtils.getLogger();
- private static final String TAG_UPGRADE_DATA = "UpgradeData";
- private static final String BLOCK_TICKS_TAG = "block_ticks";
-@@ -172,16 +172,20 @@ public class ChunkSerializer {
- if (k >= 0 && k < achunksection.length) {
- Logger logger;
- PalettedContainer datapaletteblock;
-+ // Paper start - Anti-Xray - Add preset block states
-+ BlockState[] presetBlockStates = world.chunkPacketBlockController.getPresetBlockStates(world, chunkPos, b0);
-
- if (nbttagcompound1.contains("block_states", 10)) {
-- dataresult = ChunkSerializer.BLOCK_STATE_CODEC.parse(NbtOps.INSTANCE, nbttagcompound1.getCompound("block_states")).promotePartial((s) -> {
-+ Codec<PalettedContainer<BlockState>> blockStateCodec = presetBlockStates == null ? ChunkSerializer.BLOCK_STATE_CODEC : PalettedContainer.codecRW(Block.BLOCK_STATE_REGISTRY, BlockState.CODEC, PalettedContainer.Strategy.SECTION_STATES, Blocks.AIR.defaultBlockState(), presetBlockStates);
-+ dataresult = blockStateCodec.parse(NbtOps.INSTANCE, nbttagcompound1.getCompound("block_states")).promotePartial((s) -> {
- ChunkSerializer.logErrors(chunkPos, b0, s);
- });
- logger = ChunkSerializer.LOGGER;
- Objects.requireNonNull(logger);
- datapaletteblock = (PalettedContainer) ((DataResult<PalettedContainer<BlockState>>) dataresult).getOrThrow(false, logger::error); // CraftBukkit - decompile error
- } else {
-- datapaletteblock = new PalettedContainer<>(Block.BLOCK_STATE_REGISTRY, Blocks.AIR.defaultBlockState(), PalettedContainer.Strategy.SECTION_STATES);
-+ datapaletteblock = new PalettedContainer<>(Block.BLOCK_STATE_REGISTRY, Blocks.AIR.defaultBlockState(), PalettedContainer.Strategy.SECTION_STATES, presetBlockStates);
-+ // Paper end
- }
-
- PalettedContainer object; // CraftBukkit - read/write
-@@ -194,7 +198,7 @@ public class ChunkSerializer {
- Objects.requireNonNull(logger);
- object = ((DataResult<PalettedContainer<Holder<Biome>>>) dataresult).getOrThrow(false, logger::error); // CraftBukkit - decompile error
- } else {
-- object = new PalettedContainer<>(iregistry.asHolderIdMap(), iregistry.getHolderOrThrow(Biomes.PLAINS), PalettedContainer.Strategy.SECTION_BIOMES);
-+ object = new PalettedContainer<>(iregistry.asHolderIdMap(), iregistry.getHolderOrThrow(Biomes.PLAINS), PalettedContainer.Strategy.SECTION_BIOMES, null); // Paper - Anti-Xray - Add preset biomes
- }
-
- LevelChunkSection chunksection = new LevelChunkSection(datapaletteblock, (PalettedContainer) object); // CraftBukkit - read/write
-@@ -429,7 +433,7 @@ public class ChunkSerializer {
-
- // CraftBukkit start - read/write
- private static Codec<PalettedContainer<Holder<Biome>>> makeBiomeCodecRW(Registry<Biome> iregistry) {
-- return PalettedContainer.codecRW(iregistry.asHolderIdMap(), iregistry.holderByNameCodec(), PalettedContainer.Strategy.SECTION_BIOMES, iregistry.getHolderOrThrow(Biomes.PLAINS));
-+ return PalettedContainer.codecRW(iregistry.asHolderIdMap(), iregistry.holderByNameCodec(), PalettedContainer.Strategy.SECTION_BIOMES, iregistry.getHolderOrThrow(Biomes.PLAINS), null); // Paper - Anti-Xray - Add preset biomes
- }
- // CraftBukkit end
-
-diff --git a/src/main/java/org/bukkit/craftbukkit/CraftChunk.java b/src/main/java/org/bukkit/craftbukkit/CraftChunk.java
-index e47b00912fc76e9639f9c51d96e6d39da3c963e3..105f925fb1a78879d2eb618f0c672c8b9a759dd9 100644
---- a/src/main/java/org/bukkit/craftbukkit/CraftChunk.java
-+++ b/src/main/java/org/bukkit/craftbukkit/CraftChunk.java
-@@ -55,7 +55,7 @@ public class CraftChunk implements Chunk {
- private final ServerLevel worldServer;
- private final int x;
- private final int z;
-- private static final PalettedContainer<net.minecraft.world.level.block.state.BlockState> emptyBlockIDs = new PalettedContainer<>(net.minecraft.world.level.block.Block.BLOCK_STATE_REGISTRY, Blocks.AIR.defaultBlockState(), PalettedContainer.Strategy.SECTION_STATES);
-+ private static final PalettedContainer<net.minecraft.world.level.block.state.BlockState> emptyBlockIDs = new PalettedContainer<>(net.minecraft.world.level.block.Block.BLOCK_STATE_REGISTRY, Blocks.AIR.defaultBlockState(), PalettedContainer.Strategy.SECTION_STATES, null); // Paper - Anti-Xray - Add preset block states
- private static final byte[] FULL_LIGHT = new byte[2048];
- private static final byte[] EMPTY_LIGHT = new byte[2048];
-
-diff --git a/src/main/java/org/bukkit/craftbukkit/CraftServer.java b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
-index a76f966d72b749f1706363d33caa351b54e9fa14..c9137151f0d2978adb432c40da68689465d2325d 100644
---- a/src/main/java/org/bukkit/craftbukkit/CraftServer.java
-+++ b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
-@@ -2622,7 +2622,7 @@ public final class CraftServer implements Server {
- public ChunkGenerator.ChunkData createChunkData(World world) {
- Preconditions.checkArgument(world != null, "World cannot be null");
- ServerLevel handle = ((CraftWorld) world).getHandle();
-- return new OldCraftChunkData(world.getMinHeight(), world.getMaxHeight(), handle.registryAccess().registryOrThrow(Registries.BIOME));
-+ return new OldCraftChunkData(world.getMinHeight(), world.getMaxHeight(), handle.registryAccess().registryOrThrow(Registries.BIOME), world); // Paper - Anti-Xray - Add parameters
- }
-
- // Paper start - Allow delegation to vanilla chunk gen
-diff --git a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java
-index 18a2f8c956137b8b60b07e02df4b3a2350fc6e46..01797d9791f19dfda4b168218eadeaae97f11eab 100644
---- a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java
-+++ b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java
-@@ -531,11 +531,16 @@ public class CraftWorld extends CraftRegionAccessor implements World {
- List<ServerPlayer> playersInRange = playerChunk.playerProvider.getPlayers(playerChunk.getPos(), false);
- if (playersInRange.isEmpty()) return true; // Paper - rewrite player chunk loader
-
-- ClientboundLevelChunkWithLightPacket refreshPacket = new ClientboundLevelChunkWithLightPacket(chunk, this.world.getLightEngine(), null, null);
-+ // Paper start - Anti-Xray - Bypass
-+ Map<Object, ClientboundLevelChunkWithLightPacket> refreshPackets = new HashMap<>();
- for (ServerPlayer player : playersInRange) {
- if (player.connection == null) continue;
-
-- player.connection.send(refreshPacket);
-+ Boolean shouldModify = chunk.getLevel().chunkPacketBlockController.shouldModify(player, chunk);
-+ player.connection.send(refreshPackets.computeIfAbsent(shouldModify, s -> { // Use connection to prevent creating firing event
-+ return new ClientboundLevelChunkWithLightPacket(chunk, this.world.getLightEngine(), null, null, (Boolean) s);
-+ }));
-+ // Paper end
- }
- // Paper - rewrite player chunk loader
-
-diff --git a/src/main/java/org/bukkit/craftbukkit/generator/OldCraftChunkData.java b/src/main/java/org/bukkit/craftbukkit/generator/OldCraftChunkData.java
-index 9b640705f2c810160aa7fea5006429ec41d0c858..44a010590e830fd238cf6fdda443e28b72022e66 100644
---- a/src/main/java/org/bukkit/craftbukkit/generator/OldCraftChunkData.java
-+++ b/src/main/java/org/bukkit/craftbukkit/generator/OldCraftChunkData.java
-@@ -27,8 +27,13 @@ public final class OldCraftChunkData implements ChunkGenerator.ChunkData {
- private final Registry<net.minecraft.world.level.biome.Biome> biomes;
- private Set<BlockPos> tiles;
- private final Set<BlockPos> lights = new HashSet<>();
-+ // Paper start - Anti-Xray - Add parameters
-+ private final org.bukkit.World world;
-
-- public OldCraftChunkData(int minHeight, int maxHeight, Registry<net.minecraft.world.level.biome.Biome> biomes) {
-+ @Deprecated @io.papermc.paper.annotation.DoNotUse public OldCraftChunkData(int minHeight, int maxHeight, Registry<net.minecraft.world.level.biome.Biome> biomes) { this(minHeight, maxHeight, biomes, null); }
-+ public OldCraftChunkData(int minHeight, int maxHeight, Registry<net.minecraft.world.level.biome.Biome> biomes, org.bukkit.World world) {
-+ this.world = world;
-+ // Paper end
- this.minHeight = minHeight;
- this.maxHeight = maxHeight;
- this.biomes = biomes;
-@@ -176,7 +181,7 @@ public final class OldCraftChunkData implements ChunkGenerator.ChunkData {
- int offset = (y - this.minHeight) >> 4;
- LevelChunkSection section = this.sections[offset];
- if (create && section == null) {
-- this.sections[offset] = section = new LevelChunkSection(this.biomes);
-+ this.sections[offset] = section = new LevelChunkSection(this.biomes, this.world instanceof org.bukkit.craftbukkit.CraftWorld ? ((org.bukkit.craftbukkit.CraftWorld) this.world).getHandle() : null, null, offset + (this.minHeight >> 4)); // Paper - Anti-Xray - Add parameters
- }
- return section;
- }
diff --git a/patches/unapplied/server/1003-Improve-cancelling-PreCreatureSpawnEvent-with-per-pl.patch b/patches/unapplied/server/1003-Improve-cancelling-PreCreatureSpawnEvent-with-per-pl.patch
deleted file mode 100644
index 05c9814be3..0000000000
--- a/patches/unapplied/server/1003-Improve-cancelling-PreCreatureSpawnEvent-with-per-pl.patch
+++ /dev/null
@@ -1,89 +0,0 @@
-From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
-From: kickash32 <[email protected]>
-Date: Mon, 5 Apr 2021 01:42:35 -0400
-Subject: [PATCH] Improve cancelling PreCreatureSpawnEvent with per player mob
- spawns
-
-
-diff --git a/src/main/java/net/minecraft/server/level/ChunkMap.java b/src/main/java/net/minecraft/server/level/ChunkMap.java
-index ccf0f7c7feaf47f451cec30ba02bea39ba192b3c..ac1a4ff5f83e53fa2983ff6e834775e51fba715e 100644
---- a/src/main/java/net/minecraft/server/level/ChunkMap.java
-+++ b/src/main/java/net/minecraft/server/level/ChunkMap.java
-@@ -306,8 +306,25 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
- }
- }
-
-+ // Paper start - per player mob count backoff
-+ public void updateFailurePlayerMobTypeMap(int chunkX, int chunkZ, net.minecraft.world.entity.MobCategory mobCategory) {
-+ if (!this.level.paperConfig().entities.spawning.perPlayerMobSpawns) {
-+ return;
-+ }
-+ int idx = mobCategory.ordinal();
-+ final com.destroystokyo.paper.util.maplist.ReferenceList<ServerPlayer> inRange =
-+ this.getNearbyPlayers().getPlayersByChunk(chunkX, chunkZ, io.papermc.paper.util.player.NearbyPlayers.NearbyMapType.TICK_VIEW_DISTANCE);
-+ if (inRange == null) {
-+ return;
-+ }
-+ final Object[] backingSet = inRange.getRawData();
-+ for (int i = 0, len = inRange.size(); i < len; i++) {
-+ ++((ServerPlayer)backingSet[i]).mobBackoffCounts[idx];
-+ }
-+ }
-+ // Paper end - per player mob count backoff
- public int getMobCountNear(final ServerPlayer player, final net.minecraft.world.entity.MobCategory mobCategory) {
-- return player.mobCounts[mobCategory.ordinal()];
-+ return player.mobCounts[mobCategory.ordinal()] + player.mobBackoffCounts[mobCategory.ordinal()]; // Paper - per player mob count backoff
- }
- // Paper end - Optional per player mob spawns
-
-diff --git a/src/main/java/net/minecraft/server/level/ServerChunkCache.java b/src/main/java/net/minecraft/server/level/ServerChunkCache.java
-index 059ab637adf1be576fa1fff36a91b6c5f1b5f035..5afbb5b307cc67d86dd916dc8f7521d5d021e056 100644
---- a/src/main/java/net/minecraft/server/level/ServerChunkCache.java
-+++ b/src/main/java/net/minecraft/server/level/ServerChunkCache.java
-@@ -524,7 +524,17 @@ public class ServerChunkCache extends ChunkSource {
- if ((this.spawnFriendlies || this.spawnEnemies) && this.level.paperConfig().entities.spawning.perPlayerMobSpawns) { // don't count mobs when animals and monsters are disabled
- // re-set mob counts
- for (ServerPlayer player : this.level.players) {
-- Arrays.fill(player.mobCounts, 0);
-+ // Paper start - per player mob spawning backoff
-+ for (int ii = 0; ii < ServerPlayer.MOBCATEGORY_TOTAL_ENUMS; ii++) {
-+ player.mobCounts[ii] = 0;
-+
-+ int newBackoff = player.mobBackoffCounts[ii] - 1; // TODO make configurable bleed // TODO use nonlinear algorithm?
-+ if (newBackoff < 0) {
-+ newBackoff = 0;
-+ }
-+ player.mobBackoffCounts[ii] = newBackoff;
-+ }
-+ // Paper end - per player mob spawning backoff
- }
- spawnercreature_d = NaturalSpawner.createState(naturalSpawnChunkCount, this.level.getAllEntities(), this::getFullChunk, null, true);
- } else {
-diff --git a/src/main/java/net/minecraft/server/level/ServerPlayer.java b/src/main/java/net/minecraft/server/level/ServerPlayer.java
-index 7fb2c0f2576142423cd0e50b811ce4f55795e43d..acc1751324f040accc4fc18914ed281e572358eb 100644
---- a/src/main/java/net/minecraft/server/level/ServerPlayer.java
-+++ b/src/main/java/net/minecraft/server/level/ServerPlayer.java
-@@ -260,6 +260,7 @@ public class ServerPlayer extends Player {
- public static final int MOBCATEGORY_TOTAL_ENUMS = net.minecraft.world.entity.MobCategory.values().length;
- public final int[] mobCounts = new int[MOBCATEGORY_TOTAL_ENUMS]; // Paper
- // Paper end - Optional per player mob spawns
-+ public final int[] mobBackoffCounts = new int[MOBCATEGORY_TOTAL_ENUMS]; // Paper - per player mob count backoff
-
- // CraftBukkit start
- public String displayName;
-diff --git a/src/main/java/net/minecraft/world/level/NaturalSpawner.java b/src/main/java/net/minecraft/world/level/NaturalSpawner.java
-index 14f4ceb6c0be34d23b24c1695f966145c3aaee96..da7489986848316fed029b71d1bc4e1248c9c9a8 100644
---- a/src/main/java/net/minecraft/world/level/NaturalSpawner.java
-+++ b/src/main/java/net/minecraft/world/level/NaturalSpawner.java
-@@ -277,6 +277,11 @@ public final class NaturalSpawner {
-
- // Paper start - PreCreatureSpawnEvent
- PreSpawnStatus doSpawning = isValidSpawnPostitionForType(world, group, structuremanager, chunkgenerator, biomesettingsmobs_c, blockposition_mutableblockposition, d2);
-+ // Paper start - per player mob count backoff
-+ if (doSpawning == PreSpawnStatus.ABORT || doSpawning == PreSpawnStatus.CANCELLED) {
-+ world.getChunkSource().chunkMap.updateFailurePlayerMobTypeMap(blockposition_mutableblockposition.getX() >> 4, blockposition_mutableblockposition.getZ() >> 4, group);
-+ }
-+ // Paper end - per player mob count backoff
- if (doSpawning == PreSpawnStatus.ABORT) {
- return j; // Paper - Optional per player mob spawns
- }
diff --git a/patches/unapplied/server/1004-Optimize-Collision-to-not-load-chunks.patch b/patches/unapplied/server/1004-Optimize-Collision-to-not-load-chunks.patch
deleted file mode 100644
index dd6fd94feb..0000000000
--- a/patches/unapplied/server/1004-Optimize-Collision-to-not-load-chunks.patch
+++ /dev/null
@@ -1,109 +0,0 @@
-From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
-From: Aikar <[email protected]>
-Date: Thu, 2 Apr 2020 02:37:57 -0400
-Subject: [PATCH] Optimize Collision to not load chunks
-
-The collision code takes an AABB and generates a cuboid of checks rather
-than a cylinder, so at high velocity this can generate a lot of chunk checks.
-
-Treat an unloaded chunk as a collision for entities, and also for players if
-the "prevent moving into unloaded chunks" setting is enabled.
-
-If that serting is not enabled, collisions will be ignored for players, since
-movement will load only the chunk the player enters anyways and avoids loading
-massive amounts of surrounding chunks due to large AABB lookups.
-
-diff --git a/src/main/java/net/minecraft/server/players/PlayerList.java b/src/main/java/net/minecraft/server/players/PlayerList.java
-index 461c27292af06a5150de8ec263d0c8527e8c5278..37245ff682837e7e8c9647f4afe30f0dd87cb384 100644
---- a/src/main/java/net/minecraft/server/players/PlayerList.java
-+++ b/src/main/java/net/minecraft/server/players/PlayerList.java
-@@ -935,6 +935,7 @@ public abstract class PlayerList {
- entityplayer1.setShiftKeyDown(false);
- entityplayer1.forceSetPositionRotation(location.getX(), location.getY(), location.getZ(), location.getYaw(), location.getPitch());
-
-+ worldserver1.getChunkSource().addRegionTicket(net.minecraft.server.level.TicketType.POST_TELEPORT, new net.minecraft.world.level.ChunkPos(location.getBlockX() >> 4, location.getBlockZ() >> 4), 1, entityplayer.getId()); // Paper
- while (avoidSuffocation && !worldserver1.noCollision((Entity) entityplayer1) && entityplayer1.getY() < (double) worldserver1.getMaxBuildHeight()) {
- // CraftBukkit end
- entityplayer1.setPos(entityplayer1.getX(), entityplayer1.getY() + 1.0D, entityplayer1.getZ());
-diff --git a/src/main/java/net/minecraft/world/entity/Entity.java b/src/main/java/net/minecraft/world/entity/Entity.java
-index a66fe080ee73171090abec48352ad0bd457a2a6f..d4c8f2cb1e3adf45863226ae9ad31968bc3445c9 100644
---- a/src/main/java/net/minecraft/world/entity/Entity.java
-+++ b/src/main/java/net/minecraft/world/entity/Entity.java
-@@ -242,6 +242,7 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource, S
- public org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason spawnReason; // Paper - Entity#getEntitySpawnReason
-
- public com.destroystokyo.paper.loottable.PaperLootableInventoryData lootableData; // Paper
-+ public boolean collisionLoadChunks = false; // Paper
- private CraftEntity bukkitEntity;
-
- public @org.jetbrains.annotations.Nullable net.minecraft.server.level.ChunkMap.TrackedEntity tracker; // Paper
-diff --git a/src/main/java/net/minecraft/world/level/BlockCollisions.java b/src/main/java/net/minecraft/world/level/BlockCollisions.java
-index 1c10835b59aaefa3a65ff64f784620bdc54ddcdc..cd89623a44f02d7db77f0d0f87545cf80841f403 100644
---- a/src/main/java/net/minecraft/world/level/BlockCollisions.java
-+++ b/src/main/java/net/minecraft/world/level/BlockCollisions.java
-@@ -66,18 +66,37 @@ public class BlockCollisions<T> extends AbstractIterator<T> {
- @Override
- protected T computeNext() {
- while (this.cursor.advance()) {
-- int i = this.cursor.nextX();
-- int j = this.cursor.nextY();
-- int k = this.cursor.nextZ();
-+ int i = this.cursor.nextX(); final int x = i; // Paper - OBFHELPER
-+ int j = this.cursor.nextY(); final int y = j; // Paper - OBFHELPER
-+ int k = this.cursor.nextZ(); final int z = k; // Paper - OBFHELPER
- int l = this.cursor.getNextType();
- if (l != 3) {
-- BlockGetter blockGetter = this.getChunk(i, k);
-- if (blockGetter != null) {
-- this.pos.set(i, j, k);
-- BlockState blockState = blockGetter.getBlockState(this.pos);
-- if ((!this.onlySuffocatingBlocks || blockState.isSuffocating(blockGetter, this.pos))
-- && (l != 1 || blockState.hasLargeCollisionShape())
-- && (l != 2 || blockState.is(Blocks.MOVING_PISTON))) {
-+ // Paper start - ensure we don't load chunks
-+ // BlockGetter blockGetter = this.getChunk(i, k);
-+ if (true) {
-+ final @Nullable Entity source = this.context instanceof net.minecraft.world.phys.shapes.EntityCollisionContext entityContext ? entityContext.getEntity() : null;
-+ boolean far = source != null && io.papermc.paper.util.MCUtil.distanceSq(source.getX(), y, source.getZ(), x, y, z) > 14;
-+ this.pos.set(x, y, z);
-+ BlockState blockState;
-+ if (this.collisionGetter instanceof net.minecraft.server.level.WorldGenRegion) {
-+ BlockGetter blockGetter = this.getChunk(x, z);
-+ if (blockGetter == null) {
-+ continue;
-+ }
-+ blockState = blockGetter.getBlockState(this.pos);
-+ } else if ((!far && source instanceof net.minecraft.server.level.ServerPlayer) || (source != null && source.collisionLoadChunks)) {
-+ blockState = this.collisionGetter.getBlockState(this.pos);
-+ } else {
-+ blockState = this.collisionGetter.getBlockStateIfLoaded(this.pos);
-+ }
-+ if (blockState == null) {
-+ if (!(source instanceof net.minecraft.server.level.ServerPlayer) || source.level().paperConfig().chunks.preventMovingIntoUnloadedChunks) {
-+ return this.resultProvider.apply(new BlockPos.MutableBlockPos(x, y, z), Shapes.create(far ? source.getBoundingBox() : new AABB(new BlockPos(x, y, z))));
-+ }
-+ continue;
-+ }
-+ if (/*(!this.onlySuffocatingBlocks || blockState.isSuffocating(blockGetter, this.pos)) &&*/ (l != 1 || blockState.hasLargeCollisionShape()) && (l != 2 || blockState.is(Blocks.MOVING_PISTON))) { // Paper - onlySuffocatingBlocks is only true on the client, so we don't care about it here
-+ // Paper end
- VoxelShape voxelShape = blockState.getCollisionShape(this.collisionGetter, this.pos, this.context);
- if (voxelShape == Shapes.block()) {
- if (this.box.intersects((double)i, (double)j, (double)k, (double)i + 1.0, (double)j + 1.0, (double)k + 1.0)) {
-diff --git a/src/main/java/net/minecraft/world/level/CollisionGetter.java b/src/main/java/net/minecraft/world/level/CollisionGetter.java
-index e57cb7fe53e915d24246e44c7f49971f5b2ab2cf..1ad0c976c6e2d6d31397dff850a9de7c16d16fba 100644
---- a/src/main/java/net/minecraft/world/level/CollisionGetter.java
-+++ b/src/main/java/net/minecraft/world/level/CollisionGetter.java
-@@ -44,11 +44,13 @@ public interface CollisionGetter extends BlockGetter {
- }
-
- default boolean noCollision(@Nullable Entity entity, AABB box) {
-+ try { if (entity != null) entity.collisionLoadChunks = true; // Paper
- for (VoxelShape voxelShape : this.getBlockCollisions(entity, box)) {
- if (!voxelShape.isEmpty()) {
- return false;
- }
- }
-+ } finally { if (entity != null) entity.collisionLoadChunks = false; } // Paper
-
- if (!this.getEntityCollisions(entity, box).isEmpty()) {
- return false;
diff --git a/patches/unapplied/server/1005-Optimize-GoalSelector-Goal.Flag-Set-operations.patch b/patches/unapplied/server/1005-Optimize-GoalSelector-Goal.Flag-Set-operations.patch
deleted file mode 100644
index dcbd7aff42..0000000000
--- a/patches/unapplied/server/1005-Optimize-GoalSelector-Goal.Flag-Set-operations.patch
+++ /dev/null
@@ -1,179 +0,0 @@
-From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
-From: Spottedleaf <[email protected]>
-Date: Mon, 6 Apr 2020 17:53:29 -0700
-Subject: [PATCH] Optimize GoalSelector Goal.Flag Set operations
-
-Optimise the stream.anyMatch statement to move to a bitset
-where we can replace the call with a single bitwise operation.
-
-diff --git a/src/main/java/net/minecraft/world/entity/ai/goal/Goal.java b/src/main/java/net/minecraft/world/entity/ai/goal/Goal.java
-index 16f9a98b8a939e5ca7e2dc04f87134a7ed66736b..dd423302b1baa64ef86ded87a29659342e28c142 100644
---- a/src/main/java/net/minecraft/world/entity/ai/goal/Goal.java
-+++ b/src/main/java/net/minecraft/world/entity/ai/goal/Goal.java
-@@ -4,7 +4,16 @@ import java.util.EnumSet;
- import net.minecraft.util.Mth;
-
- public abstract class Goal {
-- private final EnumSet<Goal.Flag> flags = EnumSet.noneOf(Goal.Flag.class);
-+ private final EnumSet<Goal.Flag> flags = EnumSet.noneOf(Goal.Flag.class); // Paper unused, but dummy to prevent plugins from crashing as hard. Theyll need to support paper in a special case if this is super important, but really doesn't seem like it would be.
-+ private final com.destroystokyo.paper.util.set.OptimizedSmallEnumSet<net.minecraft.world.entity.ai.goal.Goal.Flag> goalTypes = new com.destroystokyo.paper.util.set.OptimizedSmallEnumSet<>(Goal.Flag.class); // Paper - remove streams from pathfindergoalselector
-+
-+ // Paper start - remove streams from pathfindergoalselector; make sure types are not empty
-+ public Goal() {
-+ if (this.goalTypes.size() == 0) {
-+ this.goalTypes.add(Flag.UNKNOWN_BEHAVIOR);
-+ }
-+ }
-+ // Paper end - remove streams from pathfindergoalselector
-
- public abstract boolean canUse();
-
-@@ -30,8 +39,13 @@ public abstract class Goal {
- }
-
- public void setFlags(EnumSet<Goal.Flag> controls) {
-- this.flags.clear();
-- this.flags.addAll(controls);
-+ // Paper start - remove streams from pathfindergoalselector
-+ this.goalTypes.clear();
-+ this.goalTypes.addAll(controls);
-+ if (this.goalTypes.size() == 0) {
-+ this.goalTypes.add(Flag.UNKNOWN_BEHAVIOR);
-+ }
-+ // Paper end - remove streams from pathfindergoalselector
- }
-
- @Override
-@@ -39,8 +53,10 @@ public abstract class Goal {
- return this.getClass().getSimpleName();
- }
-
-- public EnumSet<Goal.Flag> getFlags() {
-- return this.flags;
-+ // Paper start - remove streams from pathfindergoalselector
-+ public com.destroystokyo.paper.util.set.OptimizedSmallEnumSet<Goal.Flag> getFlags() {
-+ return this.goalTypes;
-+ // Paper end - remove streams from pathfindergoalselector
- }
-
- protected int adjustedTickDelay(int ticks) {
-diff --git a/src/main/java/net/minecraft/world/entity/ai/goal/GoalSelector.java b/src/main/java/net/minecraft/world/entity/ai/goal/GoalSelector.java
-index 040d62effc651d14d3557f8ff582cb078b74ae1e..38af5c7280366fd6ec077f3d914ea5f3ee77451a 100644
---- a/src/main/java/net/minecraft/world/entity/ai/goal/GoalSelector.java
-+++ b/src/main/java/net/minecraft/world/entity/ai/goal/GoalSelector.java
-@@ -31,10 +31,12 @@ public class GoalSelector {
- private final Map<Goal.Flag, WrappedGoal> lockedFlags = new EnumMap<>(Goal.Flag.class);
- private final Set<WrappedGoal> availableGoals = Sets.newLinkedHashSet();
- private final Supplier<ProfilerFiller> profiler;
-- private final EnumSet<Goal.Flag> disabledFlags = EnumSet.noneOf(Goal.Flag.class);
-+ private final EnumSet<Goal.Flag> disabledFlags = EnumSet.noneOf(Goal.Flag.class); // Paper unused, but dummy to prevent plugins from crashing as hard. Theyll need to support paper in a special case if this is super important, but really doesn't seem like it would be.
-+ private final com.destroystokyo.paper.util.set.OptimizedSmallEnumSet<net.minecraft.world.entity.ai.goal.Goal.Flag> goalTypes = new com.destroystokyo.paper.util.set.OptimizedSmallEnumSet<>(Goal.Flag.class); // Paper - remove streams from pathfindergoalselector
- private int tickCount;
- private int newGoalRate = 3;
- private int curRate;
-+ private static final Goal.Flag[] GOAL_FLAG_VALUES = Goal.Flag.values(); // Paper - remove streams from pathfindergoalselector
-
- public GoalSelector(Supplier<ProfilerFiller> profiler) {
- this.profiler = profiler;
-@@ -64,22 +66,32 @@ public class GoalSelector {
- }
- // Paper end
- public void removeGoal(Goal goal) {
-- this.availableGoals.stream().filter(wrappedGoal -> wrappedGoal.getGoal() == goal).filter(WrappedGoal::isRunning).forEach(WrappedGoal::stop);
-- this.availableGoals.removeIf(wrappedGoal -> wrappedGoal.getGoal() == goal);
-- }
--
-- private static boolean goalContainsAnyFlags(WrappedGoal goal, EnumSet<Goal.Flag> controls) {
-- for (Goal.Flag flag : goal.getFlags()) {
-- if (controls.contains(flag)) {
-- return true;
-+ // Paper start - remove streams from pathfindergoalselector
-+ for (java.util.Iterator<WrappedGoal> iterator = this.availableGoals.iterator(); iterator.hasNext();) {
-+ WrappedGoal goalWrapped = iterator.next();
-+ if (goalWrapped.getGoal() != goal) {
-+ continue;
- }
-+ if (goalWrapped.isRunning()) {
-+ goalWrapped.stop();
-+ }
-+ iterator.remove();
- }
-+ // Paper end - remove streams from pathfindergoalselector
-+ }
-
-- return false;
-+ private static boolean goalContainsAnyFlags(WrappedGoal goal, com.destroystokyo.paper.util.set.OptimizedSmallEnumSet<Goal.Flag> controls) {
-+ return goal.getFlags().hasCommonElements(controls); // Paper
- }
-
- private static boolean goalCanBeReplacedForAllFlags(WrappedGoal goal, Map<Goal.Flag, WrappedGoal> goalsByControl) {
-- for (Goal.Flag flag : goal.getFlags()) {
-+ // Paper start
-+ long flagIterator = goal.getFlags().getBackingSet();
-+ int wrappedGoalSize = goal.getFlags().size();
-+ for (int i = 0; i < wrappedGoalSize; ++i) {
-+ final Goal.Flag flag = GOAL_FLAG_VALUES[Long.numberOfTrailingZeros(flagIterator)];
-+ flagIterator ^= io.papermc.paper.util.IntegerUtil.getTrailingBit(flagIterator);
-+ // Paper end
- if (!goalsByControl.getOrDefault(flag, NO_GOAL).canBeReplacedBy(goal)) {
- return false;
- }
-@@ -93,7 +105,7 @@ public class GoalSelector {
- profilerFiller.push("goalCleanup");
-
- for (WrappedGoal wrappedGoal : this.availableGoals) {
-- if (wrappedGoal.isRunning() && (goalContainsAnyFlags(wrappedGoal, this.disabledFlags) || !wrappedGoal.canContinueToUse())) {
-+ if (wrappedGoal.isRunning() && (goalContainsAnyFlags(wrappedGoal, this.goalTypes) || !wrappedGoal.canContinueToUse())) { // Paper - Perf: optimize goal types by removing streams
- wrappedGoal.stop();
- }
- }
-@@ -111,11 +123,14 @@ public class GoalSelector {
- profilerFiller.push("goalUpdate");
-
- for (WrappedGoal wrappedGoal2 : this.availableGoals) {
-- if (!wrappedGoal2.isRunning()
-- && !goalContainsAnyFlags(wrappedGoal2, this.disabledFlags)
-- && goalCanBeReplacedForAllFlags(wrappedGoal2, this.lockedFlags)
-- && wrappedGoal2.canUse()) {
-- for (Goal.Flag flag : wrappedGoal2.getFlags()) {
-+ // Paper start
-+ if (!wrappedGoal2.isRunning() && !goalContainsAnyFlags(wrappedGoal2, this.goalTypes) && goalCanBeReplacedForAllFlags(wrappedGoal2, this.lockedFlags) && wrappedGoal2.canUse()) {
-+ long flagIterator = wrappedGoal2.getFlags().getBackingSet();
-+ int wrappedGoalSize = wrappedGoal2.getFlags().size();
-+ for (int i = 0; i < wrappedGoalSize; ++i) {
-+ final Goal.Flag flag = GOAL_FLAG_VALUES[Long.numberOfTrailingZeros(flagIterator)];
-+ flagIterator ^= io.papermc.paper.util.IntegerUtil.getTrailingBit(flagIterator);
-+ // Paper end
- WrappedGoal wrappedGoal3 = this.lockedFlags.getOrDefault(flag, NO_GOAL);
- wrappedGoal3.stop();
- this.lockedFlags.put(flag, wrappedGoal2);
-@@ -155,11 +170,11 @@ public class GoalSelector {
- }
-
- public void disableControlFlag(Goal.Flag control) {
-- this.disabledFlags.add(control);
-+ this.goalTypes.add(control); // Paper - remove streams from pathfindergoalselector
- }
-
- public void enableControlFlag(Goal.Flag control) {
-- this.disabledFlags.remove(control);
-+ this.goalTypes.remove(control); // Paper - remove streams from pathfindergoalselector
- }
-
- public void setControlFlag(Goal.Flag control, boolean enabled) {
-diff --git a/src/main/java/net/minecraft/world/entity/ai/goal/WrappedGoal.java b/src/main/java/net/minecraft/world/entity/ai/goal/WrappedGoal.java
-index b02d3deb550830245c8945ef17d3073ea930fdda..65ccdbaa5230c02d44a5959bca0f6fc30237a6fd 100644
---- a/src/main/java/net/minecraft/world/entity/ai/goal/WrappedGoal.java
-+++ b/src/main/java/net/minecraft/world/entity/ai/goal/WrappedGoal.java
-@@ -69,8 +69,10 @@ public class WrappedGoal extends Goal {
- }
-
- @Override
-- public EnumSet<Goal.Flag> getFlags() {
-+ // Paper start - remove streams from pathfindergoalselector
-+ public com.destroystokyo.paper.util.set.OptimizedSmallEnumSet<Goal.Flag> getFlags() {
- return this.goal.getFlags();
-+ // Paper end - remove streams from pathfindergoalselector
- }
-
- public boolean isRunning() {
diff --git a/patches/unapplied/server/1006-Entity-load-save-limit-per-chunk.patch b/patches/unapplied/server/1006-Entity-load-save-limit-per-chunk.patch
deleted file mode 100644
index 85299c2e1e..0000000000
--- a/patches/unapplied/server/1006-Entity-load-save-limit-per-chunk.patch
+++ /dev/null
@@ -1,58 +0,0 @@
-From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
-From: Jason Penilla <[email protected]>
-Date: Wed, 18 Nov 2020 20:52:25 -0800
-Subject: [PATCH] Entity load/save limit per chunk
-
-Adds a config option to limit the number of entities saved and loaded
-to a chunk. The default values of -1 disable the limit. Although
-defaults are only included for certain entites, this allows setting
-limits for any entity type.
-
-diff --git a/src/main/java/net/minecraft/world/entity/EntityType.java b/src/main/java/net/minecraft/world/entity/EntityType.java
-index 69bdf3f2ee731e59e8d454816a9ca72cb49c0fe0..09e8445a3f8c6b3ebc852a75a9a25b41a51ba659 100644
---- a/src/main/java/net/minecraft/world/entity/EntityType.java
-+++ b/src/main/java/net/minecraft/world/entity/EntityType.java
-@@ -640,9 +640,20 @@ public class EntityType<T extends Entity> implements FeatureElement, EntityTypeT
- final Spliterator<? extends Tag> spliterator = entityNbtList.spliterator();
-
- return StreamSupport.stream(new Spliterator<Entity>() {
-+ final java.util.Map<EntityType<?>, Integer> loadedEntityCounts = new java.util.HashMap<>(); // Paper - Entity load/save limit per chunk
- public boolean tryAdvance(Consumer<? super Entity> consumer) {
- return spliterator.tryAdvance((nbtbase) -> {
- EntityType.loadEntityRecursive((CompoundTag) nbtbase, world, (entity) -> {
-+ // Paper start - Entity load/save limit per chunk
-+ final EntityType<?> entityType = entity.getType();
-+ final int saveLimit = world.paperConfig().chunks.entityPerChunkSaveLimit.getOrDefault(entityType, -1);
-+ if (saveLimit > -1) {
-+ if (this.loadedEntityCounts.getOrDefault(entityType, 0) >= saveLimit) {
-+ return null;
-+ }
-+ this.loadedEntityCounts.merge(entityType, 1, Integer::sum);
-+ }
-+ // Paper end - Entity load/save limit per chunk
- consumer.accept(entity);
- return entity;
- });
-diff --git a/src/main/java/net/minecraft/world/level/chunk/storage/EntityStorage.java b/src/main/java/net/minecraft/world/level/chunk/storage/EntityStorage.java
-index e8f8e1f2128df81705a88cee4b9a7760fb123750..995fbfa225efe40274c20608b9b30b8afa847698 100644
---- a/src/main/java/net/minecraft/world/level/chunk/storage/EntityStorage.java
-+++ b/src/main/java/net/minecraft/world/level/chunk/storage/EntityStorage.java
-@@ -109,7 +109,18 @@ public class EntityStorage implements EntityPersistentStorage<Entity> {
- }
-
- ListTag listTag = new ListTag();
-+ final java.util.Map<net.minecraft.world.entity.EntityType<?>, Integer> savedEntityCounts = new java.util.HashMap<>(); // Paper - Entity load/save limit per chunk
- entities.forEach((entity) -> { // diff here: use entities parameter
-+ // Paper start - Entity load/save limit per chunk
-+ final EntityType<?> entityType = entity.getType();
-+ final int saveLimit = level.paperConfig().chunks.entityPerChunkSaveLimit.getOrDefault(entityType, -1);
-+ if (saveLimit > -1) {
-+ if (savedEntityCounts.getOrDefault(entityType, 0) >= saveLimit) {
-+ return;
-+ }
-+ savedEntityCounts.merge(entityType, 1, Integer::sum);
-+ }
-+ // Paper end - Entity load/save limit per chunk
- CompoundTag compoundTag = new CompoundTag();
- if (entity.save(compoundTag)) {
- listTag.add(compoundTag);