diff --git a/paper-api/src/main/java/org/bukkit/Chunk.java b/paper-api/src/main/java/org/bukkit/Chunk.java index aab292e1a0..58cf0dde77 100644 --- a/paper-api/src/main/java/org/bukkit/Chunk.java +++ b/paper-api/src/main/java/org/bukkit/Chunk.java @@ -1,8 +1,10 @@ package org.bukkit; +import java.util.Collection; import org.bukkit.block.Block; import org.bukkit.block.BlockState; import org.bukkit.entity.Entity; +import org.bukkit.plugin.Plugin; import org.jetbrains.annotations.NotNull; /** @@ -147,4 +149,53 @@ public interface Chunk { * @see World#setChunkForceLoaded(int, int, boolean) */ void setForceLoaded(boolean forced); + + /** + * Adds a plugin ticket for this chunk, loading this chunk if it is not + * already loaded. + *

+ * A plugin ticket will prevent a chunk from unloading until it is + * explicitly removed. A plugin instance may only have one ticket per chunk, + * but each chunk can have multiple plugin tickets. + *

+ * + * @param plugin Plugin which owns the ticket + * @return {@code true} if a plugin ticket was added, {@code false} if the + * ticket already exists for the plugin + * @throws IllegalStateException If the specified plugin is not enabled + * @see World#addPluginChunkTicket(int, int, Plugin) + */ + boolean addPluginChunkTicket(@NotNull Plugin plugin); + + /** + * Removes the specified plugin's ticket for this chunk + *

+ * A plugin ticket will prevent a chunk from unloading until it is + * explicitly removed. A plugin instance may only have one ticket per chunk, + * but each chunk can have multiple plugin tickets. + *

+ * + * @param plugin Plugin which owns the ticket + * @return {@code true} if the plugin ticket was removed, {@code false} if + * there is no plugin ticket for the chunk + * @see World#removePluginChunkTicket(int, int, Plugin) + */ + boolean removePluginChunkTicket(@NotNull Plugin plugin); + + /** + * Retrieves a collection specifying which plugins have tickets for this + * chunk. This collection is not updated when plugin tickets are added or + * removed to this chunk. + *

+ * A plugin ticket will prevent a chunk from unloading until it is + * explicitly removed. A plugin instance may only have one ticket per chunk, + * but each chunk can have multiple plugin tickets. + *

+ * + * @return unmodifiable collection containing which plugins have tickets for + * this chunk + * @see World#getPluginChunkTickets(int, int) + */ + @NotNull + Collection getPluginChunkTickets(); } diff --git a/paper-api/src/main/java/org/bukkit/World.java b/paper-api/src/main/java/org/bukkit/World.java index 2964f9b3fc..5de5774fbf 100644 --- a/paper-api/src/main/java/org/bukkit/World.java +++ b/paper-api/src/main/java/org/bukkit/World.java @@ -24,6 +24,7 @@ import org.bukkit.generator.ChunkGenerator; import org.bukkit.inventory.ItemStack; import org.bukkit.material.MaterialData; import org.bukkit.metadata.Metadatable; +import org.bukkit.plugin.Plugin; import org.bukkit.plugin.messaging.PluginMessageRecipient; import org.bukkit.util.BoundingBox; import org.bukkit.util.Consumer; @@ -323,6 +324,94 @@ public interface World extends PluginMessageRecipient, Metadatable { @NotNull public Collection getForceLoadedChunks(); + /** + * Adds a plugin ticket for the specified chunk, loading the chunk if it is + * not already loaded. + *

+ * A plugin ticket will prevent a chunk from unloading until it is + * explicitly removed. A plugin instance may only have one ticket per chunk, + * but each chunk can have multiple plugin tickets. + *

+ * + * @param x X-coordinate of the chunk + * @param z Z-coordinate of the chunk + * @param plugin Plugin which owns the ticket + * @return {@code true} if a plugin ticket was added, {@code false} if the + * ticket already exists for the plugin + * @throws IllegalStateException If the specified plugin is not enabled + * @see #removePluginChunkTicket(int, int, Plugin) + */ + public boolean addPluginChunkTicket(int x, int z, @NotNull Plugin plugin); + + /** + * Removes the specified plugin's ticket for the specified chunk + *

+ * A plugin ticket will prevent a chunk from unloading until it is + * explicitly removed. A plugin instance may only have one ticket per chunk, + * but each chunk can have multiple plugin tickets. + *

+ * + * @param x X-coordinate of the chunk + * @param z Z-coordinate of the chunk + * @param plugin Plugin which owns the ticket + * @return {@code true} if the plugin ticket was removed, {@code false} if + * there is no plugin ticket for the chunk + * @see #addPluginChunkTicket(int, int, Plugin) + */ + public boolean removePluginChunkTicket(int x, int z, @NotNull Plugin plugin); + + /** + * Removes all plugin tickets for the specified plugin + *

+ * A plugin ticket will prevent a chunk from unloading until it is + * explicitly removed. A plugin instance may only have one ticket per chunk, + * but each chunk can have multiple plugin tickets. + *

+ * + * @param plugin Specified plugin + * @see #addPluginChunkTicket(int, int, Plugin) + * @see #removePluginChunkTicket(int, int, Plugin) + */ + public void removePluginChunkTickets(@NotNull Plugin plugin); + + /** + * Retrieves a collection specifying which plugins have tickets for the + * specified chunk. This collection is not updated when plugin tickets are + * added or removed to the chunk. + *

+ * A plugin ticket will prevent a chunk from unloading until it is + * explicitly removed. A plugin instance may only have one ticket per chunk, + * but each chunk can have multiple plugin tickets. + *

+ * + * @param x X-coordinate of the chunk + * @param z Z-coordinate of the chunk + * @return unmodifiable collection containing which plugins have tickets for + * the chunk + * @see #addPluginChunkTicket(int, int, Plugin) + * @see #removePluginChunkTicket(int, int, Plugin) + */ + @NotNull + public Collection getPluginChunkTickets(int x, int z); + + /** + * Returns a map of which plugins have tickets for what chunks. The returned + * map is not updated when plugin tickets are added or removed to chunks. If + * a plugin has no tickets, it will be absent from the map. + *

+ * A plugin ticket will prevent a chunk from unloading until it is + * explicitly removed. A plugin instance may only have one ticket per chunk, + * but each chunk can have multiple plugin tickets. + *

+ * + * @return unmodifiable map containing which plugins have tickets for what + * chunks + * @see #addPluginChunkTicket(int, int, Plugin) + * @see #removePluginChunkTicket(int, int, Plugin) + */ + @NotNull + public Map> getPluginChunkTickets(); + /** * Drops an item at the specified {@link Location} * diff --git a/paper-api/src/main/java/org/bukkit/plugin/SimplePluginManager.java b/paper-api/src/main/java/org/bukkit/plugin/SimplePluginManager.java index e776c9e041..6619236600 100644 --- a/paper-api/src/main/java/org/bukkit/plugin/SimplePluginManager.java +++ b/paper-api/src/main/java/org/bukkit/plugin/SimplePluginManager.java @@ -21,6 +21,7 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; import org.apache.commons.lang.Validate; import org.bukkit.Server; +import org.bukkit.World; import org.bukkit.command.Command; import org.bukkit.command.PluginCommandYamlParser; import org.bukkit.command.SimpleCommandMap; @@ -462,6 +463,14 @@ public final class SimplePluginManager implements PluginManager { } catch (Throwable ex) { server.getLogger().log(Level.SEVERE, "Error occurred (in the plugin loader) while unregistering plugin channels for " + plugin.getDescription().getFullName() + " (Is it up to date?)", ex); } + + try { + for (World world : server.getWorlds()) { + world.removePluginChunkTickets(plugin); + } + } 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); + } } }