diff --git a/pom.xml b/pom.xml index 3cc849e..12ea26c 100644 --- a/pom.xml +++ b/pom.xml @@ -97,6 +97,11 @@ 72 provided - + + com.songoda + epicfarming + 2.2.1 + provided + diff --git a/src/main/java/com/songoda/epichoppers/EpicHoppers.java b/src/main/java/com/songoda/epichoppers/EpicHoppers.java index 390c028..8b6ff34 100644 --- a/src/main/java/com/songoda/epichoppers/EpicHoppers.java +++ b/src/main/java/com/songoda/epichoppers/EpicHoppers.java @@ -38,6 +38,7 @@ import org.json.simple.JSONArray; import org.json.simple.JSONObject; import org.json.simple.parser.JSONParser; +import java.io.File; import java.io.InputStream; import java.io.InputStreamReader; import java.net.URL; @@ -74,6 +75,7 @@ public class EpicHoppers extends JavaPlugin { private Storage storage; private boolean liquidtanks = false; + private boolean epicfarming = false; public static EpicHoppers getInstance() { return INSTANCE; @@ -135,6 +137,9 @@ public class EpicHoppers extends JavaPlugin { // Check for liquid tanks if (pluginManager.isPluginEnabled("LiquidTanks")) liquidtanks = true; + // Check for epicfarming + if (pluginManager.isPluginEnabled("EpicFarming")) epicfarming = true; + // Start auto save int saveInterval = Setting.AUTOSAVE.getInt() * 60 * 20; Bukkit.getScheduler().runTaskTimerAsynchronously(this, this::saveToFile, saveInterval, saveInterval); @@ -303,7 +308,8 @@ public class EpicHoppers extends JavaPlugin { } private void loadLevelManager() { - saveResource("levels.yml", false); + if (!new File(this.getDataFolder(), "levels.yml").exists()) + this.saveResource("levels.yml", false); // Load an instance of LevelManager levelManager = new LevelManager(); @@ -404,4 +410,8 @@ public class EpicHoppers extends JavaPlugin { return liquidtanks; } + public boolean isEpicFarming() { + return epicfarming; + } + } diff --git a/src/main/java/com/songoda/epichoppers/gui/GUICrafting.java b/src/main/java/com/songoda/epichoppers/gui/GUICrafting.java index ee6a94d..97401c1 100644 --- a/src/main/java/com/songoda/epichoppers/gui/GUICrafting.java +++ b/src/main/java/com/songoda/epichoppers/gui/GUICrafting.java @@ -60,7 +60,7 @@ public class GUICrafting extends AbstractGUI { @Override protected void registerOnCloses() { registerOnClose(((player, inventory) -> - hopper.setAutoCrafting(inventory.getItem(13)))); + hopper.setAutoCrafting(player, inventory.getItem(13)))); } } diff --git a/src/main/java/com/songoda/epichoppers/gui/GUIFilter.java b/src/main/java/com/songoda/epichoppers/gui/GUIFilter.java index ec72c3c..9ffb596 100644 --- a/src/main/java/com/songoda/epichoppers/gui/GUIFilter.java +++ b/src/main/java/com/songoda/epichoppers/gui/GUIFilter.java @@ -156,59 +156,59 @@ public class GUIFilter extends AbstractGUI { private void compile(Player p) { - ItemStack[] items = p.getOpenInventory().getTopInventory().getContents(); + ItemStack[] items = p.getOpenInventory().getTopInventory().getContents(); - Filter filter = hopper.getFilter(); + Filter filter = hopper.getFilter(); - List owhite = new ArrayList<>(); - List oblack = new ArrayList<>(); - List ovoid = new ArrayList<>(); + List owhite = new ArrayList<>(); + List oblack = new ArrayList<>(); + List ovoid = new ArrayList<>(); - int[] awhite = {9, 10, 18, 19, 27, 28, 36, 37}; - int[] ablack = {11, 12, 20, 21, 29, 30, 38, 39}; - int[] avoid = {13, 14, 22, 23, 31, 32, 40, 41}; + int[] awhite = {9, 10, 18, 19, 27, 28, 36, 37}; + int[] ablack = {11, 12, 20, 21, 29, 30, 38, 39}; + int[] avoid = {13, 14, 22, 23, 31, 32, 40, 41}; - for (int i = 0; i < items.length; i++) { - for (int aa : awhite) { - if (aa != i) continue; - if (items[i] != null && items[i].getType() != Material.AIR) { - ItemStack item = items[i]; - if (item.getAmount() != 1) { - item.setAmount(item.getAmount() - 1); - Bukkit.getPlayer(hopper.getLastPlayer()).getInventory().addItem(item); - item.setAmount(1); - } - owhite.add(item); - } - } - for (int aa : ablack) { - if (aa != i) continue; - if (items[i] != null && items[i].getType() != Material.AIR) { - ItemStack item = items[i]; - if (item.getAmount() != 1) { - item.setAmount(item.getAmount() - 1); - Bukkit.getPlayer(hopper.getLastPlayer()).getInventory().addItem(item); - item.setAmount(1); - } - oblack.add(item); - } - } - for (int aa : avoid) { - if (aa != i) continue; - if (items[i] != null && items[i].getType() != Material.AIR) { - ItemStack item = items[i]; - if (item.getAmount() != 1) { - item.setAmount(item.getAmount() - 1); - Bukkit.getPlayer(hopper.getLastPlayer()).getInventory().addItem(item); - item.setAmount(1); - } - ovoid.add(item); + for (int i = 0; i < items.length; i++) { + for (int aa : awhite) { + if (aa != i) continue; + if (items[i] != null && items[i].getType() != Material.AIR) { + ItemStack item = items[i]; + if (item.getAmount() != 1) { + item.setAmount(item.getAmount() - 1); + Bukkit.getPlayer(hopper.getLastPlayer()).getInventory().addItem(item); + item.setAmount(1); } + owhite.add(item); } } - filter.setWhiteList(owhite); - filter.setBlackList(oblack); - filter.setVoidList(ovoid); + for (int aa : ablack) { + if (aa != i) continue; + if (items[i] != null && items[i].getType() != Material.AIR) { + ItemStack item = items[i]; + if (item.getAmount() != 1) { + item.setAmount(item.getAmount() - 1); + Bukkit.getPlayer(hopper.getLastPlayer()).getInventory().addItem(item); + item.setAmount(1); + } + oblack.add(item); + } + } + for (int aa : avoid) { + if (aa != i) continue; + if (items[i] != null && items[i].getType() != Material.AIR) { + ItemStack item = items[i]; + if (item.getAmount() != 1) { + item.setAmount(item.getAmount() - 1); + Bukkit.getPlayer(hopper.getLastPlayer()).getInventory().addItem(item); + item.setAmount(1); + } + ovoid.add(item); + } + } + } + filter.setWhiteList(owhite); + filter.setBlackList(oblack); + filter.setVoidList(ovoid); } @Override diff --git a/src/main/java/com/songoda/epichoppers/gui/GUIOverview.java b/src/main/java/com/songoda/epichoppers/gui/GUIOverview.java index e3b1ad8..d69ecbe 100644 --- a/src/main/java/com/songoda/epichoppers/gui/GUIOverview.java +++ b/src/main/java/com/songoda/epichoppers/gui/GUIOverview.java @@ -104,35 +104,13 @@ public class GUIOverview extends AbstractGUI { ItemMeta hookmeta = hook.getItemMeta(); hookmeta.setDisplayName(plugin.getLocale().getMessage("interface.hopper.synchopper")); ArrayList lorehook = new ArrayList<>(); - parts = plugin.getLocale().getMessage("interface.hopper.synclore", hopper.getLinkedBlocks().size()).split("\\|"); + parts = plugin.getLocale().getMessage("interface.hopper.synclore", hopper.getLinkedBlocks().stream().distinct().count()).split("\\|"); for (String line : parts) { lorehook.add(Methods.formatText(line)); } hookmeta.setLore(lorehook); hook.setItemMeta(hookmeta); - ItemStack itemXP = new ItemStack(Material.valueOf(plugin.getConfig().getString("Interfaces.XP Icon")), 1); - ItemMeta itemmetaXP = itemXP.getItemMeta(); - itemmetaXP.setDisplayName(plugin.getLocale().getMessage("interface.hopper.upgradewithxp")); - ArrayList loreXP = new ArrayList<>(); - if (nextLevel != null) - loreXP.add(plugin.getLocale().getMessage("interface.hopper.upgradewithxplore", nextLevel.getCostExperience())); - else - loreXP.add(plugin.getLocale().getMessage("interface.hopper.alreadymaxed")); - itemmetaXP.setLore(loreXP); - itemXP.setItemMeta(itemmetaXP); - - ItemStack itemECO = new ItemStack(Material.valueOf(plugin.getConfig().getString("Interfaces.Economy Icon")), 1); - ItemMeta itemmetaECO = itemECO.getItemMeta(); - itemmetaECO.setDisplayName(plugin.getLocale().getMessage("interface.hopper.upgradewitheconomy")); - ArrayList loreECO = new ArrayList<>(); - if (nextLevel != null) - loreECO.add(plugin.getLocale().getMessage("interface.hopper.upgradewitheconomylore", Methods.formatEconomy(nextLevel.getCostEconomy()))); - else - loreECO.add(plugin.getLocale().getMessage("interface.hopper.alreadymaxed")); - itemmetaECO.setLore(loreECO); - itemECO.setItemMeta(itemmetaECO); - int nu = 0; while (nu != 27) { inventory.setItem(nu, Methods.getGlass()); @@ -182,30 +160,54 @@ public class GUIOverview extends AbstractGUI { } } - if (plugin.getConfig().getBoolean("Main.Upgrade With XP") - && player.hasPermission("EpicHoppers.Upgrade.XP") - && level.getCostExperience() != -1) { - inventory.setItem(11, itemXP); + if (plugin.getConfig().getBoolean("Main.Allow hopper Upgrading")) { + ItemStack itemXP = new ItemStack(Material.valueOf(plugin.getConfig().getString("Interfaces.XP Icon")), 1); + ItemMeta itemmetaXP = itemXP.getItemMeta(); + itemmetaXP.setDisplayName(plugin.getLocale().getMessage("interface.hopper.upgradewithxp")); + ArrayList loreXP = new ArrayList<>(); + if (nextLevel != null) + loreXP.add(plugin.getLocale().getMessage("interface.hopper.upgradewithxplore", nextLevel.getCostExperience())); + else + loreXP.add(plugin.getLocale().getMessage("interface.hopper.alreadymaxed")); + itemmetaXP.setLore(loreXP); + itemXP.setItemMeta(itemmetaXP); - registerClickable(11, ((player, inventory, cursor, slot, type) -> { - hopper.upgrade(player, CostType.EXPERIENCE); - this.hopper.overview(player); - })); + ItemStack itemECO = new ItemStack(Material.valueOf(plugin.getConfig().getString("Interfaces.Economy Icon")), 1); + ItemMeta itemmetaECO = itemECO.getItemMeta(); + itemmetaECO.setDisplayName(plugin.getLocale().getMessage("interface.hopper.upgradewitheconomy")); + ArrayList loreECO = new ArrayList<>(); + if (nextLevel != null) + loreECO.add(plugin.getLocale().getMessage("interface.hopper.upgradewitheconomylore", Methods.formatEconomy(nextLevel.getCostEconomy()))); + else + loreECO.add(plugin.getLocale().getMessage("interface.hopper.alreadymaxed")); + itemmetaECO.setLore(loreECO); + itemECO.setItemMeta(itemmetaECO); + + if (plugin.getConfig().getBoolean("Main.Upgrade With XP") + && player.hasPermission("EpicHoppers.Upgrade.XP") + && level.getCostExperience() != -1) { + inventory.setItem(11, itemXP); + + registerClickable(11, ((player, inventory, cursor, slot, type) -> { + hopper.upgrade(player, CostType.EXPERIENCE); + this.hopper.overview(player); + })); + } + + if (plugin.getConfig().getBoolean("Main.Upgrade With Economy") + && player.hasPermission("EpicHoppers.Upgrade.ECO") + && level.getCostEconomy() != -1) { + inventory.setItem(15, itemECO); + + registerClickable(15, ((player, inventory, cursor, slot, type) -> { + hopper.upgrade(player, CostType.ECONOMY); + this.hopper.overview(player); + })); + } } inventory.setItem(13, item); - if (plugin.getConfig().getBoolean("Main.Upgrade With Economy") - && player.hasPermission("EpicHoppers.Upgrade.ECO") - && level.getCostEconomy() != -1) { - inventory.setItem(15, itemECO); - - registerClickable(15, ((player, inventory, cursor, slot, type) -> { - hopper.upgrade(player, CostType.ECONOMY); - this.hopper.overview(player); - })); - } - inventory.setItem(0, Methods.getBackgroundGlass(true)); inventory.setItem(1, Methods.getBackgroundGlass(true)); inventory.setItem(2, Methods.getBackgroundGlass(false)); diff --git a/src/main/java/com/songoda/epichoppers/handlers/TeleportHandler.java b/src/main/java/com/songoda/epichoppers/handlers/TeleportHandler.java index 353786d..b1b8ed6 100644 --- a/src/main/java/com/songoda/epichoppers/handlers/TeleportHandler.java +++ b/src/main/java/com/songoda/epichoppers/handlers/TeleportHandler.java @@ -14,7 +14,6 @@ import org.bukkit.entity.Entity; import org.bukkit.entity.EntityType; import org.bukkit.entity.LivingEntity; import org.bukkit.entity.Player; -import org.bukkit.inventory.InventoryHolder; import java.util.Date; import java.util.HashMap; @@ -23,71 +22,75 @@ import java.util.UUID; public class TeleportHandler { - //Teleport from - teleport 2 - private final Map teleportFrom = new HashMap<>(); private final Map lastTeleports = new HashMap<>(); private EpicHoppers instance; public TeleportHandler(EpicHoppers instance) { - this.instance = instance; - Bukkit.getScheduler().scheduleSyncRepeatingTask(instance, this::teleportRunner, 0, instance.getConfig().getLong("Main.Amount of Ticks Between Teleport")); + this.instance = instance; + Bukkit.getScheduler().scheduleSyncRepeatingTask(instance, this::teleportRunner, 0, instance.getConfig().getLong("Main.Amount of Ticks Between Teleport")); } private void teleportRunner() { for (World world : Bukkit.getWorlds()) { for (Entity entity : world.getEntities()) { - if (!(entity instanceof LivingEntity) ||entity.getType() == EntityType.ARMOR_STAND) continue; - - if (!instance.getConfig().getBoolean("Main.Allow Players To Teleport Through Hoppers") - || entity instanceof Player && !((Player)entity).hasPermission("EpicHoppers.Teleport")) { + if (!(entity instanceof LivingEntity) || entity.getType() == EntityType.ARMOR_STAND) + continue; + + if (!this.instance.getConfig().getBoolean("Main.Allow Players To Teleport Through Hoppers") + || (entity instanceof Player && !entity.hasPermission("EpicHoppers.Teleport"))) continue; - } Location location = entity.getLocation().getBlock().getRelative(BlockFace.DOWN).getLocation(); - if (!instance.getHopperManager().isHopper(location)) { + if (!this.instance.getHopperManager().isHopper(location)) continue; - } - Hopper hopper = instance.getHopperManager().getHopper(location); + Hopper hopper = this.instance.getHopperManager().getHopper(location); - if (hopper.getTeleportTrigger() != TeleportTrigger.WALK_ON) continue; + if (hopper.getTeleportTrigger() != TeleportTrigger.WALK_ON) + continue; - if (lastTeleports.containsKey(entity.getUniqueId())) { - long duration = (new Date()).getTime() - new Date(lastTeleports.get(entity.getUniqueId())).getTime(); - if (duration <= 5 * 1000) { + if (this.lastTeleports.containsKey(entity.getUniqueId())) { + long duration = (new Date()).getTime() - new Date(this.lastTeleports.get(entity.getUniqueId())).getTime(); + if (duration <= 5 * 1000) continue; - } } - tpEntity(entity, hopper); - lastTeleports.put(entity.getUniqueId(), System.currentTimeMillis()); + this.tpEntity(entity, hopper); + this.lastTeleports.put(entity.getUniqueId(), System.currentTimeMillis()); } } } public void tpEntity(Entity entity, Hopper hopper) { - if (hopper == null || !instance.getHopperManager().isHopper(hopper.getLocation())) return; + if (hopper == null || !this.instance.getHopperManager().isHopper(hopper.getLocation())) + return; - EpicHoppers instance = EpicHoppers.getInstance(); - Hopper lastHopper = hopper; - for (int i = 0; i < 15; i++) { - boolean empty = lastHopper.getLinkedBlocks().isEmpty(); - if (empty && i == 0) { - if (teleportFrom.containsKey(hopper.getLocation())) - doTeleport(entity, teleportFrom.get(hopper.getLocation()).clone()); - return; - } + Hopper lastHopper = this.getChain(hopper, 1); + if (hopper != lastHopper) + this.doTeleport(entity, lastHopper.getLocation()); + } - if (empty) break; - Location nextHopper = lastHopper.getLinkedBlocks().get(0); - if (!(nextHopper.getBlock().getState() instanceof InventoryHolder)) break; - lastHopper = instance.getHopperManager().getHopper(nextHopper); + /** + * Recursively gets the next hopper in the linked hopper chain + * @param lastHopper The previous hopper found in the chain + * @param currentChainLength The current length of the chain, used to cap the search length + * @return The hopper at the end of the chain (or up to 15 in depth) + */ + private Hopper getChain(Hopper lastHopper, int currentChainLength) { + if (currentChainLength > 15) + return lastHopper; + + for (Location nextHopperLocation : lastHopper.getLinkedBlocks()) { + if (nextHopperLocation.getBlock().getState() instanceof org.bukkit.block.Hopper) { + Hopper hopper = this.instance.getHopperManager().getHopper(nextHopperLocation); + if (hopper != null) + return this.getChain(hopper, currentChainLength + 1); } + } - teleportFrom.put(lastHopper.getLocation(), hopper.getLocation()); - doTeleport(entity, lastHopper.getLocation()); + return lastHopper; } private void doTeleport(Entity entity, Location location) { @@ -95,14 +98,14 @@ public class TeleportHandler { location.setPitch(entity.getLocation().getPitch()); location.setDirection(entity.getLocation().getDirection()); - if (instance.isServerVersionAtLeast(ServerVersion.V1_12)) { + if (this.instance.isServerVersionAtLeast(ServerVersion.V1_12)) { Methods.doParticles(entity, location); Methods.doParticles(entity, entity.getLocation().getBlock().getRelative(BlockFace.DOWN).getLocation()); } - + entity.teleport(location); - if (instance.isServerVersionAtLeast(ServerVersion.V1_12)) + if (this.instance.isServerVersionAtLeast(ServerVersion.V1_12)) entity.getWorld().playSound(entity.getLocation(), Sound.ENTITY_ENDERMAN_TELEPORT, 10, 10); } } diff --git a/src/main/java/com/songoda/epichoppers/hopper/Hopper.java b/src/main/java/com/songoda/epichoppers/hopper/Hopper.java index 1b68eef..77694fa 100644 --- a/src/main/java/com/songoda/epichoppers/hopper/Hopper.java +++ b/src/main/java/com/songoda/epichoppers/hopper/Hopper.java @@ -8,7 +8,11 @@ import com.songoda.epichoppers.utils.CostType; import com.songoda.epichoppers.utils.Methods; import com.songoda.epichoppers.utils.ServerVersion; import com.songoda.epichoppers.utils.TeleportTrigger; -import org.bukkit.*; +import org.bukkit.Bukkit; +import org.bukkit.GameMode; +import org.bukkit.Location; +import org.bukkit.Sound; +import org.bukkit.World; import org.bukkit.block.Block; import org.bukkit.entity.Player; import org.bukkit.inventory.ItemStack; @@ -53,16 +57,16 @@ public class Hopper { } public void overview(Player player) { - if (lastPlayer != null - && lastPlayer != player.getUniqueId() - && Bukkit.getPlayer(lastPlayer) != null) { - Bukkit.getPlayer(lastPlayer).closeInventory(); - } - if (placedBy == null) placedBy = player.getUniqueId(); + if (lastPlayer != null + && lastPlayer != player.getUniqueId() + && Bukkit.getPlayer(lastPlayer) != null) { + Bukkit.getPlayer(lastPlayer).closeInventory(); + } + if (placedBy == null) placedBy = player.getUniqueId(); EpicHoppers instance = EpicHoppers.getInstance(); - if (!player.hasPermission("epichoppers.overview")) return; - new GUIOverview(instance, this, player); + if (!player.hasPermission("epichoppers.overview")) return; + new GUIOverview(instance, this, player); } public void upgrade(Player player, CostType type) { @@ -70,118 +74,119 @@ public class Hopper { if (plugin.getLevelManager().getLevels().containsKey(this.level.getLevel() + 1)) { Level level = plugin.getLevelManager().getLevel(this.level.getLevel() + 1); - int cost = type == CostType.ECONOMY ? level.getCostEconomy() : level.getCostExperience(); + int cost = type == CostType.ECONOMY ? level.getCostEconomy() : level.getCostExperience(); - if (type == CostType.ECONOMY) { - if (plugin.getEconomy() == null) { - player.sendMessage("Economy not enabled."); - return; + if (type == CostType.ECONOMY) { + if (plugin.getEconomy() == null) { + player.sendMessage("Economy not enabled."); + return; + } + if (!plugin.getEconomy().hasBalance(player, cost)) { + player.sendMessage(plugin.references.getPrefix() + plugin.getInstance().getLocale().getMessage("event.upgrade.cannotafford")); + return; + } + plugin.getEconomy().withdrawBalance(player, cost); + upgradeFinal(level, player); + } else if (type == CostType.EXPERIENCE) { + if (player.getLevel() >= cost || player.getGameMode() == GameMode.CREATIVE) { + if (player.getGameMode() != GameMode.CREATIVE) { + player.setLevel(player.getLevel() - cost); } - if (!plugin.getEconomy().hasBalance(player, cost)) { - player.sendMessage(plugin.references.getPrefix() + plugin.getInstance().getLocale().getMessage("event.upgrade.cannotafford")); - return; - } - plugin.getEconomy().withdrawBalance(player, cost); upgradeFinal(level, player); - } else if (type == CostType.EXPERIENCE) { - if (player.getLevel() >= cost || player.getGameMode() == GameMode.CREATIVE) { - if (player.getGameMode() != GameMode.CREATIVE) { - player.setLevel(player.getLevel() - cost); - } - upgradeFinal(level, player); - } else { - player.sendMessage(plugin.references.getPrefix() + plugin.getLocale().getMessage("event.upgrade.cannotafford")); - } + } else { + player.sendMessage(plugin.references.getPrefix() + plugin.getLocale().getMessage("event.upgrade.cannotafford")); } } + } } private void upgradeFinal(Level level, Player player) { EpicHoppers instance = EpicHoppers.getInstance(); - this.level = level; - syncName(); - if (instance.getLevelManager().getHighestLevel() != level) { - player.sendMessage(instance.getLocale().getMessage("event.upgrade.success", level.getLevel())); - } else { - player.sendMessage(instance.getLocale().getMessage("event.upgrade.maxed", level.getLevel())); - } - Location loc = location.clone().add(.5, .5, .5); + this.level = level; + syncName(); + if (instance.getLevelManager().getHighestLevel() != level) { + player.sendMessage(instance.getLocale().getMessage("event.upgrade.success", level.getLevel())); + } else { + player.sendMessage(instance.getLocale().getMessage("event.upgrade.maxed", level.getLevel())); + } + Location loc = location.clone().add(.5, .5, .5); - if (!instance.isServerVersionAtLeast(ServerVersion.V1_12)) return; + if (!instance.isServerVersionAtLeast(ServerVersion.V1_12)) return; - player.getWorld().spawnParticle(org.bukkit.Particle.valueOf(instance.getConfig().getString("Main.Upgrade Particle Type")), loc, 200, .5, .5, .5); + player.getWorld().spawnParticle(org.bukkit.Particle.valueOf(instance.getConfig().getString("Main.Upgrade Particle Type")), loc, 200, .5, .5, .5); - if (instance.getLevelManager().getHighestLevel() != level) { - player.playSound(player.getLocation(), Sound.ENTITY_PLAYER_LEVELUP, 0.6F, 15.0F); - } else { - player.playSound(player.getLocation(), Sound.ENTITY_PLAYER_LEVELUP, 2F, 25.0F); + if (instance.getLevelManager().getHighestLevel() != level) { + player.playSound(player.getLocation(), Sound.ENTITY_PLAYER_LEVELUP, 0.6F, 15.0F); + } else { + player.playSound(player.getLocation(), Sound.ENTITY_PLAYER_LEVELUP, 2F, 25.0F); - if (!instance.isServerVersionAtLeast(ServerVersion.V1_13)) return; + if (!instance.isServerVersionAtLeast(ServerVersion.V1_13)) return; - player.playSound(player.getLocation(), Sound.BLOCK_NOTE_BLOCK_CHIME, 2F, 25.0F); - Bukkit.getScheduler().scheduleSyncDelayedTask(instance, () -> player.playSound(player.getLocation(), Sound.BLOCK_NOTE_BLOCK_CHIME, 1.2F, 35.0F), 5L); - Bukkit.getScheduler().scheduleSyncDelayedTask(instance, () -> player.playSound(player.getLocation(), Sound.BLOCK_NOTE_BLOCK_CHIME, 1.8F, 35.0F), 10L); - } + player.playSound(player.getLocation(), Sound.BLOCK_NOTE_BLOCK_CHIME, 2F, 25.0F); + Bukkit.getScheduler().scheduleSyncDelayedTask(instance, () -> player.playSound(player.getLocation(), Sound.BLOCK_NOTE_BLOCK_CHIME, 1.2F, 35.0F), 5L); + Bukkit.getScheduler().scheduleSyncDelayedTask(instance, () -> player.playSound(player.getLocation(), Sound.BLOCK_NOTE_BLOCK_CHIME, 1.8F, 35.0F), 10L); + } } private void syncName() { - org.bukkit.block.Hopper hopper = (org.bukkit.block.Hopper)location.getBlock().getState(); + org.bukkit.block.Hopper hopper = (org.bukkit.block.Hopper) location.getBlock().getState(); if (EpicHoppers.getInstance().isServerVersionAtLeast(ServerVersion.V1_10)) hopper.setCustomName(Methods.formatName(level.getLevel(), false)); - hopper.update(true); + hopper.update(true); } public void timeout(Player player) { EpicHoppers instance = EpicHoppers.getInstance(); - Bukkit.getScheduler().scheduleSyncDelayedTask(instance, () -> { - PlayerData playerData = instance.getPlayerDataManager().getPlayerData(player); - if (playerData.getSyncType() != null) { - player.sendMessage(instance.getLocale().getMessage("event.hopper.synctimeout")); - playerData.setSyncType(null); - } - }, instance.getConfig().getLong("Main.Timeout When Syncing Hoppers")); + Bukkit.getScheduler().scheduleSyncDelayedTask(instance, () -> { + PlayerData playerData = instance.getPlayerDataManager().getPlayerData(player); + if (playerData.getSyncType() != null) { + player.sendMessage(instance.getLocale().getMessage("event.hopper.synctimeout")); + playerData.setSyncType(null); + } + }, instance.getConfig().getLong("Main.Timeout When Syncing Hoppers")); } public void link(Block toLink, boolean filtered, Player player) { EpicHoppers instance = EpicHoppers.getInstance(); - if (location.getWorld().equals(toLink.getLocation().getWorld()) - && !player.hasPermission("EpicHoppers.Override") - && !player.hasPermission("EpicHoppers.Admin") - && location.distance(toLink.getLocation()) > level.getRange()) { - player.sendMessage(instance.references.getPrefix() + instance.getLocale().getMessage("event.hopper.syncoutofrange")); - return; - } + if (location.getWorld().equals(toLink.getLocation().getWorld()) + && !player.hasPermission("EpicHoppers.Override") + && !player.hasPermission("EpicHoppers.Admin") + && location.distance(toLink.getLocation()) > level.getRange()) { + player.sendMessage(instance.references.getPrefix() + instance.getLocale().getMessage("event.hopper.syncoutofrange")); + return; + } - if (linkedBlocks.contains(toLink)) { - player.sendMessage(instance.references.getPrefix() + instance.getLocale().getMessage("event.hopper.already")); - return; - } + if (linkedBlocks.contains(toLink)) { + player.sendMessage(instance.references.getPrefix() + instance.getLocale().getMessage("event.hopper.already")); + return; + } - if (!filtered) - this.linkedBlocks.add(toLink.getLocation()); - else { - this.filter.setEndPoint(toLink.getLocation()); - player.sendMessage(instance.references.getPrefix() + instance.getLocale().getMessage("event.hopper.syncsuccess")); - instance.getPlayerDataManager().getPlayerData(player).setSyncType(null); - return; - } - this.lastPlayer = player.getUniqueId(); - - if (level.getLinkAmount() > 1) { - if (getLinkedBlocks().size() == level.getLinkAmount()) { - player.sendMessage(instance.references.getPrefix() + instance.getLocale().getMessage("event.hopper.syncdone")); - return; - } - player.sendMessage(instance.references.getPrefix() + instance.getLocale().getMessage("event.hopper.syncsuccessmore", level.getLinkAmount() - getLinkedBlocks().size())); - return; - } + if (!filtered) + this.linkedBlocks.add(toLink.getLocation()); + else { + this.filter.setEndPoint(toLink.getLocation()); player.sendMessage(instance.references.getPrefix() + instance.getLocale().getMessage("event.hopper.syncsuccess")); instance.getPlayerDataManager().getPlayerData(player).setSyncType(null); + return; + } + this.lastPlayer = player.getUniqueId(); + + if (level.getLinkAmount() > 1) { + if (getLinkedBlocks().size() == level.getLinkAmount()) { + player.sendMessage(instance.references.getPrefix() + instance.getLocale().getMessage("event.hopper.syncdone")); + return; + } + player.sendMessage(instance.references.getPrefix() + instance.getLocale().getMessage("event.hopper.syncsuccessmore", level.getLinkAmount() - getLinkedBlocks().size())); + return; + } + player.sendMessage(instance.references.getPrefix() + instance.getLocale().getMessage("event.hopper.syncsuccess")); + instance.getPlayerDataManager().getPlayerData(player).setSyncType(null); } /** * Ticks a hopper to determine when it can transfer items next + * * @param maxTick The maximum amount the hopper can be ticked before next transferring items * @param allowLooping If true, the hopper is allowed to transfer items if the tick is also valid * @return true if the hopper should transfer an item, otherwise false @@ -239,10 +244,17 @@ public class Hopper { return autoCrafting; } - public void setAutoCrafting(ItemStack autoCrafting) { + public void setAutoCrafting(Player player, ItemStack autoCrafting) { this.autoCrafting = autoCrafting; - if (autoCrafting != null) + if (autoCrafting != null) { + int excess = autoCrafting.getAmount() - 1; autoCrafting.setAmount(1); + if (excess > 0 && player != null) { + ItemStack item = autoCrafting.clone(); + item.setAmount(excess); + player.getInventory().addItem(item); + } + } } public TeleportTrigger getTeleportTrigger() { diff --git a/src/main/java/com/songoda/epichoppers/hopper/HopperManager.java b/src/main/java/com/songoda/epichoppers/hopper/HopperManager.java index 303cf87..fe440c4 100644 --- a/src/main/java/com/songoda/epichoppers/hopper/HopperManager.java +++ b/src/main/java/com/songoda/epichoppers/hopper/HopperManager.java @@ -20,9 +20,18 @@ public class HopperManager { registeredHoppers.put(roundLocation(location), hopper); } - + /** + * Removes a hopper and unlinks it from any other hoppers + * @param location The location of the hopper to remove + * @return The removed hopper, or null if none was removed + */ public Hopper removeHopper(Location location) { - return registeredHoppers.remove(location); + Hopper removed = this.registeredHoppers.remove(location); + + for (Hopper hopper : this.registeredHoppers.values()) + hopper.removeLinkedBlock(location); + + return removed; } diff --git a/src/main/java/com/songoda/epichoppers/hopper/levels/LevelManager.java b/src/main/java/com/songoda/epichoppers/hopper/levels/LevelManager.java index 9f83438..8a04db6 100644 --- a/src/main/java/com/songoda/epichoppers/hopper/levels/LevelManager.java +++ b/src/main/java/com/songoda/epichoppers/hopper/levels/LevelManager.java @@ -21,7 +21,7 @@ public class LevelManager { } public Level getLevel(ItemStack item) { - if (item.getItemMeta().getDisplayName().contains(":")) { + if (item.hasItemMeta() && item.getItemMeta().getDisplayName().contains(":")) { String arr[] = item.getItemMeta().getDisplayName().replace(String.valueOf(ChatColor.COLOR_CHAR), "").split(":"); return getLevel(Integer.parseInt(arr[0])); } else { diff --git a/src/main/java/com/songoda/epichoppers/hopper/levels/modules/ModuleAutoCrafting.java b/src/main/java/com/songoda/epichoppers/hopper/levels/modules/ModuleAutoCrafting.java index 24d98b2..8d0dcf9 100644 --- a/src/main/java/com/songoda/epichoppers/hopper/levels/modules/ModuleAutoCrafting.java +++ b/src/main/java/com/songoda/epichoppers/hopper/levels/modules/ModuleAutoCrafting.java @@ -8,7 +8,11 @@ import com.songoda.epichoppers.utils.ServerVersion; import org.bukkit.Bukkit; import org.bukkit.Material; import org.bukkit.entity.Player; -import org.bukkit.inventory.*; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.Recipe; +import org.bukkit.inventory.ShapedRecipe; +import org.bukkit.inventory.ShapelessRecipe; import org.bukkit.inventory.meta.ItemMeta; import java.util.ArrayList; @@ -19,22 +23,6 @@ import java.util.Map; public class ModuleAutoCrafting implements Module { private final Map cachedRecipes = new HashMap<>(); - private final Map lastMaterial = new HashMap<>(); - - public static List compressItemStack(List target) { - HashMap sortingList = new HashMap<>(); - for (ItemStack item : target) { - if (sortingList.containsKey(item.getType())) { - ItemStack existing = sortingList.get(item.getType()); - existing.setAmount(existing.getAmount() + item.getAmount()); - sortingList.put(existing.getType(), existing); - } else { - sortingList.put(item.getType(), item); - } - } - List list = new ArrayList<>(sortingList.values()); - return list; - } @Override public String getName() { @@ -42,61 +30,69 @@ public class ModuleAutoCrafting implements Module { } public void run(Hopper hopper, Inventory hopperInventory) { - if (hopper.getAutoCrafting() == null || hopperInventory == null) return; + if (hopper.getAutoCrafting() == null + || hopperInventory == null + || hopperInventory.getSize() == 0 + || !canMove(hopperInventory, new ItemStack(hopper.getAutoCrafting())) + || cachedRecipes.get(hopper.getAutoCrafting()) == null) + return; - if (hopper.getAutoCrafting() != null && canMove(hopperInventory, new ItemStack(hopper.getAutoCrafting()))) { - - if (cachedRecipes.get(hopper.getAutoCrafting()) == null) return; - - top: - for (Recipe recipe : cachedRecipes.get(hopper.getAutoCrafting()).getRecipes()) { - if (!(recipe instanceof ShapedRecipe) && !(recipe instanceof ShapelessRecipe)) continue; - List ingredientMap = null; - if (recipe instanceof ShapelessRecipe) ingredientMap = ((ShapelessRecipe) recipe).getIngredientList(); - if (recipe instanceof ShapedRecipe) - ingredientMap = new ArrayList<>(((ShapedRecipe) recipe).getIngredientMap().values()); - if (hopperInventory.getSize() == 0) return; - - Map items = new HashMap<>(); - for (ItemStack item : ingredientMap) { - if (item == null) continue; - if (!items.containsKey(item.getType())) { - items.put(item.getType(), item.clone()); - } else { - items.get(item.getType()).setAmount(items.get(item.getType()).getAmount() + 1); - } - } - - for (ItemStack item : items.values()) { - int amt = 0; - for (ItemStack i : hopperInventory.getContents()) { - if (i == null) continue; - if (!i.isSimilar(item)) continue; - amt += i.getAmount(); - } - - if (amt < item.getAmount()) { - continue top; - } - } - main2: - for (ItemStack toRemove : items.values()) { - int amtRemoved = 0; - for (ItemStack i : hopperInventory.getContents()) { - if (i == null || !i.isSimilar(toRemove)) continue; - if (toRemove.getAmount() - amtRemoved <= i.getAmount()) { - toRemove.setAmount(toRemove.getAmount() - amtRemoved); - hopperInventory.removeItem(toRemove); - continue main2; - } else { - amtRemoved += i.getAmount(); - hopperInventory.removeItem(i); - } - } - } - hopperInventory.addItem(recipe.getResult()); + top: + for (Recipe recipe : cachedRecipes.get(hopper.getAutoCrafting()).getRecipes()) { + if (!(recipe instanceof ShapedRecipe) && !(recipe instanceof ShapelessRecipe)) + continue; + List ingredientMap; + if (recipe instanceof ShapelessRecipe) { + ingredientMap = ((ShapelessRecipe) recipe).getIngredientList(); + } else { + ingredientMap = new ArrayList<>(((ShapedRecipe) recipe).getIngredientMap().values()); } + + Map items = new HashMap<>(); + for (ItemStack item : ingredientMap) { + if (item == null) + continue; + + if (!items.containsKey(item.getType())) { + items.put(item.getType(), item.clone()); + } else { + items.get(item.getType()).setAmount(items.get(item.getType()).getAmount() + 1); + } + } + + for (ItemStack item : items.values()) { + int amt = 0; + for (ItemStack i : hopperInventory.getContents()) { + if (i == null || !isSimilar(i, item)) + continue; + amt += i.getAmount(); + } + + if (amt < item.getAmount()) { + continue top; + } + } + + main2: + for (ItemStack toRemove : items.values()) { + int amtRemoved = 0; + for (ItemStack i : hopperInventory.getContents()) { + if (i == null || !isSimilar(i, toRemove)) + continue; + + amtRemoved += Math.min(toRemove.getAmount() - amtRemoved, i.getAmount()); + if (amtRemoved == i.getAmount()) + hopperInventory.removeItem(i); + else + i.setAmount(i.getAmount() - amtRemoved); + + if (amtRemoved == toRemove.getAmount()) + continue main2; + } + } + + hopperInventory.addItem(recipe.getResult()); } } @@ -126,30 +122,30 @@ public class ModuleAutoCrafting implements Module { ItemStack itemStack = hopper.getAutoCrafting(); - if (itemStack.getType() == Material.AIR) return materials; + if (itemStack.getType() == Material.AIR) + return materials; - if (lastMaterial.get(hopper) != null && !lastMaterial.get(hopper).isSimilar(itemStack)) { - lastMaterial.put(hopper, itemStack); - cachedRecipes.remove(hopper); - } - - if (cachedRecipes.keySet().stream().noneMatch(itemStack1 -> itemStack1.isSimilar(itemStack))) { + if (cachedRecipes.get(itemStack) == null) { Recipes recipes = new Recipes(); for (Recipe recipe : Bukkit.getServer().getRecipesFor(itemStack)) { recipes.addRecipe(recipe); } cachedRecipes.put(itemStack, recipes); - } else { + } + + if (cachedRecipes.get(itemStack) != null) { Recipes recipes = cachedRecipes.get(itemStack); for (Recipe recipe : recipes.getRecipes()) { if (recipe instanceof ShapedRecipe) { for (ItemStack itemStack1 : ((ShapedRecipe) recipe).getIngredientMap().values()) { - if (itemStack1 == null) continue; + if (itemStack1 == null) + continue; materials.add(itemStack1.getType()); } } else if (recipe instanceof ShapelessRecipe) { for (ItemStack itemStack1 : ((ShapelessRecipe) recipe).getIngredientList()) { - if (itemStack1 == null) continue; + if (itemStack1 == null) + continue; materials.add(itemStack1.getType()); } } @@ -165,16 +161,24 @@ public class ModuleAutoCrafting implements Module { } private boolean canMove(Inventory inventory, ItemStack item) { - if (inventory.firstEmpty() != -1) return true; + if (inventory.firstEmpty() != -1) return true; - for (ItemStack stack : inventory.getContents()) { - if (stack.isSimilar(item) && (stack.getAmount() + item.getAmount()) < stack.getMaxStackSize()) { - return true; - } + for (ItemStack stack : inventory.getContents()) { + if (stack.isSimilar(item) && (stack.getAmount() + item.getAmount()) < stack.getMaxStackSize()) { + return true; } + } return false; } + private boolean isSimilar(ItemStack is1, ItemStack is2) { + if (EpicHoppers.getInstance().isServerVersionAtLeast(ServerVersion.V1_13)) { + return is1.getType() == is2.getType(); + } else { + return is1.getType() == is2.getType() && is1.getDurability() == is2.getDurability(); + } + } + class Recipes { private List recipes = new ArrayList<>(); diff --git a/src/main/java/com/songoda/epichoppers/hopper/levels/modules/ModuleAutoSell.java b/src/main/java/com/songoda/epichoppers/hopper/levels/modules/ModuleAutoSell.java index c3b7f90..1540b41 100644 --- a/src/main/java/com/songoda/epichoppers/hopper/levels/modules/ModuleAutoSell.java +++ b/src/main/java/com/songoda/epichoppers/hopper/levels/modules/ModuleAutoSell.java @@ -2,6 +2,7 @@ package com.songoda.epichoppers.hopper.levels.modules; import com.songoda.epichoppers.EpicHoppers; import com.songoda.epichoppers.hopper.Hopper; +import com.songoda.epichoppers.tasks.HopTask; import com.songoda.epichoppers.utils.Methods; import com.songoda.epichoppers.utils.ServerVersion; import org.bukkit.Bukkit; @@ -41,6 +42,8 @@ public class ModuleAutoSell implements Module { if (instance.getEconomy() == null) return; + boolean updateComparators = false; + List list = instance.getConfig().getStringList("Main.AutoSell Prices"); for (String line : list) { @@ -55,11 +58,16 @@ public class ModuleAutoSell implements Module { instance.getEconomy().deposit(Bukkit.getOfflinePlayer(hopper.getPlacedBy()), price * itemStack.getAmount()); hopperInventory.removeItem(itemStack); + + updateComparators = true; } } catch (Exception ignored) { } } hopper.setAutoSellTimer(timeOut); + + if (updateComparators) + HopTask.updateAdjacentComparators(hopper.getLocation()); } hopper.setAutoSellTimer(hopper.getAutoSellTimer() - hopperTickRate); } diff --git a/src/main/java/com/songoda/epichoppers/hopper/levels/modules/ModuleBlockBreak.java b/src/main/java/com/songoda/epichoppers/hopper/levels/modules/ModuleBlockBreak.java index 22f9656..ecfe22f 100644 --- a/src/main/java/com/songoda/epichoppers/hopper/levels/modules/ModuleBlockBreak.java +++ b/src/main/java/com/songoda/epichoppers/hopper/levels/modules/ModuleBlockBreak.java @@ -11,6 +11,7 @@ import org.bukkit.Sound; import org.bukkit.block.Block; import org.bukkit.entity.Player; import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.InventoryHolder; import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.meta.ItemMeta; @@ -37,7 +38,8 @@ public class ModuleBlockBreak implements Module { public void run(Hopper hopper, Inventory hopperInventory) { Block block = hopper.getLocation().getBlock(); - if (!hopper.isAutoBreaking()) return; + if (!hopper.isAutoBreaking()) + return; if (!blockTick.containsKey(block)) { blockTick.put(block, 1); @@ -46,11 +48,21 @@ public class ModuleBlockBreak implements Module { int tick = blockTick.get(block); int put = tick + 1; blockTick.put(block, put); - if (tick < amount) return; - Block above = block.getRelative(0, 1, 0); - if (above.getType() == Material.WATER || above.getType() == Material.LAVA) return; + if (tick < amount) + return; - if (above.getType() != Material.AIR && above.getType() != Material.HOPPER && !EpicHoppers.getInstance().getConfig().getStringList("Main.BlockBreak Blacklisted Blocks").contains(above.getType().name())) { + Block above = block.getRelative(0, 1, 0); + if (above.getType() == Material.WATER + || above.getType() == Material.LAVA + || above.getType() == Material.AIR + || above.getState() instanceof InventoryHolder) + return; + + // Don't break farm items from EpicFarming + if (EpicHoppers.getInstance().isEpicFarming() && com.songoda.epicfarming.EpicFarmingPlugin.getInstance().getFarmManager().getFarm(above) != null) + return; + + if (!EpicHoppers.getInstance().getConfig().getStringList("Main.BlockBreak Blacklisted Blocks").contains(above.getType().name())) { if (EpicHoppers.getInstance().isServerVersionAtLeast(ServerVersion.V1_9)) above.getWorld().playSound(above.getLocation(), Sound.BLOCK_STONE_BREAK, 1F, 1F); Location locationAbove = above.getLocation(); @@ -62,7 +74,17 @@ public class ModuleBlockBreak implements Module { if (EpicHoppers.getInstance().isServerVersionAtLeast(ServerVersion.V1_9)) above.getWorld().spawnParticle(Particle.valueOf(EpicHoppers.getInstance().getConfig().getString("Main.BlockBreak Particle Type")), locationAbove, 15, xx, yy, zz); + boolean waterlogged = false; + if (EpicHoppers.getInstance().isServerVersionAtLeast(ServerVersion.V1_13) + && above.getBlockData() instanceof org.bukkit.block.data.Waterlogged + && ((org.bukkit.block.data.Waterlogged)above.getBlockData()).isWaterlogged()) { + waterlogged = true; + } + above.breakNaturally(); + + if (waterlogged) + above.setType(Material.WATER); } blockTick.remove(block); } diff --git a/src/main/java/com/songoda/epichoppers/hopper/levels/modules/ModuleSuction.java b/src/main/java/com/songoda/epichoppers/hopper/levels/modules/ModuleSuction.java index fd0035a..fc44dca 100644 --- a/src/main/java/com/songoda/epichoppers/hopper/levels/modules/ModuleSuction.java +++ b/src/main/java/com/songoda/epichoppers/hopper/levels/modules/ModuleSuction.java @@ -14,7 +14,6 @@ import org.bukkit.entity.Item; import org.bukkit.entity.Player; import org.bukkit.inventory.Inventory; import org.bukkit.inventory.ItemStack; -import org.bukkit.metadata.FixedMetadataValue; import java.lang.reflect.Field; import java.lang.reflect.Method; @@ -29,6 +28,7 @@ public class ModuleSuction implements Module { public static List blacklist = new ArrayList<>(); private boolean wildStacker = Bukkit.getPluginManager().isPluginEnabled("WildStacker"); + private boolean ultimateStacker = Bukkit.getPluginManager().isPluginEnabled("UltimateStacker"); private Class clazzItemStack, clazzItem, clazzCraftItemStack; private Method methodGetItem, methodAsNMSCopy; @@ -63,7 +63,7 @@ public class ModuleSuction implements Module { hopper.getLocation().getWorld().getNearbyEntities(hopper.getLocation().add(0.5, 0.5, 0.5), radius, radius, radius).stream() .filter(entity -> entity.getType() == EntityType.DROPPED_ITEM - && entity.getTicksLived() > 10 + && entity.getTicksLived() >= ((Item)entity).getPickupDelay() && entity.getLocation().getBlock().getType() != Material.HOPPER).forEach(entity -> { Item item = (Item) entity; @@ -80,6 +80,9 @@ public class ModuleSuction implements Module { if (wildStacker) itemStack.setAmount(WildStackerAPI.getItemAmount((Item) entity)); + if (ultimateStacker && item.hasMetadata("US_AMT")) + itemStack.setAmount(item.getMetadata("US_AMT").get(0).asInt()); + if (!canMove(hopperInventory, itemStack) || blacklist.contains(item.getUniqueId())) return; @@ -90,7 +93,7 @@ public class ModuleSuction implements Module { float zz = (float) (0 + (Math.random() * .1)); if (EpicHoppers.getInstance().isServerVersionAtLeast(ServerVersion.V1_9)) - entity.getLocation().getWorld().spawnParticle(Particle.FLAME, entity.getLocation(), 5, xx, yy, zz, 0); + entity.getLocation().getWorld().spawnParticle(Particle.FLAME, entity.getLocation(), 5, xx, yy, zz, 0); for (ItemStack is : hopperInventory.addItem(itemStack).values()) { entity.getWorld().dropItemNaturally(entity.getLocation(), is); diff --git a/src/main/java/com/songoda/epichoppers/listeners/BlockListeners.java b/src/main/java/com/songoda/epichoppers/listeners/BlockListeners.java index ee39928..2b40d89 100644 --- a/src/main/java/com/songoda/epichoppers/listeners/BlockListeners.java +++ b/src/main/java/com/songoda/epichoppers/listeners/BlockListeners.java @@ -39,28 +39,27 @@ public class BlockListeners implements Listener { @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) public void onBlockPlace(BlockPlaceEvent e) { - Player player = e.getPlayer(); + Player player = e.getPlayer(); - if (e.getBlock().getType() != Material.HOPPER) return; + if (e.getBlock().getType() != Material.HOPPER) + return; - if (instance.isLiquidtanks() && net.arcaniax.liquidtanks.object.LiquidTankAPI.isLiquidTank(e.getBlock().getLocation())) - return; + if (instance.isLiquidtanks() && net.arcaniax.liquidtanks.object.LiquidTankAPI.isLiquidTank(e.getBlock().getLocation())) + return; - int amt = count(e.getBlock().getChunk()); + int amt = count(e.getBlock().getChunk()); - int max = maxHoppers(player); + int max = maxHoppers(player); - if (max != -1 && amt > max) { - player.sendMessage(instance.getLocale().getMessage("event.hopper.toomany", max)); - e.setCancelled(true); - return; - } + if (max != -1 && amt > max) { + player.sendMessage(instance.getLocale().getMessage("event.hopper.toomany", max)); + e.setCancelled(true); + return; + } - if (!e.getItemInHand().getItemMeta().hasDisplayName()) return; + ItemStack item = e.getItemInHand().clone(); - ItemStack item = e.getItemInHand().clone(); - - instance.getHopperManager().addHopper(e.getBlock().getLocation(), new Hopper(e.getBlock(), instance.getLevelManager().getLevel(item), player.getUniqueId(), player.getUniqueId(), new ArrayList<>(), new Filter(), TeleportTrigger.DISABLED, null)); + instance.getHopperManager().addHopper(e.getBlock().getLocation(), new Hopper(e.getBlock(), instance.getLevelManager().getLevel(item), player.getUniqueId(), player.getUniqueId(), new ArrayList<>(), new Filter(), TeleportTrigger.DISABLED, null)); } private int maxHoppers(Player player) { @@ -74,57 +73,57 @@ public class BlockListeners implements Listener { } private int count(Chunk c) { - int count = 0; - for (int x = 0; x < 16; x++) { - for (int z = 0; z < 16; z++) { - for (int y = 0; y < c.getWorld().getMaxHeight(); y++) { - if (c.getBlock(x, y, z).getType() == Material.HOPPER) count++; - } + int count = 0; + for (int x = 0; x < 16; x++) { + for (int z = 0; z < 16; z++) { + for (int y = 0; y < c.getWorld().getMaxHeight(); y++) { + if (c.getBlock(x, y, z).getType() == Material.HOPPER) count++; } } - return count; + } + return count; } @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) public void onBlockBreak(BlockBreakEvent event) { - Block block = event.getBlock(); - Player player = event.getPlayer(); + Block block = event.getBlock(); + Player player = event.getPlayer(); - handleSyncTouch(event); + handleSyncTouch(event); - if (event.getBlock().getType() != Material.HOPPER) return; + if (event.getBlock().getType() != Material.HOPPER) return; - if (instance.isLiquidtanks() && net.arcaniax.liquidtanks.object.LiquidTankAPI.isLiquidTank(block.getLocation())) - return; + if (instance.isLiquidtanks() && net.arcaniax.liquidtanks.object.LiquidTankAPI.isLiquidTank(block.getLocation())) + return; - Hopper hopper = instance.getHopperManager().getHopper(block); + Hopper hopper = instance.getHopperManager().getHopper(block); - Level level = hopper.getLevel(); + Level level = hopper.getLevel(); - if (level.getLevel() > 1) { - event.setCancelled(true); - ItemStack item = instance.newHopperItem(level); + if (level.getLevel() > 1) { + event.setCancelled(true); + ItemStack item = instance.newHopperItem(level); - event.getBlock().setType(Material.AIR); - event.getBlock().getLocation().getWorld().dropItemNaturally(event.getBlock().getLocation(), item); - } + event.getBlock().setType(Material.AIR); + event.getBlock().getLocation().getWorld().dropItemNaturally(event.getBlock().getLocation(), item); + } - for (ItemStack m : hopper.getFilter().getWhiteList()) { - if (m != null) - event.getBlock().getLocation().getWorld().dropItemNaturally(event.getBlock().getLocation(), m); - } + for (ItemStack m : hopper.getFilter().getWhiteList()) { + if (m != null) + event.getBlock().getLocation().getWorld().dropItemNaturally(event.getBlock().getLocation(), m); + } - for (ItemStack m : hopper.getFilter().getBlackList()) { - if (m != null) - event.getBlock().getLocation().getWorld().dropItemNaturally(event.getBlock().getLocation(), m); - } - for (ItemStack m : hopper.getFilter().getVoidList()) { - if (m != null) - event.getBlock().getLocation().getWorld().dropItemNaturally(event.getBlock().getLocation(), m); - } - instance.getHopperManager().removeHopper(block.getLocation()); + for (ItemStack m : hopper.getFilter().getBlackList()) { + if (m != null) + event.getBlock().getLocation().getWorld().dropItemNaturally(event.getBlock().getLocation(), m); + } + for (ItemStack m : hopper.getFilter().getVoidList()) { + if (m != null) + event.getBlock().getLocation().getWorld().dropItemNaturally(event.getBlock().getLocation(), m); + } + instance.getHopperManager().removeHopper(block.getLocation()); - instance.getPlayerDataManager().getPlayerData(player).setSyncType(null); + instance.getPlayerDataManager().getPlayerData(player).setSyncType(null); } private void handleSyncTouch(BlockBreakEvent event) { diff --git a/src/main/java/com/songoda/epichoppers/listeners/HopperListeners.java b/src/main/java/com/songoda/epichoppers/listeners/HopperListeners.java index d991f25..c4d192b 100644 --- a/src/main/java/com/songoda/epichoppers/listeners/HopperListeners.java +++ b/src/main/java/com/songoda/epichoppers/listeners/HopperListeners.java @@ -2,11 +2,15 @@ package com.songoda.epichoppers.listeners; import com.songoda.epichoppers.EpicHoppers; import com.songoda.epichoppers.hopper.Hopper; +import com.songoda.epichoppers.utils.HopperDirection; import org.bukkit.Bukkit; import org.bukkit.Location; import org.bukkit.block.Chest; import org.bukkit.block.DoubleChest; +import org.bukkit.block.ShulkerBox; import org.bukkit.entity.Minecart; +import org.bukkit.entity.minecart.HopperMinecart; +import org.bukkit.entity.minecart.StorageMinecart; import org.bukkit.event.EventHandler; import org.bukkit.event.Listener; import org.bukkit.event.inventory.InventoryMoveItemEvent; @@ -29,7 +33,17 @@ public class HopperListeners implements Listener { Inventory source = event.getSource(); Inventory destination = event.getDestination(); - if (!(source.getHolder() instanceof org.bukkit.block.Hopper)) return; + // Hopper minecarts should be able to take care of themselves + // Let EpicHoppers take over if the hopper is pointing down though + if (destination.getHolder() instanceof HopperMinecart && (!(source.getHolder() instanceof org.bukkit.block.Hopper) + || HopperDirection.getDirection(((org.bukkit.block.Hopper)destination.getHolder()).getRawData()) != HopperDirection.DOWN)) + return; + + // Shulker boxes have a mind of their own and relentlessly steal items from hoppers + if (destination.getHolder() instanceof ShulkerBox || !(source.getHolder() instanceof org.bukkit.block.Hopper)) { + event.setCancelled(true); + return; + } if (instance.isLiquidtanks() && net.arcaniax.liquidtanks.object.LiquidTankAPI.isLiquidTank(event.getDestination().getLocation())) return; @@ -47,7 +61,8 @@ public class HopperListeners implements Listener { return; } - if (!(destinationLocation.getBlock().getState() instanceof InventoryHolder)) return; + if (!(destinationLocation.getBlock().getState() instanceof InventoryHolder)) + return; Hopper hopper = instance.getHopperManager().getHopper(sourceHopper.getLocation()); @@ -55,6 +70,5 @@ public class HopperListeners implements Listener { hopper.addLinkedBlock(destinationLocation); event.setCancelled(true); - } } diff --git a/src/main/java/com/songoda/epichoppers/listeners/InteractListeners.java b/src/main/java/com/songoda/epichoppers/listeners/InteractListeners.java index 2750618..aaf1b56 100644 --- a/src/main/java/com/songoda/epichoppers/listeners/InteractListeners.java +++ b/src/main/java/com/songoda/epichoppers/listeners/InteractListeners.java @@ -50,58 +50,57 @@ public class InteractListeners implements Listener { @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) public void onBlockInteract(PlayerInteractEvent e) { - Player player = e.getPlayer(); - if (e.getAction() != Action.LEFT_CLICK_BLOCK - || e.getClickedBlock() == null - || player.isSneaking() - || !player.hasPermission("EpicHoppers.overview") - || !(e.getClickedBlock().getState() instanceof InventoryHolder || e.getClickedBlock().getType().equals(Material.ENDER_CHEST))) { - return; + Player player = e.getPlayer(); + if (e.getAction() != Action.LEFT_CLICK_BLOCK + || e.getClickedBlock() == null + || player.isSneaking() + || !player.hasPermission("EpicHoppers.overview") + || !(e.getClickedBlock().getState() instanceof InventoryHolder || e.getClickedBlock().getType().equals(Material.ENDER_CHEST))) { + return; + } + + if (e.getClickedBlock().getType() == Material.CHEST && Methods.isSync(player)) { + ItemStack item = e.getPlayer().getInventory().getItemInHand(); + if (item.getItemMeta().getLore().size() == 2) { + player.sendMessage(instance.getLocale().getMessage("event.hopper.desyncchest", item.getType().toString())); + instance.enchantmentHandler.createSyncTouch(item, null); + } else { + player.sendMessage(instance.getLocale().getMessage("event.hopper.syncchest", item.getType().toString())); + instance.enchantmentHandler.createSyncTouch(item, e.getClickedBlock()); } + e.setCancelled(true); + return; + } - if (e.getClickedBlock().getType() == Material.CHEST && Methods.isSync(player)) { - ItemStack item = e.getPlayer().getInventory().getItemInHand(); - if (item.getItemMeta().getLore().size() == 2) { - player.sendMessage(instance.getLocale().getMessage("event.hopper.desyncchest", item.getType().toString())); - instance.enchantmentHandler.createSyncTouch(item, null); - } else { - player.sendMessage(instance.getLocale().getMessage("event.hopper.syncchest", item.getType().toString())); - instance.enchantmentHandler.createSyncTouch(item, e.getClickedBlock()); - } - e.setCancelled(true); - return; - } + PlayerData playerData = instance.getPlayerDataManager().getPlayerData(player); - PlayerData playerData = instance.getPlayerDataManager().getPlayerData(player); - - if (playerData.getSyncType() == null) { - if (e.getClickedBlock().getType() == Material.HOPPER) { - if (instance.isLiquidtanks() && net.arcaniax.liquidtanks.object.LiquidTankAPI.isLiquidTank(e.getClickedBlock().getLocation())) - return; - Hopper hopper = instance.getHopperManager().getHopper(e.getClickedBlock()); - playerData.setLastHopper(hopper); - if (instance.getConfig().getBoolean("Main.Allow hopper Upgrading") - && !player.getInventory().getItemInHand().getType().name().contains("PICKAXE")) { - hopper.overview(player); - e.setCancelled(true); - return; - } - } - return; - } - - if (e.getClickedBlock().getState() instanceof InventoryHolder || e.getClickedBlock().getType().equals(Material.ENDER_CHEST) && instance.getConfig().getBoolean("Main.Support Enderchests")) { - Hopper hopper = playerData.getLastHopper(); - if (playerData.getSyncType() != null && e.getClickedBlock().getLocation().equals(playerData.getLastHopper().getLocation())) { - player.sendMessage(instance.getLocale().getMessage("event.hopper.syncself")); - } else if (playerData.getSyncType() != null) { - hopper.link(e.getClickedBlock(), playerData.getSyncType() == SyncType.FILTERED, player); - } - e.setCancelled(true); - int amountLinked = hopper.getLevel().getLinkAmount(); - if (hopper.getLinkedBlocks().size() >= amountLinked) { - playerData.setSyncType(null); + if (playerData.getSyncType() == null) { + if (e.getClickedBlock().getType() == Material.HOPPER) { + if (instance.isLiquidtanks() && net.arcaniax.liquidtanks.object.LiquidTankAPI.isLiquidTank(e.getClickedBlock().getLocation())) + return; + Hopper hopper = instance.getHopperManager().getHopper(e.getClickedBlock()); + playerData.setLastHopper(hopper); + if (!player.getInventory().getItemInHand().getType().name().contains("PICKAXE")) { + hopper.overview(player); + e.setCancelled(true); + return; } } + return; + } + + if (e.getClickedBlock().getState() instanceof InventoryHolder || (e.getClickedBlock().getType().equals(Material.ENDER_CHEST) && instance.getConfig().getBoolean("Main.Support Enderchests"))) { + Hopper hopper = playerData.getLastHopper(); + if (playerData.getSyncType() != null && e.getClickedBlock().getLocation().equals(playerData.getLastHopper().getLocation())) { + player.sendMessage(instance.getLocale().getMessage("event.hopper.syncself")); + } else if (playerData.getSyncType() != null) { + hopper.link(e.getClickedBlock(), playerData.getSyncType() == SyncType.FILTERED, player); + } + e.setCancelled(true); + int amountLinked = hopper.getLevel().getLinkAmount(); + if (hopper.getLinkedBlocks().size() >= amountLinked) { + playerData.setSyncType(null); + } + } } } diff --git a/src/main/java/com/songoda/epichoppers/listeners/InventoryListeners.java b/src/main/java/com/songoda/epichoppers/listeners/InventoryListeners.java index fadf178..9f21c8c 100644 --- a/src/main/java/com/songoda/epichoppers/listeners/InventoryListeners.java +++ b/src/main/java/com/songoda/epichoppers/listeners/InventoryListeners.java @@ -55,4 +55,4 @@ public class InventoryListeners implements Listener { } } } -} \ No newline at end of file +} diff --git a/src/main/java/com/songoda/epichoppers/storage/StorageItem.java b/src/main/java/com/songoda/epichoppers/storage/StorageItem.java index 19201cb..c8bde0f 100644 --- a/src/main/java/com/songoda/epichoppers/storage/StorageItem.java +++ b/src/main/java/com/songoda/epichoppers/storage/StorageItem.java @@ -54,6 +54,7 @@ public class StorageItem { public boolean asBoolean() { if (object == null) return false; + if (object instanceof Integer) return (Integer) object == 1; return (boolean) object; } @@ -63,6 +64,8 @@ public class StorageItem { } public Object asObject() { + if (object == null) return null; + if (object instanceof Boolean) return (Boolean) object ? 1 : 0; return object; } diff --git a/src/main/java/com/songoda/epichoppers/storage/types/StorageMysql.java b/src/main/java/com/songoda/epichoppers/storage/types/StorageMysql.java index 495fc7c..b7cd52e 100644 --- a/src/main/java/com/songoda/epichoppers/storage/types/StorageMysql.java +++ b/src/main/java/com/songoda/epichoppers/storage/types/StorageMysql.java @@ -105,8 +105,10 @@ public class StorageMysql extends Storage { continue; toSave.remove(to.getKey()); for (int i = 0; i < to.getValue().length; i ++) { - if (!to.getValue()[i].asObject().toString() - .equals(last.getValue()[i].asObject().toString())) { + if ((to.getValue()[i].asObject() != null && last.getValue()[i].asObject() == null) + || (last.getValue()[i].asObject() == null && to.getValue()[i].asObject() != null) + || (last.getValue()[i].asObject() != null && to.getValue()[i].asObject() != null + && !to.getValue()[i].asObject().toString().equals(last.getValue()[i].asObject().toString()))) { //Update StorageItem[] items = to.getValue(); StringBuilder sql = new StringBuilder(String.format("UPDATE `" + instance.getConfig().getString("Database.Prefix") + "%s`", toKey)); diff --git a/src/main/java/com/songoda/epichoppers/tasks/HopTask.java b/src/main/java/com/songoda/epichoppers/tasks/HopTask.java index 38fca9d..49c3fea 100644 --- a/src/main/java/com/songoda/epichoppers/tasks/HopTask.java +++ b/src/main/java/com/songoda/epichoppers/tasks/HopTask.java @@ -2,6 +2,7 @@ package com.songoda.epichoppers.tasks; import com.songoda.epichoppers.EpicHoppers; import com.songoda.epichoppers.boost.BoostData; +import com.songoda.epichoppers.hopper.HopperManager; import com.songoda.epichoppers.hopper.levels.modules.Module; import com.songoda.epichoppers.utils.HopperDirection; import com.songoda.epichoppers.utils.Methods; @@ -12,9 +13,14 @@ import org.bukkit.Location; import org.bukkit.Material; import org.bukkit.OfflinePlayer; import org.bukkit.block.Block; +import org.bukkit.block.BlockFace; import org.bukkit.block.BlockState; import org.bukkit.block.Hopper; +import org.bukkit.entity.Entity; +import org.bukkit.entity.EntityType; +import org.bukkit.entity.minecart.StorageMinecart; import org.bukkit.inventory.BrewerInventory; +import org.bukkit.inventory.DoubleChestInventory; import org.bukkit.inventory.FurnaceInventory; import org.bukkit.inventory.Inventory; import org.bukkit.inventory.InventoryHolder; @@ -25,9 +31,14 @@ import java.lang.reflect.Method; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; +import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; +import java.util.Set; +import java.util.concurrent.ThreadLocalRandom; +import java.util.stream.Collectors; +import java.util.stream.IntStream; /** * Created by songoda on 3/14/2017. @@ -43,7 +54,7 @@ public class HopTask extends BukkitRunnable { public HopTask(EpicHoppers plug) { plugin = plug; - this.hopTicks = Setting.HOP_TICKS.getInt() / 2; // Purposeful integer division + this.hopTicks = Math.max(1, Setting.HOP_TICKS.getInt() / 2); // Purposeful integer division. Don't go below 1. this.runTaskTimer(plugin, 0, 2); } @@ -52,6 +63,8 @@ public class HopTask extends BukkitRunnable { Collection hoppers = plugin.getHopperManager().getHoppers().values(); Iterator itr = hoppers.iterator(); + Set toRemove = new HashSet<>(); + main: while (itr.hasNext()) { com.songoda.epichoppers.hopper.Hopper hopper = itr.next(); @@ -69,7 +82,7 @@ public class HopTask extends BukkitRunnable { // If block is not a hopper remove and continue. if (block.getType() != Material.HOPPER) { - plugin.getHopperManager().removeHopper(location); + toRemove.add(location); continue; } @@ -112,47 +125,46 @@ public class HopTask extends BukkitRunnable { BoostData boostData = plugin.getBoostManager().getBoost(hopper.getPlacedBy()); int amount = hopper.getLevel().getAmount() * (boostData == null ? 1 : boostData.getMultiplier()); - // Fetch all hopper contents. - ItemStack[] hopperContents = hopperState.getInventory().getContents(); + // Grab items from the container above (includes storage/hopper minecarts and EpicFarming farm items) + // If the container above is a hopper, ignore it if it's pointing down + Block above = block.getRelative(BlockFace.UP); + boolean isFarmItem = false; + Collection nearbyEntities = null; + outer: + if ((above.getState() instanceof InventoryHolder + && (above.getType() != Material.HOPPER || HopperDirection.getDirection(above.getState().getRawData()) != HopperDirection.DOWN)) + || !(nearbyEntities = above.getWorld().getNearbyEntities(above.getLocation().clone().add(0.5, 0.5, 0.5), 0.5, 0.5, 0.5)).isEmpty() + || (isFarmItem = this.isFarmItem(above))) { - // Get filter endpoint - InventoryHolder filterEndpoint = this.getFilterEndpoint(hopper); - - // Loop through our container list. - for (Location destinationLocation : linkedContainers) { - - // Make sure the destination chunk is loaded. - if (!destinationLocation.getWorld().isChunkLoaded(destinationLocation.getBlockX() >> 4, - destinationLocation.getBlockZ() >> 4)) - continue; - - // Get the destination block. - Block destinationBlock = destinationLocation.getBlock(); - - // Get the destination state. - BlockState blockState = destinationBlock.getState(); - - // Remove if destination is not a inventory holder. - if (!(blockState instanceof InventoryHolder)) { - hopper.removeLinkedBlock(destinationLocation); - continue; + // Get the inventory holder. Special check for EpicFarming. + // Get the slots that we can pull items from. + InventoryHolder aboveInvHolder; + int[] pullableSlots; + if (isFarmItem) { + aboveInvHolder = this.getEpicFarmingItemWrapped(above); + pullableSlots = IntStream.rangeClosed(27, 53).toArray(); + } else if (nearbyEntities != null) { + if ((aboveInvHolder = this.getRandomInventoryHolderFromEntities(nearbyEntities)) == null) + break outer; + if (aboveInvHolder instanceof StorageMinecart) { + pullableSlots = IntStream.rangeClosed(0, 26).toArray(); + } else { + pullableSlots = IntStream.rangeClosed(0, 4).toArray(); + } + } else { + aboveInvHolder = (InventoryHolder) above.getState(); + pullableSlots = this.getPullableSlots(aboveInvHolder, above.getType()); } - // Cast blockState to container - InventoryHolder destinationContainer = ((InventoryHolder) blockState); + ItemStack[] contents = aboveInvHolder.getInventory().getContents(); - // Loop through all of our hoppers item slots. - for (int i = 0; i < 5; i++) { + // Loop over the pullable slots and try to pull something. + for (int i : pullableSlots) { + // Get the item + ItemStack item = contents[i]; - // Skip if slot empty. - if (hopperContents[i] == null) - continue; - - // Get potential item to move. - ItemStack item = hopperContents[i]; - - // Skip if item blacklisted. - if ((this.blacklist.containsKey(hopperState) && this.blacklist.get(hopperState).isSimilar(item)) || blockedMaterials.contains(item.getType())) + // If item is invalid, try the next slot. + if (item == null) continue; // Get amount to move. @@ -162,14 +174,92 @@ public class HopTask extends BukkitRunnable { ItemStack itemToMove = item.clone(); itemToMove.setAmount(amountToMove); - // Process void. - if (hopper.getFilter().getVoidList().stream().anyMatch(itemStack -> itemStack.isSimilar(item))) { - item.setAmount(item.getAmount() - amountToMove); - continue; - } + // Add item to container and break on success. + if (this.addItem(hopper, aboveInvHolder, hopperState, block.getType(), item, itemToMove, amountToMove)) + break; + } + } - // Set current destination. - InventoryHolder currentDestination = destinationContainer; + // Fetch all hopper contents. + ItemStack[] hopperContents = hopperState.getInventory().getContents(); + + // Loop over hopper inventory to process void filtering. + if (!hopper.getFilter().getVoidList().isEmpty()) { + for (ItemStack item : hopperContents) { + // Skip if slot empty. + if (item == null) + continue; + + // Try to void it out + int amountToVoid = item.getAmount() < amount ? item.getAmount() : amount; + if (hopper.getFilter().getVoidList().stream().anyMatch(itemStack -> itemStack.isSimilar(item))) { + item.setAmount(item.getAmount() - amountToVoid); + break; + } + } + } + + // Get filter endpoint + InventoryHolder filterEndpoint = this.getFilterEndpoint(hopper); + + // Keep track of any destination containers + List destinationContainers = new ArrayList<>(); + + // Add linked containers to the destinations + for (Location linkedContainerLocation : linkedContainers) { + // Make sure the destination chunk is loaded. + if (!linkedContainerLocation.getWorld().isChunkLoaded(linkedContainerLocation.getBlockX() >> 4, + linkedContainerLocation.getBlockZ() >> 4)) + continue; + + // Get the destination block. + Block destinationBlock = linkedContainerLocation.getBlock(); + + // Get the destination state. + BlockState blockState = destinationBlock.getState(); + + // Remove if destination is not a inventory holder. + if (!(blockState instanceof InventoryHolder)) { + hopper.removeLinkedBlock(linkedContainerLocation); + continue; + } + + // Add to the destination containers list + destinationContainers.add((InventoryHolder) blockState); + } + + // Add storage/hopper minecarts the hopper is pointing into to the list if there aren't any destinations + if (destinationContainers.size() < 2) { + destinationContainers.addAll(block.getWorld().getNearbyEntities(hopperDirection.getLocation(location).clone().add(0.5, 0.5, 0.5), 0.5, 0.5, 0.5) + .stream().filter(e -> e.getType() == EntityType.MINECART_CHEST || e.getType() == EntityType.MINECART_HOPPER) + .map(e -> (InventoryHolder) e).collect(Collectors.toSet())); + } + + // Loop through our destination list. + for (InventoryHolder currentDestination : destinationContainers) { + + // Loop through all of our hoppers item slots. + for (int i = 0; i < 5; i++) { + + // Get potential item to move. + ItemStack item = hopperContents[i]; + + // Skip if slot empty. + if (item == null) + continue; + + // Skip if item blacklisted or void. + if ((this.blacklist.containsKey(hopperState) && this.blacklist.get(hopperState).isSimilar(item)) + || blockedMaterials.contains(item.getType()) + || hopper.getFilter().getVoidList().stream().anyMatch(itemStack -> itemStack.isSimilar(item))) + continue; + + // Get amount to move. + int amountToMove = item.getAmount() < amount ? item.getAmount() : amount; + + // Create item that will be moved. + ItemStack itemToMove = item.clone(); + itemToMove.setAmount(amountToMove); // Process whitelist and blacklist. boolean blocked = (!hopper.getFilter().getWhiteList().isEmpty() && hopper.getFilter().getWhiteList().stream().noneMatch(itemStack -> itemStack.isSimilar(item)) @@ -179,12 +269,15 @@ public class HopTask extends BukkitRunnable { // otherwise set the current destination to the endpoint. if (blocked) { if (filterEndpoint == null || !this.canMove(filterEndpoint.getInventory(), itemToMove)) - break; + continue; currentDestination = filterEndpoint; } + // Get the material of the destination + Material destinationMaterial = currentDestination instanceof BlockState ? ((BlockState) currentDestination).getType() : Material.AIR; + // Add item to container and continue on success. - if (this.addItem(hopper, hopperState, currentDestination, destinationBlock.getType(), item, itemToMove, amountToMove)) + if (this.addItem(hopper, hopperState, currentDestination, destinationMaterial, item, itemToMove, amountToMove)) continue main; } } @@ -193,6 +286,10 @@ public class HopTask extends BukkitRunnable { } } + // Clear out invalid hoppers + HopperManager hopperManager = plugin.getHopperManager(); + toRemove.forEach(hopperManager::removeHopper); + // Empty blacklist in preparation for next cycle. this.blacklist.clear(); } @@ -202,6 +299,10 @@ public class HopTask extends BukkitRunnable { Inventory destinationInventory = currentDestination.getInventory(); + // Don't transfer shulker boxes into other shulker boxes, that's a bad idea. + if (destinationType.name().contains("SHULKER_BOX") && item.getType().name().contains("SHULKER_BOX")) + return false; + switch (destinationType.name()) { case "ENDER_CHEST": OfflinePlayer op = Bukkit.getOfflinePlayer(hopper.getPlacedBy()); @@ -209,24 +310,6 @@ public class HopTask extends BukkitRunnable { if (op.isOnline()) destinationInventory = op.getPlayer().getEnderChest(); break; - case "BLACK_SHULKER_BOX": - case "BLUE_SHULKER_BOX": - case "BROWN_SHULKER_BOX": - case "CYAN_SHULKER_BOX": - case "GRAY_SHULKER_BOX": - case "GREEN_SHULKER_BOX": - case "LIGHT_BLUE_SHULKER_BOX": - case "LIGHT_GRAY_SHULKER_BOX": - case "LIME_SHULKER_BOX": - case "MAGENTA_SHULKER_BOX": - case "ORANGE_SHULKER_BOX": - case "PINK_SHULKER_BOX": - case "PURPLE_SHULKER_BOX": - case "RED_SHULKER_BOX": - case "SHULKER_BOX": - case "WHITE_SHULKER_BOX": - case "YELLOW_SHULKER_BOX": - return false; case "BREWING_STAND": { BrewerInventory brewerInventory = (BrewerInventory) destinationInventory; @@ -264,6 +347,7 @@ public class HopTask extends BukkitRunnable { this.debt(item, amountToMove, currentHolder); return true; } + case "SMOKER": case "BLAST_FURNACE": case "BURNING_FURNACE": case "FURNACE": { @@ -298,7 +382,8 @@ public class HopTask extends BukkitRunnable { // Prevent item from being moved again during this cycle. // Only block if the hopper being transfered into doesn't already contain the same item. - if (!destinationInventory.contains(itemToMove)) + // Don't blacklist if the block is transfering items into itself + if (!destinationInventory.contains(itemToMove) && currentDestination != currentHolder && currentHolder instanceof Hopper) this.blacklist.put(currentDestination, itemToMove); // Move item to destination. @@ -307,8 +392,9 @@ public class HopTask extends BukkitRunnable { // Debt hopper this.debt(item, amountToMove, currentHolder); - // Update comparators for destination hopper. - updateAdjacentComparators(((BlockState) currentDestination).getLocation()); + // Update comparators for destination block. + if (currentDestination instanceof BlockState) + updateAdjacentComparators(((BlockState) currentDestination).getLocation()); // Update comparators for current hopper. updateAdjacentComparators(hopper.getLocation()); @@ -394,4 +480,74 @@ public class HopTask extends BukkitRunnable { return false; } + /** + * Gets a set of slots that can be pulled from based on the given material + * @param material The material to get pullable slots for + * @return A set of valid pullable slots + */ + private int[] getPullableSlots(InventoryHolder inventoryHolder, Material material) { + if (material.name().contains("SHULKER_BOX")) + return IntStream.rangeClosed(0, 26).toArray(); + + switch (material.name()) { + case "BARREL": + case "CHEST": + case "TRAPPED_CHEST": + if (inventoryHolder.getInventory() instanceof DoubleChestInventory) + return IntStream.rangeClosed(0, 53).toArray(); + return IntStream.rangeClosed(0, 26).toArray(); + case "BREWING_STAND": + return IntStream.rangeClosed(0, 2).toArray(); + case "HOPPER": + return IntStream.rangeClosed(0, 4).toArray(); + case "DISPENSER": + case "DROPPER": + return IntStream.rangeClosed(0, 8).toArray(); + case "SMOKER": + case "BLAST_FURNACE": + case "BURNING_FURNACE": + case "FURNACE": + return IntStream.of(2).toArray(); + default: + return IntStream.empty().toArray(); + } + } + + /** + * Gets a random InventoryHolder from a collection of entities + * Only grabs InventoryHolders from StorageMinecarts and HopperMinecarts + * @param entities The collection of entities + * @return A random InventoryHolder if one exists, otherwise null + */ + private InventoryHolder getRandomInventoryHolderFromEntities(Collection entities) { + List inventoryHolders = new ArrayList<>(); + entities.stream().filter(e -> e.getType() == EntityType.MINECART_CHEST || e.getType() == EntityType.MINECART_HOPPER) + .forEach(e -> inventoryHolders.add((InventoryHolder) e)); + if (inventoryHolders.isEmpty()) + return null; + if (inventoryHolders.size() == 1) + return inventoryHolders.get(0); + return inventoryHolders.get(ThreadLocalRandom.current().nextInt(inventoryHolders.size())); + } + + /** + * Checks if a given block is an EpicFarming farm item + * @param block The block to check + * @return true if the block is a farm item, otherwise false + */ + private boolean isFarmItem(Block block) { + return EpicHoppers.getInstance().isEpicFarming() && com.songoda.epicfarming.EpicFarmingPlugin.getInstance().getFarmManager().getFarm(block) != null; + } + + /** + * Gets an EpicFarming block as an InventoryHolder + * Needed because EpicFarming doesn't natively support having an InventoryHolder for the farm item + * + * @param block The block to effectively attach an InventoryHolder to + * @return An InventoryHolder wrapping the EpicFarming inventory + */ + private InventoryHolder getEpicFarmingItemWrapped(Block block) { + return () -> com.songoda.epicfarming.EpicFarmingPlugin.getInstance().getFarmManager().getFarm(block).getInventory(); + } + } \ No newline at end of file diff --git a/src/main/java/com/songoda/epichoppers/utils/HopperDirection.java b/src/main/java/com/songoda/epichoppers/utils/HopperDirection.java index 374015c..61849e8 100644 --- a/src/main/java/com/songoda/epichoppers/utils/HopperDirection.java +++ b/src/main/java/com/songoda/epichoppers/utils/HopperDirection.java @@ -35,7 +35,7 @@ public enum HopperDirection { } public Location getLocation(Location location) { - return location.add(getX(), getY(), getZ()); + return location.clone().add(getX(), getY(), getZ()); } public int getX() { diff --git a/src/main/java/com/songoda/epichoppers/utils/MySQLDatabase.java b/src/main/java/com/songoda/epichoppers/utils/MySQLDatabase.java index fa70851..e8bf34f 100644 --- a/src/main/java/com/songoda/epichoppers/utils/MySQLDatabase.java +++ b/src/main/java/com/songoda/epichoppers/utils/MySQLDatabase.java @@ -1,6 +1,7 @@ package com.songoda.epichoppers.utils; import com.songoda.epichoppers.EpicHoppers; +import org.bukkit.Bukkit; import java.sql.Connection; import java.sql.DriverManager; @@ -33,8 +34,8 @@ public class MySQLDatabase { "\t`whitelist` TEXT NULL,\n" + "\t`blacklist` TEXT NULL,\n" + "\t`void` TEXT NULL,\n" + - "\t`black` TEXT NULL\n" + - "\t`autobreak` TEXT NULL\n" + + "\t`black` TEXT NULL,\n" + + "\t`autobreak` TINYINT(1) NULL\n" + ")"); connection.createStatement().execute("CREATE TABLE IF NOT EXISTS `" + instance.getConfig().getString("Database.Prefix") + "boosts` (\n" + @@ -44,7 +45,8 @@ public class MySQLDatabase { ")"); } catch (ClassNotFoundException | SQLException e) { - System.out.println("Database connection failed."); + Bukkit.getLogger().severe("Database connection failed."); + Bukkit.getLogger().severe(e.getMessage()); } } diff --git a/src/main/java/com/songoda/epichoppers/utils/Serializers.java b/src/main/java/com/songoda/epichoppers/utils/Serializers.java index 97bf6fe..7f3aca4 100644 --- a/src/main/java/com/songoda/epichoppers/utils/Serializers.java +++ b/src/main/java/com/songoda/epichoppers/utils/Serializers.java @@ -36,7 +36,7 @@ public class Serializers { public static ItemStack deserialize(String serializedItem) { String[] strings = serializedItem.split(" "); - Map enchants = new HashMap(); + Map enchants = new HashMap<>(); String[] args; ItemStack item = new ItemStack(Material.AIR); for (String str : strings) { @@ -53,21 +53,22 @@ public class Serializers { } for (String str : strings) { args = str.split(":", 2); + Bukkit.broadcastMessage(Arrays.toString(args)); if (isNumber(args[0])) item.setAmount(Integer.parseInt(args[0])); if (args.length == 1) continue; - if (args[0].equalsIgnoreCase("name:")) { + if (args[0].equalsIgnoreCase("name")) { setName(item, ChatColor.translateAlternateColorCodes('&', args[1])); continue; } - if (args[0].equalsIgnoreCase("lore:")) { + if (args[0].equalsIgnoreCase("lore")) { setLore(item, ChatColor.translateAlternateColorCodes('&', args[1])); continue; } - if (args[0].equalsIgnoreCase("rgb:")) { + if (args[0].equalsIgnoreCase("rgb")) { setArmorColor(item, args[1]); continue; } - if (args[0].equalsIgnoreCase("owner:")) { + if (args[0].equalsIgnoreCase("owner")) { setOwner(item, args[1]); continue; } @@ -102,7 +103,7 @@ public class Serializers { } private static void setName(ItemStack item, String name) { - name = name.replace("_", " "); + name = name.replace("_", " ").replace('&', ChatColor.COLOR_CHAR); ItemMeta meta = item.getItemMeta(); meta.setDisplayName(name); item.setItemMeta(meta); @@ -120,7 +121,7 @@ public class Serializers { } private static void setLore(ItemStack item, String lore) { - lore = lore.replace("_", " "); + lore = lore.replace("_", " ").replace('&', ChatColor.COLOR_CHAR); ItemMeta meta = item.getItemMeta(); meta.setLore(Arrays.asList(lore.split("\\|"))); item.setItemMeta(meta); diff --git a/src/main/java/com/songoda/epichoppers/utils/settings/Setting.java b/src/main/java/com/songoda/epichoppers/utils/settings/Setting.java index cf4eb76..62e4bba 100644 --- a/src/main/java/com/songoda/epichoppers/utils/settings/Setting.java +++ b/src/main/java/com/songoda/epichoppers/utils/settings/Setting.java @@ -50,7 +50,8 @@ public enum Setting { BLOCKBREAK_PARTICLE("Main.BlockBreak Particle Type", "LAVA", "The particle shown when the block break module performs a block break."), - BLACKLIST("Main.BlockBreak Blacklisted Blocks", Arrays.asList("BEDROCK"), "" + + BLACKLIST("Main.BlockBreak Blacklisted Blocks", + Arrays.asList("BEDROCK", "END_PORTAL", "ENDER_PORTAL", "END_PORTAL_FRAME", "ENDER_PORTAL_FRAME", "PISTON_HEAD", "PISTON_EXTENSION", "RAIL", "RAILS", "ACTIVATOR_RAIL", "DETECTOR_RAIL", "POWERED_RAIL"), "Anything listed here will not be broken by the block break module."), AUTOSELL_PRICES("Main.AutoSell Prices", diff --git a/src/main/resources/en_US.lang b/src/main/resources/en_US.lang index 7fca323..30dd7f1 100644 --- a/src/main/resources/en_US.lang +++ b/src/main/resources/en_US.lang @@ -27,7 +27,7 @@ interface.hopper.autosell = "&7AutoSell: Every &6%seconds%s" interface.hopper.linkamount = "&7Link Overflow: &6%amount%" interface.hopper.blockbreak = "&7Block Break: &6Every %ticks% ticks" interface.hopper.alreadymaxed = "&7This hopper is already maxed out!" -interface.hopper.synclore = "|&7Left-Click then click a another|&7hopper or chest to link!||&7Right-Click to unlink.|&7Currently linked to &6%amount% hopper(s)&7." +interface.hopper.synclore = "|&7Left-Click then click a another|&7hopper or chest to link!||&7Right-Click to unlink.|&7Currently linked to &6%amount% container(s)&7." interface.hopper.perltitle = "&6Click to Teleport" interface.hopper.perllore2 = "|&7Left-Click to teleport to|&7the end of the chain.||&7Right-Click to switch the|&7teleport trigger mode.|&7Currently set to: &a%type%&7." interface.hopper.filtertitle = "&cClick to Filter" diff --git a/src/main/resources/plugin.yml b/src/main/resources/plugin.yml index 848d6ff..5afb935 100644 --- a/src/main/resources/plugin.yml +++ b/src/main/resources/plugin.yml @@ -1,7 +1,7 @@ name: EpicHoppers description: EpicHoppers main: com.songoda.epichoppers.EpicHoppers -softdepend: [LiquidTanks, WildStacker, Towny, RedProtect, Kingdoms, PlotsSquared, GriefPrevention, USkyBlock, ASkyBlock, WorldGuard, Factions, Vault] +softdepend: [LiquidTanks, WildStacker, Towny, RedProtect, Kingdoms, PlotsSquared, GriefPrevention, USkyBlock, ASkyBlock, WorldGuard, Factions, Vault, EpicFarming] version: maven-version-number author: Songoda api-version: 1.13