Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
328 changes: 328 additions & 0 deletions Spigot-API-Patches/0242-NamespacedKey-based-plugin-chunk-tickets.patch
Original file line number Diff line number Diff line change
@@ -0,0 +1,328 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Trigary <trigary0@gmail.com>
Date: Sat, 22 Aug 2020 14:41:43 +0200
Subject: [PATCH] NamespacedKey based plugin chunk tickets


diff --git a/src/main/java/io/papermc/paper/world/TickingLevel.java b/src/main/java/io/papermc/paper/world/TickingLevel.java
new file mode 100644
index 0000000000000000000000000000000000000000..64b77f2c7ca920a44d6628b3edacbaeb3f727b1f
--- /dev/null
+++ b/src/main/java/io/papermc/paper/world/TickingLevel.java
@@ -0,0 +1,55 @@
+package io.papermc.paper.world;
+
+import org.apache.commons.lang.Validate;
+import org.bukkit.Chunk;
+import org.jetbrains.annotations.Contract;
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * Represents what kind of ticking is done on the {@link Chunk}.
+ */
+public enum TickingLevel {
+
+ /**
+ * Both blocks and entities get ticked in this level.
+ */
+ ENTITY,
+
+ /**
+ * Blocks get ticked in this level, but entities do not.
+ */
+ BLOCK,
+
+ /**
+ * Nothing gets ticked in this level, but the {@link Chunk} stays loaded.
+ */
+ NONE;
+
+ /**
+ * Gets whether this ticking level does at least as much as the specified one.
+ * Examples (all of these expressions evaluate to {@code true}):
+ * <ul>
+ * <li>{@code BLOCK.includes(ENTITY) == false}</li>
+ * <li>{@code BLOCK.includes(BLOCK) == true}</li>
+ * <li>{@code BLOCK.includes(NONE) == true}</li>
+ * </ul>
+ *
+ * @param other the ticking level to test
+ * @return {@code true} if the current instance does at least everything
+ * the other one does, {@code false} otherwise
+ */
+ @Contract(pure = true)
+ public boolean includes(@NotNull TickingLevel other) {
+ Validate.notNull(other, "Ticking level cannot be null");
+ switch (this) {
+ case ENTITY:
+ return true;
+ case BLOCK:
+ return other != ENTITY;
+ case NONE:
+ return other == NONE;
+ default:
+ throw new AssertionError("Unhandled case: " + this);
+ }
+ }
+}
diff --git a/src/main/java/org/bukkit/Chunk.java b/src/main/java/org/bukkit/Chunk.java
index b4ef6297f78d1f0c216e718024a21e6aa07cd1c6..94c36228346a1b79529e2308f4654c80db4d6614 100644
--- a/src/main/java/org/bukkit/Chunk.java
+++ b/src/main/java/org/bukkit/Chunk.java
@@ -8,6 +8,7 @@ import org.bukkit.entity.Entity;
import org.bukkit.persistence.PersistentDataHolder;
import org.bukkit.plugin.Plugin;
import org.jetbrains.annotations.NotNull;
+import io.papermc.paper.world.TickingLevel; // Paper

