mirror of
https://github.com/PaperMC/Paper.git
synced 2025-01-03 23:07:40 +01:00
LootTable API and replenishable lootables
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" == AT == public org.bukkit.craftbukkit.block.CraftBlockEntityState getTileEntity()Lnet/minecraft/world/level/block/entity/BlockEntity; public org.bukkit.craftbukkit.block.CraftLootable setLootTable(Lorg/bukkit/loot/LootTable;J)V public org.bukkit.craftbukkit.entity.CraftMinecartContainer setLootTable(Lorg/bukkit/loot/LootTable;J)V
This commit is contained in:
parent
b8d05bbc27
commit
07c767b6f4
@ -0,0 +1,92 @@
|
|||||||
|
--- a/net/minecraft/world/RandomizableContainer.java
|
||||||
|
+++ b/net/minecraft/world/RandomizableContainer.java
|
||||||
|
@@ -28,7 +28,7 @@
|
||||||
|
|
||||||
|
void setLootTable(@Nullable ResourceKey<LootTable> lootTable);
|
||||||
|
|
||||||
|
- default void setLootTable(ResourceKey<LootTable> lootTableId, long lootTableSeed) {
|
||||||
|
+ default void setLootTable(@Nullable ResourceKey<LootTable> lootTableId, long lootTableSeed) { // Paper - add nullable
|
||||||
|
this.setLootTable(lootTableId);
|
||||||
|
this.setLootTableSeed(lootTableSeed);
|
||||||
|
}
|
||||||
|
@@ -51,13 +51,14 @@
|
||||||
|
default boolean tryLoadLootTable(CompoundTag nbt) {
|
||||||
|
if (nbt.contains("LootTable", 8)) {
|
||||||
|
this.setLootTable(ResourceKey.create(Registries.LOOT_TABLE, ResourceLocation.parse(nbt.getString("LootTable"))));
|
||||||
|
+ if (this.lootableData() != null && this.getLootTable() != null) this.lootableData().loadNbt(nbt); // Paper - LootTable API
|
||||||
|
if (nbt.contains("LootTableSeed", 4)) {
|
||||||
|
this.setLootTableSeed(nbt.getLong("LootTableSeed"));
|
||||||
|
} else {
|
||||||
|
this.setLootTableSeed(0L);
|
||||||
|
}
|
||||||
|
|
||||||
|
- return true;
|
||||||
|
+ return this.lootableData() == null; // Paper - only track the loot table if there is chance for replenish
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
@@ -69,26 +70,44 @@
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
nbt.putString("LootTable", resourceKey.location().toString());
|
||||||
|
+ if (this.lootableData() != null) this.lootableData().saveNbt(nbt); // Paper - LootTable API
|
||||||
|
long l = this.getLootTableSeed();
|
||||||
|
if (l != 0L) {
|
||||||
|
nbt.putLong("LootTableSeed", l);
|
||||||
|
}
|
||||||
|
|
||||||
|
- return true;
|
||||||
|
+ return this.lootableData() == null; // Paper - only track the loot table if there is chance for replenish
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
default void unpackLootTable(@Nullable Player player) {
|
||||||
|
+ // Paper start - LootTable API
|
||||||
|
+ this.unpackLootTable(player, false);
|
||||||
|
+ }
|
||||||
|
+ default void unpackLootTable(@Nullable final Player player, final boolean forceClearLootTable) {
|
||||||
|
+ // Paper end - LootTable API
|
||||||
|
Level level = this.getLevel();
|
||||||
|
BlockPos blockPos = this.getBlockPos();
|
||||||
|
ResourceKey<LootTable> resourceKey = this.getLootTable();
|
||||||
|
- if (resourceKey != null && level != null && level.getServer() != null) {
|
||||||
|
+ // Paper start - LootTable API
|
||||||
|
+ lootReplenish: if (resourceKey != null && level != null && level.getServer() != null) {
|
||||||
|
+ if (this.lootableData() != null && !this.lootableData().shouldReplenish(this, com.destroystokyo.paper.loottable.PaperLootableInventoryData.CONTAINER, player)) {
|
||||||
|
+ if (forceClearLootTable) {
|
||||||
|
+ this.setLootTable(null);
|
||||||
|
+ }
|
||||||
|
+ break lootReplenish;
|
||||||
|
+ }
|
||||||
|
+ // Paper end - LootTable API
|
||||||
|
LootTable lootTable = level.getServer().reloadableRegistries().getLootTable(resourceKey);
|
||||||
|
if (player instanceof ServerPlayer) {
|
||||||
|
CriteriaTriggers.GENERATE_LOOT.trigger((ServerPlayer)player, resourceKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
- this.setLootTable(null);
|
||||||
|
+ // Paper start - LootTable API
|
||||||
|
+ if (forceClearLootTable || this.lootableData() == null || this.lootableData().shouldClearLootTable(this, com.destroystokyo.paper.loottable.PaperLootableInventoryData.CONTAINER, player)) {
|
||||||
|
+ this.setLootTable(null);
|
||||||
|
+ }
|
||||||
|
+ // Paper end - LootTable API
|
||||||
|
LootParams.Builder builder = new LootParams.Builder((ServerLevel)level).withParameter(LootContextParams.ORIGIN, Vec3.atCenterOf(blockPos));
|
||||||
|
if (player != null) {
|
||||||
|
builder.withLuck(player.getLuck()).withParameter(LootContextParams.THIS_ENTITY, player);
|
||||||
|
@@ -97,4 +116,16 @@
|
||||||
|
lootTable.fill(this, builder.create(LootContextParamSets.CHEST), this.getLootTableSeed());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
+
|
||||||
|
+ // Paper start - LootTable API
|
||||||
|
+ @Nullable @org.jetbrains.annotations.Contract(pure = true)
|
||||||
|
+ default com.destroystokyo.paper.loottable.PaperLootableInventoryData lootableData() {
|
||||||
|
+ return null; // some containers don't really have a "replenish" ability like decorated pots
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ default com.destroystokyo.paper.loottable.PaperLootableInventory getLootableInventory() {
|
||||||
|
+ final org.bukkit.block.Block block = org.bukkit.craftbukkit.block.CraftBlock.at(java.util.Objects.requireNonNull(this.getLevel(), "Cannot manage loot tables on block entities not in world"), this.getBlockPos());
|
||||||
|
+ return (com.destroystokyo.paper.loottable.PaperLootableInventory) block.getState(false);
|
||||||
|
+ }
|
||||||
|
+ // Paper end - LootTable API
|
||||||
|
}
|
@ -37,11 +37,28 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -212,4 +228,51 @@
|
@@ -165,7 +181,7 @@
|
||||||
|
@Nullable
|
||||||
|
@Override
|
||||||
|
public AbstractContainerMenu createMenu(int syncId, Inventory playerInventory, Player player) {
|
||||||
|
- if (this.lootTable != null && player.isSpectator()) {
|
||||||
|
+ if (this.lootTable != null && player.isSpectator()) { // Paper - LootTable API (TODO spectators can open chests that aren't ready to be re-generated but this doesn't support that)
|
||||||
|
return null;
|
||||||
|
} else {
|
||||||
|
this.unpackLootTable(playerInventory.player);
|
||||||
|
@@ -212,4 +228,59 @@
|
||||||
public void stopOpen(Player player) {
|
public void stopOpen(Player player) {
|
||||||
this.level().gameEvent((Holder) GameEvent.CONTAINER_CLOSE, this.position(), GameEvent.Context.of((Entity) player));
|
this.level().gameEvent((Holder) GameEvent.CONTAINER_CLOSE, this.position(), GameEvent.Context.of((Entity) player));
|
||||||
}
|
}
|
||||||
+
|
+
|
||||||
|
+ // Paper start - LootTable API
|
||||||
|
+ final com.destroystokyo.paper.loottable.PaperLootableInventoryData lootableData = new com.destroystokyo.paper.loottable.PaperLootableInventoryData();
|
||||||
|
+
|
||||||
|
+ @Override
|
||||||
|
+ public com.destroystokyo.paper.loottable.PaperLootableInventoryData lootableData() {
|
||||||
|
+ return this.lootableData;
|
||||||
|
+ }
|
||||||
|
+ // Paper end - LootTable API
|
||||||
+ // CraftBukkit start
|
+ // CraftBukkit start
|
||||||
+ public List<HumanEntity> transaction = new java.util.ArrayList<HumanEntity>();
|
+ public List<HumanEntity> transaction = new java.util.ArrayList<HumanEntity>();
|
||||||
+ private int maxStack = MAX_STACK;
|
+ private int maxStack = MAX_STACK;
|
||||||
|
@ -15,10 +15,18 @@
|
|||||||
|
|
||||||
public abstract class AbstractMinecartContainer extends AbstractMinecart implements ContainerEntity {
|
public abstract class AbstractMinecartContainer extends AbstractMinecart implements ContainerEntity {
|
||||||
|
|
||||||
@@ -28,9 +36,50 @@
|
@@ -28,9 +36,58 @@
|
||||||
public ResourceKey<LootTable> lootTable;
|
public ResourceKey<LootTable> lootTable;
|
||||||
public long lootTableSeed;
|
public long lootTableSeed;
|
||||||
|
|
||||||
|
+ // Paper start - LootTable API
|
||||||
|
+ final com.destroystokyo.paper.loottable.PaperLootableInventoryData lootableData = new com.destroystokyo.paper.loottable.PaperLootableInventoryData();
|
||||||
|
+
|
||||||
|
+ @Override
|
||||||
|
+ public com.destroystokyo.paper.loottable.PaperLootableInventoryData lootableData() {
|
||||||
|
+ return this.lootableData;
|
||||||
|
+ }
|
||||||
|
+ // Paper end - LootTable API
|
||||||
+ // CraftBukkit start
|
+ // CraftBukkit start
|
||||||
+ public List<HumanEntity> transaction = new java.util.ArrayList<HumanEntity>();
|
+ public List<HumanEntity> transaction = new java.util.ArrayList<HumanEntity>();
|
||||||
+ private int maxStack = MAX_STACK;
|
+ private int maxStack = MAX_STACK;
|
||||||
@ -67,7 +75,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -74,11 +123,18 @@
|
@@ -74,11 +131,18 @@
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void remove(Entity.RemovalReason reason) {
|
public void remove(Entity.RemovalReason reason) {
|
||||||
|
@ -0,0 +1,69 @@
|
|||||||
|
--- a/net/minecraft/world/entity/vehicle/ContainerEntity.java
|
||||||
|
+++ b/net/minecraft/world/entity/vehicle/ContainerEntity.java
|
||||||
|
@@ -62,22 +62,26 @@
|
||||||
|
default void addChestVehicleSaveData(CompoundTag nbt, HolderLookup.Provider registries) {
|
||||||
|
if (this.getContainerLootTable() != null) {
|
||||||
|
nbt.putString("LootTable", this.getContainerLootTable().location().toString());
|
||||||
|
+ this.lootableData().saveNbt(nbt); // Paper
|
||||||
|
if (this.getContainerLootTableSeed() != 0L) {
|
||||||
|
nbt.putLong("LootTableSeed", this.getContainerLootTableSeed());
|
||||||
|
}
|
||||||
|
- } else {
|
||||||
|
- ContainerHelper.saveAllItems(nbt, this.getItemStacks(), registries);
|
||||||
|
}
|
||||||
|
+ ContainerHelper.saveAllItems(nbt, this.getItemStacks(), registries); // Paper - always save the items, table may still remain
|
||||||
|
}
|
||||||
|
|
||||||
|
default void readChestVehicleSaveData(CompoundTag nbt, HolderLookup.Provider registries) {
|
||||||
|
this.clearItemStacks();
|
||||||
|
if (nbt.contains("LootTable", 8)) {
|
||||||
|
this.setContainerLootTable(ResourceKey.create(Registries.LOOT_TABLE, ResourceLocation.parse(nbt.getString("LootTable"))));
|
||||||
|
+ // Paper start - LootTable API
|
||||||
|
+ if (this.getContainerLootTable() != null) {
|
||||||
|
+ this.lootableData().loadNbt(nbt);
|
||||||
|
+ }
|
||||||
|
+ // Paper end - LootTable API
|
||||||
|
this.setContainerLootTableSeed(nbt.getLong("LootTableSeed"));
|
||||||
|
- } else {
|
||||||
|
- ContainerHelper.loadAllItems(nbt, this.getItemStacks(), registries);
|
||||||
|
}
|
||||||
|
+ ContainerHelper.loadAllItems(nbt, this.getItemStacks(), registries); // Paper - always save the items, table may still remain
|
||||||
|
}
|
||||||
|
|
||||||
|
default void chestVehicleDestroyed(DamageSource source, ServerLevel world, Entity vehicle) {
|
||||||
|
@@ -97,13 +101,18 @@
|
||||||
|
|
||||||
|
default void unpackChestVehicleLootTable(@Nullable Player player) {
|
||||||
|
MinecraftServer minecraftServer = this.level().getServer();
|
||||||
|
- if (this.getContainerLootTable() != null && minecraftServer != null) {
|
||||||
|
+ if (minecraftServer != null && this.lootableData().shouldReplenish(this, com.destroystokyo.paper.loottable.PaperLootableInventoryData.ENTITY, player)) { // Paper - LootTable API
|
||||||
|
LootTable lootTable = minecraftServer.reloadableRegistries().getLootTable(this.getContainerLootTable());
|
||||||
|
if (player != null) {
|
||||||
|
CriteriaTriggers.GENERATE_LOOT.trigger((ServerPlayer)player, this.getContainerLootTable());
|
||||||
|
}
|
||||||
|
|
||||||
|
- this.setContainerLootTable(null);
|
||||||
|
+ // Paper start - LootTable API
|
||||||
|
+ if (this.lootableData().shouldClearLootTable(this, com.destroystokyo.paper.loottable.PaperLootableInventoryData.ENTITY, player)) {
|
||||||
|
+ this.setContainerLootTable(null);
|
||||||
|
+ }
|
||||||
|
+ // Paper end - LootTable API
|
||||||
|
+
|
||||||
|
LootParams.Builder builder = new LootParams.Builder((ServerLevel)this.level()).withParameter(LootContextParams.ORIGIN, this.position());
|
||||||
|
if (player != null) {
|
||||||
|
builder.withLuck(player.getLuck()).withParameter(LootContextParams.THIS_ENTITY, player);
|
||||||
|
@@ -173,4 +182,14 @@
|
||||||
|
default boolean isChestVehicleStillValid(Player player) {
|
||||||
|
return !this.isRemoved() && player.canInteractWithEntity(this.getBoundingBox(), 4.0);
|
||||||
|
}
|
||||||
|
+
|
||||||
|
+ // Paper start - LootTable API
|
||||||
|
+ default com.destroystokyo.paper.loottable.PaperLootableInventoryData lootableData() {
|
||||||
|
+ throw new UnsupportedOperationException("Implement this method");
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ default com.destroystokyo.paper.loottable.PaperLootableInventory getLootableInventory() {
|
||||||
|
+ return ((com.destroystokyo.paper.loottable.PaperLootableInventory) ((net.minecraft.world.entity.Entity) this).getBukkitEntity());
|
||||||
|
+ }
|
||||||
|
+ // Paper end - LootTable API
|
||||||
|
}
|
@ -0,0 +1,41 @@
|
|||||||
|
--- a/net/minecraft/world/level/block/ShulkerBoxBlock.java
|
||||||
|
+++ b/net/minecraft/world/level/block/ShulkerBoxBlock.java
|
||||||
|
@@ -137,7 +137,7 @@
|
||||||
|
itemEntity.setDefaultPickUpDelay();
|
||||||
|
world.addFreshEntity(itemEntity);
|
||||||
|
} else {
|
||||||
|
- shulkerBoxBlockEntity.unpackLootTable(player);
|
||||||
|
+ shulkerBoxBlockEntity.unpackLootTable(player, true); // Paper - force clear loot table so replenish data isn't persisted in the stack
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@@ -147,7 +147,15 @@
|
||||||
|
@Override
|
||||||
|
protected List<ItemStack> getDrops(BlockState state, LootParams.Builder builder) {
|
||||||
|
BlockEntity blockEntity = builder.getOptionalParameter(LootContextParams.BLOCK_ENTITY);
|
||||||
|
+ Runnable reAdd = null; // Paper
|
||||||
|
if (blockEntity instanceof ShulkerBoxBlockEntity shulkerBoxBlockEntity) {
|
||||||
|
+ // Paper start - clear loot table if it was already used
|
||||||
|
+ if (shulkerBoxBlockEntity.lootableData().getLastFill() != -1 || !builder.getLevel().paperConfig().lootables.retainUnlootedShulkerBoxLootTableOnNonPlayerBreak) {
|
||||||
|
+ net.minecraft.resources.ResourceKey<net.minecraft.world.level.storage.loot.LootTable> lootTableResourceKey = shulkerBoxBlockEntity.getLootTable();
|
||||||
|
+ reAdd = () -> shulkerBoxBlockEntity.setLootTable(lootTableResourceKey);
|
||||||
|
+ shulkerBoxBlockEntity.setLootTable(null);
|
||||||
|
+ }
|
||||||
|
+ // Paper end
|
||||||
|
builder = builder.withDynamicDrop(CONTENTS, lootConsumer -> {
|
||||||
|
for (int i = 0; i < shulkerBoxBlockEntity.getContainerSize(); i++) {
|
||||||
|
lootConsumer.accept(shulkerBoxBlockEntity.getItem(i));
|
||||||
|
@@ -155,7 +163,13 @@
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
+ // Paper start - re-set loot table if it was cleared
|
||||||
|
+ try {
|
||||||
|
return super.getDrops(state, builder);
|
||||||
|
+ } finally {
|
||||||
|
+ if (reAdd != null) reAdd.run();
|
||||||
|
+ }
|
||||||
|
+ // Paper end - re-set loot table if it was cleared
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
@ -0,0 +1,16 @@
|
|||||||
|
--- a/net/minecraft/world/level/block/entity/RandomizableContainerBlockEntity.java
|
||||||
|
+++ b/net/minecraft/world/level/block/entity/RandomizableContainerBlockEntity.java
|
||||||
|
@@ -115,4 +115,13 @@
|
||||||
|
nbt.remove("LootTable");
|
||||||
|
nbt.remove("LootTableSeed");
|
||||||
|
}
|
||||||
|
+
|
||||||
|
+ // Paper start - LootTable API
|
||||||
|
+ final com.destroystokyo.paper.loottable.PaperLootableInventoryData lootableData = new com.destroystokyo.paper.loottable.PaperLootableInventoryData(); // Paper
|
||||||
|
+
|
||||||
|
+ @Override
|
||||||
|
+ public com.destroystokyo.paper.loottable.PaperLootableInventoryData lootableData() {
|
||||||
|
+ return this.lootableData;
|
||||||
|
+ }
|
||||||
|
+ // Paper end - LootTable API
|
||||||
|
}
|
@ -0,0 +1,21 @@
|
|||||||
|
package com.destroystokyo.paper.loottable;
|
||||||
|
|
||||||
|
import org.bukkit.loot.LootTable;
|
||||||
|
import org.bukkit.loot.Lootable;
|
||||||
|
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||||
|
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||||
|
import org.checkerframework.framework.qual.DefaultQualifier;
|
||||||
|
|
||||||
|
@DefaultQualifier(NonNull.class)
|
||||||
|
public interface PaperLootable extends Lootable {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
default void setLootTable(final @Nullable LootTable table) {
|
||||||
|
this.setLootTable(table, this.getSeed());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
default void setSeed(final long seed) {
|
||||||
|
this.setLootTable(this.getLootTable(), seed);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,27 @@
|
|||||||
|
package com.destroystokyo.paper.loottable;
|
||||||
|
|
||||||
|
import net.minecraft.world.RandomizableContainer;
|
||||||
|
import org.bukkit.craftbukkit.CraftLootTable;
|
||||||
|
import org.bukkit.loot.LootTable;
|
||||||
|
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||||
|
|
||||||
|
public interface PaperLootableBlock extends PaperLootable {
|
||||||
|
|
||||||
|
RandomizableContainer getRandomizableContainer();
|
||||||
|
|
||||||
|
/* Lootable */
|
||||||
|
@Override
|
||||||
|
default @Nullable LootTable getLootTable() {
|
||||||
|
return CraftLootTable.minecraftToBukkit(this.getRandomizableContainer().getLootTable());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
default void setLootTable(final @Nullable LootTable table, final long seed) {
|
||||||
|
this.getRandomizableContainer().setLootTable(CraftLootTable.bukkitToMinecraft(table), seed);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
default long getSeed() {
|
||||||
|
return this.getRandomizableContainer().getLootTableSeed();
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,26 @@
|
|||||||
|
package com.destroystokyo.paper.loottable;
|
||||||
|
|
||||||
|
import java.util.Objects;
|
||||||
|
import net.minecraft.core.BlockPos;
|
||||||
|
import org.bukkit.block.Block;
|
||||||
|
import org.bukkit.craftbukkit.block.CraftBlock;
|
||||||
|
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||||
|
import org.checkerframework.framework.qual.DefaultQualifier;
|
||||||
|
|
||||||
|
@DefaultQualifier(NonNull.class)
|
||||||
|
public interface PaperLootableBlockInventory extends LootableBlockInventory, PaperLootableInventory, PaperLootableBlock {
|
||||||
|
|
||||||
|
/* PaperLootableInventory */
|
||||||
|
@Override
|
||||||
|
default PaperLootableInventoryData lootableDataForAPI() {
|
||||||
|
return Objects.requireNonNull(this.getRandomizableContainer().lootableData(), "Can only manage loot tables on tile entities with lootableData");
|
||||||
|
}
|
||||||
|
|
||||||
|
/* LootableBlockInventory */
|
||||||
|
@Override
|
||||||
|
default Block getBlock() {
|
||||||
|
final BlockPos position = this.getRandomizableContainer().getBlockPos();
|
||||||
|
return CraftBlock.at(this.getNMSWorld(), position);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,29 @@
|
|||||||
|
package com.destroystokyo.paper.loottable;
|
||||||
|
|
||||||
|
import net.minecraft.world.entity.vehicle.ContainerEntity;
|
||||||
|
import org.bukkit.craftbukkit.CraftLootTable;
|
||||||
|
import org.bukkit.loot.LootTable;
|
||||||
|
import org.bukkit.loot.Lootable;
|
||||||
|
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||||
|
|
||||||
|
public interface PaperLootableEntity extends Lootable {
|
||||||
|
|
||||||
|
ContainerEntity getHandle();
|
||||||
|
|
||||||
|
/* Lootable */
|
||||||
|
@Override
|
||||||
|
default @Nullable LootTable getLootTable() {
|
||||||
|
return CraftLootTable.minecraftToBukkit(this.getHandle().getContainerLootTable());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
default void setLootTable(final @Nullable LootTable table, final long seed) {
|
||||||
|
this.getHandle().setContainerLootTable(CraftLootTable.bukkitToMinecraft(table));
|
||||||
|
this.getHandle().setContainerLootTableSeed(seed);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
default long getSeed() {
|
||||||
|
return this.getHandle().getContainerLootTableSeed();
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,26 @@
|
|||||||
|
package com.destroystokyo.paper.loottable;
|
||||||
|
|
||||||
|
import net.minecraft.world.level.Level;
|
||||||
|
import org.bukkit.entity.Entity;
|
||||||
|
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||||
|
import org.checkerframework.framework.qual.DefaultQualifier;
|
||||||
|
|
||||||
|
@DefaultQualifier(NonNull.class)
|
||||||
|
public interface PaperLootableEntityInventory extends LootableEntityInventory, PaperLootableInventory, PaperLootableEntity {
|
||||||
|
|
||||||
|
/* PaperLootableInventory */
|
||||||
|
@Override
|
||||||
|
default Level getNMSWorld() {
|
||||||
|
return this.getHandle().level();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
default PaperLootableInventoryData lootableDataForAPI() {
|
||||||
|
return this.getHandle().lootableData();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* LootableEntityInventory */
|
||||||
|
default Entity getEntity() {
|
||||||
|
return ((net.minecraft.world.entity.Entity) this.getHandle()).getBukkitEntity();
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,79 @@
|
|||||||
|
package com.destroystokyo.paper.loottable;
|
||||||
|
|
||||||
|
import java.util.UUID;
|
||||||
|
import net.minecraft.world.level.Level;
|
||||||
|
import org.bukkit.World;
|
||||||
|
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||||
|
import org.checkerframework.framework.qual.DefaultQualifier;
|
||||||
|
|
||||||
|
@DefaultQualifier(NonNull.class)
|
||||||
|
public interface PaperLootableInventory extends PaperLootable, LootableInventory {
|
||||||
|
|
||||||
|
/* impl */
|
||||||
|
PaperLootableInventoryData lootableDataForAPI();
|
||||||
|
|
||||||
|
Level getNMSWorld();
|
||||||
|
|
||||||
|
default World getBukkitWorld() {
|
||||||
|
return this.getNMSWorld().getWorld();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* LootableInventory */
|
||||||
|
@Override
|
||||||
|
default boolean isRefillEnabled() {
|
||||||
|
return this.getNMSWorld().paperConfig().lootables.autoReplenish;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
default boolean hasBeenFilled() {
|
||||||
|
return this.getLastFilled() != -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
default boolean hasPlayerLooted(final UUID player) {
|
||||||
|
return this.lootableDataForAPI().hasPlayerLooted(player);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
default boolean canPlayerLoot(final UUID player) {
|
||||||
|
return this.lootableDataForAPI().canPlayerLoot(player, this.getNMSWorld().paperConfig());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
default Long getLastLooted(final UUID player) {
|
||||||
|
return this.lootableDataForAPI().getLastLooted(player);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
default boolean setHasPlayerLooted(final UUID player, final boolean looted) {
|
||||||
|
final boolean hasLooted = this.hasPlayerLooted(player);
|
||||||
|
if (hasLooted != looted) {
|
||||||
|
this.lootableDataForAPI().setPlayerLootedState(player, looted);
|
||||||
|
}
|
||||||
|
return hasLooted;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
default boolean hasPendingRefill() {
|
||||||
|
final long nextRefill = this.lootableDataForAPI().getNextRefill();
|
||||||
|
return nextRefill != -1 && nextRefill > this.lootableDataForAPI().getLastFill();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
default long getLastFilled() {
|
||||||
|
return this.lootableDataForAPI().getLastFill();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
default long getNextRefill() {
|
||||||
|
return this.lootableDataForAPI().getNextRefill();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
default long setNextRefill(long refillAt) {
|
||||||
|
if (refillAt < -1) {
|
||||||
|
refillAt = -1;
|
||||||
|
}
|
||||||
|
return this.lootableDataForAPI().setNextRefill(refillAt);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,249 @@
|
|||||||
|
package com.destroystokyo.paper.loottable;
|
||||||
|
|
||||||
|
import io.papermc.paper.configuration.WorldConfiguration;
|
||||||
|
import io.papermc.paper.configuration.type.DurationOrDisabled;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.Random;
|
||||||
|
import java.util.UUID;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
import net.minecraft.nbt.CompoundTag;
|
||||||
|
import net.minecraft.nbt.ListTag;
|
||||||
|
import net.minecraft.nbt.Tag;
|
||||||
|
import net.minecraft.world.RandomizableContainer;
|
||||||
|
import net.minecraft.world.entity.vehicle.ContainerEntity;
|
||||||
|
import org.bukkit.entity.Player;
|
||||||
|
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||||
|
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||||
|
import org.checkerframework.framework.qual.DefaultQualifier;
|
||||||
|
|
||||||
|
@DefaultQualifier(NonNull.class)
|
||||||
|
public class PaperLootableInventoryData {
|
||||||
|
|
||||||
|
private static final Random RANDOM = new Random();
|
||||||
|
|
||||||
|
private long lastFill = -1;
|
||||||
|
private long nextRefill = -1;
|
||||||
|
private int numRefills = 0;
|
||||||
|
private @Nullable Map<UUID, Long> lootedPlayers;
|
||||||
|
|
||||||
|
public long getLastFill() {
|
||||||
|
return this.lastFill;
|
||||||
|
}
|
||||||
|
|
||||||
|
long getNextRefill() {
|
||||||
|
return this.nextRefill;
|
||||||
|
}
|
||||||
|
|
||||||
|
long setNextRefill(final long nextRefill) {
|
||||||
|
final long prev = this.nextRefill;
|
||||||
|
this.nextRefill = nextRefill;
|
||||||
|
return prev;
|
||||||
|
}
|
||||||
|
|
||||||
|
public <T> boolean shouldReplenish(final T lootTableHolder, final LootTableInterface<T> holderInterface, final net.minecraft.world.entity.player.@Nullable Player player) {
|
||||||
|
|
||||||
|
// No Loot Table associated
|
||||||
|
if (!holderInterface.hasLootTable(lootTableHolder)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ALWAYS process the first fill or if the feature is disabled
|
||||||
|
if (this.lastFill == -1 || !holderInterface.paperConfig(lootTableHolder).lootables.autoReplenish) {
|
||||||
|
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 WorldConfiguration paperConfig = holderInterface.paperConfig(lootTableHolder);
|
||||||
|
|
||||||
|
// Check if max refills has been hit
|
||||||
|
if (paperConfig.lootables.maxRefills != -1 && this.numRefills >= paperConfig.lootables.maxRefills) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Refill has not been reached
|
||||||
|
if (this.nextRefill > System.currentTimeMillis()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
final Player bukkitPlayer = (Player) player.getBukkitEntity();
|
||||||
|
final LootableInventoryReplenishEvent event = new LootableInventoryReplenishEvent(bukkitPlayer, holderInterface.getInventoryForEvent(lootTableHolder));
|
||||||
|
event.setCancelled(!this.canPlayerLoot(player.getUUID(), paperConfig));
|
||||||
|
return event.callEvent();
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface LootTableInterface<T> {
|
||||||
|
|
||||||
|
WorldConfiguration paperConfig(T holder);
|
||||||
|
|
||||||
|
void setSeed(T holder, long seed);
|
||||||
|
|
||||||
|
boolean hasLootTable(T holder);
|
||||||
|
|
||||||
|
LootableInventory getInventoryForEvent(T holder);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static final LootTableInterface<RandomizableContainer> CONTAINER = new LootTableInterface<>() {
|
||||||
|
@Override
|
||||||
|
public WorldConfiguration paperConfig(final RandomizableContainer holder) {
|
||||||
|
return Objects.requireNonNull(holder.getLevel(), "Can only manager loot replenishment on block entities in a world").paperConfig();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setSeed(final RandomizableContainer holder, final long seed) {
|
||||||
|
holder.setLootTableSeed(seed);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean hasLootTable(final RandomizableContainer holder) {
|
||||||
|
return holder.getLootTable() != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public LootableInventory getInventoryForEvent(final RandomizableContainer holder) {
|
||||||
|
return holder.getLootableInventory();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
public static final LootTableInterface<ContainerEntity> ENTITY = new LootTableInterface<>() {
|
||||||
|
@Override
|
||||||
|
public WorldConfiguration paperConfig(final ContainerEntity holder) {
|
||||||
|
return holder.level().paperConfig();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setSeed(final ContainerEntity holder, final long seed) {
|
||||||
|
holder.setContainerLootTableSeed(seed);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean hasLootTable(final ContainerEntity holder) {
|
||||||
|
return holder.getContainerLootTable() != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public LootableInventory getInventoryForEvent(final ContainerEntity holder) {
|
||||||
|
return holder.getLootableInventory();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
public <T> boolean shouldClearLootTable(final T lootTableHolder, final LootTableInterface<T> holderInterface, final net.minecraft.world.entity.player.@Nullable Player player) {
|
||||||
|
this.lastFill = System.currentTimeMillis();
|
||||||
|
final WorldConfiguration paperConfig = holderInterface.paperConfig(lootTableHolder);
|
||||||
|
if (paperConfig.lootables.autoReplenish) {
|
||||||
|
final long min = paperConfig.lootables.refreshMin.seconds();
|
||||||
|
final long max = paperConfig.lootables.refreshMax.seconds();
|
||||||
|
this.nextRefill = this.lastFill + (min + RANDOM.nextLong(max - min + 1)) * 1000L;
|
||||||
|
this.numRefills++;
|
||||||
|
if (paperConfig.lootables.resetSeedOnFill) {
|
||||||
|
holderInterface.setSeed(lootTableHolder, 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);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final String ROOT = "Paper.LootableData";
|
||||||
|
private static final String LAST_FILL = "lastFill";
|
||||||
|
private static final String NEXT_REFILL = "nextRefill";
|
||||||
|
private static final String NUM_REFILLS = "numRefills";
|
||||||
|
private static final String LOOTED_PLAYERS = "lootedPlayers";
|
||||||
|
|
||||||
|
public void loadNbt(final CompoundTag base) {
|
||||||
|
if (!base.contains(ROOT, Tag.TAG_COMPOUND)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
final CompoundTag comp = base.getCompound(ROOT);
|
||||||
|
if (comp.contains(LAST_FILL)) {
|
||||||
|
this.lastFill = comp.getLong(LAST_FILL);
|
||||||
|
}
|
||||||
|
if (comp.contains(NEXT_REFILL)) {
|
||||||
|
this.nextRefill = comp.getLong(NEXT_REFILL);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (comp.contains(NUM_REFILLS)) {
|
||||||
|
this.numRefills = comp.getInt(NUM_REFILLS);
|
||||||
|
}
|
||||||
|
if (comp.contains(LOOTED_PLAYERS, Tag.TAG_LIST)) {
|
||||||
|
final ListTag list = comp.getList(LOOTED_PLAYERS, Tag.TAG_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);
|
||||||
|
this.lootedPlayers.put(cmp.getUUID("UUID"), cmp.getLong("Time"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void saveNbt(final CompoundTag base) {
|
||||||
|
final CompoundTag comp = new CompoundTag();
|
||||||
|
if (this.nextRefill != -1) {
|
||||||
|
comp.putLong(NEXT_REFILL, this.nextRefill);
|
||||||
|
}
|
||||||
|
if (this.lastFill != -1) {
|
||||||
|
comp.putLong(LAST_FILL, this.lastFill);
|
||||||
|
}
|
||||||
|
if (this.numRefills != 0) {
|
||||||
|
comp.putInt(NUM_REFILLS, this.numRefills);
|
||||||
|
}
|
||||||
|
if (this.lootedPlayers != null && !this.lootedPlayers.isEmpty()) {
|
||||||
|
final ListTag list = new ListTag();
|
||||||
|
for (final Map.Entry<UUID, Long> entry : this.lootedPlayers.entrySet()) {
|
||||||
|
final CompoundTag cmp = new CompoundTag();
|
||||||
|
cmp.putUUID("UUID", entry.getKey());
|
||||||
|
cmp.putLong("Time", entry.getValue());
|
||||||
|
list.add(cmp);
|
||||||
|
}
|
||||||
|
comp.put(LOOTED_PLAYERS, list);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!comp.isEmpty()) {
|
||||||
|
base.put(ROOT, comp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void setPlayerLootedState(final UUID player, final boolean looted) {
|
||||||
|
if (looted && this.lootedPlayers == null) {
|
||||||
|
this.lootedPlayers = new HashMap<>();
|
||||||
|
}
|
||||||
|
if (looted) {
|
||||||
|
this.lootedPlayers.put(player, System.currentTimeMillis());
|
||||||
|
} else if (this.lootedPlayers != null) {
|
||||||
|
this.lootedPlayers.remove(player);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean canPlayerLoot(final UUID player, final WorldConfiguration worldConfiguration) {
|
||||||
|
final @Nullable Long lastLooted = this.getLastLooted(player);
|
||||||
|
if (!worldConfiguration.lootables.restrictPlayerReloot || lastLooted == null) return true;
|
||||||
|
|
||||||
|
final DurationOrDisabled restrictPlayerRelootTime = worldConfiguration.lootables.restrictPlayerRelootTime;
|
||||||
|
if (restrictPlayerRelootTime.value().isEmpty()) return false;
|
||||||
|
|
||||||
|
return TimeUnit.SECONDS.toMillis(restrictPlayerRelootTime.value().get().seconds()) + lastLooted < System.currentTimeMillis();
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean hasPlayerLooted(final UUID player) {
|
||||||
|
return this.lootedPlayers != null && this.lootedPlayers.containsKey(player);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable Long getLastLooted(final UUID player) {
|
||||||
|
return this.lootedPlayers != null ? this.lootedPlayers.get(player) : null;
|
||||||
|
}
|
||||||
|
}
|
@ -58,7 +58,8 @@ public class CraftBrushableBlock extends CraftBlockEntityState<BrushableBlockEnt
|
|||||||
this.setLootTable(this.getLootTable(), seed);
|
this.setLootTable(this.getLootTable(), seed);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setLootTable(LootTable table, long seed) {
|
@Override // Paper - this is now an override
|
||||||
|
public void setLootTable(LootTable table, long seed) { // Paper - make public since it overrides a public method
|
||||||
this.getSnapshot().setLootTable(CraftLootTable.bukkitToMinecraft(table), seed);
|
this.getSnapshot().setLootTable(CraftLootTable.bukkitToMinecraft(table), seed);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -8,7 +8,7 @@ import org.bukkit.craftbukkit.CraftLootTable;
|
|||||||
import org.bukkit.loot.LootTable;
|
import org.bukkit.loot.LootTable;
|
||||||
import org.bukkit.loot.Lootable;
|
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) {
|
public CraftLootable(World world, T tileEntity) {
|
||||||
super(world, tileEntity);
|
super(world, tileEntity);
|
||||||
@ -27,29 +27,17 @@ public abstract class CraftLootable<T extends RandomizableContainerBlockEntity>
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Paper start - move to PaperLootableBlockInventory
|
||||||
@Override
|
@Override
|
||||||
public LootTable getLootTable() {
|
public net.minecraft.world.level.Level getNMSWorld() {
|
||||||
return CraftLootTable.minecraftToBukkit(this.getSnapshot().lootTable);
|
return ((org.bukkit.craftbukkit.CraftWorld) this.getWorld()).getHandle();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setLootTable(LootTable table) {
|
public net.minecraft.world.RandomizableContainer getRandomizableContainer() {
|
||||||
this.setLootTable(table, this.getSeed());
|
return this.getSnapshot();
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public long getSeed() {
|
|
||||||
return this.getSnapshot().lootTableSeed;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setSeed(long seed) {
|
|
||||||
this.setLootTable(this.getLootTable(), seed);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setLootTable(LootTable table, long seed) {
|
|
||||||
this.getSnapshot().setLootTable(CraftLootTable.bukkitToMinecraft(table), seed);
|
|
||||||
}
|
}
|
||||||
|
// Paper end - move to PaperLootableBlockInventory
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public abstract CraftLootable<T> copy();
|
public abstract CraftLootable<T> copy();
|
||||||
|
@ -7,8 +7,7 @@ import org.bukkit.craftbukkit.inventory.CraftInventory;
|
|||||||
import org.bukkit.inventory.Inventory;
|
import org.bukkit.inventory.Inventory;
|
||||||
import org.bukkit.loot.LootTable;
|
import org.bukkit.loot.LootTable;
|
||||||
|
|
||||||
public abstract class CraftChestBoat extends CraftBoat implements org.bukkit.entity.ChestBoat {
|
public abstract class CraftChestBoat extends CraftBoat implements org.bukkit.entity.ChestBoat, com.destroystokyo.paper.loottable.PaperLootableEntityInventory { // Paper
|
||||||
|
|
||||||
private final Inventory inventory;
|
private final Inventory inventory;
|
||||||
|
|
||||||
public CraftChestBoat(CraftServer server, AbstractChestBoat entity) {
|
public CraftChestBoat(CraftServer server, AbstractChestBoat entity) {
|
||||||
@ -31,28 +30,6 @@ public abstract class CraftChestBoat extends CraftBoat implements org.bukkit.ent
|
|||||||
return this.inventory;
|
return this.inventory;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
// Paper - moved loot table logic to PaperLootableEntityInventory
|
||||||
public void setLootTable(LootTable table) {
|
|
||||||
this.setLootTable(table, this.getSeed());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public LootTable getLootTable() {
|
|
||||||
return CraftLootTable.minecraftToBukkit(this.getHandle().getContainerLootTable());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setSeed(long seed) {
|
|
||||||
this.setLootTable(this.getLootTable(), seed);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public long getSeed() {
|
|
||||||
return this.getHandle().getContainerLootTableSeed();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void setLootTable(LootTable table, long seed) {
|
|
||||||
this.getHandle().setContainerLootTable(CraftLootTable.bukkitToMinecraft(table));
|
|
||||||
this.getHandle().setContainerLootTableSeed(seed);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -7,7 +7,7 @@ import org.bukkit.entity.minecart.StorageMinecart;
|
|||||||
import org.bukkit.inventory.Inventory;
|
import org.bukkit.inventory.Inventory;
|
||||||
|
|
||||||
@SuppressWarnings("deprecation")
|
@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;
|
private final CraftInventory inventory;
|
||||||
|
|
||||||
public CraftMinecartChest(CraftServer server, MinecartChest entity) {
|
public CraftMinecartChest(CraftServer server, MinecartChest entity) {
|
||||||
|
@ -7,7 +7,7 @@ import org.bukkit.craftbukkit.CraftServer;
|
|||||||
import org.bukkit.loot.LootTable;
|
import org.bukkit.loot.LootTable;
|
||||||
import org.bukkit.loot.Lootable;
|
import org.bukkit.loot.Lootable;
|
||||||
|
|
||||||
public abstract class CraftMinecartContainer extends CraftMinecart implements Lootable {
|
public abstract class CraftMinecartContainer extends CraftMinecart implements com.destroystokyo.paper.loottable.PaperLootableEntityInventory { // Paper
|
||||||
|
|
||||||
public CraftMinecartContainer(CraftServer server, AbstractMinecart entity) {
|
public CraftMinecartContainer(CraftServer server, AbstractMinecart entity) {
|
||||||
super(server, entity);
|
super(server, entity);
|
||||||
@ -18,27 +18,5 @@ public abstract class CraftMinecartContainer extends CraftMinecart implements Lo
|
|||||||
return (AbstractMinecartContainer) this.entity;
|
return (AbstractMinecartContainer) this.entity;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
// Paper - moved loot table logic to PaperLootableEntityInventory
|
||||||
public void setLootTable(LootTable table) {
|
|
||||||
this.setLootTable(table, this.getSeed());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public LootTable getLootTable() {
|
|
||||||
return CraftLootTable.minecraftToBukkit(this.getHandle().lootTable);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setSeed(long seed) {
|
|
||||||
this.setLootTable(this.getLootTable(), seed);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public long getSeed() {
|
|
||||||
return this.getHandle().lootTableSeed;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setLootTable(LootTable table, long seed) {
|
|
||||||
this.getHandle().setLootTable(CraftLootTable.bukkitToMinecraft(table), seed);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -6,7 +6,7 @@ import org.bukkit.craftbukkit.inventory.CraftInventory;
|
|||||||
import org.bukkit.entity.minecart.HopperMinecart;
|
import org.bukkit.entity.minecart.HopperMinecart;
|
||||||
import org.bukkit.inventory.Inventory;
|
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;
|
private final CraftInventory inventory;
|
||||||
|
|
||||||
public CraftMinecartHopper(CraftServer server, MinecartHopper entity) {
|
public CraftMinecartHopper(CraftServer server, MinecartHopper entity) {
|
||||||
|
Loading…
Reference in New Issue
Block a user