Paper/patches/server/0092-LootTable-API-Replenis...

698 lines
29 KiB
Diff

From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Aikar <aikar@aikar.co>
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 7d92c9830206dacf34fc0f2f02d9453b3e0c0a5e..7661e41950c38bce94a2c7de4269ba6ebb2f6c52 100644
--- a/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java
+++ b/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java
@@ -315,4 +315,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..70ca5625ff5d13a8e9cd64953066a7e1547ff223
--- /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.BlockPos;
+import net.minecraft.world.level.Level;
+import net.minecraft.world.level.block.entity.RandomizableContainerBlockEntity;
+import org.bukkit.Chunk;
+import org.bukkit.block.Block;
+
+public interface PaperLootableBlockInventory extends LootableBlockInventory, PaperLootableInventory {
+
+ RandomizableContainerBlockEntity getTileEntity();
+
+ @Override
+ default LootableInventory getAPILootableInventory() {
+ return this;
+ }
+
+ @Override
+ default Level getNMSWorld() {
+ return getTileEntity().getLevel();
+ }
+
+ default Block getBlock() {
+ final BlockPos position = getTileEntity().getBlockPos();
+ final Chunk bukkitChunk = getTileEntity().getLevel().getChunkAt(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..2fba5bc0f982e143ad5f5bda55d768edc5f847df
--- /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.Level;
+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 Level getNMSWorld() {
+ return getHandle().getCommandSenderWorld();
+ }
+
+ @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..16b3527d7bc782c47e6f6c3ecd7165bd16b0ab0a
--- /dev/null
+++ b/src/main/java/com/destroystokyo/paper/loottable/PaperLootableInventory.java
@@ -0,0 +1,70 @@
+package com.destroystokyo.paper.loottable;
+
+import org.bukkit.loot.Lootable;
+import java.util.UUID;
+import net.minecraft.world.level.Level;
+
+public interface PaperLootableInventory extends LootableInventory, Lootable {
+
+ PaperLootableInventoryData getLootableData();
+ LootableInventory getAPILootableInventory();
+
+ Level 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..20cfe7b9b7127ddeb97aa91d759fc17b4a548eaf
--- /dev/null
+++ b/src/main/java/com/destroystokyo/paper/loottable/PaperLootableInventoryData.java
@@ -0,0 +1,179 @@
+package com.destroystokyo.paper.loottable;
+
+import com.destroystokyo.paper.PaperWorldConfig;
+import org.bukkit.entity.Player;
+import org.bukkit.loot.LootTable;
+import javax.annotation.Nullable;
+import net.minecraft.nbt.CompoundTag;
+import net.minecraft.nbt.ListTag;
+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 net.minecraft.world.entity.player.Player 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.getUUID())) {
+ event.setCancelled(true);
+ }
+ return event.callEvent();
+ }
+ public void processRefill(@Nullable net.minecraft.world.entity.player.Player 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.getUUID(), true);
+ }
+ } else {
+ this.lootable.clearLootTable();
+ }
+ }
+
+
+ public void loadNbt(CompoundTag base) {
+ if (!base.contains("Paper.LootableData", 10)) { // 10 = compound
+ return;
+ }
+ CompoundTag comp = base.getCompound("Paper.LootableData");
+ if (comp.contains("lastFill")) {
+ this.lastFill = comp.getLong("lastFill");
+ }
+ if (comp.contains("nextRefill")) {
+ this.nextRefill = comp.getLong("nextRefill");
+ }
+
+ if (comp.contains("numRefills")) {
+ this.numRefills = comp.getInt("numRefills");
+ }
+ if (comp.contains("lootedPlayers", 9)) { // 9 = list
+ ListTag 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 CompoundTag cmp = list.getCompound(i);
+ lootedPlayers.put(cmp.getUUID("UUID"), cmp.getLong("Time"));
+ }
+ }
+ }
+ public void saveNbt(CompoundTag base) {
+ CompoundTag comp = new CompoundTag();
+ if (this.nextRefill != -1) {
+ comp.putLong("nextRefill", this.nextRefill);
+ }
+ if (this.lastFill != -1) {
+ comp.putLong("lastFill", this.lastFill);
+ }
+ if (this.numRefills != 0) {
+ comp.putInt("numRefills", this.numRefills);
+ }
+ if (this.lootedPlayers != null && !this.lootedPlayers.isEmpty()) {
+ ListTag list = new ListTag();
+ for (Map.Entry<UUID, Long> entry : this.lootedPlayers.entrySet()) {
+ CompoundTag cmp = new CompoundTag();
+ cmp.putUUID("UUID", entry.getKey());
+ cmp.putLong("Time", entry.getValue());
+ list.add(cmp);
+ }
+ comp.put("lootedPlayers", list);
+ }
+
+ if (!comp.isEmpty()) {
+ base.put("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..6d2e0493729b7b4e109ff103a6ac36c9901568c0
--- /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.AbstractMinecartContainer;
+import net.minecraft.world.level.Level;
+import org.bukkit.Bukkit;
+import org.bukkit.craftbukkit.util.CraftNamespacedKey;
+
+public class PaperMinecartLootableInventory implements PaperLootableEntityInventory {
+
+ private AbstractMinecartContainer entity;
+
+ public PaperMinecartLootableInventory(AbstractMinecartContainer 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 Level getNMSWorld() {
+ return entity.level;
+ }
+}
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..3377b86c337d0234bbb9b0349e4034a7cd450a97
--- /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.Level;
+import net.minecraft.world.level.block.entity.RandomizableContainerBlockEntity;
+import org.bukkit.Bukkit;
+import org.bukkit.craftbukkit.util.CraftNamespacedKey;
+
+public class PaperTileEntityLootableInventory implements PaperLootableBlockInventory {
+ private RandomizableContainerBlockEntity tileEntityLootable;
+
+ public PaperTileEntityLootableInventory(RandomizableContainerBlockEntity 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 RandomizableContainerBlockEntity getTileEntity() {
+ return tileEntityLootable;
+ }
+
+ @Override
+ public LootableInventory getAPILootableInventory() {
+ Level world = tileEntityLootable.getLevel();
+ if (world == null) {
+ return null;
+ }
+ return (LootableInventory) getBukkitWorld().getBlockAt(MCUtil.toLocation(world, tileEntityLootable.getBlockPos())).getState();
+ }
+
+ @Override
+ public Level getNMSWorld() {
+ return tileEntityLootable.getLevel();
+ }
+}
diff --git a/src/main/java/net/minecraft/world/entity/Entity.java b/src/main/java/net/minecraft/world/entity/Entity.java
index f12e282dc4683d0430b9dba552757c4da5dee988..6f33e481e70a7b34a25d1413079288ca59b058c1 100644
--- a/src/main/java/net/minecraft/world/entity/Entity.java
+++ b/src/main/java/net/minecraft/world/entity/Entity.java
@@ -170,6 +170,7 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource, i
};
// 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/AbstractMinecartContainer.java b/src/main/java/net/minecraft/world/entity/vehicle/AbstractMinecartContainer.java
index 298f7e29412ecaf15b3fb15da9ee3d6b250f772a..8a07d5d25086d7544757bb86181fbe2b5e743d29 100644
--- a/src/main/java/net/minecraft/world/entity/vehicle/AbstractMinecartContainer.java
+++ b/src/main/java/net/minecraft/world/entity/vehicle/AbstractMinecartContainer.java
@@ -46,6 +46,7 @@ public abstract class AbstractMinecartContainer extends AbstractMinecart impleme
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;
@@ -200,12 +201,13 @@ public abstract class AbstractMinecartContainer extends AbstractMinecart impleme
@Override
protected void addAdditionalSaveData(CompoundTag nbt) {
super.addAdditionalSaveData(nbt);
+ this.lootableData.saveNbt(nbt); // Paper
if (this.lootTable != null) {
nbt.putString("LootTable", this.lootTable.toString());
if (this.lootTableSeed != 0L) {
nbt.putLong("LootTableSeed", this.lootTableSeed);
}
- } else {
+ } if (true) { // Paper - Always save the items, Table may stick around
ContainerHelper.saveAllItems(nbt, this.itemStacks);
}
@@ -214,11 +216,12 @@ public abstract class AbstractMinecartContainer extends AbstractMinecart impleme
@Override
protected void readAdditionalSaveData(CompoundTag nbt) {
super.readAdditionalSaveData(nbt);
+ this.lootableData.loadNbt(nbt); // Paper
this.itemStacks = NonNullList.withSize(this.getContainerSize(), ItemStack.EMPTY);
if (nbt.contains("LootTable", 8)) {
this.lootTable = new ResourceLocation(nbt.getString("LootTable"));
this.lootTableSeed = nbt.getLong("LootTableSeed");
- } else {
+ } if (true) { // Paper - always load the items, table may still remain
ContainerHelper.loadAllItems(nbt, this.itemStacks);
}
@@ -254,14 +257,15 @@ public abstract class AbstractMinecartContainer extends AbstractMinecart impleme
}
public void unpackLootTable(@Nullable Player player) {
- if (this.lootTable != null && this.level.getServer() != null) {
+ if (this.lootableData.shouldReplenish(player) && this.level.getServer() != null) { // Paper
LootTable loottable = this.level.getServer().getLootTables().get(this.lootTable);
if (player instanceof ServerPlayer) {
CriteriaTriggers.GENERATE_LOOT.trigger((ServerPlayer) player, this.lootTable);
}
- this.lootTable = null;
+ //this.lootTable = null; // Paper
+ this.lootableData.processRefill(player); // Paper
LootContext.Builder loottableinfo_builder = (new LootContext.Builder((ServerLevel) this.level)).withParameter(LootContextParams.ORIGIN, this.position()).withOptionalRandomSeed(this.lootTableSeed);
if (player != null) {
diff --git a/src/main/java/net/minecraft/world/level/block/entity/RandomizableContainerBlockEntity.java b/src/main/java/net/minecraft/world/level/block/entity/RandomizableContainerBlockEntity.java
index b79d9d26a8e60f9c0ecd69e9c2f9cfd087e21d23..f23fff80d07ac7d06715efe67cb49ebbe704967b 100644
--- a/src/main/java/net/minecraft/world/level/block/entity/RandomizableContainerBlockEntity.java
+++ b/src/main/java/net/minecraft/world/level/block/entity/RandomizableContainerBlockEntity.java
@@ -28,6 +28,7 @@ public abstract class RandomizableContainerBlockEntity extends BaseContainerBloc
@Nullable
public ResourceLocation 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 RandomizableContainerBlockEntity(BlockEntityType<?> type, BlockPos pos, BlockState state) {
super(type, pos, state);
@@ -42,16 +43,19 @@ public abstract class RandomizableContainerBlockEntity extends BaseContainerBloc
}
protected boolean tryLoadLootTable(CompoundTag nbt) {
+ this.lootableData.loadNbt(nbt); // Paper
if (nbt.contains("LootTable", 8)) {
this.lootTable = new ResourceLocation(nbt.getString("LootTable"));
+ try { org.bukkit.craftbukkit.util.CraftNamespacedKey.fromMinecraft(this.lootTable); } catch (IllegalArgumentException ex) { this.lootTable = null; } // Paper - validate
this.lootTableSeed = nbt.getLong("LootTableSeed");
- return true;
+ return false; // Paper - always load the items, table may still remain
} else {
return false;
}
}
protected boolean trySaveLootTable(CompoundTag nbt) {
+ this.lootableData.saveNbt(nbt); // Paper
if (this.lootTable == null) {
return false;
} else {
@@ -60,18 +64,19 @@ public abstract class RandomizableContainerBlockEntity extends BaseContainerBloc
nbt.putLong("LootTableSeed", this.lootTableSeed);
}
- return true;
+ return false; // Paper - always save the items, table may still remain
}
}
public void unpackLootTable(@Nullable Player player) {
- if (this.lootTable != null && this.level.getServer() != null) {
+ if (this.lootableData.shouldReplenish(player) && this.level.getServer() != null) { // Paper
LootTable lootTable = this.level.getServer().getLootTables().get(this.lootTable);
if (player instanceof ServerPlayer) {
CriteriaTriggers.GENERATE_LOOT.trigger((ServerPlayer)player, this.lootTable);
}
- this.lootTable = null;
+ //this.lootTable = null; // Paper
+ this.lootableData.processRefill(player); // Paper
LootContext.Builder builder = (new LootContext.Builder((ServerLevel)this.level)).withParameter(LootContextParams.ORIGIN, Vec3.atCenterOf(this.worldPosition)).withOptionalRandomSeed(this.lootTableSeed);
if (player != null) {
builder.withLuck(player.getLuck()).withParameter(LootContextParams.THIS_ENTITY, player);
diff --git a/src/main/java/org/bukkit/craftbukkit/block/CraftChest.java b/src/main/java/org/bukkit/craftbukkit/block/CraftChest.java
index d0c1121fabe46e8268cd2691398361c182be9db5..9796e2d3cd9601416124ad5c36f962ed3f8682e8 100644
--- a/src/main/java/org/bukkit/craftbukkit/block/CraftChest.java
+++ b/src/main/java/org/bukkit/craftbukkit/block/CraftChest.java
@@ -13,8 +13,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<ChestBlockEntity> implements Chest {
+public class CraftChest extends CraftLootable<ChestBlockEntity> implements Chest, PaperLootableBlockInventory { // Paper
public CraftChest(World world, ChestBlockEntity tileEntity) {
super(world, tileEntity);
diff --git a/src/main/java/org/bukkit/craftbukkit/block/CraftLootable.java b/src/main/java/org/bukkit/craftbukkit/block/CraftLootable.java
index 982adacb361b0590799dc68f9b7c13c7195627fd..e49eece9bff3a53469673d03a7bbf8f9cf8776b8 100644
--- a/src/main/java/org/bukkit/craftbukkit/block/CraftLootable.java
+++ b/src/main/java/org/bukkit/craftbukkit/block/CraftLootable.java
@@ -9,7 +9,7 @@ import org.bukkit.craftbukkit.util.CraftNamespacedKey;
import org.bukkit.loot.LootTable;
import org.bukkit.loot.Lootable;
-public abstract class CraftLootable<T extends RandomizableContainerBlockEntity> extends CraftContainer<T> implements Nameable, Lootable {
+public abstract class CraftLootable<T extends RandomizableContainerBlockEntity> extends CraftContainer<T> implements Nameable, Lootable, com.destroystokyo.paper.loottable.PaperLootableBlockInventory { // Paper
public CraftLootable(World world, T tileEntity) {
super(world, tileEntity);
diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftMinecartChest.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftMinecartChest.java
index eb21b8457774d5ac765fa9008157cb29d9b72509..abf58bef2042a9efba5a78fd7f97339deceaa780 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, MinecartChest entity) {
diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftMinecartHopper.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftMinecartHopper.java
index 34b8f103625f087bb725bed595dd9c30f4a6f70c..ee9648739fb39c5842063d7442df6eb5c9336d7f 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, MinecartHopper entity) {