/**
* Represents a chunk of blocks
@@ -240,6 +241,78 @@ public interface Chunk extends PersistentDataHolder {
@NotNull
Collection<Plugin> getPluginChunkTickets();

+ // Paper start
+ /**
+ * Adds a {@link NamespacedKey} based plugin chunk ticket
+ * with {@link TickingLevel#ENTITY}.
+ * If a ticket with a different level is already present, it will be removed.
+ *
+ * @param key the identifier of the ticket
+ * @return the {@link TickingLevel} that was overwritten
+ * or {@code null} if no ticket was present with the specified key
+ * @see World#addKeyedPluginChunkTicket(int, int, NamespacedKey)
+ */
+ @org.jetbrains.annotations.Nullable
+ default TickingLevel addKeyedPluginChunkTicket(@NotNull NamespacedKey key) {
+ return getWorld().addKeyedPluginChunkTicket(getX(), getZ(), key);
+ }
+
+ /**
+ * Adds a {@link NamespacedKey} based plugin chunk ticket
+ * with the specified {@link TickingLevel}.
+ * If a ticket with a different level is already present, it will be removed.
+ *
+ * @param key the identifier of the ticket
+ * @param level the level of the ticket
+ * @return the {@link TickingLevel} that was overwritten
+ * or {@code null} if no ticket was present with the specified key
+ * @see World#addKeyedPluginChunkTicket(int, int, NamespacedKey, TickingLevel)
+ */
+ @org.jetbrains.annotations.Nullable
+ default TickingLevel addKeyedPluginChunkTicket(@NotNull NamespacedKey key, @NotNull TickingLevel level) {
+ return getWorld().addKeyedPluginChunkTicket(getX(), getZ(), key, level);
+ }
+
+ /**
+ * Removes the {@link NamespacedKey} based plugin chunk ticket
+ * which has the specified key, if there is one.
+ *
+ * @param key the identifier of the tickets
+ * @return the {@link TickingLevel} that was removed
+ * or {@code null} if no ticket was present with the specified key
+ * @see World#removeKeyedPluginChunkTicket(int, int, NamespacedKey)
+ */
+ @org.jetbrains.annotations.Nullable
+ default TickingLevel removeKeyedPluginChunkTicket(@NotNull NamespacedKey key) {
+ return getWorld().removeKeyedPluginChunkTicket(getX(), getZ(), key);
+ }
+
+ /**
+ * Gets the {@link NamespacedKey} based plugin chunk ticket level
+ * with the specified key, if there is one.
+ *
+ * @param key the identifier of the tickets
+ * @return the level which is present with the specified key or {@code null} if there is none
+ * @see #getKeyedPluginChunkTicketLevel(NamespacedKey)
+ */
+ @org.jetbrains.annotations.Nullable
+ default TickingLevel getKeyedPluginChunkTicketLevel(@NotNull NamespacedKey key) {
+ return getWorld().getKeyedPluginChunkTicketLevel(getX(), getZ(), key);
+ }
+
+ /**
+ * Gets the keys of the {@link NamespacedKey} based plugin chunk tickets
+ * which are present in this chunk.
+ *
+ * @return the keys which are present
+ * @see World#getKeyedPluginChunkTicketKeys(int, int)
+ */
+ @NotNull
+ default Collection<NamespacedKey> getKeyedPluginChunkTicketKeys() {
+ return getWorld().getKeyedPluginChunkTicketKeys(getX(), getZ());
+ }
+ // Paper end
+
/**
* Gets the amount of time in ticks that this chunk has been inhabited.
*
diff --git a/src/main/java/org/bukkit/World.java b/src/main/java/org/bukkit/World.java
index e6d2abf284c103a8bcddd8b4f9cb34d86a4f2fa6..b94ea04f2ca9474e32a9cdeacd058310775ca884 100644
--- a/src/main/java/org/bukkit/World.java
+++ b/src/main/java/org/bukkit/World.java
@@ -39,6 +39,7 @@ import org.bukkit.util.Vector;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
+import io.papermc.paper.world.TickingLevel; // Paper

/**
* Represents a world, which may contain entities, chunks and blocks
@@ -1115,6 +1116,145 @@ public interface World extends PluginMessageRecipient, Metadatable {
@NotNull
public Map<Plugin, Collection<Chunk>> getPluginChunkTickets();

+ // Paper start
+ /**
+ * Adds a {@link NamespacedKey} based plugin chunk ticket
+ * to the specified chunk with {@link TickingLevel#ENTITY}.
+ * If a ticket with a different level is already present, it will be removed.
+ * Asynchronously loads the specified {@link Chunk} if it isn't loaded.
+ *
+ * @param x X-coordinate of the chunk
+ * @param z Z-coordinate of the chunk
+ * @param key the identifier of the ticket
+ * @return the {@link TickingLevel} that was overwritten
+ * or {@code null} if no ticket was present with the specified key
+ * @see #addKeyedPluginChunkTicket(int, int, NamespacedKey, TickingLevel)
+ */
+ @Nullable
+ TickingLevel addKeyedPluginChunkTicket(int x, int z, @NotNull NamespacedKey key);
+
+ /**
+ * Adds a {@link NamespacedKey} based plugin chunk ticket
+ * to the specified chunk with the specified {@link TickingLevel}.
+ * If a ticket with a different level is already present, it will be removed.
+ * Asynchronously loads the specified {@link Chunk} if it isn't loaded.
+ *
+ * @param x X-coordinate of the chunk
+ * @param z Z-coordinate of the chunk
+ * @param key the identifier of the ticket
+ * @param level the level of the ticket
+ * @return the {@link TickingLevel} that was overwritten
+ * or {@code null} if no ticket was present with the specified key
+ */
+ @Nullable
+ TickingLevel addKeyedPluginChunkTicket(int x, int z, @NotNull NamespacedKey key, @NotNull TickingLevel level);
+
+ /**
+ * Removes the {@link NamespacedKey} based plugin chunk ticket
+ * from the specified chunk which has the specified key, if there is one.
+ *
+ * @param x X-coordinate of the chunk
+ * @param z Z-coordinate of the chunk
+ * @param key the identifier of the tickets
+ * @return the {@link TickingLevel} that was removed
+ * or {@code null} if no ticket was present with the specified key
+ */
+ @Nullable
+ TickingLevel removeKeyedPluginChunkTicket(int x, int z, @NotNull NamespacedKey key);
+
+ /**
+ * Gets the {@link NamespacedKey} based plugin chunk ticket level
+ * in the specified chunk with the specified key, if there is one.
+ *
+ * @param x X-coordinate of the chunk
+ * @param z Z-coordinate of the chunk
+ * @param key the identifier of the tickets
+ * @return the level which is present with the specified key or {@code null} if there is none
+ */
+ @Nullable
+ TickingLevel getKeyedPluginChunkTicketLevel(int x, int z, @NotNull NamespacedKey key);
+
+ /**
+ * Gets the keys of the {@link NamespacedKey} based plugin chunk tickets
+ * which are present in the specified chunk.
+ *
+ * @param x X-coordinate of the chunk
+ * @param z Z-coordinate of the chunk
+ * @return the keys which are present
+ */
+ @NotNull
+ Collection<NamespacedKey> getKeyedPluginChunkTicketKeys(int x, int z);
+
+ /**
+ * Removes all {@link NamespacedKey} based plugin chunk tickets
+ * (from each chunk in this world) which are owned by the specified plugin
+ * according to {@link NamespacedKey#getNamespace()}.
+ *
+ * @param plugin the plugin whose tickets to remove
+ */
+ void removeKeyedPluginChunkTickets(@NotNull Plugin plugin);
+
+ /**
+ * Removes all {@link NamespacedKey} based plugin chunk tickets
+ * (from each chunk in this world) which have the specified key.
+ *
+ * @param key the identifier of the tickets which should be removed
+ */
+ void removeKeyedPluginChunkTickets(@NotNull NamespacedKey key);
+
+ /**
+ * Gets all chunk keys (which can be used in eg. {@link World#getChunkAt(long)})
+ * which have a {@link NamespacedKey} based plugin chunk ticket with the specified key.
+ * Keep in mind that these chunks might not be loaded yet:
+ * having a chunk ticket only guarantees that chunk loading has begun,
+ * not that the loading has already been completed.
+ * If only loaded chunks are needed, please use
+ * {@link #getKeyedPluginChunkTicketLoadedChunks(NamespacedKey)} instead.
+ *
+ * @return the chunk keys which have tickets with the specified key
+ */
+ @NotNull
+ Collection<Long> getKeyedPluginChunkTicketChunks(@NotNull NamespacedKey key);
+
+ /**
+ * Gets all loaded chunks which have a {@link NamespacedKey}
+ * based plugin chunk ticket with the specified key.
+ * Keep in mind that only loaded chunks are included.
+ * If that's not desired, please use
+ * {@link #getKeyedPluginChunkTicketChunks(NamespacedKey)} instead.
+ *
+ * @return the loaded chunks which have tickets with the specified key
+ */
+ @NotNull
+ Collection<Chunk> getKeyedPluginChunkTicketLoadedChunks(@NotNull NamespacedKey key);
+
+ /**
+ * Gets all {@link NamespacedKey} based plugin chunk tickets
+ * and their chunk keys (which can be used in eg. {@link World#getChunkAt(long)}).
+ * The chunk keys point to the ticket keys they contain.
+ * Keep in mind that these chunks might not be loaded yet:
+ * having a chunk ticket only guarantees that chunk loading has begun,
+ * not that the loading has already been completed.
+ * If only loaded chunks are needed, please use
+ * {@link #getAllLoadedKeyedPluginChunkTickets()} instead.
+ *
+ * @return the chunk keys and their ticket keys
+ */
+ @NotNull
+ com.google.common.collect.Multimap<Long, NamespacedKey> getAllKeyedPluginChunkTickets();
+
+ /**
+ * Gets all {@link NamespacedKey} based plugin chunk tickets whose chunks are loaded,
+ * and their chunks. The chunks point to the ticket keys they contain.
+ * Keep in mind that only chunks are included.
+ * If that's not desired, please use {@link #getAllKeyedPluginChunkTickets()} instead.
+ *
+ * @return the chunks and their ticket keys
+ */
+ @NotNull
+ com.google.common.collect.Multimap<Chunk, NamespacedKey> getAllLoadedKeyedPluginChunkTickets();
+ // Paper end
+
/**
* Drops an item at the specified {@link Location}
*
diff --git a/src/main/java/org/bukkit/plugin/SimplePluginManager.java b/src/main/java/org/bukkit/plugin/SimplePluginManager.java
index 26685f59b235ea5b4c4fb7ae21acb5149edaa2b3..4db22c112a3e81ca5d118403f3d6e502244ace94 100644
--- a/src/main/java/org/bukkit/plugin/SimplePluginManager.java
+++ b/src/main/java/org/bukkit/plugin/SimplePluginManager.java
@@ -551,6 +551,7 @@ public final class SimplePluginManager implements PluginManager {
try {
for (World world : server.getWorlds()) {
world.removePluginChunkTickets(plugin);
+ world.removeKeyedPluginChunkTickets(plugin); // Paper
}
} catch (Throwable ex) {
server.getLogger().log(Level.SEVERE, "Error occurred (in the plugin loader) while removing chunk tickets for " + plugin.getDescription().getFullName() + " (Is it up to date?)", ex);
Loading