Paper/patches/api/0039-LootTable-API.patch
2024-10-22 18:23:19 +02:00

464 lines
18 KiB
Diff

From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Aikar <aikar@aikar.co>
Date: Sun, 1 May 2016 15:19:49 -0400
Subject: [PATCH] LootTable API
Provides API to control what Loot Table an object uses.
Also provides an Event to control if a lootable inventory should
auto replenish for a player.
Provides methods to determine players looted state for an object
diff --git a/src/main/java/com/destroystokyo/paper/loottable/LootableBlockInventory.java b/src/main/java/com/destroystokyo/paper/loottable/LootableBlockInventory.java
new file mode 100644
index 0000000000000000000000000000000000000000..6a03252d66a3e13c1960568ea23f6dcc673f34af
--- /dev/null
+++ b/src/main/java/com/destroystokyo/paper/loottable/LootableBlockInventory.java
@@ -0,0 +1,17 @@
+package com.destroystokyo.paper.loottable;
+
+import org.bukkit.block.Block;
+import org.jspecify.annotations.NullMarked;
+
+/**
+ * Represents an Inventory that can generate loot, such as Chests inside of Fortresses and Mineshafts
+ */
+@NullMarked
+public interface LootableBlockInventory extends LootableInventory {
+
+ /**
+ * Gets the block that is lootable
+ * @return The Block
+ */
+ Block getBlock();
+}
diff --git a/src/main/java/com/destroystokyo/paper/loottable/LootableEntityInventory.java b/src/main/java/com/destroystokyo/paper/loottable/LootableEntityInventory.java
new file mode 100644
index 0000000000000000000000000000000000000000..31ca54dea65dc0363a0ff7991ba5be3b06533876
--- /dev/null
+++ b/src/main/java/com/destroystokyo/paper/loottable/LootableEntityInventory.java
@@ -0,0 +1,17 @@
+package com.destroystokyo.paper.loottable;
+
+import org.bukkit.entity.Entity;
+import org.jspecify.annotations.NullMarked;
+
+/**
+ * Represents an Inventory that can generate loot, such as Minecarts inside of Mineshafts
+ */
+@NullMarked
+public interface LootableEntityInventory extends LootableInventory {
+
+ /**
+ * Gets the entity that is lootable
+ * @return The Entity
+ */
+ Entity getEntity();
+}
diff --git a/src/main/java/com/destroystokyo/paper/loottable/LootableInventory.java b/src/main/java/com/destroystokyo/paper/loottable/LootableInventory.java
new file mode 100644
index 0000000000000000000000000000000000000000..9fb621200fb3969e8e440341e906acc3547b4039
--- /dev/null
+++ b/src/main/java/com/destroystokyo/paper/loottable/LootableInventory.java
@@ -0,0 +1,128 @@
+package com.destroystokyo.paper.loottable;
+
+import java.util.UUID;
+import org.bukkit.entity.Player;
+import org.bukkit.loot.Lootable;
+import org.jspecify.annotations.NullMarked;
+import org.jspecify.annotations.Nullable;
+
+/**
+ * Represents an Inventory that contains a Loot Table associated to it that will
+ * automatically fill on first open.
+ * <p>
+ * A new feature and API is provided to support automatically refreshing the contents
+ * of the inventory based on that Loot Table after a configurable amount of time has passed.
+ * <p>
+ * The behavior of how the Inventory is filled based on the loot table may vary based
+ * on Minecraft versions and the Loot Table feature.
+ */
+@NullMarked
+public interface LootableInventory extends Lootable {
+
+ /**
+ * Server owners have to enable whether an object in a world should refill
+ *
+ * @return If the world this inventory is currently in has Replenishable Lootables enabled
+ */
+ boolean isRefillEnabled();
+
+ /**
+ * Whether this object has ever been filled
+ *
+ * @return Has ever been filled
+ */
+ boolean hasBeenFilled();
+
+ /**
+ * Has this player ever looted this block
+ *
+ * @param player The player to check
+ * @return Whether this player has looted this block
+ */
+ default boolean hasPlayerLooted(final Player player) {
+ return this.hasPlayerLooted(player.getUniqueId());
+ }
+
+ /**
+ * Checks if this player can loot this block. Takes into account the "restrict player reloot" settings
+ *
+ * @param player the player to check
+ * @return Whether this player can loot this block
+ */
+ boolean canPlayerLoot(UUID player);
+
+ /**
+ * Has this player ever looted this block
+ *
+ * @param player The player to check
+ * @return Whether this player has looted this block
+ */
+ boolean hasPlayerLooted(UUID player);
+
+ /**
+ * Gets the timestamp, in milliseconds, of when the player last looted this object
+ *
+ * @param player The player to check
+ * @return Timestamp last looted, or null if player has not looted this object
+ */
+ default @Nullable Long getLastLooted(final Player player) {
+ return this.getLastLooted(player.getUniqueId());
+ }
+
+ /**
+ * Gets the timestamp, in milliseconds, of when the player last looted this object
+ *
+ * @param player The player to check
+ * @return Timestamp last looted, or null if player has not looted this object
+ */
+ @Nullable Long getLastLooted(UUID player);
+
+ /**
+ * Change the state of whether a player has looted this block
+ *
+ * @param player The player to change state for
+ * @param looted true to add player to looted list, false to remove
+ * @return The previous state of whether the player had looted this or not
+ */
+ default boolean setHasPlayerLooted(final Player player, final boolean looted) {
+ return this.setHasPlayerLooted(player.getUniqueId(), looted);
+ }
+
+ /**
+ * Change the state of whether a player has looted this block
+ *
+ * @param player The player to change state for
+ * @param looted true to add player to looted list, false to remove
+ * @return The previous state of whether the player had looted this or not
+ */
+ boolean setHasPlayerLooted(UUID player, boolean looted);
+
+ /**
+ * Returns Whether this object has been filled and now has a pending refill
+ *
+ * @return Has pending refill
+ */
+ boolean hasPendingRefill();
+
+ /**
+ * Gets the timestamp in milliseconds that the Lootable object was last refilled
+ *
+ * @return -1 if it was never refilled, or timestamp in milliseconds
+ */
+ long getLastFilled();
+
+ /**
+ * Gets the timestamp in milliseconds that the Lootable object will refill
+ *
+ * @return -1 if it is not scheduled for refill, or timestamp in milliseconds
+ */
+ long getNextRefill();
+
+ /**
+ * Sets the timestamp in milliseconds of the next refill for this object
+ *
+ * @param refillAt timestamp in milliseconds. -1 to clear next refill
+ * @return The previous scheduled time to refill, or -1 if was not scheduled
+ */
+ long setNextRefill(long refillAt);
+}
diff --git a/src/main/java/com/destroystokyo/paper/loottable/LootableInventoryReplenishEvent.java b/src/main/java/com/destroystokyo/paper/loottable/LootableInventoryReplenishEvent.java
new file mode 100644
index 0000000000000000000000000000000000000000..994c2183db89fc40d5991d5e1906e4bd04db6291
--- /dev/null
+++ b/src/main/java/com/destroystokyo/paper/loottable/LootableInventoryReplenishEvent.java
@@ -0,0 +1,46 @@
+package com.destroystokyo.paper.loottable;
+
+import org.bukkit.entity.Player;
+import org.bukkit.event.Cancellable;
+import org.bukkit.event.HandlerList;
+import org.bukkit.event.player.PlayerEvent;
+import org.jetbrains.annotations.ApiStatus;
+import org.jspecify.annotations.NullMarked;
+
+@NullMarked
+public class LootableInventoryReplenishEvent extends PlayerEvent implements Cancellable {
+
+ private static final HandlerList HANDLER_LIST = new HandlerList();
+
+ private final LootableInventory inventory;
+ private boolean cancelled;
+
+ @ApiStatus.Internal
+ public LootableInventoryReplenishEvent(final Player player, final LootableInventory inventory) {
+ super(player);
+ this.inventory = inventory;
+ }
+
+ public LootableInventory getInventory() {
+ return this.inventory;
+ }
+
+ @Override
+ public boolean isCancelled() {
+ return this.cancelled;
+ }
+
+ @Override
+ public void setCancelled(final boolean cancel) {
+ this.cancelled = cancel;
+ }
+
+ @Override
+ public HandlerList getHandlers() {
+ return HANDLER_LIST;
+ }
+
+ public static HandlerList getHandlerList() {
+ return HANDLER_LIST;
+ }
+}
diff --git a/src/main/java/org/bukkit/block/Barrel.java b/src/main/java/org/bukkit/block/Barrel.java
index aa1bb7a1a1c94b0b029bb60026efbc7f55584dd7..d3789b2b7dd71d7c1872a0d84698d35a1884101b 100644
--- a/src/main/java/org/bukkit/block/Barrel.java
+++ b/src/main/java/org/bukkit/block/Barrel.java
@@ -5,4 +5,4 @@ import org.bukkit.loot.Lootable;
/**
* Represents a captured state of a Barrel.
*/
-public interface Barrel extends Container, Lootable, Lidded { }
+public interface Barrel extends Container, com.destroystokyo.paper.loottable.LootableBlockInventory, Lidded { } // Paper
diff --git a/src/main/java/org/bukkit/block/Chest.java b/src/main/java/org/bukkit/block/Chest.java
index b451191312e4fb19f2131c2d0a0c0337953f6c7c..db6affbc78106b2d93b41953b624a0bca0ca1d72 100644
--- a/src/main/java/org/bukkit/block/Chest.java
+++ b/src/main/java/org/bukkit/block/Chest.java
@@ -1,5 +1,7 @@
package org.bukkit.block;
+import com.destroystokyo.paper.loottable.LootableBlockInventory; // Paper
+import org.bukkit.Nameable; // Paper
import org.bukkit.inventory.Inventory;
import org.bukkit.loot.Lootable;
import org.jetbrains.annotations.NotNull;
@@ -7,7 +9,7 @@ import org.jetbrains.annotations.NotNull;
/**
* Represents a captured state of a chest.
*/
-public interface Chest extends Container, Lootable, Lidded {
+public interface Chest extends Container, LootableBlockInventory, Lidded { // Paper
/**
* Gets the inventory of the chest block represented by this block state.
diff --git a/src/main/java/org/bukkit/block/Crafter.java b/src/main/java/org/bukkit/block/Crafter.java
index e004920ec1e13daaa2f0969a5cf97b6a7de25df9..8d2dd78fc588a6817dfede8040b9909a7d5bde67 100644
--- a/src/main/java/org/bukkit/block/Crafter.java
+++ b/src/main/java/org/bukkit/block/Crafter.java
@@ -7,7 +7,7 @@ import org.jetbrains.annotations.ApiStatus;
* Represents a captured state of a crafter.
*/
@ApiStatus.Experimental
-public interface Crafter extends Container, Lootable {
+public interface Crafter extends Container, com.destroystokyo.paper.loottable.LootableBlockInventory { // Paper - LootTable API
/**
* Gets the number of ticks which this block will remain in the crafting
diff --git a/src/main/java/org/bukkit/block/Dispenser.java b/src/main/java/org/bukkit/block/Dispenser.java
index 74cd194c9a98245dc52e7e352d7d6c046e1e5cf3..07af1a3f011d4b96275f919d302ac367198e923e 100644
--- a/src/main/java/org/bukkit/block/Dispenser.java
+++ b/src/main/java/org/bukkit/block/Dispenser.java
@@ -1,5 +1,6 @@
package org.bukkit.block;
+import com.destroystokyo.paper.loottable.LootableBlockInventory;
import org.bukkit.Nameable;
import org.bukkit.loot.Lootable;
import org.bukkit.projectiles.BlockProjectileSource;
@@ -8,7 +9,7 @@ import org.jetbrains.annotations.Nullable;
/**
* Represents a captured state of a dispenser.
*/
-public interface Dispenser extends Container, Nameable, Lootable {
+public interface Dispenser extends Container, Nameable, LootableBlockInventory { // Paper
/**
* Gets the BlockProjectileSource object for the dispenser.
diff --git a/src/main/java/org/bukkit/block/Dropper.java b/src/main/java/org/bukkit/block/Dropper.java
index 424392fb5ed4628199b0e73689522aa3c90740cb..c76202321e29ad67597ca3017eb8d9baf6787383 100644
--- a/src/main/java/org/bukkit/block/Dropper.java
+++ b/src/main/java/org/bukkit/block/Dropper.java
@@ -1,11 +1,12 @@
package org.bukkit.block;
+import com.destroystokyo.paper.loottable.LootableBlockInventory;
import org.bukkit.loot.Lootable;
/**
* Represents a captured state of a dropper.
*/
-public interface Dropper extends Container, Lootable {
+public interface Dropper extends Container, LootableBlockInventory { // Paper
/**
* Tries to drop a randomly selected item from the dropper's inventory,
diff --git a/src/main/java/org/bukkit/block/Hopper.java b/src/main/java/org/bukkit/block/Hopper.java
index 58e493099810fb8d4705ecd49b4a5e1e1949b87b..7ade312f180b7e30871d3a3240c76325cc369c26 100644
--- a/src/main/java/org/bukkit/block/Hopper.java
+++ b/src/main/java/org/bukkit/block/Hopper.java
@@ -1,8 +1,9 @@
package org.bukkit.block;
+import com.destroystokyo.paper.loottable.LootableBlockInventory;
import org.bukkit.loot.Lootable;
/**
* Represents a captured state of a hopper.
*/
-public interface Hopper extends Container, Lootable { }
+public interface Hopper extends Container, LootableBlockInventory { } // Paper
diff --git a/src/main/java/org/bukkit/block/ShulkerBox.java b/src/main/java/org/bukkit/block/ShulkerBox.java
index 387b2892886e1ccb2bd928e5111fb9bd41d777ab..5dc5318b0a451937228a8a059dfec1cd9de389a6 100644
--- a/src/main/java/org/bukkit/block/ShulkerBox.java
+++ b/src/main/java/org/bukkit/block/ShulkerBox.java
@@ -1,5 +1,6 @@
package org.bukkit.block;
+import com.destroystokyo.paper.loottable.LootableBlockInventory;
import org.bukkit.DyeColor;
import org.bukkit.loot.Lootable;
import org.jetbrains.annotations.Nullable;
@@ -7,7 +8,7 @@ import org.jetbrains.annotations.Nullable;
/**
* Represents a captured state of a ShulkerBox.
*/
-public interface ShulkerBox extends Container, Lootable, Lidded {
+public interface ShulkerBox extends Container, LootableBlockInventory, Lidded { // Paper
/**
* Get the {@link DyeColor} corresponding to this ShulkerBox
diff --git a/src/main/java/org/bukkit/entity/ChestBoat.java b/src/main/java/org/bukkit/entity/ChestBoat.java
index 5b5c3be107fdaa6c55ceb1bca2c223ebc6ab7f43..4ebe1033c55dbd58d0794809435c935236fabcc2 100644
--- a/src/main/java/org/bukkit/entity/ChestBoat.java
+++ b/src/main/java/org/bukkit/entity/ChestBoat.java
@@ -6,5 +6,5 @@ import org.bukkit.loot.Lootable;
/**
* A {@link Boat} with a chest.
*/
-public interface ChestBoat extends Boat, InventoryHolder, Lootable {
+public interface ChestBoat extends Boat, InventoryHolder, com.destroystokyo.paper.loottable.LootableEntityInventory { // Paper
}
diff --git a/src/main/java/org/bukkit/entity/Mob.java b/src/main/java/org/bukkit/entity/Mob.java
index 2926fa6071bc7640cc10280b5c3962b0ce7686f1..f3f62e13cc1b6172808c52f2d5f520f1f584e6db 100644
--- a/src/main/java/org/bukkit/entity/Mob.java
+++ b/src/main/java/org/bukkit/entity/Mob.java
@@ -61,4 +61,12 @@ public interface Mob extends LivingEntity, Lootable {
*/
@Nullable
public Sound getAmbientSound();
+
+ // Paper start - LootTable API
+ @Override
+ default void setLootTable(final @Nullable org.bukkit.loot.LootTable table, final long seed) {
+ this.setLootTable(table);
+ this.setSeed(seed);
+ }
+ // Paper end - LootTable API
}
diff --git a/src/main/java/org/bukkit/entity/minecart/HopperMinecart.java b/src/main/java/org/bukkit/entity/minecart/HopperMinecart.java
index 937b99f8734d71b2ad33af142afbc251b81d9745..db69687a7ad4b18d17ab1677cae5d8dd4dcd3678 100644
--- a/src/main/java/org/bukkit/entity/minecart/HopperMinecart.java
+++ b/src/main/java/org/bukkit/entity/minecart/HopperMinecart.java
@@ -1,5 +1,6 @@
package org.bukkit.entity.minecart;
+import com.destroystokyo.paper.loottable.LootableEntityInventory;
import org.bukkit.entity.Minecart;
import org.bukkit.inventory.InventoryHolder;
import org.bukkit.loot.Lootable;
@@ -7,7 +8,7 @@ import org.bukkit.loot.Lootable;
/**
* Represents a Minecart with a Hopper inside it
*/
-public interface HopperMinecart extends Minecart, InventoryHolder, Lootable {
+public interface HopperMinecart extends Minecart, InventoryHolder, LootableEntityInventory {
/**
* Checks whether or not this Minecart will pick up
diff --git a/src/main/java/org/bukkit/entity/minecart/StorageMinecart.java b/src/main/java/org/bukkit/entity/minecart/StorageMinecart.java
index 9ea403e6fd8e960d017660e0aec118abeda2c42b..238d118f7788b13cd86b7e9ea3a0fc38e2e09715 100644
--- a/src/main/java/org/bukkit/entity/minecart/StorageMinecart.java
+++ b/src/main/java/org/bukkit/entity/minecart/StorageMinecart.java
@@ -1,5 +1,6 @@
package org.bukkit.entity.minecart;
+import com.destroystokyo.paper.loottable.LootableEntityInventory;
import org.bukkit.entity.Minecart;
import org.bukkit.inventory.InventoryHolder;
import org.bukkit.loot.Lootable;
@@ -9,5 +10,5 @@ import org.bukkit.loot.Lootable;
* minecarts} have their own inventory that can be accessed using methods
* from the {@link InventoryHolder} interface.
*/
-public interface StorageMinecart extends Minecart, InventoryHolder, Lootable {
+public interface StorageMinecart extends Minecart, InventoryHolder, LootableEntityInventory { // Paper
}
diff --git a/src/main/java/org/bukkit/loot/Lootable.java b/src/main/java/org/bukkit/loot/Lootable.java
index b3e9347496fd60aa4f5d18ff256e8d4d73f2d9cd..649dd959035843604525a637dba639a4fbd34f97 100644
--- a/src/main/java/org/bukkit/loot/Lootable.java
+++ b/src/main/java/org/bukkit/loot/Lootable.java
@@ -35,6 +35,31 @@ public interface Lootable {
@Nullable
LootTable getLootTable();
+ // Paper start
+ /**
+ * Set the loot table and seed for a container or entity at the same time.
+ *
+ * @param table the Loot Table this {@link org.bukkit.block.Container} or {@link org.bukkit.entity.Mob} will have.
+ * @param seed the seed to used to generate loot. Default is 0.
+ */
+ void setLootTable(final @Nullable LootTable table, final long seed);
+
+ /**
+ * Returns whether or not this object has a Loot Table
+ * @return Has a loot table
+ */
+ default boolean hasLootTable() {
+ return this.getLootTable() != null;
+ }
+
+ /**
+ * Clears the associated Loot Table to this object
+ */
+ default void clearLootTable() {
+ this.setLootTable(null);
+ }
+ // Paper end
+
/**
* Set the seed used when this Loot Table generates loot.
*