mirror of
https://github.com/PaperMC/Paper.git
synced 2024-11-29 14:06:17 +01:00
740 lines
31 KiB
Diff
740 lines
31 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 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..fda64b8860cb696e209eedcfb200e7193d216732
|
||
|
--- /dev/null
|
||
|
+++ b/src/main/java/com/destroystokyo/paper/loottable/PaperLootableBlockInventory.java
|
||
|
@@ -0,0 +1,34 @@
|
||
|
+package com.destroystokyo.paper.loottable;
|
||
|
+
|
||
|
+import LootableInventory;
|
||
|
+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..292d5ef8a1c428893af729b298eecd32b4c4659a
|
||
|
--- /dev/null
|
||
|
+++ b/src/main/java/com/destroystokyo/paper/loottable/PaperLootableEntityInventory.java
|
||
|
@@ -0,0 +1,29 @@
|
||
|
+package com.destroystokyo.paper.loottable;
|
||
|
+
|
||
|
+import LootableInventory;
|
||
|
+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..b3def19a50081cfa758b6e25707b2fc6fed8d3ca
|
||
|
--- /dev/null
|
||
|
+++ b/src/main/java/com/destroystokyo/paper/loottable/PaperLootableInventory.java
|
||
|
@@ -0,0 +1,71 @@
|
||
|
+package com.destroystokyo.paper.loottable;
|
||
|
+
|
||
|
+import org.bukkit.loot.Lootable;
|
||
|
+import LootableInventory;
|
||
|
+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..88542462d34ba24e8590294bd896d7e73932ef9c
|
||
|
--- /dev/null
|
||
|
+++ b/src/main/java/com/destroystokyo/paper/loottable/PaperLootableInventoryData.java
|
||
|
@@ -0,0 +1,180 @@
|
||
|
+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.setUUID("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..d9b31c8a21fdffb33d1f75b1a16606f218145b39
|
||
|
--- /dev/null
|
||
|
+++ b/src/main/java/com/destroystokyo/paper/loottable/PaperMinecartLootableInventory.java
|
||
|
@@ -0,0 +1,63 @@
|
||
|
+package com.destroystokyo.paper.loottable;
|
||
|
+
|
||
|
+import LootableInventory;
|
||
|
+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..6bc899ec4dc03b09cc978bc7a763a9755a3d2dc4
|
||
|
--- /dev/null
|
||
|
+++ b/src/main/java/com/destroystokyo/paper/loottable/PaperTileEntityLootableInventory.java
|
||
|
@@ -0,0 +1,66 @@
|
||
|
+package com.destroystokyo.paper.loottable;
|
||
|
+
|
||
|
+import LootableInventory;
|
||
|
+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 61048140cf0adca03bfb57193ada0adaee73b1bb..171697e88f5a4d8c0be2a47b67b865bbdc4dfe8c 100644
|
||
|
--- a/src/main/java/net/minecraft/world/entity/Entity.java
|
||
|
+++ b/src/main/java/net/minecraft/world/entity/Entity.java
|
||
|
@@ -157,6 +157,7 @@ public abstract class Entity implements Nameable, CommandSource, net.minecraft.s
|
||
|
};
|
||
|
// 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 f4758251e58fbb36526cea5c4825561d62c9665a..5b96b1e7428a43c8c5f4a96ea37d5189f0d84f56 100644
|
||
|
--- a/src/main/java/net/minecraft/world/entity/vehicle/AbstractMinecartContainer.java
|
||
|
+++ b/src/main/java/net/minecraft/world/entity/vehicle/AbstractMinecartContainer.java
|
||
|
@@ -45,6 +45,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;
|
||
|
|
||
|
@@ -202,12 +203,13 @@ public abstract class AbstractMinecartContainer extends AbstractMinecart impleme
|
||
|
@Override
|
||
|
protected void addAdditionalSaveData(CompoundTag tag) {
|
||
|
super.addAdditionalSaveData(tag);
|
||
|
+ this.lootableData.saveNbt(tag); // Paper
|
||
|
if (this.lootTable != null) {
|
||
|
tag.putString("LootTable", this.lootTable.toString());
|
||
|
if (this.lootTableSeed != 0L) {
|
||
|
tag.putLong("LootTableSeed", this.lootTableSeed);
|
||
|
}
|
||
|
- } else {
|
||
|
+ } if (true) { // Paper - Always save the items, Table may stick around
|
||
|
ContainerHelper.saveAllItems(tag, this.itemStacks);
|
||
|
}
|
||
|
|
||
|
@@ -216,11 +218,12 @@ public abstract class AbstractMinecartContainer extends AbstractMinecart impleme
|
||
|
@Override
|
||
|
protected void readAdditionalSaveData(CompoundTag tag) {
|
||
|
super.readAdditionalSaveData(tag);
|
||
|
+ this.lootableData.loadNbt(tag); // Paper
|
||
|
this.itemStacks = NonNullList.a(this.getContainerSize(), ItemStack.EMPTY);
|
||
|
if (tag.contains("LootTable", 8)) {
|
||
|
this.lootTable = new ResourceLocation(tag.getString("LootTable"));
|
||
|
this.lootTableSeed = tag.getLong("LootTableSeed");
|
||
|
- } else {
|
||
|
+ } if (true) { // Paper - always load the items, table may still remain
|
||
|
ContainerHelper.loadAllItems(tag, this.itemStacks);
|
||
|
}
|
||
|
|
||
|
@@ -251,14 +254,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 9d33bc31c8088bfba66be1aecbf20e7ee86e4f83..5ad419941ff1113ef29b9a4593f44d8f35ba8424 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
|
||
|
@@ -27,6 +27,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) {
|
||
|
super(type);
|
||
|
@@ -42,16 +43,19 @@ public abstract class RandomizableContainerBlockEntity extends BaseContainerBloc
|
||
|
}
|
||
|
|
||
|
protected boolean tryLoadLootTable(CompoundTag nbttagcompound) {
|
||
|
+ this.lootableData.loadNbt(nbttagcompound); // Paper
|
||
|
if (nbttagcompound.contains("LootTable", 8)) {
|
||
|
this.lootTable = new ResourceLocation(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 trySaveLootTable(CompoundTag nbttagcompound) {
|
||
|
+ this.lootableData.saveNbt(nbttagcompound); // Paper
|
||
|
if (this.lootTable == null) {
|
||
|
return false;
|
||
|
} else {
|
||
|
@@ -60,19 +64,20 @@ public abstract class RandomizableContainerBlockEntity extends BaseContainerBloc
|
||
|
nbttagcompound.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 loottableinfo_builder = (new LootContext.Builder((ServerLevel) this.level)).withParameter(LootContextParams.ORIGIN, Vec3.atCenterOf((Vec3i) this.worldPosition)).withOptionalRandomSeed(this.lootTableSeed);
|
||
|
|
||
|
if (player != null) {
|
||
|
diff --git a/src/main/java/org/bukkit/craftbukkit/block/CraftBlockEntityState.java b/src/main/java/org/bukkit/craftbukkit/block/CraftBlockEntityState.java
|
||
|
index 1e2e94b0cd2ede8fb7ae5902dcd0b639bd8dcf52..e89a93082fe07fdb14df8ffef5beca5bd52d7866 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 BlockEntity> extends CraftBlockStat
|
||
|
}
|
||
|
|
||
|
// 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 20d9a192ff102e04687a8aa3eff1ba36a69b6c03..a821df3e13e2ddc479dc5f55540671f43563cdac 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<ChestBlockEntity> implements Chest {
|
||
|
+public class CraftChest extends CraftLootable<ChestBlockEntity> implements Chest, PaperLootableBlockInventory { // Paper
|
||
|
|
||
|
public CraftChest(final Block block) {
|
||
|
super(block, ChestBlockEntity.class);
|
||
|
diff --git a/src/main/java/org/bukkit/craftbukkit/block/CraftLootable.java b/src/main/java/org/bukkit/craftbukkit/block/CraftLootable.java
|
||
|
index 309650aad43d8b6ce4bb13f8c172028f3feab299..5babbcfcacb89e62f00f8184af2ceea227f9ff69 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 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(Block block, Class<T> tileEntityClass) {
|
||
|
super(block, tileEntityClass);
|
||
|
@@ -54,7 +54,7 @@ public abstract class CraftLootable<T extends RandomizableContainerBlockEntity>
|
||
|
setLootTable(getLootTable(), seed);
|
||
|
}
|
||
|
|
||
|
- private void setLootTable(LootTable table, long seed) {
|
||
|
+ public void setLootTable(LootTable table, long seed) { // Paper - public
|
||
|
ResourceLocation 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 f0a30acb0199e396d6863a473db433cbe112d8a5..293b222565d8e0592f9f355a2ee8cdfbc868a08e 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/CraftMinecartContainer.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftMinecartContainer.java
|
||
|
index 12044062cb746bd5c77abacf8acddc67e08e78ce..ce14bc4791bd282d16af0ee91fc431acefa3b909 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
|
||
|
ResourceLocation 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 c1af739369715d8c628c466b269fdde99a2f6286..c8c5f60b6b32248696363d9b63bbbe43810743d3 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) {
|