package com.destroystokyo.paper.loottable; import com.destroystokyo.paper.PaperWorldConfig; import com.koloboke.collect.map.hash.HashObjObjMaps; import net.minecraft.server.*; import org.bukkit.entity.Player; import javax.annotation.Nullable; import java.util.HashMap; import java.util.Map; import java.util.Random; import java.util.UUID; public class CraftLootableInventoryData { private static final Random RANDOM = new Random(); private long lastFill = -1; private long nextRefill = -1; private int numRefills = 0; private Map<UUID, Long> lootedPlayers; private final CraftLootableInventory lootable; public CraftLootableInventoryData(CraftLootableInventory lootable) { this.lootable = lootable; } long getLastFill() { return this.lastFill; } long getNextRefill() { return this.nextRefill; } long setNextRefill(long nextRefill) { long prev = this.nextRefill; this.nextRefill = nextRefill; return prev; } CraftLootableInventory getLootable() { return lootable; } public boolean shouldReplenish(@Nullable EntityHuman player) { String tableName = this.lootable.getLootTableName(); // No Loot Table associated if (tableName == null) { return false; } // ALWAYS process the first fill if (this.lastFill == -1) { return true; } // Only process refills when a player is set if (player == null) { return false; } // Chest is not scheduled for refill if (this.nextRefill == -1) { return false; } final PaperWorldConfig paperConfig = this.lootable.getNMSWorld().paperConfig; // Check if max refills has been hit if (paperConfig.maxLootableRefills != -1 && this.numRefills >= paperConfig.maxLootableRefills) { return false; } // Refill has not been reached if (this.nextRefill > System.currentTimeMillis()) { return false; } final Player bukkitPlayer = (Player) player.getBukkitEntity(); LootableInventoryReplenishEvent event = new LootableInventoryReplenishEvent(bukkitPlayer, lootable.getAPILootableInventory()); if (paperConfig.restrictPlayerReloot && hasPlayerLooted(player.getUniqueID())) { event.setCancelled(true); } return event.callEvent(); } public void processRefill(@Nullable EntityHuman player) { this.lastFill = System.currentTimeMillis(); final PaperWorldConfig paperConfig = this.lootable.getNMSWorld().paperConfig; if (paperConfig.autoReplenishLootables) { int min = paperConfig.lootableRegenMin * 1000; int max = paperConfig.lootableRegenMax * 1000; this.nextRefill = this.lastFill + min + RANDOM.nextInt(max - min + 1); this.numRefills++; if (paperConfig.changeLootTableSeedOnFill) { this.lootable.setLootTableSeed(0); } if (player != null) { // This means that numRefills can be incremented without a player being in the lootedPlayers list - Seems to be EntityMinecartChest specific this.setPlayerLootedState(player.getUniqueID(), true); } } else { this.lootable.clearLootTable(); } } public void loadNbt(NBTTagCompound base) { if (!base.hasKeyOfType("Paper.LootableData", 10)) { // 10 = compound return; } NBTTagCompound comp = base.getCompound("Paper.LootableData"); if (comp.hasKey("lastFill")) { this.lastFill = comp.getLong("lastFill"); } if (comp.hasKey("nextRefill")) { this.nextRefill = comp.getLong("nextRefill"); } if (comp.hasKey("numRefills")) { this.numRefills = comp.getInt("numRefills"); } if (comp.hasKeyOfType("lootedPlayers", 9)) { // 9 = list NBTTagList list = comp.getList("lootedPlayers", 10); // 10 = compound final int size = list.size(); if (size > 0) { this.lootedPlayers = HashObjObjMaps.newMutableMap(list.size()); } for (int i = 0; i < size; i++) { final NBTTagCompound cmp = list.get(i); lootedPlayers.put(cmp.getUUID("UUID"), cmp.getLong("Time")); } } } public void saveNbt(NBTTagCompound base) { NBTTagCompound comp = new NBTTagCompound(); if (this.nextRefill != -1) { comp.setLong("nextRefill", this.nextRefill); } if (this.lastFill != -1) { comp.setLong("lastFill", this.lastFill); } if (this.numRefills != 0) { comp.setInt("numRefills", this.numRefills); } if (this.lootedPlayers != null && !this.lootedPlayers.isEmpty()) { NBTTagList list = new NBTTagList(); for (Map.Entry<UUID, Long> entry : this.lootedPlayers.entrySet()) { NBTTagCompound cmp = new NBTTagCompound(); cmp.setUUID("UUID", entry.getKey()); cmp.setLong("Time", entry.getValue()); list.add(cmp); } comp.set("lootedPlayers", list); } if (!comp.isEmpty()) { base.set("Paper.LootableData", comp); } } void setPlayerLootedState(UUID player, boolean looted) { if (looted && this.lootedPlayers == null) { this.lootedPlayers = HashObjObjMaps.newMutableMap(); } 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; } }