From e14d7c0737c1f81061801a2c96f98c8c35a9dddb Mon Sep 17 00:00:00 2001 From: AStrangeNewUser Date: Sun, 2 Dec 2018 12:36:12 -0600 Subject: [PATCH] Fixes --- dist/AutomaticInventory^3 - 1810161704.jar | Bin 0 -> 41092 bytes .../AutomaticInventory/AIEventHandler.java | 578 ++++++++---------- .../AutomaticInventory.java | 531 +++++++--------- .../CustomizableMessage.java | 17 - .../AutomaticInventory/DataStore.java | 137 ++--- .../AutomaticInventory/DepositRecord.java | 7 +- .../AutomaticInventory/Features.java | 3 +- .../AutomaticInventory/FindChestsThread.java | 141 ++--- .../AutomaticInventory/Messages.java | 19 +- .../AutomaticInventory/PlayerData.java | 272 ++++----- .../SendPlayerMessageTask.java | 41 +- .../AutomaticInventory/TextMode.java | 13 +- 12 files changed, 718 insertions(+), 1041 deletions(-) create mode 100644 dist/AutomaticInventory^3 - 1810161704.jar delete mode 100644 src/me/ryanhamshire/AutomaticInventory/CustomizableMessage.java diff --git a/dist/AutomaticInventory^3 - 1810161704.jar b/dist/AutomaticInventory^3 - 1810161704.jar new file mode 100644 index 0000000000000000000000000000000000000000..133774013723bb3daee3a43de6458e249345306a GIT binary patch literal 41092 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 literal 0 HcmV?d00001 diff --git a/src/me/ryanhamshire/AutomaticInventory/AIEventHandler.java b/src/me/ryanhamshire/AutomaticInventory/AIEventHandler.java index 99a8871..cd22f1f 100644 --- a/src/me/ryanhamshire/AutomaticInventory/AIEventHandler.java +++ b/src/me/ryanhamshire/AutomaticInventory/AIEventHandler.java @@ -6,7 +6,6 @@ import org.bukkit.GameMode; import org.bukkit.Material; import org.bukkit.block.*; -import org.bukkit.enchantments.Enchantment; import org.bukkit.entity.HumanEntity; import org.bukkit.entity.Player; import org.bukkit.entity.minecart.StorageMinecart; @@ -22,488 +21,393 @@ import org.bukkit.event.inventory.InventoryType; import org.bukkit.event.player.*; import org.bukkit.inventory.*; -import org.bukkit.inventory.meta.ItemMeta; import org.bukkit.projectiles.ProjectileSource; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; -public class AIEventHandler implements Listener -{ - private EquipmentSlot getSlotWithItemStack(PlayerInventory inventory, ItemStack brokenItem) - { - if(itemsAreSimilar(inventory.getItemInMainHand(), brokenItem, false)) - { +public class AIEventHandler implements Listener { + static boolean featureEnabled(Features feature, Player player) { + if (!AutomaticInventory.hasPermission(feature, player)) return false; + + PlayerData data = PlayerData.FromPlayer(player); + + switch (feature) { + case SortInventory: + if (data.isSortInventory()) return true; + break; + case SortChests: + if (data.isSortChests()) return true; + break; + case RefillStacks: + return true; + case QuickDeposit: + return true; + case DepositAll: + return true; + } + + return false; + } + + static void sortPlayerIfEnabled(Player player, PlayerData playerData, Inventory inventory) { + if (featureEnabled(Features.SortInventory, player)) { + new InventorySorter(inventory, 9).run(); + + if (!playerData.isGotInventorySortInfo()) { + AutomaticInventory.sendMessage(player, TextMode.Info, Messages.InventorySortEducation); + playerData.setGotInventorySortInfo(true); + } + } + } + + static boolean isSortableChestInventory(Inventory inventory) { + if (inventory == null) return false; + + InventoryType inventoryType = inventory.getType(); + if (inventoryType != InventoryType.CHEST && inventoryType != InventoryType.ENDER_CHEST && inventoryType != InventoryType.SHULKER_BOX) + return false; + + String name = inventory.getName(); + if (name != null && name.contains("*")) return false; + + InventoryHolder holder = inventory.getHolder(); + return holder != null && (holder instanceof Chest || holder instanceof DoubleChest || holder instanceof StorageMinecart || holder instanceof ShulkerBox); + } + + private EquipmentSlot getSlotWithItemStack(PlayerInventory inventory, ItemStack brokenItem) { + if (inventory.getItemInMainHand().isSimilar(brokenItem)) { return EquipmentSlot.HAND; } - if(itemsAreSimilar(inventory.getItemInOffHand(), brokenItem, false)) - { + if (inventory.getItemInOffHand().isSimilar(brokenItem)) { return EquipmentSlot.OFF_HAND; } - + return null; } - + + @EventHandler(ignoreCancelled = true, priority = EventPriority.MONITOR) + public void onToolBreak(PlayerItemBreakEvent event) { + Player player = event.getPlayer(); + PlayerInventory inventory = player.getInventory(); + EquipmentSlot slot = this.getSlotWithItemStack(inventory, event.getBrokenItem()); + + tryRefillStackInHand(player, slot, false); + } + + @EventHandler(ignoreCancelled = true, priority = EventPriority.MONITOR) + public void onBlockPlace(BlockPlaceEvent event) { + Player player = event.getPlayer(); + tryRefillStackInHand(player, event.getHand(), true); + } + @EventHandler(ignoreCancelled = true, priority = EventPriority.MONITOR) - public void onToolBreak(PlayerItemBreakEvent event) - { - Player player = event.getPlayer(); - PlayerInventory inventory = player.getInventory(); - EquipmentSlot slot = this.getSlotWithItemStack(inventory, event.getBrokenItem()); - - tryRefillStackInHand(player, slot, false); - } - - @EventHandler(ignoreCancelled = true, priority = EventPriority.MONITOR) - public void onBlockPlace(BlockPlaceEvent event) - { - Player player = event.getPlayer(); - tryRefillStackInHand(player, event.getHand(), true); - } - - @EventHandler(ignoreCancelled = true, priority = EventPriority.MONITOR) - public void onConsumeItem(PlayerItemConsumeEvent event) - { + public void onConsumeItem(PlayerItemConsumeEvent event) { Player player = event.getPlayer(); PlayerInventory inventory = player.getInventory(); EquipmentSlot slot = this.getSlotWithItemStack(inventory, event.getItem()); tryRefillStackInHand(player, slot, true); } - - @EventHandler(ignoreCancelled = true, priority = EventPriority.MONITOR) - public void onProjectileLaunch(ProjectileLaunchEvent event) - { + + @EventHandler(ignoreCancelled = true, priority = EventPriority.MONITOR) + public void onProjectileLaunch(ProjectileLaunchEvent event) { ProjectileSource source = event.getEntity().getShooter(); - if(source == null || !(source instanceof Player)) return; - - Player player = (Player)source; + if (source == null || !(source instanceof Player)) return; + + Player player = (Player) source; tryRefillStackInHand(player, EquipmentSlot.HAND, false); } @SuppressWarnings("deprecation") - private void tryRefillStackInHand(Player player, EquipmentSlot slot, boolean dataValueMatters) - { - if(slot == null) return; - - if(!featureEnabled(Features.RefillStacks, player)) return; - + private void tryRefillStackInHand(Player player, EquipmentSlot slot, boolean dataValueMatters) { + if (slot == null) return; + + if (!featureEnabled(Features.RefillStacks, player)) return; + ItemStack stack = null; int slotIndex = 0; - if(slot == EquipmentSlot.HAND) - { + if (slot == EquipmentSlot.HAND) { stack = player.getInventory().getItemInMainHand(); slotIndex = player.getInventory().getHeldItemSlot(); - } - else if(slot == EquipmentSlot.OFF_HAND) - { + } else if (slot == EquipmentSlot.OFF_HAND) { stack = player.getInventory().getItemInOffHand(); slotIndex = 40; - } - else - { + } else { return; } if (AutomaticInventory.instance.config_noAutoRefillIDs.contains(stack.getType())) return; - if(!dataValueMatters || stack.getAmount() == 1) - { - PlayerInventory inventory = player.getInventory(); - AutomaticInventory.instance.getServer().getScheduler().scheduleSyncDelayedTask( - AutomaticInventory.instance, - new AutoRefillHotBarTask(player, inventory, slotIndex, stack.clone(), dataValueMatters), - 2L); - } - } - - static boolean featureEnabled(Features feature, Player player) - { - if(!AutomaticInventory.hasPermission(feature, player)) return false; - - PlayerData data = PlayerData.FromPlayer(player); - - switch(feature) - { - case SortInventory: - if(data.isSortInventory()) return true; - break; - case SortChests: - if(data.isSortChests()) return true; - break; - case RefillStacks: - return true; - case QuickDeposit: - return true; - case DepositAll: - return true; + if (!dataValueMatters || stack.getAmount() == 1) { + PlayerInventory inventory = player.getInventory(); + AutomaticInventory.instance.getServer().getScheduler().scheduleSyncDelayedTask( + AutomaticInventory.instance, + new AutoRefillHotBarTask(player, inventory, slotIndex, stack.clone(), dataValueMatters), + 2L); } - - return false; } - - @SuppressWarnings("deprecation") - private static boolean itemsAreSimilar(ItemStack a, ItemStack b, boolean dataValueMatters) - { - if(a.getType() == b.getType() && (!dataValueMatters || a.getData().getData() == b.getData().getData())) - { - if(a.containsEnchantment(Enchantment.LOOT_BONUS_BLOCKS) || a.containsEnchantment(Enchantment.SILK_TOUCH) || a.containsEnchantment(Enchantment.LOOT_BONUS_MOBS)) return false; - - if(a.hasItemMeta() != b.hasItemMeta()) return false; - - //compare metadata - if(a.hasItemMeta()) - { - if(!b.hasItemMeta()) return false; - - ItemMeta meta1 = a.getItemMeta(); - ItemMeta meta2 = b.getItemMeta(); - - //compare names - if(meta1.hasDisplayName()) - { - if(!meta2.hasDisplayName()) return false; - - return meta1.getDisplayName().equals(meta2.getDisplayName()); - } - } - - return true; - } - return false; - } + @EventHandler(ignoreCancelled = true, priority = EventPriority.MONITOR) + public void onBlockDamage(BlockDamageEvent event) { + Player player = event.getPlayer(); + if (!player.isSneaking()) return; - class AutoRefillHotBarTask implements Runnable - { - private Player player; - private PlayerInventory targetInventory; - private int slotToRefill; - private ItemStack stackToReplace; - private boolean dataValueMatters; - - public AutoRefillHotBarTask(Player player, PlayerInventory targetInventory, int slotToRefill, ItemStack stackToReplace, boolean dataValueMatters) - { - this.player = player; - this.targetInventory = targetInventory; - this.slotToRefill = slotToRefill; - this.stackToReplace = stackToReplace; - this.dataValueMatters = dataValueMatters; - } + if (!featureEnabled(Features.QuickDeposit, player)) return; - @Override - public void run() - { - ItemStack currentStack = this.targetInventory.getItem(this.slotToRefill); - if(currentStack != null) return; - - ItemStack bestMatchStack = null; - int bestMatchSlot = -1; - int bestMatchStackSize = Integer.MAX_VALUE; - for(int i = 0; i < 36; i++) - { - ItemStack itemInSlot = this.targetInventory.getItem(i); - if(itemInSlot == null) continue; - if(itemsAreSimilar(itemInSlot, this.stackToReplace, dataValueMatters)) - { - int stackSize = itemInSlot.getAmount(); - if(stackSize < bestMatchStackSize) - { - bestMatchStack = itemInSlot; - bestMatchSlot = i; - bestMatchStackSize = stackSize; - } - - if(bestMatchStackSize == 1) break; - } - } - - if(bestMatchStack == null) return; - - this.targetInventory.setItem(this.slotToRefill, bestMatchStack); - this.targetInventory.clear(bestMatchSlot); - - PlayerData playerData = PlayerData.FromPlayer(player); - if(!playerData.isGotRestackInfo()) - { - AutomaticInventory.sendMessage(player, TextMode.Info, Messages.AutoRefillEducation); - playerData.setGotRestackInfo(true); - } - } - } - - @EventHandler(ignoreCancelled = true, priority = EventPriority.MONITOR) - public void onBlockDamage(BlockDamageEvent event) - { - Player player = event.getPlayer(); - if(!player.isSneaking()) return; - - if(!featureEnabled(Features.QuickDeposit, player)) return; - Block clickedBlock = event.getBlock(); - if(clickedBlock == null) return; + if (clickedBlock == null) return; if (!(clickedBlock.getState() instanceof Container)) return; - + PlayerInteractEvent fakeEvent = AutomaticInventory.instance.new FakePlayerInteractEvent(player, Action.RIGHT_CLICK_BLOCK, player.getItemInHand(), clickedBlock, BlockFace.EAST); Bukkit.getServer().getPluginManager().callEvent(fakeEvent); - if(fakeEvent.isCancelled()) return; - - InventoryHolder chest = (InventoryHolder)clickedBlock.getState(); + if (fakeEvent.isCancelled()) return; + + InventoryHolder chest = (InventoryHolder) clickedBlock.getState(); Inventory chestInventory = chest.getInventory(); PlayerInventory playerInventory = player.getInventory(); - + event.setCancelled(true); Material aboveBlockID = clickedBlock.getRelative(BlockFace.UP).getType(); - if(AutomaticInventory.preventsChestOpen(aboveBlockID)) - { + if (AutomaticInventory.preventsChestOpen(aboveBlockID)) { AutomaticInventory.sendMessage(player, TextMode.Err, Messages.ChestLidBlocked); return; } - + DepositRecord deposits = AutomaticInventory.depositMatching(playerInventory, chestInventory, true); - + //send confirmation message to player with counts deposited. if none deposited, give instructions on how to set up the chest. - if(deposits.destinationFull && deposits.totalItems == 0) - { + if (deposits.destinationFull && deposits.totalItems == 0) { AutomaticInventory.sendMessage(player, TextMode.Err, Messages.FailedDepositChestFull2); - } - else if(deposits.totalItems == 0) - { + } else if (deposits.totalItems == 0) { AutomaticInventory.sendMessage(player, TextMode.Info, Messages.FailedDepositNoMatch); - } - else - { + } else { AutomaticInventory.sendMessage(player, TextMode.Success, Messages.SuccessfulDeposit2, String.valueOf(deposits.totalItems)); - + //make a note that quick deposit was used so that player will not be bothered with advertisement messages again. PlayerData playerData = PlayerData.FromPlayer(player); - if(!playerData.isUsedQuickDeposit()) - { + if (!playerData.isUsedQuickDeposit()) { playerData.setUsedQuickDeposit(true); } } - } - + } + @EventHandler(ignoreCancelled = true, priority = EventPriority.MONITOR) - public void onInventoryOpen(InventoryOpenEvent event) - { - Inventory bottomInventory = event.getView().getBottomInventory(); - if(bottomInventory == null) return; - if(bottomInventory.getType() != InventoryType.PLAYER) return; - - HumanEntity holder = ((PlayerInventory)bottomInventory).getHolder(); - if(!(holder instanceof Player)) return; - - Player player = (Player)holder; - PlayerData playerData = PlayerData.FromPlayer(player); - sortPlayerIfEnabled(player, playerData, bottomInventory); - - if(!player.isSneaking() && featureEnabled(Features.SortChests, player)) - { - Inventory topInventory = event.getView().getTopInventory(); - if(!isSortableChestInventory(topInventory)) return; - + public void onInventoryOpen(InventoryOpenEvent event) { + Inventory bottomInventory = event.getView().getBottomInventory(); + if (bottomInventory == null) return; + if (bottomInventory.getType() != InventoryType.PLAYER) return; + + HumanEntity holder = ((PlayerInventory) bottomInventory).getHolder(); + if (!(holder instanceof Player)) return; + + Player player = (Player) holder; + PlayerData playerData = PlayerData.FromPlayer(player); + sortPlayerIfEnabled(player, playerData, bottomInventory); + + if (!player.isSneaking() && featureEnabled(Features.SortChests, player)) { + Inventory topInventory = event.getView().getTopInventory(); + if (!isSortableChestInventory(topInventory)) return; + InventorySorter sorter = new InventorySorter(topInventory, 0); Bukkit.getServer().getScheduler().scheduleSyncDelayedTask(AutomaticInventory.instance, sorter, 1L); - - if(!playerData.isGotChestSortInfo()) - { + + if (!playerData.isGotChestSortInfo()) { AutomaticInventory.sendMessage(player, TextMode.Info, Messages.ChestSortEducation3); playerData.setGotChestSortInfo(true); } } } - - @EventHandler(ignoreCancelled = true, priority = EventPriority.MONITOR) - public void onInventoryClose(InventoryCloseEvent event) - { + + @EventHandler(ignoreCancelled = true, priority = EventPriority.MONITOR) + public void onInventoryClose(InventoryCloseEvent event) { Inventory bottomInventory = event.getView().getBottomInventory(); - if(bottomInventory == null) return; - if(bottomInventory.getType() != InventoryType.PLAYER) return; - - HumanEntity holder = ((PlayerInventory)bottomInventory).getHolder(); - if(!(holder instanceof Player)) return; - - Player player = (Player)holder; + if (bottomInventory == null) return; + if (bottomInventory.getType() != InventoryType.PLAYER) return; + + HumanEntity holder = ((PlayerInventory) bottomInventory).getHolder(); + if (!(holder instanceof Player)) return; + + Player player = (Player) holder; PlayerData playerData = PlayerData.FromPlayer(player); - + sortPlayerIfEnabled(player, playerData, bottomInventory); - - if(player.getGameMode() != GameMode.CREATIVE && Math.random() < .1 && !playerData.isGotDepositAllInfo() && featureEnabled(Features.DepositAll, player)) - { + + if (player.getGameMode() != GameMode.CREATIVE && Math.random() < .1 && !playerData.isGotDepositAllInfo() && featureEnabled(Features.DepositAll, player)) { Inventory topInventory = event.getView().getTopInventory(); - if(topInventory != null && topInventory.getType() == InventoryType.CHEST) - { + if (topInventory != null && topInventory.getType() == InventoryType.CHEST) { AutomaticInventory.sendMessage(player, TextMode.Instr, Messages.DepositAllAdvertisement); playerData.setGotDepositAllInfo(true); } } } - - @EventHandler(ignoreCancelled = true, priority = EventPriority.MONITOR) - public void onPickupItem(PlayerPickupItemEvent event) - { + + @EventHandler(ignoreCancelled = true, priority = EventPriority.MONITOR) + public void onPickupItem(PlayerPickupItemEvent event) { Player player = event.getPlayer(); - if(featureEnabled(Features.SortInventory, player)) - { + if (featureEnabled(Features.SortInventory, player)) { PlayerData playerData = PlayerData.FromPlayer(player); - if(playerData.firstEmptySlot >= 0) return; - + if (playerData.firstEmptySlot >= 0) return; + PlayerInventory inventory = player.getInventory(); int firstEmpty = inventory.firstEmpty(); - if(firstEmpty < 9) return; - playerData.firstEmptySlot = firstEmpty; + if (firstEmpty < 9) return; + playerData.firstEmptySlot = firstEmpty; PickupSortTask task = new PickupSortTask(player, playerData, inventory); Bukkit.getServer().getScheduler().scheduleSyncDelayedTask(AutomaticInventory.instance, task, 100L); } } - - static void sortPlayerIfEnabled(Player player, PlayerData playerData, Inventory inventory) - { - if(featureEnabled(Features.SortInventory, player)) - { - new InventorySorter(inventory, 9).run(); - - if(!playerData.isGotInventorySortInfo()) - { - AutomaticInventory.sendMessage(player, TextMode.Info, Messages.InventorySortEducation); - playerData.setGotInventorySortInfo(true); - } - } - } - - static boolean isSortableChestInventory(Inventory inventory) - { - if(inventory == null) return false; - - InventoryType inventoryType = inventory.getType(); - if(inventoryType != InventoryType.CHEST && inventoryType != InventoryType.ENDER_CHEST) return false; - - String name = inventory.getName(); - if(name != null && name.contains("*")) return false; - - InventoryHolder holder = inventory.getHolder(); - return holder != null && (holder instanceof Chest || holder instanceof DoubleChest || holder instanceof StorageMinecart); + + @EventHandler(ignoreCancelled = true, priority = EventPriority.MONITOR) + void onPlayerJoin(PlayerJoinEvent event) { + Player player = event.getPlayer(); + PlayerData.Preload(player); } @EventHandler(ignoreCancelled = true, priority = EventPriority.MONITOR) - void onPlayerJoin(PlayerJoinEvent event) - { - Player player = event.getPlayer(); - PlayerData.Preload(player); - } - - @EventHandler(ignoreCancelled = true, priority = EventPriority.MONITOR) - void onPlayerQuit(PlayerQuitEvent event) - { + void onPlayerQuit(PlayerQuitEvent event) { Player player = event.getPlayer(); PlayerData.FromPlayer(player).saveChanges(); } + + class AutoRefillHotBarTask implements Runnable { + private Player player; + private PlayerInventory targetInventory; + private int slotToRefill; + private ItemStack stackToReplace; + private boolean dataValueMatters; + + public AutoRefillHotBarTask(Player player, PlayerInventory targetInventory, int slotToRefill, ItemStack stackToReplace, boolean dataValueMatters) { + this.player = player; + this.targetInventory = targetInventory; + this.slotToRefill = slotToRefill; + this.stackToReplace = stackToReplace; + this.dataValueMatters = dataValueMatters; + } + + @Override + public void run() { + ItemStack currentStack = this.targetInventory.getItem(this.slotToRefill); + if (currentStack != null) return; + + ItemStack bestMatchStack = null; + int bestMatchSlot = -1; + int bestMatchStackSize = Integer.MAX_VALUE; + for (int i = 0; i < 36; i++) { + ItemStack itemInSlot = this.targetInventory.getItem(i); + if (itemInSlot == null) continue; + if (itemInSlot.isSimilar(this.stackToReplace)) { + int stackSize = itemInSlot.getAmount(); + if (stackSize < bestMatchStackSize) { + bestMatchStack = itemInSlot; + bestMatchSlot = i; + bestMatchStackSize = stackSize; + } + + if (bestMatchStackSize == 1) break; + } + } + + if (bestMatchStack == null) return; + + this.targetInventory.setItem(this.slotToRefill, bestMatchStack); + this.targetInventory.clear(bestMatchSlot); + + PlayerData playerData = PlayerData.FromPlayer(player); + if (!playerData.isGotRestackInfo()) { + AutomaticInventory.sendMessage(player, TextMode.Info, Messages.AutoRefillEducation); + playerData.setGotRestackInfo(true); + } + } + } } -class PickupSortTask implements Runnable -{ +class PickupSortTask implements Runnable { private Player player; private PlayerData playerData; private Inventory playerInventory; - - PickupSortTask(Player player, PlayerData playerData, Inventory playerInventory) - { + + PickupSortTask(Player player, PlayerData playerData, Inventory playerInventory) { this.player = player; this.playerData = playerData; this.playerInventory = playerInventory; } - + @Override - public void run() - { - if(this.playerData.firstEmptySlot == playerInventory.firstEmpty()) - { + public void run() { + if (this.playerData.firstEmptySlot == playerInventory.firstEmpty()) { this.playerData.firstEmptySlot = -1; return; } - + AIEventHandler.sortPlayerIfEnabled(this.player, this.playerData, this.playerInventory); - + this.playerData.firstEmptySlot = -1; } } -class InventorySorter implements Runnable -{ +class InventorySorter implements Runnable { private Inventory inventory; private int startIndex; - InventorySorter(Inventory inventory, int startIndex) - { + InventorySorter(Inventory inventory, int startIndex) { this.inventory = inventory; this.startIndex = startIndex; } - + @Override - public void run() - { + public void run() { ArrayList stacks = new ArrayList(); - ItemStack [] contents = this.inventory.getContents(); + ItemStack[] contents = this.inventory.getContents(); int inventorySize = contents.length; - if(this.inventory.getType() == InventoryType.PLAYER) inventorySize = Math.min(contents.length, 36); - for(int i = this.startIndex; i < inventorySize; i++) - { + if (this.inventory.getType() == InventoryType.PLAYER) inventorySize = Math.min(contents.length, 36); + for (int i = this.startIndex; i < inventorySize; i++) { ItemStack stack = contents[i]; - if(stack != null) - { + if (stack != null) { stacks.add(stack); } } - + Collections.sort(stacks, new StackComparator()); - for(int i = 1; i < stacks.size(); i++) - { + for (int i = 1; i < stacks.size(); i++) { ItemStack prevStack = stacks.get(i - 1); ItemStack thisStack = stacks.get(i); - if(prevStack.isSimilar(thisStack)) - { - if(prevStack.getAmount() < prevStack.getMaxStackSize()) - { + if (prevStack.isSimilar(thisStack)) { + if (prevStack.getAmount() < prevStack.getMaxStackSize()) { int moveCount = Math.min(prevStack.getMaxStackSize() - prevStack.getAmount(), thisStack.getAmount()); prevStack.setAmount(prevStack.getAmount() + moveCount); thisStack.setAmount(thisStack.getAmount() - moveCount); - if(thisStack.getAmount() == 0) - { + if (thisStack.getAmount() == 0) { stacks.remove(i); i--; } } } } - + int i; - for(i = 0; i < stacks.size(); i++) - { + for (i = 0; i < stacks.size(); i++) { this.inventory.setItem(i + this.startIndex, stacks.get(i)); } - - for(i = i + this.startIndex; i < inventorySize; i++) - { + + for (i = i + this.startIndex; i < inventorySize; i++) { this.inventory.clear(i); } } - - private class StackComparator implements Comparator - { + + private class StackComparator implements Comparator { @SuppressWarnings("deprecation") @Override - public int compare(ItemStack a, ItemStack b) - { + public int compare(ItemStack a, ItemStack b) { int result = new Integer(b.getMaxStackSize()).compareTo(a.getMaxStackSize()); - if(result != 0) return result; + if (result != 0) return result; result = b.getType().compareTo(a.getType()); - if(result != 0) return result; - + if (result != 0) return result; + result = new Byte(b.getData().getData()).compareTo(a.getData().getData()); - if(result != 0) return result; - + if (result != 0) return result; + result = new Integer(b.getAmount()).compareTo(a.getAmount()); return result; } diff --git a/src/me/ryanhamshire/AutomaticInventory/AutomaticInventory.java b/src/me/ryanhamshire/AutomaticInventory/AutomaticInventory.java index 752cd9d..3dab8d8 100644 --- a/src/me/ryanhamshire/AutomaticInventory/AutomaticInventory.java +++ b/src/me/ryanhamshire/AutomaticInventory/AutomaticInventory.java @@ -23,240 +23,20 @@ import java.util.*; import java.util.logging.Logger; -public class AutomaticInventory extends JavaPlugin -{ - //for convenience, a reference to the instance of this plugin - public static AutomaticInventory instance; - - //for logging to the console and log file - private static Logger log = Logger.getLogger("Minecraft"); +public class AutomaticInventory extends JavaPlugin { + //for convenience, a reference to the instance of this plugin + public static AutomaticInventory instance; + //for logging to the console and log file + public static Logger logger; + //this handles data storage, like player and region data + public DataStore dataStore; Set config_noAutoRefillIDs = new HashSet<>(); Set config_noAutoDepositIDs = new HashSet<>(); - - //this handles data storage, like player and region data - public DataStore dataStore; - - public synchronized static void AddLogEntry(String entry) - { - log.info("AutomaticInventory: " + entry); - } - - public void onEnable() - { - AddLogEntry("AutomaticInventory enabled."); - - instance = this; - - this.dataStore = new DataStore(); - - //read configuration settings (note defaults) - this.getDataFolder().mkdirs(); - File configFile = new File(this.getDataFolder().getPath() + File.separatorChar + "config.yml"); - FileConfiguration config = YamlConfiguration.loadConfiguration(configFile); - FileConfiguration outConfig = new YamlConfiguration(); - - List noAutoRefillIDs_string = config.getStringList("Auto Refill.Excluded Items"); - if(noAutoRefillIDs_string.size() == 0) - { - noAutoRefillIDs_string.add("AIR"); - noAutoRefillIDs_string.add("373"); - } - - for(String idString : noAutoRefillIDs_string) - { - try - { - this.config_noAutoRefillIDs.add(Material.valueOf(idString.toUpperCase())); - } catch (Exception e) { - } - } - - outConfig.set("Auto Refill.Excluded Items", noAutoRefillIDs_string); - - List noAutoDepositIDs_string = config.getStringList("Auto Deposit.Excluded Items"); - if(noAutoDepositIDs_string.size() == 0) - { - noAutoDepositIDs_string.add("0"); - noAutoDepositIDs_string.add("262"); - noAutoDepositIDs_string.add("439"); - noAutoDepositIDs_string.add("440"); - } - - for(String idString : noAutoDepositIDs_string) - { - try - { - this.config_noAutoDepositIDs.add(Material.valueOf(idString.toUpperCase())); - } catch (Exception e) { - } - } - - outConfig.set("Auto Deposit.Excluded Items", noAutoDepositIDs_string); - - try - { - outConfig.save(configFile); - } - catch(IOException e) - { - AddLogEntry("Encountered an issue while writing to the config file."); - e.printStackTrace(); - } - - //register for events - PluginManager pluginManager = this.getServer().getPluginManager(); - - AIEventHandler aIEventHandler = new AIEventHandler(); - pluginManager.registerEvents(aIEventHandler, this); - - @SuppressWarnings("unchecked") - Collection players = (Collection)this.getServer().getOnlinePlayers(); - for(Player player : players) - { - PlayerData.Preload(player); - } - } - - public boolean onCommand(CommandSender sender, Command cmd, String commandLabel, String[] args) - { - Player player = null; - PlayerData playerData = null; - if (sender instanceof Player) - { - player = (Player) sender; - playerData = PlayerData.FromPlayer(player); - } - - if(cmd.getName().equalsIgnoreCase("debugai") && player != null) - { - PlayerInventory inventory = player.getInventory(); - inventory.getItemInMainHand().setDurability(Short.MAX_VALUE); - /* - for(int i = 0; i < inventory.getSize(); i++) - { - ItemStack stack = inventory.getItem(i); - if(stack != null) - AutomaticInventory.AddLogEntry(String.valueOf(i) + " : " + stack.getType().name()); - }*/ - - return true; - } - - else if(cmd.getName().equalsIgnoreCase("autosort") && player != null) - { - if(args.length < 1) - { - sendMessage(player, TextMode.Instr, Messages.AutoSortHelp); - return true; - } - - String optionName = args[0].toLowerCase(); - if(optionName.startsWith("chest")) - { - if(!hasPermission(Features.SortChests, player)) - { - sendMessage(player, TextMode.Err, Messages.NoPermissionForFeature); - return true; - } - - playerData.setSortChests(!playerData.isSortChests()); - - if(playerData.isSortChests()) - sendMessage(player, TextMode.Success, Messages.ChestSortEnabled); - else - sendMessage(player, TextMode.Success, Messages.ChestSortDisabled); - } - else if(optionName.startsWith("inv")) - { - if(!hasPermission(Features.SortInventory, player)) - { - sendMessage(player, TextMode.Err, Messages.NoPermissionForFeature); - return true; - } - - playerData.setSortInventory(!playerData.isSortInventory()); - - if(playerData.isSortInventory()) - sendMessage(player, TextMode.Success, Messages.InventorySortEnabled); - else - sendMessage(player, TextMode.Success, Messages.InventorySortDisabled); - } - else - { - sendMessage(player, TextMode.Err, Messages.AutoSortHelp); - return true; - } - - DeliverTutorialHyperlink(player); - - return true; - } - - else if(cmd.getName().equalsIgnoreCase("depositall") && player != null) - { - //ensure player has feature enabled - if(!AIEventHandler.featureEnabled(Features.DepositAll, player)) - { - AutomaticInventory.sendMessage(player, TextMode.Err, Messages.NoPermissionForFeature); - return true; - } - - //gather snapshots of adjacent chunks - Location location = player.getLocation(); - Chunk centerChunk = location.getChunk(); - World world = location.getWorld(); - ChunkSnapshot [][] snapshots = new ChunkSnapshot[3][3]; - for(int x = -1; x <= 1; x++) - { - for(int z = -1; z <= 1; z++) - { - Chunk chunk = world.getChunkAt(centerChunk.getX() + x, centerChunk.getZ() + z); - snapshots[x + 1][z + 1] = chunk.getChunkSnapshot(); - } - } - - //create a thread to search those snapshots and create a chain of quick deposit attempts - int minY = Math.max(0, player.getEyeLocation().getBlockY() - 10); - int maxY = Math.min(world.getMaxHeight(), player.getEyeLocation().getBlockY() + 10); - int startY = player.getEyeLocation().getBlockY(); - int startX = player.getEyeLocation().getBlockX(); - int startZ = player.getEyeLocation().getBlockZ(); - Thread thread = new FindChestsThread(world, snapshots, minY, maxY, startX, startY, startZ, player); - thread.setPriority(Thread.MIN_PRIORITY); - thread.start(); - - playerData.setUsedDepositAll(true); - return true; - } - - return false; - } - void DeliverTutorialHyperlink(Player player) - { - //todo: deliver tutorial link to player - } - - public void onDisable() - { - @SuppressWarnings("unchecked") - Collection players = (Collection)this.getServer().getOnlinePlayers(); - for(Player player : players) - { - PlayerData data = PlayerData.FromPlayer(player); - data.saveChanges(); - data.waitForSaveComplete(); - } - - AddLogEntry("AutomaticInventory disabled."); - } - - static boolean hasPermission(Features feature, Player player) - { + static boolean hasPermission(Features feature, Player player) { boolean hasPermission = false; - switch(feature) - { + switch (feature) { case SortInventory: hasPermission = player.hasPermission("automaticinventory.sortinventory"); break; @@ -273,103 +53,67 @@ static boolean hasPermission(Features feature, Player player) hasPermission = player.hasPermission("automaticinventory.depositall"); break; } - + return hasPermission; } - - @SuppressWarnings("unused") - private static void sendMessage(Player player, String message) - { - if(player != null) - { - player.sendMessage(message); - } - else - { - AutomaticInventory.AddLogEntry(message); - } - } - - static void sendMessage(Player player, ChatColor color, Messages messageID, String... args) - { + + static void sendMessage(Player player, ChatColor color, Messages messageID, String... args) { sendMessage(player, color, messageID, 0, args); } - - static void sendMessage(Player player, ChatColor color, Messages messageID, long delayInTicks, String... args) - { + + static void sendMessage(Player player, ChatColor color, Messages messageID, long delayInTicks, String... args) { String message = AutomaticInventory.instance.dataStore.getMessage(messageID, args); sendMessage(player, color, message, delayInTicks); } - - static void sendMessage(Player player, ChatColor color, String message) - { - if(message == null || message.length() == 0) return; - - if(player == null) - { - AutomaticInventory.AddLogEntry(color + message); - } - else - { - player.sendMessage(color + message); - } + + static void sendMessage(Player player, ChatColor color, String message) { + if (message == null || message.length() == 0) return; + + if (player == null) logger.info(color + message); + else player.sendMessage(color + message); } - - static void sendMessage(Player player, ChatColor color, String message, long delayInTicks) - { + + static void sendMessage(Player player, ChatColor color, String message, long delayInTicks) { SendPlayerMessageTask task = new SendPlayerMessageTask(player, color, message); - if(delayInTicks > 0) - { + if (delayInTicks > 0) { AutomaticInventory.instance.getServer().getScheduler().runTaskLater(AutomaticInventory.instance, task, delayInTicks); - } - else - { + } else { task.run(); } } @SuppressWarnings("deprecation") - static DepositRecord depositMatching(PlayerInventory source, Inventory destination, boolean depositHotbar) - { + static DepositRecord depositMatching(PlayerInventory source, Inventory destination, boolean depositHotbar) { HashSet eligibleSignatures = new HashSet(); DepositRecord deposits = new DepositRecord(); - for(int i = 0; i < destination.getSize(); i++) - { + for (int i = 0; i < destination.getSize(); i++) { ItemStack destinationStack = destination.getItem(i); - if(destinationStack == null) continue; - + if (destinationStack == null) continue; + String signature = getSignature(destinationStack); eligibleSignatures.add(signature); } int sourceStartIndex = depositHotbar ? 0 : 9; int sourceSize = Math.min(source.getSize(), 36); - for(int i = sourceStartIndex; i < sourceSize; i++) - { + for (int i = sourceStartIndex; i < sourceSize; i++) { ItemStack sourceStack = source.getItem(i); - if(sourceStack == null) continue; + if (sourceStack == null) continue; if (AutomaticInventory.instance.config_noAutoDepositIDs.contains(sourceStack.getType())) continue; - + String signature = getSignature(sourceStack); int sourceStackSize = sourceStack.getAmount(); - if(eligibleSignatures.contains(signature)) - { + if (eligibleSignatures.contains(signature)) { HashMap notMoved = destination.addItem(sourceStack); - if(notMoved.isEmpty()) - { + if (notMoved.isEmpty()) { source.clear(i); deposits.totalItems += sourceStackSize; - } - else - { + } else { int notMovedCount = notMoved.values().iterator().next().getAmount(); int movedCount = sourceStackSize - notMovedCount; - if(movedCount == 0) - { + if (movedCount == 0) { eligibleSignatures.remove(signature); - } - else - { + } else { int newAmount = sourceStackSize - movedCount; sourceStack.setAmount(newAmount); deposits.totalItems += movedCount; @@ -377,35 +121,204 @@ static DepositRecord depositMatching(PlayerInventory source, Inventory destinati } } } - - if(destination.firstEmpty() == -1) - { + + if (destination.firstEmpty() == -1) { deposits.destinationFull = true; } - + return deposits; } - - @SuppressWarnings("deprecation") - private static String getSignature(ItemStack stack) - { + + private static String getSignature(ItemStack stack) { String signature = stack.getType().name(); if (stack.getMaxStackSize() > 1) { signature += "." + String.valueOf(stack.getData().getData()); } return signature; } - - public class FakePlayerInteractEvent extends PlayerInteractEvent - { - public FakePlayerInteractEvent(Player player, Action rightClickBlock, ItemStack itemInHand, Block clickedBlock, BlockFace blockFace) - { - super(player, rightClickBlock, itemInHand, clickedBlock, blockFace); + + static boolean preventsChestOpen(Material aboveBlockID) { + return aboveBlockID != Material.CHEST && aboveBlockID.isSolid(); + } + + public void onEnable() { + logger = getLogger(); + logger.info("AutomaticInventory enabled."); + instance = this; + dataStore = new DataStore(); + + //read configuration settings (note defaults) + this.getDataFolder().mkdirs(); + File configFile = new File(this.getDataFolder().getPath() + File.separatorChar + "config.yml"); + FileConfiguration config = YamlConfiguration.loadConfiguration(configFile); + FileConfiguration outConfig = new YamlConfiguration(); + + List noAutoRefillIDs_string = config.getStringList("Auto Refill.Excluded Items"); + if (noAutoRefillIDs_string.size() == 0) { + noAutoRefillIDs_string.add("AIR"); + noAutoRefillIDs_string.add("373"); + } + + for (String idString : noAutoRefillIDs_string) { + try { + this.config_noAutoRefillIDs.add(Material.valueOf(idString.toUpperCase())); + } catch (Exception e) { + } + } + + outConfig.set("Auto Refill.Excluded Items", noAutoRefillIDs_string); + + List noAutoDepositIDs_string = config.getStringList("Auto Deposit.Excluded Items"); + if (noAutoDepositIDs_string.size() == 0) { + noAutoDepositIDs_string.add("0"); + noAutoDepositIDs_string.add("262"); + noAutoDepositIDs_string.add("439"); + noAutoDepositIDs_string.add("440"); + } + + for (String idString : noAutoDepositIDs_string) { + try { + this.config_noAutoDepositIDs.add(Material.valueOf(idString.toUpperCase())); + } catch (Exception e) { + } + } + + outConfig.set("Auto Deposit.Excluded Items", noAutoDepositIDs_string); + + try { + outConfig.save(configFile); + } catch (IOException e) { + logger.info("Encountered an issue while writing to the config file."); + e.printStackTrace(); + } + + //register for events + PluginManager pluginManager = this.getServer().getPluginManager(); + + AIEventHandler aIEventHandler = new AIEventHandler(); + pluginManager.registerEvents(aIEventHandler, this); + + @SuppressWarnings("unchecked") + Collection players = (Collection) this.getServer().getOnlinePlayers(); + for (Player player : players) { + PlayerData.Preload(player); } } - static boolean preventsChestOpen(Material aboveBlockID) - { - return aboveBlockID != Material.CHEST && aboveBlockID.isSolid(); + public boolean onCommand(CommandSender sender, Command cmd, String commandLabel, String[] args) { + Player player = null; + PlayerData playerData = null; + if (sender instanceof Player) { + player = (Player) sender; + playerData = PlayerData.FromPlayer(player); + } + + if (cmd.getName().equalsIgnoreCase("debugai") && player != null) { + PlayerInventory inventory = player.getInventory(); + inventory.getItemInMainHand().setDurability(Short.MAX_VALUE); + /* + for(int i = 0; i < inventory.getSize(); i++) + { + ItemStack stack = inventory.getItem(i); + if(stack != null) + AutomaticInventory.logger.info(String.valueOf(i) + " : " + stack.getType().name()); + }*/ + + return true; + } else if (cmd.getName().equalsIgnoreCase("autosort") && player != null) { + if (args.length < 1) { + sendMessage(player, TextMode.Instr, Messages.AutoSortHelp); + return true; + } + + String optionName = args[0].toLowerCase(); + if (optionName.startsWith("chest")) { + if (!hasPermission(Features.SortChests, player)) { + sendMessage(player, TextMode.Err, Messages.NoPermissionForFeature); + return true; + } + + playerData.setSortChests(!playerData.isSortChests()); + + if (playerData.isSortChests()) + sendMessage(player, TextMode.Success, Messages.ChestSortEnabled); + else + sendMessage(player, TextMode.Success, Messages.ChestSortDisabled); + } else if (optionName.startsWith("inv")) { + if (!hasPermission(Features.SortInventory, player)) { + sendMessage(player, TextMode.Err, Messages.NoPermissionForFeature); + return true; + } + + playerData.setSortInventory(!playerData.isSortInventory()); + + if (playerData.isSortInventory()) + sendMessage(player, TextMode.Success, Messages.InventorySortEnabled); + else + sendMessage(player, TextMode.Success, Messages.InventorySortDisabled); + } else { + sendMessage(player, TextMode.Err, Messages.AutoSortHelp); + return true; + } + + DeliverTutorialHyperlink(player); + + return true; + } else if (cmd.getName().equalsIgnoreCase("depositall") && player != null) { + //ensure player has feature enabled + if (!AIEventHandler.featureEnabled(Features.DepositAll, player)) { + AutomaticInventory.sendMessage(player, TextMode.Err, Messages.NoPermissionForFeature); + return true; + } + + //gather snapshots of adjacent chunks + Location location = player.getLocation(); + Chunk centerChunk = location.getChunk(); + World world = location.getWorld(); + ChunkSnapshot[][] snapshots = new ChunkSnapshot[3][3]; + for (int x = -1; x <= 1; x++) { + for (int z = -1; z <= 1; z++) { + Chunk chunk = world.getChunkAt(centerChunk.getX() + x, centerChunk.getZ() + z); + snapshots[x + 1][z + 1] = chunk.getChunkSnapshot(); + } + } + + //create a thread to search those snapshots and create a chain of quick deposit attempts + int minY = Math.max(0, player.getEyeLocation().getBlockY() - 10); + int maxY = Math.min(world.getMaxHeight(), player.getEyeLocation().getBlockY() + 10); + int startY = player.getEyeLocation().getBlockY(); + int startX = player.getEyeLocation().getBlockX(); + int startZ = player.getEyeLocation().getBlockZ(); + Thread thread = new FindChestsThread(world, snapshots, minY, maxY, startX, startY, startZ, player); + thread.setPriority(Thread.MIN_PRIORITY); + thread.start(); + + playerData.setUsedDepositAll(true); + return true; + } + + return false; + } + + void DeliverTutorialHyperlink(Player player) { + //todo: deliver tutorial link to player + } + + public void onDisable() { + @SuppressWarnings("unchecked") + Collection players = (Collection) this.getServer().getOnlinePlayers(); + for (Player player : players) { + PlayerData data = PlayerData.FromPlayer(player); + data.saveChanges(); + data.waitForSaveComplete(); + } + + logger.info("AutomaticInventory disabled."); + } + + public class FakePlayerInteractEvent extends PlayerInteractEvent { + public FakePlayerInteractEvent(Player player, Action rightClickBlock, ItemStack itemInHand, Block clickedBlock, BlockFace blockFace) { + super(player, rightClickBlock, itemInHand, clickedBlock, blockFace); + } } } \ No newline at end of file diff --git a/src/me/ryanhamshire/AutomaticInventory/CustomizableMessage.java b/src/me/ryanhamshire/AutomaticInventory/CustomizableMessage.java deleted file mode 100644 index 4031f48..0000000 --- a/src/me/ryanhamshire/AutomaticInventory/CustomizableMessage.java +++ /dev/null @@ -1,17 +0,0 @@ -//Copyright 2015 Ryan Hamshire - -package me.ryanhamshire.AutomaticInventory; - -public class CustomizableMessage -{ - public Messages id; - public String text; - public String notes; - - public CustomizableMessage(Messages id, String text, String notes) - { - this.id = id; - this.text = text; - this.notes = notes; - } -} \ No newline at end of file diff --git a/src/me/ryanhamshire/AutomaticInventory/DataStore.java b/src/me/ryanhamshire/AutomaticInventory/DataStore.java index 72dd22c..2d668eb 100644 --- a/src/me/ryanhamshire/AutomaticInventory/DataStore.java +++ b/src/me/ryanhamshire/AutomaticInventory/DataStore.java @@ -1,122 +1,87 @@ //Copyright 2015 Ryan Hamshire package me.ryanhamshire.AutomaticInventory; +import org.bukkit.ChatColor; +import org.bukkit.configuration.file.FileConfiguration; +import org.bukkit.configuration.file.YamlConfiguration; + import java.io.File; import java.io.IOException; import java.util.HashMap; -import org.bukkit.configuration.file.FileConfiguration; -import org.bukkit.configuration.file.YamlConfiguration; - -public class DataStore -{ - //in-memory cache for messages - private String [] messages; - +public class DataStore { private final static String dataLayerFolderPath = "plugins" + File.separator + "AutomaticInventory"; final static String playerDataFolderPath = dataLayerFolderPath + File.separator + "PlayerData"; - final static String messagesFilePath = dataLayerFolderPath + File.separator + "messages.yml"; + private final static String messagesFilePath = dataLayerFolderPath + File.separator + "messages.yml"; + //in-memory cache for messages + private HashMap messages; - public DataStore() - { + public DataStore() { //ensure data folders exist File playerDataFolder = new File(playerDataFolderPath); - if(!playerDataFolder.exists()) - { + if (!playerDataFolder.exists()) { playerDataFolder.mkdirs(); } - + this.loadMessages(); - } - - private void loadMessages() - { - Messages [] messageIDs = Messages.values(); - this.messages = new String[Messages.values().length]; - - HashMap defaults = new HashMap(); - + } + + private void loadMessages() { + Messages[] messageIDs = Messages.values(); + this.messages = new HashMap<>(); + //initialize defaults - //this.addDefault(defaults, Messages.NoManagedWorld, "The PopulationDensity plugin has not been properly configured. Please update your config.yml to specify a world to manage.", null); - this.addDefault(defaults, Messages.NoPermissionForFeature, "You don't have permission to use that feature.", null); - this.addDefault(defaults, Messages.ChestSortEnabled, "Now auto-sorting any chests you use.", null); - this.addDefault(defaults, Messages.ChestSortDisabled, "Stopped auto-sorting chests you use.", null); - this.addDefault(defaults, Messages.InventorySortEnabled, "Now auto-sorting your personal inventory.", null); - this.addDefault(defaults, Messages.InventorySortDisabled, "Stopped auto-sorting your personal inventory.", null); - this.addDefault(defaults, Messages.AutoSortHelp, "Options are /AutoSort Chests and /AutoSort Inventory.", null); - this.addDefault(defaults, Messages.AutoRefillEducation, "AutomaticInventory(AI) will auto-replace broken tools and depleted hotbar stacks from your inventory.", null); - this.addDefault(defaults, Messages.InventorySortEducation, "AutomaticInventory(AI) will keep your inventory sorted. Use /AutoSort to disable.", null); - this.addDefault(defaults, Messages.ChestSortEducation3, "AutomaticInventory(AI) will sort the contents of chests you access. Use /AutoSort to toggle. TIP: Want some chests sorted but not others? Chests with names including an asterisk (*) won't auto-sort. You can rename any chest using an anvil.", null); - this.addDefault(defaults, Messages.SuccessfulDeposit2, "Deposited {0} items.", null); - this.addDefault(defaults, Messages.FailedDepositNoMatch, "No items deposited - none of your inventory items match items in that chest.", null); - this.addDefault(defaults, Messages.QuickDepositAdvertisement3, "Want to deposit quickly from your hotbar? Just pick a specific chest and sneak (hold shift) while hitting it.", null); - this.addDefault(defaults, Messages.FailedDepositChestFull2, "That chest is full.", null); - this.addDefault(defaults, Messages.SuccessfulDepositAll2, "Deposited {0} items into nearby chests.", null); - this.addDefault(defaults, Messages.ChestLidBlocked, "That chest isn't accessible.", null); - this.addDefault(defaults, Messages.DepositAllAdvertisement, "TIP: Instantly deposit all items from your inventory into all the right nearby boxes with /DepositAll!", null); - + //messages.put(Messages.NoManagedWorld, "The PopulationDensity plugin has not been properly configured. Please update your config.yml to specify a world to manage."); + messages.put(Messages.NoPermissionForFeature, "You don't have permission to use that feature."); + messages.put(Messages.ChestSortEnabled, "Now auto-sorting any chests you use."); + messages.put(Messages.ChestSortDisabled, "Stopped auto-sorting chests you use."); + messages.put(Messages.InventorySortEnabled, "Now auto-sorting your personal inventory."); + messages.put(Messages.InventorySortDisabled, "Stopped auto-sorting your personal inventory."); + messages.put(Messages.AutoSortHelp, "Options are /AutoSort Chests and /AutoSort Inventory."); + messages.put(Messages.AutoRefillEducation, "AutomaticInventory(AI) will auto-replace broken tools and depleted hotbar stacks from your inventory."); + messages.put(Messages.InventorySortEducation, "AutomaticInventory(AI) will keep your inventory sorted. Use /AutoSort to disable."); + messages.put(Messages.ChestSortEducation3, "AutomaticInventory(AI) will sort the contents of chests you access. Use /AutoSort to toggle. TIP: Want some chests sorted but not others? Chests with names including an asterisk (*) won't auto-sort. You can rename any chest using an anvil."); + messages.put(Messages.SuccessfulDeposit2, "Deposited {0} items."); + messages.put(Messages.FailedDepositNoMatch, "No items deposited - none of your inventory items match items in that chest."); + messages.put(Messages.QuickDepositAdvertisement3, "Want to deposit quickly from your hotbar? Just pick a specific chest and sneak (hold shift) while hitting it."); + messages.put(Messages.FailedDepositChestFull2, "That chest is full."); + messages.put(Messages.SuccessfulDepositAll2, "Deposited {0} items into nearby chests."); + messages.put(Messages.ChestLidBlocked, "That chest isn't accessible."); + messages.put(Messages.DepositAllAdvertisement, "TIP: Instantly deposit all items from your inventory into all the right nearby boxes with /DepositAll!"); + //load the config file FileConfiguration config = YamlConfiguration.loadConfiguration(new File(messagesFilePath)); FileConfiguration outConfig = new YamlConfiguration(); - + //for each message ID - for(int i = 0; i < messageIDs.length; i++) - { + for (Messages messageID : messageIDs) { //get default for this message - Messages messageID = messageIDs[i]; - CustomizableMessage messageData = defaults.get(messageID.name()); - - //if default is missing, log an error and use some fake data for now so that the plugin can run - if(messageData == null) - { - AutomaticInventory.AddLogEntry("Missing message for " + messageID.name() + ". Please contact the developer."); - messageData = new CustomizableMessage(messageID, "Missing message! ID: " + messageID.name() + ". Please contact a server admin.", null); - } - + String message = config.getString("messages." + messageID.name() + ".Text", messages.get(messageID)); + //read the message from the file, use default if necessary - this.messages[messageID.ordinal()] = config.getString("Messages." + messageID.name() + ".Text", messageData.text); - outConfig.set("Messages." + messageID.name() + ".Text", this.messages[messageID.ordinal()]); - + outConfig.set("messages." + messageID.name() + ".Text", message); + //support formatting codes - this.messages[messageID.ordinal()] = this.messages[messageID.ordinal()].replace('&', (char)0x00A7); - - if(messageData.notes != null) - { - messageData.notes = config.getString("Messages." + messageID.name() + ".Notes", messageData.notes); - outConfig.set("Messages." + messageID.name() + ".Notes", messageData.notes); - } + messages.put(messageID, ChatColor.translateAlternateColorCodes('&', message)); } - + //save any changes - try - { + try { outConfig.options().header("Use a YAML editor like NotepadPlusPlus to edit this file. \nAfter editing, back up your changes before reloading the server in case you made a syntax error. \nUse ampersands (&) for formatting codes, which are documented here: http://minecraft.gamepedia.com/Formatting_codes"); outConfig.save(DataStore.messagesFilePath); + } catch (IOException exception) { + AutomaticInventory.logger.info("Unable to write to the configuration file at \"" + DataStore.messagesFilePath + "\""); } - catch(IOException exception) - { - AutomaticInventory.AddLogEntry("Unable to write to the configuration file at \"" + DataStore.messagesFilePath + "\""); - } - - defaults.clear(); } - private void addDefault(HashMap defaults, Messages id, String text, String notes) - { - CustomizableMessage message = new CustomizableMessage(id, text, notes); - defaults.put(id.name(), message); - } + public synchronized String getMessage(Messages messageID, String... args) { + String message = messages.get(messageID); - synchronized public String getMessage(Messages messageID, String... args) - { - String message = messages[messageID.ordinal()]; - - for(int i = 0; i < args.length; i++) - { + for (int i = 0; i < args.length; i++) { String param = args[i]; message = message.replace("{" + i + "}", param); } - - return message; + + return message; } } diff --git a/src/me/ryanhamshire/AutomaticInventory/DepositRecord.java b/src/me/ryanhamshire/AutomaticInventory/DepositRecord.java index a405073..0680ab5 100644 --- a/src/me/ryanhamshire/AutomaticInventory/DepositRecord.java +++ b/src/me/ryanhamshire/AutomaticInventory/DepositRecord.java @@ -1,8 +1,9 @@ package me.ryanhamshire.AutomaticInventory; -class DepositRecord -{ - DepositRecord() {} +class DepositRecord { int totalItems = 0; boolean destinationFull = false; + + DepositRecord() { + } } diff --git a/src/me/ryanhamshire/AutomaticInventory/Features.java b/src/me/ryanhamshire/AutomaticInventory/Features.java index b96dd14..599ddc0 100644 --- a/src/me/ryanhamshire/AutomaticInventory/Features.java +++ b/src/me/ryanhamshire/AutomaticInventory/Features.java @@ -1,7 +1,6 @@ package me.ryanhamshire.AutomaticInventory; -enum Features -{ +enum Features { RefillStacks, SortInventory, SortChests, diff --git a/src/me/ryanhamshire/AutomaticInventory/FindChestsThread.java b/src/me/ryanhamshire/AutomaticInventory/FindChestsThread.java index 750d0d3..5d6309d 100644 --- a/src/me/ryanhamshire/AutomaticInventory/FindChestsThread.java +++ b/src/me/ryanhamshire/AutomaticInventory/FindChestsThread.java @@ -15,8 +15,7 @@ import java.util.Queue; import java.util.concurrent.ConcurrentLinkedQueue; -public class FindChestsThread extends Thread -{ +public class FindChestsThread extends Thread { private World world; private ChunkSnapshot[][] snapshots; private int minY; @@ -26,11 +25,10 @@ public class FindChestsThread extends Thread private int startZ; private Player player; private ChunkSnapshot smallestChunk; - - private boolean [][][] seen; - - public FindChestsThread(World world, ChunkSnapshot[][] snapshots, int minY, int maxY, int startX, int startY, int startZ, Player player) - { + + private boolean[][][] seen; + + public FindChestsThread(World world, ChunkSnapshot[][] snapshots, int minY, int maxY, int startX, int startY, int startZ, Player player) { this.world = world; this.snapshots = snapshots; this.minY = minY; @@ -39,70 +37,61 @@ public FindChestsThread(World world, ChunkSnapshot[][] snapshots, int minY, int this.startX = startX - this.smallestChunk.getX() * 16; this.startY = startY; this.startZ = startZ - this.smallestChunk.getZ() * 16; - if(this.maxY >= world.getMaxHeight()) this.maxY = world.getMaxHeight() - 1; + if (this.maxY >= world.getMaxHeight()) this.maxY = world.getMaxHeight() - 1; this.player = player; - this.seen = new boolean[48][this.maxY - this.minY + 1][48]; + this.seen = new boolean[48][this.maxY - this.minY + 1][48]; } - + @Override - public void run() - { + public void run() { Queue chestLocations = new ConcurrentLinkedQueue(); Queue leftToVisit = new ConcurrentLinkedQueue(); Vector start = new Vector(this.startX, this.startY, this.startZ); leftToVisit.add(start); this.markSeen(start); - while(!leftToVisit.isEmpty()) - { + while (!leftToVisit.isEmpty()) { Vector current = leftToVisit.remove(); Material type = this.getType(current); - if (type == Material.TRAPPED_CHEST || type == Material.ENDER_CHEST || type.name().contains("SHULKER")) - { + if (type == Material.TRAPPED_CHEST || type == Material.ENDER_CHEST || type.name().contains("SHULKER")) { Material overType = this.getType(new Vector(current.getBlockX(), current.getBlockY() + 1, current.getBlockZ())); - if (!AutomaticInventory.preventsChestOpen(overType)) - { + if (!AutomaticInventory.preventsChestOpen(overType)) { chestLocations.add(this.makeLocation(current)); } } - if (this.isPassableID(type)) - { - Vector [] adjacents = new Vector [] { - new Vector(current.getBlockX() + 1, current.getBlockY(), current.getBlockZ()), - new Vector(current.getBlockX() - 1, current.getBlockY(), current.getBlockZ()), - new Vector(current.getBlockX(), current.getBlockY() + 1, current.getBlockZ()), - new Vector(current.getBlockX(), current.getBlockY() - 1, current.getBlockZ()), - new Vector(current.getBlockX(), current.getBlockY(), current.getBlockZ() + 1), - new Vector(current.getBlockX(), current.getBlockY(), current.getBlockZ() - 1), + if (this.isPassableID(type)) { + Vector[] adjacents = new Vector[]{ + new Vector(current.getBlockX() + 1, current.getBlockY(), current.getBlockZ()), + new Vector(current.getBlockX() - 1, current.getBlockY(), current.getBlockZ()), + new Vector(current.getBlockX(), current.getBlockY() + 1, current.getBlockZ()), + new Vector(current.getBlockX(), current.getBlockY() - 1, current.getBlockZ()), + new Vector(current.getBlockX(), current.getBlockY(), current.getBlockZ() + 1), + new Vector(current.getBlockX(), current.getBlockY(), current.getBlockZ() - 1), }; - - for(Vector adjacent : adjacents) - { - if(!this.alreadySeen(adjacent)) - { + + for (Vector adjacent : adjacents) { + if (!this.alreadySeen(adjacent)) { leftToVisit.add(adjacent); this.markSeen(adjacent); } } } } - + QuickDepositChain chain = new QuickDepositChain(chestLocations, new DepositRecord(), player, true); Bukkit.getScheduler().runTaskLater(AutomaticInventory.instance, chain, 1L); } - - private Location makeLocation(Vector location) - { + + private Location makeLocation(Vector location) { return new Location( - this.world, - this.smallestChunk.getX() * 16 + location.getBlockX(), - location.getBlockY(), - this.smallestChunk.getZ() * 16 + location.getBlockZ()); + this.world, + this.smallestChunk.getX() * 16 + location.getBlockX(), + location.getBlockY(), + this.smallestChunk.getZ() * 16 + location.getBlockZ()); } - private Material getType(Vector location) - { + private Material getType(Vector location) { if (this.outOfBounds(location)) return null; int chunkx = location.getBlockX() / 16; int chunkz = location.getBlockZ() / 16; @@ -112,32 +101,28 @@ private Material getType(Vector location) return chunk.getBlockType(x, location.getBlockY(), z); } - private boolean alreadySeen(Vector location) - { - if(this.outOfBounds(location)) return true; + private boolean alreadySeen(Vector location) { + if (this.outOfBounds(location)) return true; int y = location.getBlockY() - this.minY; return this.seen[location.getBlockX()][y][location.getBlockZ()]; } - private void markSeen(Vector location) - { - if(this.outOfBounds(location)) return; + private void markSeen(Vector location) { + if (this.outOfBounds(location)) return; int y = location.getBlockY() - this.minY; this.seen[location.getBlockX()][y][location.getBlockZ()] = true; } - private boolean outOfBounds(Vector location) - { - if(location.getBlockY() > this.maxY) return true; - if(location.getBlockY() < this.minY) return true; - if(location.getBlockX() >= 48) return true; - if(location.getBlockX() < 0) return true; - if(location.getBlockZ() >= 48) return true; + private boolean outOfBounds(Vector location) { + if (location.getBlockY() > this.maxY) return true; + if (location.getBlockY() < this.minY) return true; + if (location.getBlockX() >= 48) return true; + if (location.getBlockX() < 0) return true; + if (location.getBlockZ() >= 48) return true; return location.getBlockZ() < 0; } - private boolean isPassableID(Material material) - { + private boolean isPassableID(Material material) { switch (material) { case AIR: case CHEST: @@ -150,16 +135,14 @@ private boolean isPassableID(Material material) return false; } } - - class QuickDepositChain implements Runnable - { + + class QuickDepositChain implements Runnable { private Queue remainingChestLocations; private DepositRecord runningDepositRecord; private Player player; private boolean respectExclusions; - - QuickDepositChain(Queue remainingChestLocations, DepositRecord runningDepositRecord, Player player, boolean respectExclusions) - { + + QuickDepositChain(Queue remainingChestLocations, DepositRecord runningDepositRecord, Player player, boolean respectExclusions) { super(); this.remainingChestLocations = remainingChestLocations; this.runningDepositRecord = runningDepositRecord; @@ -168,42 +151,34 @@ class QuickDepositChain implements Runnable } @Override - public void run() - { + public void run() { Location chestLocation = this.remainingChestLocations.poll(); - if(chestLocation == null) - { + if (chestLocation == null) { AutomaticInventory.sendMessage(this.player, TextMode.Success, Messages.SuccessfulDepositAll2, String.valueOf(this.runningDepositRecord.totalItems)); PlayerData playerData = PlayerData.FromPlayer(player); - if(Math.random() < .1 && !playerData.isGotQuickDepositInfo() && AIEventHandler.featureEnabled(Features.QuickDeposit, player)) - { + if (Math.random() < .1 && !playerData.isGotQuickDepositInfo() && AIEventHandler.featureEnabled(Features.QuickDeposit, player)) { AutomaticInventory.sendMessage(player, TextMode.Instr, Messages.QuickDepositAdvertisement3); playerData.setGotQuickDepositInfo(true); } - } - else - { + } else { Block block = chestLocation.getBlock(); PlayerInteractEvent fakeEvent = AutomaticInventory.instance.new FakePlayerInteractEvent(player, Action.RIGHT_CLICK_BLOCK, player.getInventory().getItemInMainHand(), block, BlockFace.UP); Bukkit.getServer().getPluginManager().callEvent(fakeEvent); - if(!fakeEvent.isCancelled()) - { + if (!fakeEvent.isCancelled()) { BlockState state = block.getState(); - if(state instanceof InventoryHolder) - { - InventoryHolder chest = (InventoryHolder)state; + if (state instanceof InventoryHolder) { + InventoryHolder chest = (InventoryHolder) state; Inventory chestInventory = chest.getInventory(); - if(!this.respectExclusions || AIEventHandler.isSortableChestInventory(chestInventory)) - { + if (!this.respectExclusions || AIEventHandler.isSortableChestInventory(chestInventory)) { PlayerInventory playerInventory = player.getInventory(); - - DepositRecord deposits = AutomaticInventory.depositMatching(playerInventory, chestInventory, false); - + + DepositRecord deposits = AutomaticInventory.depositMatching(playerInventory, chestInventory, false); + this.runningDepositRecord.totalItems += deposits.totalItems; } } } - + QuickDepositChain chain = new QuickDepositChain(this.remainingChestLocations, this.runningDepositRecord, this.player, this.respectExclusions); Bukkit.getScheduler().runTaskLater(AutomaticInventory.instance, chain, 1L); } diff --git a/src/me/ryanhamshire/AutomaticInventory/Messages.java b/src/me/ryanhamshire/AutomaticInventory/Messages.java index a8328ae..2166ba6 100644 --- a/src/me/ryanhamshire/AutomaticInventory/Messages.java +++ b/src/me/ryanhamshire/AutomaticInventory/Messages.java @@ -2,16 +2,15 @@ package me.ryanhamshire.AutomaticInventory; -public enum Messages -{ - AutoSortHelp, - NoPermissionForFeature, - ChestSortEnabled, - ChestSortDisabled, - InventorySortEnabled, - InventorySortDisabled, - AutoRefillEducation, - InventorySortEducation, +public enum Messages { + AutoSortHelp, + NoPermissionForFeature, + ChestSortEnabled, + ChestSortDisabled, + InventorySortEnabled, + InventorySortDisabled, + AutoRefillEducation, + InventorySortEducation, ChestSortEducation3, SuccessfulDeposit2, FailedDepositNoMatch, diff --git a/src/me/ryanhamshire/AutomaticInventory/PlayerData.java b/src/me/ryanhamshire/AutomaticInventory/PlayerData.java index 48cfae7..74fe5b9 100644 --- a/src/me/ryanhamshire/AutomaticInventory/PlayerData.java +++ b/src/me/ryanhamshire/AutomaticInventory/PlayerData.java @@ -2,21 +2,21 @@ package me.ryanhamshire.AutomaticInventory; -import java.io.File; -import java.io.PrintWriter; -import java.io.StringWriter; -import java.util.List; -import java.util.UUID; - import org.bukkit.configuration.file.FileConfiguration; import org.bukkit.configuration.file.YamlConfiguration; import org.bukkit.entity.Player; import org.bukkit.metadata.FixedMetadataValue; import org.bukkit.metadata.MetadataValue; -class PlayerData -{ +import java.io.File; +import java.io.PrintWriter; +import java.io.StringWriter; +import java.util.List; +import java.util.UUID; + +class PlayerData { private final static String METADATA_TAG = "AI_PlayerData"; + int firstEmptySlot = -1; private Thread loadingThread; private Thread savingThread; private String playerName; @@ -28,179 +28,138 @@ class PlayerData private boolean gotQuickDepositInfo = false; private boolean gotDepositAllInfo = false; private boolean usedDepositAll = false; - - boolean isUsedQuickDeposit() - { + private UUID playerID; + private boolean isDirty = false; + private boolean sortChests = true; + private boolean sortInventory = true; + + private PlayerData(Player player) { + this.playerName = player.getName(); + this.playerID = player.getUniqueId(); + this.loadingThread = new Thread(new DataLoader()); + this.loadingThread.start(); + player.setMetadata(METADATA_TAG, new FixedMetadataValue(AutomaticInventory.instance, this)); + } + + static void Preload(Player player) { + new PlayerData(player); + } + + static PlayerData FromPlayer(Player player) { + List data = player.getMetadata(METADATA_TAG); + if (data == null || data.isEmpty()) { + return new PlayerData(player); + } else { + return (PlayerData) (data.get(0).value()); + } + } + + boolean isUsedQuickDeposit() { return usedQuickDeposit; } - void setUsedQuickDeposit(boolean usedQuickDeposit) - { + void setUsedQuickDeposit(boolean usedQuickDeposit) { this.usedQuickDeposit = usedQuickDeposit; this.isDirty = true; } - - boolean isUsedDepositAll() - { - return usedDepositAll; - } - void setUsedDepositAll(boolean usedDepositAll) - { + void setUsedDepositAll(boolean usedDepositAll) { this.usedDepositAll = usedDepositAll; this.isDirty = true; } - boolean isGotChestSortInfo() - { + boolean isGotChestSortInfo() { return gotChestSortInfo; } - void setGotChestSortInfo(boolean gotChestSortInfo) - { + void setGotChestSortInfo(boolean gotChestSortInfo) { this.gotChestSortInfo = gotChestSortInfo; this.isDirty = true; } - boolean isGotInventorySortInfo() - { + boolean isGotInventorySortInfo() { return gotInventorySortInfo; } - void setGotInventorySortInfo(boolean gotInventorySortInfo) - { + void setGotInventorySortInfo(boolean gotInventorySortInfo) { this.gotInventorySortInfo = gotInventorySortInfo; this.isDirty = true; } - boolean isGotRestackInfo() - { + boolean isGotRestackInfo() { return gotRestackInfo; } - void setGotRestackInfo(boolean gotRestackInfo) - { + void setGotRestackInfo(boolean gotRestackInfo) { this.gotRestackInfo = gotRestackInfo; this.isDirty = true; } - private UUID playerID; - - static void Preload(Player player) - { - new PlayerData(player); - } - - static PlayerData FromPlayer(Player player) - { - List data = player.getMetadata(METADATA_TAG); - if(data == null || data.isEmpty()) - { - return new PlayerData(player); - } - else - { - PlayerData playerData = (PlayerData)(data.get(0).value()); - return playerData; - } - } - - private PlayerData(Player player) - { - this.playerName = player.getName(); - this.playerID = player.getUniqueId(); - this.loadingThread = new Thread(new DataLoader()); - this.loadingThread.start(); - player.setMetadata(METADATA_TAG, new FixedMetadataValue(AutomaticInventory.instance, this)); - } - - int firstEmptySlot = -1; - - private boolean isDirty = false; - - private boolean sortChests = true; - boolean isSortChests() - { + boolean isSortChests() { this.waitForLoadComplete(); return sortChests; } - void setSortChests(boolean sortChests) - { + + void setSortChests(boolean sortChests) { this.isDirty = true; this.sortChests = sortChests; } - private boolean sortInventory = true; - - boolean isSortInventory() - { + boolean isSortInventory() { this.waitForLoadComplete(); return sortInventory; } - void setSortInventory(boolean sortInventory) - { + + void setSortInventory(boolean sortInventory) { this.isDirty = true; this.sortInventory = sortInventory; } - - void incrementManualDeposits() - { + + void incrementManualDeposits() { this.manualDepositsThisSession++; } - - int getManualDeposits() - { + + int getManualDeposits() { return this.manualDepositsThisSession; } - - boolean isGotQuickDepositInfo() - { + + boolean isGotQuickDepositInfo() { return gotQuickDepositInfo; } - void setGotQuickDepositInfo(boolean newValue) - { + void setGotQuickDepositInfo(boolean newValue) { this.gotQuickDepositInfo = newValue; } - void saveChanges() - { - if(!this.isDirty) return; - + void saveChanges() { + if (!this.isDirty) return; + this.waitForLoadComplete(); this.savingThread = new Thread(new DataSaver()); this.savingThread.start(); } - - private void waitForLoadComplete() - { - if(this.loadingThread != null) - { - try - { + + private void waitForLoadComplete() { + if (this.loadingThread != null) { + try { this.loadingThread.join(); + } catch (InterruptedException e) { } - catch(InterruptedException e){} this.loadingThread = null; } } - - void waitForSaveComplete() - { - if(this.savingThread != null) - { - try - { + + void waitForSaveComplete() { + if (this.savingThread != null) { + try { this.savingThread.join(); + } catch (InterruptedException e) { } - catch(InterruptedException e){} } } - - private void writeDataToFile() - { - try - { + + private void writeDataToFile() { + try { FileConfiguration config = new YamlConfiguration(); config.set("Player Name", this.playerName); config.set("Sort Chests", this.sortChests); @@ -212,32 +171,26 @@ private void writeDataToFile() config.set("Received Messages.Deposit All", this.gotDepositAllInfo); File playerFile = new File(DataStore.playerDataFolderPath + File.separator + this.playerID.toString()); config.save(playerFile); - } - catch(Exception e) - { + } catch (Exception e) { StringWriter errors = new StringWriter(); e.printStackTrace(new PrintWriter(errors)); - AutomaticInventory.AddLogEntry("Failed to save player data for " + playerID + " " + errors.toString()); + AutomaticInventory.logger.severe("Failed to save player data for " + playerID + " " + errors.toString()); } - + this.savingThread = null; this.isDirty = false; } - - private void readDataFromFile() - { + + private void readDataFromFile() { File playerFile = new File(DataStore.playerDataFolderPath + File.separator + this.playerID.toString()); - + //if it exists as a file, read the file - if(playerFile.exists()) - { - boolean needRetry = false; + if (playerFile.exists()) { + boolean needRetry; int retriesRemaining = 5; Exception latestException = null; - do - { - try - { + do { + try { needRetry = false; FileConfiguration config = YamlConfiguration.loadConfiguration(playerFile); this.sortChests = config.getBoolean("Sort Chests", true); @@ -248,59 +201,50 @@ private void readDataFromFile() this.gotRestackInfo = config.getBoolean("Received Messages.Restacker", false); this.gotDepositAllInfo = config.getBoolean("Received Messages.Deposit All", false); } - + //if there's any problem with the file's content, retry up to 5 times with 5 milliseconds between - catch(Exception e) - { + catch (Exception e) { latestException = e; needRetry = true; retriesRemaining--; } - - try - { - if(needRetry) Thread.sleep(5); + + try { + if (needRetry) Thread.sleep(5); + } catch (InterruptedException exception) { } - catch(InterruptedException exception) {} - - }while(needRetry && retriesRemaining >= 0); - + + } while (needRetry && retriesRemaining >= 0); + //if last attempt failed, log information about the problem - if(needRetry) - { + if (needRetry) { StringWriter errors = new StringWriter(); latestException.printStackTrace(new PrintWriter(errors)); - AutomaticInventory.AddLogEntry("Failed to load data for " + playerID + " " + errors.toString()); + AutomaticInventory.logger.severe("Failed to load data for " + playerID + " " + errors.toString()); } } } - - private class DataSaver implements Runnable - { + + boolean isGotDepositAllInfo() { + return this.gotDepositAllInfo; + } + + public void setGotDepositAllInfo(boolean status) { + this.gotDepositAllInfo = status; + this.isDirty = true; + } + + private class DataSaver implements Runnable { @Override - public void run() - { + public void run() { writeDataToFile(); } } - - private class DataLoader implements Runnable - { + + private class DataLoader implements Runnable { @Override - public void run() - { + public void run() { readDataFromFile(); } } - - boolean isGotDepositAllInfo() - { - return this.gotDepositAllInfo; - } - - public void setGotDepositAllInfo(boolean status) - { - this.gotDepositAllInfo = status; - this.isDirty = true; - } } \ No newline at end of file diff --git a/src/me/ryanhamshire/AutomaticInventory/SendPlayerMessageTask.java b/src/me/ryanhamshire/AutomaticInventory/SendPlayerMessageTask.java index 1ebe963..9c305ab 100644 --- a/src/me/ryanhamshire/AutomaticInventory/SendPlayerMessageTask.java +++ b/src/me/ryanhamshire/AutomaticInventory/SendPlayerMessageTask.java @@ -7,28 +7,23 @@ //sends a message to a player //used to send delayed messages, for example help text triggered by a player's chat -class SendPlayerMessageTask implements Runnable -{ - private Player player; - private ChatColor color; - private String message; - - public SendPlayerMessageTask(Player player, ChatColor color, String message) - { - this.player = player; - this.color = color; - this.message = message; - } +class SendPlayerMessageTask implements Runnable { + private Player player; + private ChatColor color; + private String message; - @Override - public void run() - { - if(player == null) - { - AutomaticInventory.AddLogEntry(color + message); - return; - } - - AutomaticInventory.sendMessage(this.player, this.color, this.message); - } + public SendPlayerMessageTask(Player player, ChatColor color, String message) { + this.player = player; + this.color = color; + this.message = message; + } + + @Override + public void run() { + if (player == null) { + AutomaticInventory.logger.info(color + message); + return; + } + AutomaticInventory.sendMessage(this.player, this.color, this.message); + } } diff --git a/src/me/ryanhamshire/AutomaticInventory/TextMode.java b/src/me/ryanhamshire/AutomaticInventory/TextMode.java index 68c29ca..e5e4b4a 100644 --- a/src/me/ryanhamshire/AutomaticInventory/TextMode.java +++ b/src/me/ryanhamshire/AutomaticInventory/TextMode.java @@ -5,11 +5,10 @@ import org.bukkit.ChatColor; //just a few constants for chat color codes -class TextMode -{ - final static ChatColor Info = ChatColor.AQUA; - final static ChatColor Instr = ChatColor.YELLOW; - final static ChatColor Warn = ChatColor.GOLD; - final static ChatColor Err = ChatColor.RED; - final static ChatColor Success = ChatColor.GREEN; +class TextMode { + final static ChatColor Info = ChatColor.AQUA; + final static ChatColor Instr = ChatColor.YELLOW; + final static ChatColor Warn = ChatColor.GOLD; + final static ChatColor Err = ChatColor.RED; + final static ChatColor Success = ChatColor.GREEN; }