From 83e8df21605fee2b292d84c84fd1d19f40db2794 Mon Sep 17 00:00:00 2001 From: Aikar 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 31b6dce..a1ccdf1 100644 --- a/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java +++ b/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java @@ -333,4 +333,26 @@ public class PaperWorldConfig { this.frostedIceDelayMax = this.getInt("frosted-ice.delay.max", this.frostedIceDelayMax); this.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/CraftLootable.java b/src/main/java/com/destroystokyo/paper/loottable/CraftLootable.java new file mode 100644 index 0000000..36c36d1 --- /dev/null +++ b/src/main/java/com/destroystokyo/paper/loottable/CraftLootable.java @@ -0,0 +1,12 @@ +package com.destroystokyo.paper.loottable; + +import net.minecraft.server.World; + +interface CraftLootable extends Lootable { + + World getNMSWorld(); + + default org.bukkit.World getBukkitWorld() { + return getNMSWorld().getWorld(); + } +} diff --git a/src/main/java/com/destroystokyo/paper/loottable/CraftLootableBlockInventory.java b/src/main/java/com/destroystokyo/paper/loottable/CraftLootableBlockInventory.java new file mode 100644 index 0000000..20d236c --- /dev/null +++ b/src/main/java/com/destroystokyo/paper/loottable/CraftLootableBlockInventory.java @@ -0,0 +1,33 @@ +package com.destroystokyo.paper.loottable; + +import net.minecraft.server.BlockPosition; +import net.minecraft.server.TileEntityLootable; +import net.minecraft.server.World; +import org.bukkit.Chunk; +import org.bukkit.block.Block; + +public interface CraftLootableBlockInventory extends LootableBlockInventory, CraftLootableInventory { + + 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 CraftLootableInventoryData getLootableData() { + return getTileEntity().getLootableData(); + } +} diff --git a/src/main/java/com/destroystokyo/paper/loottable/CraftLootableEntityInventory.java b/src/main/java/com/destroystokyo/paper/loottable/CraftLootableEntityInventory.java new file mode 100644 index 0000000..1150dee --- /dev/null +++ b/src/main/java/com/destroystokyo/paper/loottable/CraftLootableEntityInventory.java @@ -0,0 +1,31 @@ +package com.destroystokyo.paper.loottable; + +import net.minecraft.server.World; +import org.bukkit.entity.Entity; + +public interface CraftLootableEntityInventory extends LootableEntityInventory, CraftLootableInventory { + + net.minecraft.server.Entity getHandle(); + + @Override + default LootableInventory getAPILootableInventory() { + return this; + } + + default Entity getEntity() { + return getHandle().getBukkitEntity(); + } + + @Override + default World getNMSWorld() { + return getHandle().getWorld(); + } + + @Override + default CraftLootableInventoryData getLootableData() { + if (getHandle() instanceof CraftLootableInventory) { + return ((CraftLootableInventory) getHandle()).getLootableData(); + } + return null; + } +} diff --git a/src/main/java/com/destroystokyo/paper/loottable/CraftLootableInventory.java b/src/main/java/com/destroystokyo/paper/loottable/CraftLootableInventory.java new file mode 100644 index 0000000..6680976 --- /dev/null +++ b/src/main/java/com/destroystokyo/paper/loottable/CraftLootableInventory.java @@ -0,0 +1,88 @@ +package com.destroystokyo.paper.loottable; + +import org.apache.commons.lang.Validate; + +import java.util.UUID; + +public interface CraftLootableInventory extends CraftLootable, LootableInventory { + + CraftLootableInventoryData getLootableData(); + LootableInventory getAPILootableInventory(); + + @Override + default boolean isRefillEnabled() { + return getNMSWorld().paperConfig.autoReplenishLootables; + } + + @Override + default boolean hasBeenFilled() { + return getLastFilled() != -1; + } + + @Override + default String getLootTableName() { + return getLootableData().getLootable().getLootTableName(); + } + + @Override + default String setLootTable(String name, long seed) { + Validate.notNull(name); + + String prevLootTable = getLootTableName(); + getLootableData().getLootable().setLootTable(name, seed); + return prevLootTable; + } + + @Override + default long getLootTableSeed() { + return getLootableData().getLootable().getLootTableSeed(); + } + + @Override + default void clearLootTable() { + getLootableData().getLootable().clearLootTable(); + } + + @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/CraftLootableInventoryData.java b/src/main/java/com/destroystokyo/paper/loottable/CraftLootableInventoryData.java new file mode 100644 index 0000000..01c2713 --- /dev/null +++ b/src/main/java/com/destroystokyo/paper/loottable/CraftLootableInventoryData.java @@ -0,0 +1,182 @@ +package com.destroystokyo.paper.loottable; + +import com.destroystokyo.paper.PaperWorldConfig; +import net.minecraft.server.*; +import org.bukkit.entity.Player; + +import javax.annotation.Nullable; +import java.util.HashMap; +import java.util.Map; +import java.util.Random; +import java.util.UUID; + +public class CraftLootableInventoryData { + + private static final Random RANDOM = new Random(); + + private long lastFill = -1; + private long nextRefill = -1; + private int numRefills = 0; + private Map lootedPlayers; + private final CraftLootableInventory lootable; + + public CraftLootableInventoryData(CraftLootableInventory 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; + } + + CraftLootableInventory getLootable() { + return lootable; + } + + public boolean shouldReplenish(@Nullable EntityHuman player) { + String tableName = this.lootable.getLootTableName(); + + // No Loot Table associated + if (tableName == null) { + return false; + } + + // ALWAYS process the first fill + if (this.lastFill == -1) { + 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 * 1000; + int max = paperConfig.lootableRegenMax * 1000; + this.nextRefill = this.lastFill + min + RANDOM.nextInt(max - min + 1); + this.numRefills++; + if (paperConfig.changeLootTableSeedOnFill) { + this.lootable.setLootTableSeed(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.get(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 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/net/minecraft/server/EntityMinecartContainer.java b/src/main/java/net/minecraft/server/EntityMinecartContainer.java index 19f9eb6..7216fa5 100644 --- a/src/main/java/net/minecraft/server/EntityMinecartContainer.java +++ b/src/main/java/net/minecraft/server/EntityMinecartContainer.java @@ -5,17 +5,21 @@ import javax.annotation.Nullable; // CraftBukkit start import java.util.List; import org.bukkit.Location; + +import com.destroystokyo.paper.loottable.CraftLootableInventoryData; // Paper +import com.destroystokyo.paper.loottable.CraftLootableInventory; // Paper +import com.destroystokyo.paper.loottable.LootableInventory; // Paper import org.bukkit.craftbukkit.entity.CraftHumanEntity; import org.bukkit.entity.HumanEntity; import org.bukkit.inventory.InventoryHolder; // CraftBukkit end -public abstract class EntityMinecartContainer extends EntityMinecartAbstract implements ITileInventory, ILootable { +public abstract class EntityMinecartContainer extends EntityMinecartAbstract implements ITileInventory, ILootable, CraftLootableInventory { // Paper private ItemStack[] items = new ItemStack[27]; // CraftBukkit - 36 -> 27 private boolean b = true; private MinecraftKey c; - private long d; + private long d;public long getLootTableSeed() { return d; } // Paper // OBFHELPER // CraftBukkit start public List transaction = new java.util.ArrayList(); @@ -141,6 +145,7 @@ public abstract class EntityMinecartContainer extends EntityMinecartAbstract imp protected void b(NBTTagCompound nbttagcompound) { super.b(nbttagcompound); + lootableData.saveNbt(nbttagcompound); // Paper if (this.c != null) { nbttagcompound.setString("LootTable", this.c.toString()); if (this.d != 0L) { @@ -167,6 +172,7 @@ public abstract class EntityMinecartContainer extends EntityMinecartAbstract imp protected void a(NBTTagCompound nbttagcompound) { super.a(nbttagcompound); this.items = new ItemStack[this.getSize()]; + lootableData.loadNbt(nbttagcompound); // Paper if (nbttagcompound.hasKeyOfType("LootTable", 8)) { this.c = new MinecraftKey(nbttagcompound.getString("LootTable")); this.d = nbttagcompound.getLong("LootTableSeed"); @@ -228,10 +234,10 @@ public abstract class EntityMinecartContainer extends EntityMinecartAbstract imp } public void f(@Nullable EntityHuman entityhuman) { - if (this.c != null) { + if (lootableData.shouldReplenish(entityhuman)) { // Paper LootTable loottable = this.world.ak().a(this.c); - this.c = null; + lootableData.processRefill(entityhuman); // Paper Random random; if (this.d == 0L) { @@ -260,12 +266,52 @@ public abstract class EntityMinecartContainer extends EntityMinecartAbstract imp } + public void setLootTable(MinecraftKey key, long seed) { a(key, seed);} // Paper // OBFHELPER public void a(MinecraftKey minecraftkey, long i) { this.c = minecraftkey; this.d = i; } + + public MinecraftKey getLootTableKey() { return b(); } // Paper // OBFHELPER public MinecraftKey b() { return this.c; } + + // Paper start + private final CraftLootableInventoryData lootableData = new CraftLootableInventoryData(this); + + @Override + public CraftLootableInventoryData getLootableData() { + return lootableData; + } + + @Override + public LootableInventory getAPILootableInventory() { + return (LootableInventory) this.getBukkitEntity(); + } + + @Override + public World getNMSWorld() { + return this.world; + } + + public String getLootTableName() { + final MinecraftKey key = getLootTableKey(); + return key != null ? key.toString() : null; + } + + @Override + public String setLootTable(String name, long seed) { + String prev = getLootTableName(); + setLootTable(new MinecraftKey(name), seed); + return prev; + } + + @Override + public void clearLootTable() { + //noinspection RedundantCast + this.c = (MinecraftKey) null; + } + // Paper end } diff --git a/src/main/java/net/minecraft/server/TileEntityLootable.java b/src/main/java/net/minecraft/server/TileEntityLootable.java index 3dc58bf..7717316 100644 --- a/src/main/java/net/minecraft/server/TileEntityLootable.java +++ b/src/main/java/net/minecraft/server/TileEntityLootable.java @@ -1,16 +1,21 @@ package net.minecraft.server; +import com.destroystokyo.paper.loottable.CraftLootableInventoryData; // Paper +import com.destroystokyo.paper.loottable.CraftLootableInventory; // Paper +import com.destroystokyo.paper.loottable.LootableInventory; // Paper + import java.util.Random; import javax.annotation.Nullable; -public abstract class TileEntityLootable extends TileEntityContainer implements ILootable { +public abstract class TileEntityLootable extends TileEntityContainer implements ILootable, CraftLootableInventory { // Paper protected MinecraftKey m; - protected long n; + protected long n; public long getLootTableSeed() { return n; } // Paper // OBFHELPER public TileEntityLootable() {} protected boolean d(NBTTagCompound nbttagcompound) { + lootableData.loadNbt(nbttagcompound); // Paper if (nbttagcompound.hasKeyOfType("LootTable", 8)) { this.m = new MinecraftKey(nbttagcompound.getString("LootTable")); this.n = nbttagcompound.getLong("LootTableSeed"); @@ -21,6 +26,7 @@ public abstract class TileEntityLootable extends TileEntityContainer implements } protected boolean e(NBTTagCompound nbttagcompound) { + lootableData.saveNbt(nbttagcompound); // Paper if (this.m != null) { nbttagcompound.setString("LootTable", this.m.toString()); if (this.n != 0L) { @@ -34,10 +40,10 @@ public abstract class TileEntityLootable extends TileEntityContainer implements } protected void d(@Nullable EntityHuman entityhuman) { - if (this.m != null) { + if (lootableData.shouldReplenish(entityhuman)) { // Paper LootTable loottable = this.world.ak().a(this.m); - this.m = null; + lootableData.processRefill(entityhuman); // Paper Random random; if (this.n == 0L) { @@ -57,12 +63,51 @@ public abstract class TileEntityLootable extends TileEntityContainer implements } + public MinecraftKey getLootTableKey() { return b(); } // Paper // OBFHELPER public MinecraftKey b() { return this.m; } + public void setLootTable(MinecraftKey key, long seed) { a(key, seed);} // Paper // OBFHELPER public void a(MinecraftKey minecraftkey, long i) { this.m = minecraftkey; this.n = i; } + + // Paper start - LootTable API + private final CraftLootableInventoryData lootableData = new CraftLootableInventoryData(this); + + @Override + public CraftLootableInventoryData getLootableData() { + return lootableData; + } + + @Override + public LootableInventory getAPILootableInventory() { + return (LootableInventory) getBukkitWorld().getBlockAt(MCUtil.toLocation(world, getPosition())).getState(); + } + + @Override + public World getNMSWorld() { + return world; + } + + public String getLootTableName() { + final MinecraftKey key = getLootTableKey(); + return key != null ? key.toString() : null; + } + + @Override + public String setLootTable(String name, long seed) { + String prev = getLootTableName(); + setLootTable(new MinecraftKey(name), seed); + return prev; + } + + @Override + public void clearLootTable() { + //noinspection RedundantCast + this.m = (MinecraftKey) null; + } + // Paper end } diff --git a/src/main/java/org/bukkit/craftbukkit/block/CraftChest.java b/src/main/java/org/bukkit/craftbukkit/block/CraftChest.java index ce36ee4..99b039b 100644 --- a/src/main/java/org/bukkit/craftbukkit/block/CraftChest.java +++ b/src/main/java/org/bukkit/craftbukkit/block/CraftChest.java @@ -1,5 +1,6 @@ package org.bukkit.craftbukkit.block; +import com.destroystokyo.paper.loottable.CraftLootableBlockInventory; // Paper import net.minecraft.server.BlockPosition; import net.minecraft.server.TileEntityChest; @@ -11,7 +12,7 @@ import org.bukkit.craftbukkit.inventory.CraftInventory; import org.bukkit.craftbukkit.inventory.CraftInventoryDoubleChest; import org.bukkit.inventory.Inventory; -public class CraftChest extends CraftBlockState implements Chest { +public class CraftChest extends CraftBlockState implements Chest, CraftLootableBlockInventory { // Paper private final CraftWorld world; private final TileEntityChest chest; diff --git a/src/main/java/org/bukkit/craftbukkit/block/CraftDispenser.java b/src/main/java/org/bukkit/craftbukkit/block/CraftDispenser.java index a3ca3a5..cd7e65d 100644 --- a/src/main/java/org/bukkit/craftbukkit/block/CraftDispenser.java +++ b/src/main/java/org/bukkit/craftbukkit/block/CraftDispenser.java @@ -1,5 +1,6 @@ package org.bukkit.craftbukkit.block; +import com.destroystokyo.paper.loottable.CraftLootableBlockInventory; // Paper import net.minecraft.server.BlockDispenser; import net.minecraft.server.BlockPosition; import net.minecraft.server.Blocks; @@ -14,7 +15,7 @@ import org.bukkit.craftbukkit.projectiles.CraftBlockProjectileSource; import org.bukkit.inventory.Inventory; import org.bukkit.projectiles.BlockProjectileSource; -public class CraftDispenser extends CraftBlockState implements Dispenser { +public class CraftDispenser extends CraftBlockState implements Dispenser, CraftLootableBlockInventory { // Paper private final CraftWorld world; private final TileEntityDispenser dispenser; diff --git a/src/main/java/org/bukkit/craftbukkit/block/CraftHopper.java b/src/main/java/org/bukkit/craftbukkit/block/CraftHopper.java index db844a3..1e805a7 100644 --- a/src/main/java/org/bukkit/craftbukkit/block/CraftHopper.java +++ b/src/main/java/org/bukkit/craftbukkit/block/CraftHopper.java @@ -1,5 +1,6 @@ package org.bukkit.craftbukkit.block; +import com.destroystokyo.paper.loottable.CraftLootableBlockInventory; // Paper import net.minecraft.server.TileEntityHopper; import org.bukkit.Material; import org.bukkit.block.Block; @@ -8,7 +9,7 @@ import org.bukkit.craftbukkit.CraftWorld; import org.bukkit.craftbukkit.inventory.CraftInventory; import org.bukkit.inventory.Inventory; -public class CraftHopper extends CraftBlockState implements Hopper { +public class CraftHopper extends CraftBlockState implements Hopper, CraftLootableBlockInventory { // Paper private final TileEntityHopper hopper; public CraftHopper(final Block block) { diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftMinecartChest.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftMinecartChest.java index f5a1875..b2c5679 100644 --- a/src/main/java/org/bukkit/craftbukkit/entity/CraftMinecartChest.java +++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftMinecartChest.java @@ -1,5 +1,6 @@ package org.bukkit.craftbukkit.entity; +import com.destroystokyo.paper.loottable.CraftLootableEntityInventory; // Paper import net.minecraft.server.EntityMinecartChest; import org.bukkit.craftbukkit.CraftServer; @@ -9,7 +10,7 @@ import org.bukkit.entity.StorageMinecart; import org.bukkit.inventory.Inventory; @SuppressWarnings("deprecation") -public class CraftMinecartChest extends CraftMinecart implements StorageMinecart { +public class CraftMinecartChest extends CraftMinecart implements StorageMinecart, CraftLootableEntityInventory { // Paper private final CraftInventory inventory; public CraftMinecartChest(CraftServer server, EntityMinecartChest entity) { diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftMinecartHopper.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftMinecartHopper.java index e9963e2..acb4dee 100644 --- a/src/main/java/org/bukkit/craftbukkit/entity/CraftMinecartHopper.java +++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftMinecartHopper.java @@ -1,5 +1,6 @@ package org.bukkit.craftbukkit.entity; +import com.destroystokyo.paper.loottable.CraftLootableEntityInventory; // Paper import net.minecraft.server.EntityMinecartHopper; import org.bukkit.craftbukkit.CraftServer; @@ -8,7 +9,7 @@ import org.bukkit.entity.EntityType; import org.bukkit.entity.minecart.HopperMinecart; import org.bukkit.inventory.Inventory; -final class CraftMinecartHopper extends CraftMinecart implements HopperMinecart { +final class CraftMinecartHopper extends CraftMinecart implements HopperMinecart, CraftLootableEntityInventory { // Paper private final CraftInventory inventory; CraftMinecartHopper(CraftServer server, EntityMinecartHopper entity) { -- 2.8.3