diff --git a/patches/api/0013-PlayerPreChunkLoadEvent.patch b/patches/api/0013-PlayerPreChunkLoadEvent.patch new file mode 100644 index 000000000..187918fd7 --- /dev/null +++ b/patches/api/0013-PlayerPreChunkLoadEvent.patch @@ -0,0 +1,95 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Cryptite +Date: Mon, 7 Nov 2022 22:44:23 -0600 +Subject: [PATCH] PlayerPreChunkLoadEvent + + +diff --git a/src/main/java/io/papermc/paper/event/packet/PlayerPreChunkLoadEvent.java b/src/main/java/io/papermc/paper/event/packet/PlayerPreChunkLoadEvent.java +new file mode 100644 +index 0000000000000000000000000000000000000000..b5cc9538a70c7ce0b494d4878d51b52134c2fd75 +--- /dev/null ++++ b/src/main/java/io/papermc/paper/event/packet/PlayerPreChunkLoadEvent.java +@@ -0,0 +1,69 @@ ++package io.papermc.paper.event.packet; ++ ++import org.bukkit.Chunk; ++import org.bukkit.World; ++import org.bukkit.entity.Player; ++import org.bukkit.event.Cancellable; ++import org.bukkit.event.Event; ++import org.bukkit.event.HandlerList; ++import org.jetbrains.annotations.NotNull; ++ ++/** ++ * Is called when a {@link Player} is about to receive a {@link Chunk} ++ *

++ * Can be cancelled, but only use if you really really mean it. ++ */ ++public class PlayerPreChunkLoadEvent extends Event implements Cancellable { ++ ++ private static final HandlerList handlers = new HandlerList(); ++ private final World world; ++ private final int chunkX; ++ private final int chunkZ; ++ private final Player player; ++ private boolean cancel; ++ ++ public PlayerPreChunkLoadEvent(World world, int chunkX, int chunkZ, @NotNull Player player) { ++ this.world = world; ++ this.chunkX = chunkX; ++ this.chunkZ = chunkZ; ++ this.player = player; ++ } ++ ++ @NotNull ++ @Override ++ public HandlerList getHandlers() { ++ return handlers; ++ } ++ ++ public World getWorld() { ++ return world; ++ } ++ ++ public int getChunkX() { ++ return chunkX; ++ } ++ ++ public int getChunkZ() { ++ return chunkZ; ++ } ++ ++ @NotNull ++ public Player getPlayer() { ++ return player; ++ } ++ ++ @Override ++ public boolean isCancelled() { ++ return cancel; ++ } ++ ++ @Override ++ public void setCancelled(boolean cancel) { ++ this.cancel = cancel; ++ } ++ ++ @NotNull ++ public static HandlerList getHandlerList() { ++ return handlers; ++ } ++} +\ No newline at end of file +diff --git a/src/main/java/org/bukkit/World.java b/src/main/java/org/bukkit/World.java +index fde06c5d88ffb63910bcb06e3e70e3c0a4af92c7..12d0d7355e7f15a073e1ee895f4fcaa935c4405e 100644 +--- a/src/main/java/org/bukkit/World.java ++++ b/src/main/java/org/bukkit/World.java +@@ -526,6 +526,8 @@ public interface World extends RegionAccessor, WorldInfo, PluginMessageRecipient + //@Deprecated // Paper + public boolean refreshChunk(int x, int z); + ++ it.unimi.dsi.fastutil.longs.LongOpenHashSet getSentChunks(Player p); // Slice ++ + /** + * Gets whether the chunk at the specified chunk coordinates is force + * loaded. diff --git a/patches/server/0027-PlayerPreChunkLoadEvent.patch b/patches/server/0027-PlayerPreChunkLoadEvent.patch new file mode 100644 index 000000000..385feac49 --- /dev/null +++ b/patches/server/0027-PlayerPreChunkLoadEvent.patch @@ -0,0 +1,81 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Cryptite +Date: Mon, 7 Nov 2022 22:44:22 -0600 +Subject: [PATCH] PlayerPreChunkLoadEvent + + +diff --git a/src/main/java/io/papermc/paper/chunk/PlayerChunkLoader.java b/src/main/java/io/papermc/paper/chunk/PlayerChunkLoader.java +index 12bcf96ec1ba4314c7ea2eab9f3d140559f1dc08..d8773c7c36314b4f5953b9e7abc310c74401c23c 100644 +--- a/src/main/java/io/papermc/paper/chunk/PlayerChunkLoader.java ++++ b/src/main/java/io/papermc/paper/chunk/PlayerChunkLoader.java +@@ -3,6 +3,7 @@ package io.papermc.paper.chunk; + import com.destroystokyo.paper.PaperConfig; + import com.destroystokyo.paper.util.misc.PlayerAreaMap; + import com.destroystokyo.paper.util.misc.PooledLinkedHashSets; ++import io.papermc.paper.event.packet.PlayerPreChunkLoadEvent; + import io.papermc.paper.util.CoordinateUtils; + import io.papermc.paper.util.IntervalledCounter; + import io.papermc.paper.util.TickThread; +@@ -787,7 +788,7 @@ public final class PlayerChunkLoader { + // warning: modifications of this field must be aware that the loadQueue inside PlayerChunkLoader uses this field + // in a comparator! + protected final ArrayDeque loadQueue = new ArrayDeque<>(); +- protected final LongOpenHashSet sentChunks = new LongOpenHashSet(); ++ public final LongOpenHashSet sentChunks = new LongOpenHashSet(); // Slice - public + protected final LongOpenHashSet chunksToBeSent = new LongOpenHashSet(); + + protected final TreeSet sendQueue = new TreeSet<>((final ChunkPriorityHolder p1, final ChunkPriorityHolder p2) -> { +@@ -871,7 +872,14 @@ public final class PlayerChunkLoader { + } + + public void sendChunk(final int chunkX, final int chunkZ, final Runnable onChunkSend) { +- if (this.sentChunks.add(CoordinateUtils.getChunkKey(chunkX, chunkZ))) { ++ // Slice start ++ if (!new PlayerPreChunkLoadEvent(this.player.getBukkitEntity().getWorld(), chunkX, chunkZ, this.player.getBukkitEntity()).callEvent()) { ++ this.player.connection.connection.execute(onChunkSend); ++ return; ++ } ++ // Slice end ++ ++ if (this.sentChunks.add(CoordinateUtils.getChunkKey(chunkX, chunkZ))) { // Slice + this.player.getLevel().getChunkSource().chunkMap.updateChunkTracking(this.player, + new ChunkPos(chunkX, chunkZ), new MutableObject<>(), false, true); // unloaded, loaded + this.player.connection.connection.execute(onChunkSend); +diff --git a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java +index 9d400d82eeab062322acea81b87f6fc572151700..ee35b5ac41d2016a775b320b1a84fceb6329c4be 100644 +--- a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java ++++ b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java +@@ -528,6 +528,13 @@ public class CraftWorld extends CraftRegionAccessor implements World { + return true; + } + ++ @Override ++ public it.unimi.dsi.fastutil.longs.LongOpenHashSet getSentChunks(Player p) { ++ CraftPlayer craftPlayer = (CraftPlayer) p; ++ io.papermc.paper.chunk.PlayerChunkLoader.PlayerLoaderData data = this.world.chunkSource.chunkMap.playerChunkManager.getData(craftPlayer.getHandle()); ++ return data != null ? data.sentChunks : null; ++ } ++ + @Override + public boolean isChunkInUse(int x, int z) { + return this.isChunkLoaded(x, z); +diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java +index 2e7c1a99697c31f005a62b5d663708c9d4bc85df..b62a427b123f0573e15e6e38ff67029eadd69966 100644 +--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java ++++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java +@@ -2214,13 +2214,13 @@ public class CraftPlayer extends CraftHumanEntity implements Player { + } + // Paper end + } +- ++ + @Override + public void sendHealthUpdate() { + this.sendHealthUpdate(this.getScaledHealth(), this.getHandle().getFoodData().getFoodLevel(), this.getHandle().getFoodData().getSaturationLevel()); + } + // Paper end +- ++ + public void injectScaledMaxHealth(Collection collection, boolean force) { + if (!this.scaledHealth && !force) { + return;