diff options
Diffstat (limited to 'Spigot-Server-Patches-Unmapped/0096-LootTable-API-Replenishable-Lootables-Feature.patch')
-rw-r--r-- | Spigot-Server-Patches-Unmapped/0096-LootTable-API-Replenishable-Lootables-Feature.patch | 736 |
1 files changed, 736 insertions, 0 deletions
diff --git a/Spigot-Server-Patches-Unmapped/0096-LootTable-API-Replenishable-Lootables-Feature.patch b/Spigot-Server-Patches-Unmapped/0096-LootTable-API-Replenishable-Lootables-Feature.patch new file mode 100644 index 0000000000..7d27f20665 --- /dev/null +++ b/Spigot-Server-Patches-Unmapped/0096-LootTable-API-Replenishable-Lootables-Feature.patch @@ -0,0 +1,736 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Aikar <[email protected]> +Date: Sun, 1 May 2016 21:19:14 -0400 +Subject: [PATCH] LootTable API & Replenishable Lootables Feature + +Provides an API to control the loot table for an object. +Also provides a feature that any Lootable Inventory (Chests in Structures) +can automatically replenish after a given time. + +This feature is good for long term worlds so that newer players +do not suffer with "Every chest has been looted" + +diff --git a/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java b/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java +index 5baccb8d50c135ab20c38ffd0690f585514ce5af..eb04fdb172a50ec1f5b7fe78fa0e7655246abd60 100644 +--- a/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java ++++ b/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java +@@ -269,4 +269,26 @@ public class PaperWorldConfig { + this.frostedIceDelayMax = this.getInt("frosted-ice.delay.max", this.frostedIceDelayMax); + log("Frosted Ice: " + (this.frostedIceEnabled ? "enabled" : "disabled") + " / delay: min=" + this.frostedIceDelayMin + ", max=" + this.frostedIceDelayMax); + } ++ ++ public boolean autoReplenishLootables; ++ public boolean restrictPlayerReloot; ++ public boolean changeLootTableSeedOnFill; ++ public int maxLootableRefills; ++ public int lootableRegenMin; ++ public int lootableRegenMax; ++ private void enhancedLootables() { ++ autoReplenishLootables = getBoolean("lootables.auto-replenish", false); ++ restrictPlayerReloot = getBoolean("lootables.restrict-player-reloot", true); ++ changeLootTableSeedOnFill = getBoolean("lootables.reset-seed-on-fill", true); ++ maxLootableRefills = getInt("lootables.max-refills", -1); ++ lootableRegenMin = PaperConfig.getSeconds(getString("lootables.refresh-min", "12h")); ++ lootableRegenMax = PaperConfig.getSeconds(getString("lootables.refresh-max", "2d")); ++ if (autoReplenishLootables) { ++ log("Lootables: Replenishing every " + ++ PaperConfig.timeSummary(lootableRegenMin) + " to " + ++ PaperConfig.timeSummary(lootableRegenMax) + ++ (restrictPlayerReloot ? " (restricting reloot)" : "") ++ ); ++ } ++ } + } +diff --git a/src/main/java/com/destroystokyo/paper/loottable/PaperLootableBlockInventory.java b/src/main/java/com/destroystokyo/paper/loottable/PaperLootableBlockInventory.java +new file mode 100644 +index 0000000000000000000000000000000000000000..5dfc3c8008d64ad4ed71b4904c897f5005491349 +--- /dev/null ++++ b/src/main/java/com/destroystokyo/paper/loottable/PaperLootableBlockInventory.java +@@ -0,0 +1,33 @@ ++package com.destroystokyo.paper.loottable; ++ ++import net.minecraft.core.BlockPosition; ++import net.minecraft.world.level.World; ++import net.minecraft.world.level.block.entity.TileEntityLootable; ++import org.bukkit.Chunk; ++import org.bukkit.block.Block; ++ ++public interface PaperLootableBlockInventory extends LootableBlockInventory, PaperLootableInventory { ++ ++ TileEntityLootable getTileEntity(); ++ ++ @Override ++ default LootableInventory getAPILootableInventory() { ++ return this; ++ } ++ ++ @Override ++ default World getNMSWorld() { ++ return getTileEntity().getWorld(); ++ } ++ ++ default Block getBlock() { ++ final BlockPosition position = getTileEntity().getPosition(); ++ final Chunk bukkitChunk = getTileEntity().getWorld().getChunkAtWorldCoords(position).bukkitChunk; ++ return bukkitChunk.getBlock(position.getX(), position.getY(), position.getZ()); ++ } ++ ++ @Override ++ default PaperLootableInventoryData getLootableData() { ++ return getTileEntity().lootableData; ++ } ++} +diff --git a/src/main/java/com/destroystokyo/paper/loottable/PaperLootableEntityInventory.java b/src/main/java/com/destroystokyo/paper/loottable/PaperLootableEntityInventory.java +new file mode 100644 +index 0000000000000000000000000000000000000000..019a06fa2b43cacd3bbd4d58aba71b3728f37581 +--- /dev/null ++++ b/src/main/java/com/destroystokyo/paper/loottable/PaperLootableEntityInventory.java +@@ -0,0 +1,28 @@ ++package com.destroystokyo.paper.loottable; ++ ++import net.minecraft.world.level.World; ++import org.bukkit.entity.Entity; ++ ++public interface PaperLootableEntityInventory extends LootableEntityInventory, PaperLootableInventory { ++ ++ net.minecraft.world.entity.Entity getHandle(); ++ ++ @Override ++ default LootableInventory getAPILootableInventory() { ++ return this; ++ } ++ ++ default Entity getEntity() { ++ return getHandle().getBukkitEntity(); ++ } ++ ++ @Override ++ default World getNMSWorld() { ++ return getHandle().getWorld(); ++ } ++ ++ @Override ++ default PaperLootableInventoryData getLootableData() { ++ return getHandle().lootableData; ++ } ++} +diff --git a/src/main/java/com/destroystokyo/paper/loottable/PaperLootableInventory.java b/src/main/java/com/destroystokyo/paper/loottable/PaperLootableInventory.java +new file mode 100644 +index 0000000000000000000000000000000000000000..59e8aea749bbba079e3304d9a5854280db2692e9 +--- /dev/null ++++ b/src/main/java/com/destroystokyo/paper/loottable/PaperLootableInventory.java +@@ -0,0 +1,71 @@ ++package com.destroystokyo.paper.loottable; ++ ++import net.minecraft.world.level.World; ++import org.bukkit.loot.Lootable; ++ ++import java.util.UUID; ++ ++public interface PaperLootableInventory extends LootableInventory, Lootable { ++ ++ PaperLootableInventoryData getLootableData(); ++ LootableInventory getAPILootableInventory(); ++ ++ World getNMSWorld(); ++ ++ default org.bukkit.World getBukkitWorld() { ++ return getNMSWorld().getWorld(); ++ } ++ ++ @Override ++ default boolean isRefillEnabled() { ++ return getNMSWorld().paperConfig.autoReplenishLootables; ++ } ++ ++ @Override ++ default boolean hasBeenFilled() { ++ return getLastFilled() != -1; ++ } ++ ++ @Override ++ default boolean hasPlayerLooted(UUID player) { ++ return getLootableData().hasPlayerLooted(player); ++ } ++ ++ @Override ++ default Long getLastLooted(UUID player) { ++ return getLootableData().getLastLooted(player); ++ } ++ ++ @Override ++ default boolean setHasPlayerLooted(UUID player, boolean looted) { ++ final boolean hasLooted = hasPlayerLooted(player); ++ if (hasLooted != looted) { ++ getLootableData().setPlayerLootedState(player, looted); ++ } ++ return hasLooted; ++ } ++ ++ @Override ++ default boolean hasPendingRefill() { ++ long nextRefill = getLootableData().getNextRefill(); ++ return nextRefill != -1 && nextRefill > getLootableData().getLastFill(); ++ } ++ ++ @Override ++ default long getLastFilled() { ++ return getLootableData().getLastFill(); ++ } ++ ++ @Override ++ default long getNextRefill() { ++ return getLootableData().getNextRefill(); ++ } ++ ++ @Override ++ default long setNextRefill(long refillAt) { ++ if (refillAt < -1) { ++ refillAt = -1; ++ } ++ return getLootableData().setNextRefill(refillAt); ++ } ++} +diff --git a/src/main/java/com/destroystokyo/paper/loottable/PaperLootableInventoryData.java b/src/main/java/com/destroystokyo/paper/loottable/PaperLootableInventoryData.java +new file mode 100644 +index 0000000000000000000000000000000000000000..904332454ede006f4ee33337d46b11674d78bef7 +--- /dev/null ++++ b/src/main/java/com/destroystokyo/paper/loottable/PaperLootableInventoryData.java +@@ -0,0 +1,181 @@ ++package com.destroystokyo.paper.loottable; ++ ++import com.destroystokyo.paper.PaperWorldConfig; ++import net.minecraft.nbt.NBTTagCompound; ++import net.minecraft.nbt.NBTTagList; ++import net.minecraft.world.entity.player.EntityHuman; ++import org.bukkit.entity.Player; ++import org.bukkit.loot.LootTable; ++ ++import javax.annotation.Nullable; ++import java.util.HashMap; ++import java.util.Map; ++import java.util.Random; ++import java.util.UUID; ++ ++public class PaperLootableInventoryData { ++ ++ private static final Random RANDOM = new Random(); ++ ++ private long lastFill = -1; ++ private long nextRefill = -1; ++ private int numRefills = 0; ++ private Map<UUID, Long> lootedPlayers; ++ private final PaperLootableInventory lootable; ++ ++ public PaperLootableInventoryData(PaperLootableInventory lootable) { ++ this.lootable = lootable; ++ } ++ ++ long getLastFill() { ++ return this.lastFill; ++ } ++ ++ long getNextRefill() { ++ return this.nextRefill; ++ } ++ ++ long setNextRefill(long nextRefill) { ++ long prev = this.nextRefill; ++ this.nextRefill = nextRefill; ++ return prev; ++ } ++ ++ public boolean shouldReplenish(@Nullable EntityHuman player) { ++ LootTable table = this.lootable.getLootTable(); ++ ++ // No Loot Table associated ++ if (table == null) { ++ return false; ++ } ++ ++ // ALWAYS process the first fill or if the feature is disabled ++ if (this.lastFill == -1 || !this.lootable.getNMSWorld().paperConfig.autoReplenishLootables) { ++ return true; ++ } ++ ++ // Only process refills when a player is set ++ if (player == null) { ++ return false; ++ } ++ ++ // Chest is not scheduled for refill ++ if (this.nextRefill == -1) { ++ return false; ++ } ++ ++ final PaperWorldConfig paperConfig = this.lootable.getNMSWorld().paperConfig; ++ ++ // Check if max refills has been hit ++ if (paperConfig.maxLootableRefills != -1 && this.numRefills >= paperConfig.maxLootableRefills) { ++ return false; ++ } ++ ++ // Refill has not been reached ++ if (this.nextRefill > System.currentTimeMillis()) { ++ return false; ++ } ++ ++ ++ final Player bukkitPlayer = (Player) player.getBukkitEntity(); ++ LootableInventoryReplenishEvent event = new LootableInventoryReplenishEvent(bukkitPlayer, lootable.getAPILootableInventory()); ++ if (paperConfig.restrictPlayerReloot && hasPlayerLooted(player.getUniqueID())) { ++ event.setCancelled(true); ++ } ++ return event.callEvent(); ++ } ++ public void processRefill(@Nullable EntityHuman player) { ++ this.lastFill = System.currentTimeMillis(); ++ final PaperWorldConfig paperConfig = this.lootable.getNMSWorld().paperConfig; ++ if (paperConfig.autoReplenishLootables) { ++ int min = paperConfig.lootableRegenMin; ++ int max = paperConfig.lootableRegenMax; ++ this.nextRefill = this.lastFill + (min + RANDOM.nextInt(max - min + 1)) * 1000L; ++ this.numRefills++; ++ if (paperConfig.changeLootTableSeedOnFill) { ++ this.lootable.setSeed(0); ++ } ++ if (player != null) { // This means that numRefills can be incremented without a player being in the lootedPlayers list - Seems to be EntityMinecartChest specific ++ this.setPlayerLootedState(player.getUniqueID(), true); ++ } ++ } else { ++ this.lootable.clearLootTable(); ++ } ++ } ++ ++ ++ public void loadNbt(NBTTagCompound base) { ++ if (!base.hasKeyOfType("Paper.LootableData", 10)) { // 10 = compound ++ return; ++ } ++ NBTTagCompound comp = base.getCompound("Paper.LootableData"); ++ if (comp.hasKey("lastFill")) { ++ this.lastFill = comp.getLong("lastFill"); ++ } ++ if (comp.hasKey("nextRefill")) { ++ this.nextRefill = comp.getLong("nextRefill"); ++ } ++ ++ if (comp.hasKey("numRefills")) { ++ this.numRefills = comp.getInt("numRefills"); ++ } ++ if (comp.hasKeyOfType("lootedPlayers", 9)) { // 9 = list ++ NBTTagList list = comp.getList("lootedPlayers", 10); // 10 = compound ++ final int size = list.size(); ++ if (size > 0) { ++ this.lootedPlayers = new HashMap<>(list.size()); ++ } ++ for (int i = 0; i < size; i++) { ++ final NBTTagCompound cmp = list.getCompound(i); ++ lootedPlayers.put(cmp.getUUID("UUID"), cmp.getLong("Time")); ++ } ++ } ++ } ++ public void saveNbt(NBTTagCompound base) { ++ NBTTagCompound comp = new NBTTagCompound(); ++ if (this.nextRefill != -1) { ++ comp.setLong("nextRefill", this.nextRefill); ++ } ++ if (this.lastFill != -1) { ++ comp.setLong("lastFill", this.lastFill); ++ } ++ if (this.numRefills != 0) { ++ comp.setInt("numRefills", this.numRefills); ++ } ++ if (this.lootedPlayers != null && !this.lootedPlayers.isEmpty()) { ++ NBTTagList list = new NBTTagList(); ++ for (Map.Entry<UUID, Long> entry : this.lootedPlayers.entrySet()) { ++ NBTTagCompound cmp = new NBTTagCompound(); ++ cmp.setUUID("UUID", entry.getKey()); ++ cmp.setLong("Time", entry.getValue()); ++ list.add(cmp); ++ } ++ comp.set("lootedPlayers", list); ++ } ++ ++ if (!comp.isEmpty()) { ++ base.set("Paper.LootableData", comp); ++ } ++ } ++ ++ void setPlayerLootedState(UUID player, boolean looted) { ++ if (looted && this.lootedPlayers == null) { ++ this.lootedPlayers = new HashMap<>(); ++ } ++ if (looted) { ++ if (!this.lootedPlayers.containsKey(player)) { ++ this.lootedPlayers.put(player, System.currentTimeMillis()); ++ } ++ } else if (this.lootedPlayers != null) { ++ this.lootedPlayers.remove(player); ++ } ++ } ++ ++ boolean hasPlayerLooted(UUID player) { ++ return this.lootedPlayers != null && this.lootedPlayers.containsKey(player); ++ } ++ ++ Long getLastLooted(UUID player) { ++ return lootedPlayers != null ? lootedPlayers.get(player) : null; ++ } ++} +diff --git a/src/main/java/com/destroystokyo/paper/loottable/PaperMinecartLootableInventory.java b/src/main/java/com/destroystokyo/paper/loottable/PaperMinecartLootableInventory.java +new file mode 100644 +index 0000000000000000000000000000000000000000..c682bd7700d8103533026d46cfc63a7abde5a5f4 +--- /dev/null ++++ b/src/main/java/com/destroystokyo/paper/loottable/PaperMinecartLootableInventory.java +@@ -0,0 +1,62 @@ ++package com.destroystokyo.paper.loottable; ++ ++import net.minecraft.world.entity.Entity; ++import net.minecraft.world.entity.vehicle.EntityMinecartContainer; ++import net.minecraft.world.level.World; ++import org.bukkit.Bukkit; ++import org.bukkit.craftbukkit.util.CraftNamespacedKey; ++ ++public class PaperMinecartLootableInventory implements PaperLootableEntityInventory { ++ ++ private EntityMinecartContainer entity; ++ ++ public PaperMinecartLootableInventory(EntityMinecartContainer entity) { ++ this.entity = entity; ++ } ++ ++ @Override ++ public org.bukkit.loot.LootTable getLootTable() { ++ return entity.lootTable != null ? Bukkit.getLootTable(CraftNamespacedKey.fromMinecraft(entity.lootTable)) : null; ++ } ++ ++ @Override ++ public void setLootTable(org.bukkit.loot.LootTable table, long seed) { ++ setLootTable(table); ++ setSeed(seed); ++ } ++ ++ @Override ++ public void setSeed(long seed) { ++ entity.lootTableSeed = seed; ++ } ++ ++ @Override ++ public long getSeed() { ++ return entity.lootTableSeed; ++ } ++ ++ @Override ++ public void setLootTable(org.bukkit.loot.LootTable table) { ++ entity.lootTable = (table == null) ? null : CraftNamespacedKey.toMinecraft(table.getKey()); ++ } ++ ++ @Override ++ public PaperLootableInventoryData getLootableData() { ++ return entity.lootableData; ++ } ++ ++ @Override ++ public Entity getHandle() { ++ return entity; ++ } ++ ++ @Override ++ public LootableInventory getAPILootableInventory() { ++ return (LootableInventory) entity.getBukkitEntity(); ++ } ++ ++ @Override ++ public World getNMSWorld() { ++ return entity.world; ++ } ++} +diff --git a/src/main/java/com/destroystokyo/paper/loottable/PaperTileEntityLootableInventory.java b/src/main/java/com/destroystokyo/paper/loottable/PaperTileEntityLootableInventory.java +new file mode 100644 +index 0000000000000000000000000000000000000000..9dae34370d014a291f025f83b55e18bff4619a23 +--- /dev/null ++++ b/src/main/java/com/destroystokyo/paper/loottable/PaperTileEntityLootableInventory.java +@@ -0,0 +1,65 @@ ++package com.destroystokyo.paper.loottable; ++ ++import net.minecraft.server.MCUtil; ++import net.minecraft.world.level.World; ++import net.minecraft.world.level.block.entity.TileEntityLootable; ++import org.bukkit.Bukkit; ++import org.bukkit.craftbukkit.util.CraftNamespacedKey; ++ ++public class PaperTileEntityLootableInventory implements PaperLootableBlockInventory { ++ private TileEntityLootable tileEntityLootable; ++ ++ public PaperTileEntityLootableInventory(TileEntityLootable tileEntityLootable) { ++ this.tileEntityLootable = tileEntityLootable; ++ } ++ ++ @Override ++ public org.bukkit.loot.LootTable getLootTable() { ++ return tileEntityLootable.lootTable != null ? Bukkit.getLootTable(CraftNamespacedKey.fromMinecraft(tileEntityLootable.lootTable)) : null; ++ } ++ ++ @Override ++ public void setLootTable(org.bukkit.loot.LootTable table, long seed) { ++ setLootTable(table); ++ setSeed(seed); ++ } ++ ++ @Override ++ public void setLootTable(org.bukkit.loot.LootTable table) { ++ tileEntityLootable.lootTable = (table == null) ? null : CraftNamespacedKey.toMinecraft(table.getKey()); ++ } ++ ++ @Override ++ public void setSeed(long seed) { ++ tileEntityLootable.lootTableSeed = seed; ++ } ++ ++ @Override ++ public long getSeed() { ++ return tileEntityLootable.lootTableSeed; ++ } ++ ++ @Override ++ public PaperLootableInventoryData getLootableData() { ++ return tileEntityLootable.lootableData; ++ } ++ ++ @Override ++ public TileEntityLootable getTileEntity() { ++ return tileEntityLootable; ++ } ++ ++ @Override ++ public LootableInventory getAPILootableInventory() { ++ World world = tileEntityLootable.getWorld(); ++ if (world == null) { ++ return null; ++ } ++ return (LootableInventory) getBukkitWorld().getBlockAt(MCUtil.toLocation(world, tileEntityLootable.getPosition())).getState(); ++ } ++ ++ @Override ++ public World getNMSWorld() { ++ return tileEntityLootable.getWorld(); ++ } ++} +diff --git a/src/main/java/net/minecraft/world/entity/Entity.java b/src/main/java/net/minecraft/world/entity/Entity.java +index 6cbb797cb0de4b26d8ddd7f0bf567f49bd36f9c0..2b1b46bda48c0b137fe914c47a387e6e72a1be40 100644 +--- a/src/main/java/net/minecraft/world/entity/Entity.java ++++ b/src/main/java/net/minecraft/world/entity/Entity.java +@@ -158,6 +158,7 @@ public abstract class Entity implements INamableTileEntity, ICommandListener, ne + }; + // Paper end + ++ public com.destroystokyo.paper.loottable.PaperLootableInventoryData lootableData; // Paper + private CraftEntity bukkitEntity; + + public CraftEntity getBukkitEntity() { +diff --git a/src/main/java/net/minecraft/world/entity/vehicle/EntityMinecartContainer.java b/src/main/java/net/minecraft/world/entity/vehicle/EntityMinecartContainer.java +index c4b970c37b1792ac0022936f2df4740183621a0d..0166d11cb540a536390f486e1069d6119d8d23d6 100644 +--- a/src/main/java/net/minecraft/world/entity/vehicle/EntityMinecartContainer.java ++++ b/src/main/java/net/minecraft/world/entity/vehicle/EntityMinecartContainer.java +@@ -46,6 +46,7 @@ public abstract class EntityMinecartContainer extends EntityMinecartAbstract imp + public long lootTableSeed; + + // CraftBukkit start ++ { this.lootableData = new com.destroystokyo.paper.loottable.PaperLootableInventoryData(new com.destroystokyo.paper.loottable.PaperMinecartLootableInventory(this)); } // Paper + public List<HumanEntity> transaction = new java.util.ArrayList<HumanEntity>(); + private int maxStack = MAX_STACK; + +@@ -203,12 +204,13 @@ public abstract class EntityMinecartContainer extends EntityMinecartAbstract imp + @Override + protected void saveData(NBTTagCompound nbttagcompound) { + super.saveData(nbttagcompound); ++ this.lootableData.saveNbt(nbttagcompound); // Paper + if (this.lootTable != null) { + nbttagcompound.setString("LootTable", this.lootTable.toString()); + if (this.lootTableSeed != 0L) { + nbttagcompound.setLong("LootTableSeed", this.lootTableSeed); + } +- } else { ++ } if (true) { // Paper - Always save the items, Table may stick around + ContainerUtil.a(nbttagcompound, this.items); + } + +@@ -217,11 +219,12 @@ public abstract class EntityMinecartContainer extends EntityMinecartAbstract imp + @Override + protected void loadData(NBTTagCompound nbttagcompound) { + super.loadData(nbttagcompound); ++ this.lootableData.loadNbt(nbttagcompound); // Paper + this.items = NonNullList.a(this.getSize(), ItemStack.b); + if (nbttagcompound.hasKeyOfType("LootTable", 8)) { + this.lootTable = new MinecraftKey(nbttagcompound.getString("LootTable")); + this.lootTableSeed = nbttagcompound.getLong("LootTableSeed"); +- } else { ++ } if (true) { // Paper - always load the items, table may still remain + ContainerUtil.b(nbttagcompound, this.items); + } + +@@ -252,14 +255,15 @@ public abstract class EntityMinecartContainer extends EntityMinecartAbstract imp + } + + public void d(@Nullable EntityHuman entityhuman) { +- if (this.lootTable != null && this.world.getMinecraftServer() != null) { ++ if (this.lootableData.shouldReplenish(entityhuman) && this.world.getMinecraftServer() != null) { // Paper + LootTable loottable = this.world.getMinecraftServer().getLootTableRegistry().getLootTable(this.lootTable); + + if (entityhuman instanceof EntityPlayer) { + CriterionTriggers.N.a((EntityPlayer) entityhuman, this.lootTable); + } + +- this.lootTable = null; ++ //this.lootTable = null; // Paper ++ this.lootableData.processRefill(entityhuman); // Paper + LootTableInfo.Builder loottableinfo_builder = (new LootTableInfo.Builder((WorldServer) this.world)).set(LootContextParameters.ORIGIN, this.getPositionVector()).a(this.lootTableSeed); + + if (entityhuman != null) { +diff --git a/src/main/java/net/minecraft/world/level/block/entity/TileEntityLootable.java b/src/main/java/net/minecraft/world/level/block/entity/TileEntityLootable.java +index 62e6833a90d7adae3c7df33e3bc73b4288e0370b..1508e267a38555820e2d31f3075adca185fbd4b6 100644 +--- a/src/main/java/net/minecraft/world/level/block/entity/TileEntityLootable.java ++++ b/src/main/java/net/minecraft/world/level/block/entity/TileEntityLootable.java +@@ -27,6 +27,7 @@ public abstract class TileEntityLootable extends TileEntityContainer { + @Nullable + public MinecraftKey lootTable; + public long lootTableSeed; ++ public final com.destroystokyo.paper.loottable.PaperLootableInventoryData lootableData = new com.destroystokyo.paper.loottable.PaperLootableInventoryData(new com.destroystokyo.paper.loottable.PaperTileEntityLootableInventory(this)); // Paper + + protected TileEntityLootable(TileEntityTypes<?> tileentitytypes) { + super(tileentitytypes); +@@ -42,16 +43,19 @@ public abstract class TileEntityLootable extends TileEntityContainer { + } + + protected boolean b(NBTTagCompound nbttagcompound) { ++ this.lootableData.loadNbt(nbttagcompound); // Paper + if (nbttagcompound.hasKeyOfType("LootTable", 8)) { + this.lootTable = new MinecraftKey(nbttagcompound.getString("LootTable")); ++ try { org.bukkit.craftbukkit.util.CraftNamespacedKey.fromMinecraft(this.lootTable); } catch (IllegalArgumentException ex) { this.lootTable = null; } // Paper - validate + this.lootTableSeed = nbttagcompound.getLong("LootTableSeed"); +- return true; ++ return false; // Paper - always load the items, table may still remain + } else { + return false; + } + } + + protected boolean c(NBTTagCompound nbttagcompound) { ++ this.lootableData.saveNbt(nbttagcompound); // Paper + if (this.lootTable == null) { + return false; + } else { +@@ -60,19 +64,20 @@ public abstract class TileEntityLootable extends TileEntityContainer { + nbttagcompound.setLong("LootTableSeed", this.lootTableSeed); + } + +- return true; ++ return false; // Paper - always save the items, table may still remain + } + } + + public void d(@Nullable EntityHuman entityhuman) { +- if (this.lootTable != null && this.world.getMinecraftServer() != null) { ++ if (this.lootableData.shouldReplenish(entityhuman) && this.world.getMinecraftServer() != null) { // Paper + LootTable loottable = this.world.getMinecraftServer().getLootTableRegistry().getLootTable(this.lootTable); + + if (entityhuman instanceof EntityPlayer) { + CriterionTriggers.N.a((EntityPlayer) entityhuman, this.lootTable); + } + +- this.lootTable = null; ++ //this.lootTable = null; // Paper ++ this.lootableData.processRefill(entityhuman); // Paper + LootTableInfo.Builder loottableinfo_builder = (new LootTableInfo.Builder((WorldServer) this.world)).set(LootContextParameters.ORIGIN, Vec3D.a((BaseBlockPosition) this.position)).a(this.lootTableSeed); + + if (entityhuman != null) { +diff --git a/src/main/java/org/bukkit/craftbukkit/block/CraftBlockEntityState.java b/src/main/java/org/bukkit/craftbukkit/block/CraftBlockEntityState.java +index 524f27830752f424493c3ae8d793b871f6495594..dcf3f9265b0b00a7bbb9ff428e10da3c198ba08a 100644 +--- a/src/main/java/org/bukkit/craftbukkit/block/CraftBlockEntityState.java ++++ b/src/main/java/org/bukkit/craftbukkit/block/CraftBlockEntityState.java +@@ -64,7 +64,7 @@ public class CraftBlockEntityState<T extends TileEntity> extends CraftBlockState + } + + // gets the wrapped TileEntity +- protected T getTileEntity() { ++ public T getTileEntity() { // Paper - protected -> public + return tileEntity; + } + +diff --git a/src/main/java/org/bukkit/craftbukkit/block/CraftChest.java b/src/main/java/org/bukkit/craftbukkit/block/CraftChest.java +index 486fa8937d644f59a770db163482259525a7e465..54eb170fd533b0e91572601268fcbc167ed9bb5c 100644 +--- a/src/main/java/org/bukkit/craftbukkit/block/CraftChest.java ++++ b/src/main/java/org/bukkit/craftbukkit/block/CraftChest.java +@@ -12,8 +12,9 @@ import org.bukkit.craftbukkit.CraftWorld; + import org.bukkit.craftbukkit.inventory.CraftInventory; + import org.bukkit.craftbukkit.inventory.CraftInventoryDoubleChest; + import org.bukkit.inventory.Inventory; ++import com.destroystokyo.paper.loottable.PaperLootableBlockInventory; // Paper + +-public class CraftChest extends CraftLootable<TileEntityChest> implements Chest { ++public class CraftChest extends CraftLootable<TileEntityChest> implements Chest, PaperLootableBlockInventory { // Paper + + public CraftChest(final Block block) { + super(block, TileEntityChest.class); +diff --git a/src/main/java/org/bukkit/craftbukkit/block/CraftLootable.java b/src/main/java/org/bukkit/craftbukkit/block/CraftLootable.java +index f0a7e61a26c4668a9aa823d641f29bdecd42dd1f..3512054ede5fd1dd7605444e827e30a0be47f935 100644 +--- a/src/main/java/org/bukkit/craftbukkit/block/CraftLootable.java ++++ b/src/main/java/org/bukkit/craftbukkit/block/CraftLootable.java +@@ -10,7 +10,7 @@ import org.bukkit.craftbukkit.util.CraftNamespacedKey; + import org.bukkit.loot.LootTable; + import org.bukkit.loot.Lootable; + +-public abstract class CraftLootable<T extends TileEntityLootable> extends CraftContainer<T> implements Nameable, Lootable { ++public abstract class CraftLootable<T extends TileEntityLootable> extends CraftContainer<T> implements Nameable, Lootable, com.destroystokyo.paper.loottable.PaperLootableBlockInventory { // Paper + + public CraftLootable(Block block, Class<T> tileEntityClass) { + super(block, tileEntityClass); +@@ -54,7 +54,7 @@ public abstract class CraftLootable<T extends TileEntityLootable> extends CraftC + setLootTable(getLootTable(), seed); + } + +- private void setLootTable(LootTable table, long seed) { ++ public void setLootTable(LootTable table, long seed) { // Paper - public + MinecraftKey key = (table == null) ? null : CraftNamespacedKey.toMinecraft(table.getKey()); + getSnapshot().setLootTable(key, seed); + } +diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftMinecartChest.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftMinecartChest.java +index cbd121c21adfaf098dadca33de16a2e68d83c19a..d9a2552782c9242fb84cc0c8309a614a44777509 100644 +--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftMinecartChest.java ++++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftMinecartChest.java +@@ -8,7 +8,7 @@ import org.bukkit.entity.minecart.StorageMinecart; + import org.bukkit.inventory.Inventory; + + @SuppressWarnings("deprecation") +-public class CraftMinecartChest extends CraftMinecartContainer implements StorageMinecart { ++public class CraftMinecartChest extends CraftMinecartContainer implements StorageMinecart, com.destroystokyo.paper.loottable.PaperLootableEntityInventory { // Paper + private final CraftInventory inventory; + + public CraftMinecartChest(CraftServer server, EntityMinecartChest entity) { +diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftMinecartContainer.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftMinecartContainer.java +index 5ffb8108f456c2f7f3ed1a25249baccb4cbf4add..bf8b5b25d1af0c5129261e10abf2866521b2c375 100644 +--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftMinecartContainer.java ++++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftMinecartContainer.java +@@ -47,7 +47,7 @@ public abstract class CraftMinecartContainer extends CraftMinecart implements Lo + return getHandle().lootTableSeed; + } + +- private void setLootTable(LootTable table, long seed) { ++ public void setLootTable(LootTable table, long seed) { // Paper + MinecraftKey newKey = (table == null) ? null : CraftNamespacedKey.toMinecraft(table.getKey()); + getHandle().setLootTable(newKey, seed); + } +diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftMinecartHopper.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftMinecartHopper.java +index 17a42aec76f32a28b0c9885c60d1ed50c6727161..bfdcf01d2c6570493e86330d56500427dbb23146 100644 +--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftMinecartHopper.java ++++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftMinecartHopper.java +@@ -7,7 +7,7 @@ import org.bukkit.entity.EntityType; + import org.bukkit.entity.minecart.HopperMinecart; + import org.bukkit.inventory.Inventory; + +-public final class CraftMinecartHopper extends CraftMinecartContainer implements HopperMinecart { ++public final class CraftMinecartHopper extends CraftMinecartContainer implements HopperMinecart, com.destroystokyo.paper.loottable.PaperLootableEntityInventory { // Paper + private final CraftInventory inventory; + + public CraftMinecartHopper(CraftServer server, EntityMinecartHopper entity) { |