diff --git a/patches/removed/1.18/0765-Optimise-chunk-tick-iteration.patch b/patches/removed/1.18/0765-Optimise-chunk-tick-iteration.patch
deleted file mode 100644
index 7ca2497335..0000000000
--- a/patches/removed/1.18/0765-Optimise-chunk-tick-iteration.patch
+++ /dev/null
@@ -1,94 +0,0 @@
-From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
-From: Spottedleaf <spottedleaf@spottedleaf.dev>
-Date: Thu, 7 May 2020 05:48:54 -0700
-Subject: [PATCH] Optimise chunk tick iteration
-
-Use a dedicated list of entity ticking chunks to reduce the cost
-
-diff --git a/src/main/java/net/minecraft/server/level/ServerChunkCache.java b/src/main/java/net/minecraft/server/level/ServerChunkCache.java
-index b5c8f3f57d09de4caffeb9f3e20e9bf4daba1cdd..d6981bbcf480c5856b51960013d144beba2361b3 100644
---- a/src/main/java/net/minecraft/server/level/ServerChunkCache.java
-+++ b/src/main/java/net/minecraft/server/level/ServerChunkCache.java
-@@ -1007,19 +1007,35 @@ public class ServerChunkCache extends ChunkSource {
- 
-             this.lastSpawnState = spawnercreature_d;
-             this.level.getProfiler().pop();
--            List<ChunkHolder> list = Lists.newArrayList(this.chunkMap.getChunks());
--
--            Collections.shuffle(list);
-+            // Paper - moved down, enabled if per-player = false
-             // Paper - moved natural spawn event up
-             this.level.timings.chunkTicks.startTiming(); // Paper
--            list.forEach((playerchunk) -> {
--                Optional<LevelChunk> optional = ((Either) playerchunk.getTickingChunkFuture().getNow(ChunkHolder.UNLOADED_LEVEL_CHUNK)).left();
--
--                if (optional.isPresent()) {
--                    LevelChunk chunk = (LevelChunk) optional.get();
-+            // Paper start
-+            java.util.Iterator<LevelChunk> iterator;
-+            if (this.level.paperConfig.perPlayerMobSpawns) {
-+                iterator = this.entityTickingChunks.iterator();
-+            } else {
-+                iterator = this.entityTickingChunks.unsafeIterator();
-+                List<LevelChunk> shuffled = new java.util.ArrayList<>(this.entityTickingChunks.size());
-+                while (iterator.hasNext()) {
-+                    shuffled.add(iterator.next());
-+                }
-+                Collections.shuffle(shuffled);
-+                iterator = shuffled.iterator();
-+            }
-+            try { while (iterator.hasNext()) {
-+                LevelChunk chunk = iterator.next();
-+                ChunkHolder playerchunk = chunk.playerChunk;
-+                if (playerchunk != null) {
-+                    this.level.getProfiler().push("broadcast");
-+                    this.level.timings.broadcastChunkUpdates.startTiming(); // Paper - timings
-+                    playerchunk.broadcastChanges(chunk);
-+                    this.level.timings.broadcastChunkUpdates.stopTiming(); // Paper - timings
-+                    this.level.getProfiler().pop();
-+                    // Paper end
-                     ChunkPos chunkcoordintpair = chunk.getPos();
- 
--                    if (this.level.isPositionEntityTicking(chunkcoordintpair) && !this.chunkMap.isOutsideOfRange(playerchunk, chunkcoordintpair, false)) { // Paper - optimise isOutsideOfRange
-+                    if ((true || this.level.isPositionEntityTicking(chunkcoordintpair)) && !this.chunkMap.isOutsideOfRange(playerchunk, chunkcoordintpair, false)) { // Paper - optimise isOutsideOfRange // Paper - we only iterate entity ticking chunks
-                         chunk.setInhabitedTime(chunk.getInhabitedTime() + j);
-                         if (flag1 && (this.spawnEnemies || this.spawnFriendlies) && this.level.getWorldBorder().isWithinBounds(chunk.getPos()) && !this.chunkMap.isOutsideOfRange(playerchunk, chunkcoordintpair, true)) { // Spigot // Paper - optimise isOutsideOfRange
-                             NaturalSpawner.spawnForChunk(this.level, chunk, spawnercreature_d, this.spawnFriendlies, this.spawnEnemies, flag2);
-@@ -1030,7 +1046,13 @@ public class ServerChunkCache extends ChunkSource {
-                         // this.level.timings.doTickTiles.stopTiming(); // Spigot // Paper
-                     }
-                 }
--            });
-+            } // Paper start - optimise chunk tick iteration
-+            } finally {
-+                if (iterator instanceof io.papermc.paper.util.maplist.IteratorSafeOrderedReferenceSet.Iterator) {
-+                    ((io.papermc.paper.util.maplist.IteratorSafeOrderedReferenceSet.Iterator<LevelChunk>)iterator).finishedIterating();
-+                }
-+            }
-+            // Paper end - optimise chunk tick iteration
-             this.level.timings.chunkTicks.stopTiming(); // Paper
-             this.level.getProfiler().push("customSpawners");
-             if (flag1) {
-@@ -1039,21 +1061,7 @@ public class ServerChunkCache extends ChunkSource {
-                 } // Paper - timings
-             }
- 
--            this.level.getProfiler().popPush("broadcast");
--            this.chunkMap.getChunks().forEach((playerchunk) -> { // Paper - no... just no...
--                Optional<LevelChunk> optional = ((Either) playerchunk.getTickingChunkFuture().getNow(ChunkHolder.UNLOADED_LEVEL_CHUNK)).left(); // CraftBukkit - decompile error
--
--                Objects.requireNonNull(playerchunk);
--
--                // Paper start - timings
--                optional.ifPresent(chunk -> {
--                    this.level.timings.broadcastChunkUpdates.startTiming(); // Paper - timings
--                    playerchunk.broadcastChanges(chunk);
--                    this.level.timings.broadcastChunkUpdates.stopTiming(); // Paper - timings
--                });
--                // Paper end
--            });
--            this.level.getProfiler().pop();
-+            // Paper - no, iterating just ONCE is expensive enough! Don't do it TWICE! Code moved up
-             this.level.getProfiler().pop();
-         }
- 
diff --git a/patches/server/0006-MC-Utils.patch b/patches/server/0006-MC-Utils.patch
index f5a4027421..b1fe48c977 100644
--- a/patches/server/0006-MC-Utils.patch
+++ b/patches/server/0006-MC-Utils.patch
@@ -5477,7 +5477,7 @@ index ef87c37633cee4ab438f1991144188ade1c4e65f..19d3802becd353e130b785f8286e595e
  
      protected void purgeStaleTickets() {
 diff --git a/src/main/java/net/minecraft/server/level/ServerChunkCache.java b/src/main/java/net/minecraft/server/level/ServerChunkCache.java
-index d672c467267ef4d96184e104c35d0379116880db..566c32a71054479257b7fba981507fa2559aa9e6 100644
+index d672c467267ef4d96184e104c35d0379116880db..be11caf7c0dcdb11e24cfb053cda45ac1867da63 100644
 --- a/src/main/java/net/minecraft/server/level/ServerChunkCache.java
 +++ b/src/main/java/net/minecraft/server/level/ServerChunkCache.java
 @@ -48,6 +48,8 @@ import net.minecraft.world.level.storage.LevelData;
@@ -5874,15 +5874,6 @@ index d672c467267ef4d96184e104c35d0379116880db..566c32a71054479257b7fba981507fa2
      @Nullable
      @Override
      public ChunkAccess getChunk(int x, int z, ChunkStatus leastStatus, boolean create) {
-@@ -444,7 +817,7 @@ public class ServerChunkCache extends ChunkSource {
-             gameprofilerfiller.popPush("spawnAndTick");
-             boolean flag2 = this.level.getGameRules().getBoolean(GameRules.RULE_DOMOBSPAWNING) && !this.level.players().isEmpty(); // CraftBukkit
- 
--            Collections.shuffle(list);
-+            //Collections.shuffle(list); // Paper - no... just no...
-             Iterator iterator1 = list.iterator();
- 
-             while (iterator1.hasNext()) {
 diff --git a/src/main/java/net/minecraft/server/level/ServerLevel.java b/src/main/java/net/minecraft/server/level/ServerLevel.java
 index 9812116e2f28d01423373990ecce0e5ab6229ce2..e66b4837498a8bfefabc4dd95e91ccfba67843e4 100644
 --- a/src/main/java/net/minecraft/server/level/ServerLevel.java
diff --git a/patches/server/0172-PlayerNaturallySpawnCreaturesEvent.patch b/patches/server/0172-PlayerNaturallySpawnCreaturesEvent.patch
index 8ea98d2d14..d05077e4a2 100644
--- a/patches/server/0172-PlayerNaturallySpawnCreaturesEvent.patch
+++ b/patches/server/0172-PlayerNaturallySpawnCreaturesEvent.patch
@@ -40,13 +40,13 @@ index e954365c9fe7a5f442c4ab31ab8c8db9684409e8..fb9a82b4fabdb73e9a44bcbc20b848d8
  
              return true;
 diff --git a/src/main/java/net/minecraft/server/level/ServerChunkCache.java b/src/main/java/net/minecraft/server/level/ServerChunkCache.java
-index 109d6638d8fe035498bd717c180c9143ff00e0ad..9090bc174c0ccb542616e756be66967f55f0a626 100644
+index 8f5e9f1bdfd2e02512e1341f91b147b16e959e5b..87c9a7ffc69206554cf37c7d2c9939eb3cbea3a9 100644
 --- a/src/main/java/net/minecraft/server/level/ServerChunkCache.java
 +++ b/src/main/java/net/minecraft/server/level/ServerChunkCache.java
 @@ -827,6 +827,15 @@ public class ServerChunkCache extends ChunkSource {
              boolean flag2 = this.level.getGameRules().getBoolean(GameRules.RULE_DOMOBSPAWNING) && !this.level.players().isEmpty(); // CraftBukkit
  
-             //Collections.shuffle(list); // Paper - no... just no...
+             Collections.shuffle(list);
 +            // Paper start - call player naturally spawn event
 +            int chunkRange = level.spigotConfig.mobSpawnRange;
 +            chunkRange = (chunkRange > level.spigotConfig.viewDistance) ? (byte) level.spigotConfig.viewDistance : chunkRange;
diff --git a/patches/server/0435-Optimize-anyPlayerCloseEnoughForSpawning-to-use-dist.patch b/patches/server/0435-Optimize-anyPlayerCloseEnoughForSpawning-to-use-dist.patch
index dda84a76de..2d9c5091d3 100644
--- a/patches/server/0435-Optimize-anyPlayerCloseEnoughForSpawning-to-use-dist.patch
+++ b/patches/server/0435-Optimize-anyPlayerCloseEnoughForSpawning-to-use-dist.patch
@@ -272,7 +272,7 @@ index 8868ffcda194e8c2300181a2cdda9337dbde6284..95f195980e28bb59f43e5ca1d5e79ebe
  
      public String getDebugStatus() {
 diff --git a/src/main/java/net/minecraft/server/level/ServerChunkCache.java b/src/main/java/net/minecraft/server/level/ServerChunkCache.java
-index 3c36dfa4f4c6025b02ec455d997f13226de75a64..76b56ea346d843aba482c52c4bfe877fdf0e9225 100644
+index 41d2027cd4cf8f5de7bd59283361f7f1075356cb..24d0b02264e4cced08a60f36b5c41bb350a1dc60 100644
 --- a/src/main/java/net/minecraft/server/level/ServerChunkCache.java
 +++ b/src/main/java/net/minecraft/server/level/ServerChunkCache.java
 @@ -881,6 +881,37 @@ public class ServerChunkCache extends ChunkSource {
@@ -316,7 +316,7 @@ index 3c36dfa4f4c6025b02ec455d997f13226de75a64..76b56ea346d843aba482c52c4bfe877f
 @@ -928,15 +959,7 @@ public class ServerChunkCache extends ChunkSource {
              boolean flag2 = this.level.getGameRules().getBoolean(GameRules.RULE_DOMOBSPAWNING) && !this.level.players().isEmpty(); // CraftBukkit
  
-             //Collections.shuffle(list); // Paper - no... just no...
+             Collections.shuffle(list);
 -            // Paper start - call player naturally spawn event
 -            int chunkRange = level.spigotConfig.mobSpawnRange;
 -            chunkRange = (chunkRange > level.spigotConfig.viewDistance) ? (byte) level.spigotConfig.viewDistance : chunkRange;
diff --git a/patches/server/0827-Optimise-chunk-tick-iteration.patch b/patches/server/0827-Optimise-chunk-tick-iteration.patch
new file mode 100644
index 0000000000..129bc70c02
--- /dev/null
+++ b/patches/server/0827-Optimise-chunk-tick-iteration.patch
@@ -0,0 +1,106 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Spottedleaf <spottedleaf@spottedleaf.dev>
+Date: Thu, 7 May 2020 05:48:54 -0700
+Subject: [PATCH] Optimise chunk tick iteration
+
+Use a dedicated list of entity ticking chunks to reduce the cost
+
+diff --git a/src/main/java/net/minecraft/server/level/ServerChunkCache.java b/src/main/java/net/minecraft/server/level/ServerChunkCache.java
+index 51c4ca36221e9af074fa92f6ab94fa7ba5403080..ca16bde30da84e79fef856304ada9e3b8bfb9806 100644
+--- a/src/main/java/net/minecraft/server/level/ServerChunkCache.java
++++ b/src/main/java/net/minecraft/server/level/ServerChunkCache.java
+@@ -1001,34 +1001,46 @@ public class ServerChunkCache extends ChunkSource {
+ 
+             this.lastSpawnState = spawnercreature_d;
+             gameprofilerfiller.popPush("filteringLoadedChunks");
+-            List<ServerChunkCache.ChunkAndHolder> list = Lists.newArrayListWithCapacity(l);
+-            Iterator iterator = this.chunkMap.getChunks().iterator();
++            // Paper - moved down
+             this.level.timings.chunkTicks.startTiming(); // Paper
+ 
+-            while (iterator.hasNext()) {
+-                ChunkHolder playerchunk = (ChunkHolder) iterator.next();
+-                LevelChunk chunk = playerchunk.getTickingChunk();
+-
+-                if (chunk != null) {
+-                    list.add(new ServerChunkCache.ChunkAndHolder(chunk, playerchunk));
+-                }
+-            }
++            // Paper - moved down
+ 
+             gameprofilerfiller.popPush("spawnAndTick");
+             boolean flag2 = this.level.getGameRules().getBoolean(GameRules.RULE_DOMOBSPAWNING) && !this.level.players().isEmpty(); // CraftBukkit
+ 
+-            Collections.shuffle(list);
++            // Paper - only shuffle if per-player mob spawning is disabled
+             // Paper - moved natural spawn event up
+-            Iterator iterator1 = list.iterator();
++            // Paper start - optimise chunk tick iteration
++            Iterator<LevelChunk> iterator1;
++            if (this.level.paperConfig.perPlayerMobSpawns) {
++                iterator1 = this.entityTickingChunks.iterator();
++            } else {
++                iterator1 = this.entityTickingChunks.unsafeIterator();
++                List<LevelChunk> shuffled = Lists.newArrayListWithCapacity(this.entityTickingChunks.size());
++                while (iterator1.hasNext()) {
++                    shuffled.add(iterator1.next());
++                }
++                Collections.shuffle(shuffled);
++                iterator1 = shuffled.iterator();
++            }
+ 
++            try {
+             while (iterator1.hasNext()) {
+-                ServerChunkCache.ChunkAndHolder chunkproviderserver_a = (ServerChunkCache.ChunkAndHolder) iterator1.next();
+-                LevelChunk chunk1 = chunkproviderserver_a.chunk;
++                LevelChunk chunk1 = iterator1.next();
++                ChunkHolder holder = chunk1.playerChunk;
++                if (holder != null) {
++                    gameprofilerfiller.popPush("broadcast");
++                    this.level.timings.broadcastChunkUpdates.startTiming(); // Paper - timing
++                    holder.broadcastChanges(chunk1);
++                    this.level.timings.broadcastChunkUpdates.stopTiming(); // Paper - timing
++                    gameprofilerfiller.pop();
++            // Paper end - optimise chunk tick iteration
+                 ChunkPos chunkcoordintpair = chunk1.getPos();
+ 
+-                if (this.level.isPositionEntityTicking(chunkcoordintpair) && this.chunkMap.anyPlayerCloseEnoughForSpawning(chunkproviderserver_a.holder, chunkcoordintpair, false)) { // Paper - optimise anyPlayerCloseEnoughForSpawning
++                if ((true || this.level.isPositionEntityTicking(chunkcoordintpair)) && this.chunkMap.anyPlayerCloseEnoughForSpawning(holder, chunkcoordintpair, false)) { // Paper - optimise anyPlayerCloseEnoughForSpawning & optimise chunk tick iteration
+                     chunk1.incrementInhabitedTime(j);
+-                    if (flag2 && (this.spawnEnemies || this.spawnFriendlies) && this.level.getWorldBorder().isWithinBounds(chunkcoordintpair) && this.chunkMap.anyPlayerCloseEnoughForSpawning(chunkproviderserver_a.holder, chunkcoordintpair, true)) { // Spigot // Paper - optimise anyPlayerCloseEnoughForSpawning
++                    if (flag2 && (this.spawnEnemies || this.spawnFriendlies) && this.level.getWorldBorder().isWithinBounds(chunkcoordintpair) && this.chunkMap.anyPlayerCloseEnoughForSpawning(holder, chunkcoordintpair, true)) { // Spigot // Paper - optimise anyPlayerCloseEnoughForSpawning & optimise chunk tick iteration
+                         NaturalSpawner.spawnForChunk(this.level, chunk1, spawnercreature_d, this.spawnFriendlies, this.spawnEnemies, flag1);
+                     }
+ 
+@@ -1036,7 +1048,16 @@ public class ServerChunkCache extends ChunkSource {
+                         this.level.tickChunk(chunk1, k);
+                     }
+                 }
++            // Paper start - optimise chunk tick iteration
++                }
+             }
++
++            } finally {
++                if (iterator1 instanceof io.papermc.paper.util.maplist.IteratorSafeOrderedReferenceSet.Iterator safeIterator) {
++                    safeIterator.finishedIterating();
++                }
++            }
++            // Paper end - optimise chunk tick iteration
+             this.level.timings.chunkTicks.stopTiming(); // Paper
+             gameprofilerfiller.popPush("customSpawners");
+             if (flag2) {
+@@ -1045,13 +1066,7 @@ public class ServerChunkCache extends ChunkSource {
+                 } // Paper - timings
+             }
+ 
+-            gameprofilerfiller.popPush("broadcast");
+-            list.forEach((chunkproviderserver_a1) -> {
+-                this.level.timings.broadcastChunkUpdates.startTiming(); // Paper - timing
+-                chunkproviderserver_a1.holder.broadcastChanges(chunkproviderserver_a1.chunk);
+-                this.level.timings.broadcastChunkUpdates.stopTiming(); // Paper - timing
+-            });
+-            gameprofilerfiller.pop();
++            //  Paper - no, iterating just ONCE is expensive enough! Don't do it TWICE! Code moved up
+             gameprofilerfiller.pop();
+             // Paper start - controlled flush for entity tracker packets
+             List<net.minecraft.network.Connection> disabledFlushes = new java.util.ArrayList<>(this.level.players.size());