Merge branch 'development' into 'master'

4.2.0 hotfix: respect filter in suction module

See merge request Songoda/epichoppers!45
This commit is contained in:
Brianna O'Keefe 2019-08-13 03:31:59 +00:00
commit ff0d9b92d9
12 changed files with 122 additions and 34 deletions

View File

@ -4,7 +4,7 @@ stages:
variables:
name: "EpicHoppers"
path: "/builds/$CI_PROJECT_PATH"
version: "4.2.0"
version: "4.2.3"
build:
stage: build

View File

@ -107,9 +107,11 @@ public class EpicHoppers extends JavaPlugin {
PluginManager pluginManager = Bukkit.getPluginManager();
// Setup Economy
if (Setting.VAULT_ECONOMY.getBoolean() && pluginManager.isPluginEnabled("Vault"))
this.economy = new VaultEconomy();
else if (Setting.RESERVE_ECONOMY.getBoolean() && pluginManager.isPluginEnabled("Reserve"))
if (Setting.VAULT_ECONOMY.getBoolean() && pluginManager.isPluginEnabled("Vault")) {
// try to load Vault for economy
if (!((VaultEconomy) (this.economy = new VaultEconomy())).isLoaded())
this.economy = null; // vault load error
} else if (Setting.RESERVE_ECONOMY.getBoolean() && pluginManager.isPluginEnabled("Reserve"))
this.economy = new ReserveEconomy();
else if (Setting.PLAYER_POINTS_ECONOMY.getBoolean() && pluginManager.isPluginEnabled("PlayerPoints"))
this.economy = new PlayerPointsEconomy();
@ -185,6 +187,10 @@ public class EpicHoppers extends JavaPlugin {
* Saves registered hoppers to file.
*/
private void saveToFile() {
// double-check that we're ok to save
if(EpicHoppers.getInstance().getLevelManager() == null)
return;
checkStorage();
for (Level level : EpicHoppers.getInstance().getLevelManager().getLevels().values())

View File

@ -2,27 +2,34 @@ package com.songoda.epichoppers.economy;
import org.bukkit.Bukkit;
import org.bukkit.OfflinePlayer;
import org.bukkit.plugin.RegisteredServiceProvider;
public class VaultEconomy implements Economy {
private final net.milkbowl.vault.economy.Economy vault;
public VaultEconomy() {
this.vault = Bukkit.getServicesManager().
getRegistration(net.milkbowl.vault.economy.Economy.class).getProvider();
// separate this out to weed out some conflicts in 1.8
RegisteredServiceProvider rsp = Bukkit.getServicesManager().
getRegistration(net.milkbowl.vault.economy.Economy.class);
this.vault = (net.milkbowl.vault.economy.Economy) (rsp == null ? null : rsp.getProvider());
}
public boolean isLoaded() {
return vault != null;
}
@Override
public boolean hasBalance(OfflinePlayer player, double cost) {
return vault.has(player, cost);
return vault != null && vault.has(player, cost);
}
@Override
public boolean withdrawBalance(OfflinePlayer player, double cost) {
return vault.withdrawPlayer(player, cost).transactionSuccess();
return vault != null && vault.withdrawPlayer(player, cost).transactionSuccess();
}
@Override
public boolean deposit(OfflinePlayer player, double amount) {
return vault.depositPlayer(player, amount).transactionSuccess();
return vault != null && vault.depositPlayer(player, amount).transactionSuccess();
}
}

View File

@ -6,6 +6,7 @@ import com.songoda.epichoppers.hopper.Hopper;
import com.songoda.epichoppers.utils.Methods;
import com.songoda.epichoppers.utils.ServerVersion;
import com.songoda.epichoppers.utils.StorageContainerCache;
import com.songoda.epichoppers.utils.settings.Setting;
import org.bukkit.Bukkit;
import org.bukkit.Material;
import org.bukkit.entity.Player;
@ -23,15 +24,18 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Stream;
public class ModuleAutoCrafting extends Module {
private static final Map<ItemStack, Recipes> cachedRecipes = new ConcurrentHashMap<>();
private static final Map<Hopper, ItemStack> cachedCrafting = new ConcurrentHashMap<>();
static final ItemStack noCraft = new ItemStack(Material.AIR);
boolean crafterEjection;
public ModuleAutoCrafting(EpicHoppers plugin) {
super(plugin);
crafterEjection = Setting.AUTOCRAFT_JAM_EJECT.getBoolean();
}
@Override
@ -45,6 +49,21 @@ public class ModuleAutoCrafting extends Module {
if (hopper == null || (toCraft = getAutoCrafting(hopper)) == null || toCraft.getType() == Material.AIR)
return;
// jam check: is this hopper gummed up?
if(crafterEjection) {
final List<Material> allMaterials = getRecipes(toCraft).getAllMaterials();
if(Stream.of(hopperCache.cachedInventory)
.allMatch(item -> item != null && allMaterials.stream().anyMatch(mat -> mat == item.getType()))) {
// Crafter can't function if there's nowhere to put the output
// ¯\_()_/¯
// forcibly open the last slot
ItemStack last = hopperCache.cachedInventory[4];
hopperCache.setItem(4, null);
// and yeet into space!
hopper.getWorld().dropItemNaturally(hopper.getLocation(), last);
}
}
top:
for (SimpleRecipe recipe : getRecipes(toCraft).recipes) {
@ -52,7 +71,7 @@ public class ModuleAutoCrafting extends Module {
for(ItemStack item : recipe.recipe) {
int amountHave = 0;
for (ItemStack hopperItem : hopperCache.cachedInventory) {
if (hopperItem != null && Methods.isSimilar(hopperItem, item))
if (hopperItem != null && Methods.isSimilarMaterial(hopperItem, item))
amountHave += hopperItem.getAmount();
}
if (amountHave < item.getAmount()) {
@ -119,7 +138,22 @@ public class ModuleAutoCrafting extends Module {
Recipes getRecipes(ItemStack toCraft) {
Recipes recipes = cachedRecipes.get(toCraft);
if (recipes == null) {
try {
recipes = new Recipes(Bukkit.getServer().getRecipesFor(toCraft));
} catch (Throwable t) {
// extremely rare, but y'know - some plugins are dumb
recipes = new Recipes();
// how's about we try this manually?
java.util.Iterator<Recipe> recipeIterator = Bukkit.getServer().recipeIterator();
while (recipeIterator.hasNext()) {
try {
Recipe recipe = recipeIterator.next();
ItemStack stack = recipe.getResult();
if (Methods.isSimilarMaterial(stack, toCraft))
recipes.addRecipe(recipe);
} catch (Throwable ignored) {}
}
}
// adding broken recipe for wood planks
final String toType = toCraft.getType().name();

View File

@ -21,6 +21,7 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Stream;
public class ModuleBlockBreak extends Module {
@ -56,6 +57,12 @@ public class ModuleBlockBreak extends Module {
if (!isEnabled(hopper))
return;
// don't try to break stuff if we can't grab stuff
// (for simplicity, just assume that no empty slots mean there's a good chance we won't be able to pick something new up)
if(Stream.of(hopperCache.cachedInventory)
.allMatch(item -> item != null && item.getAmount() > 0))
return;
Integer tick = blockTick.get(hopper);
if (tick == null) {
blockTick.put(hopper, 1);

View File

@ -3,9 +3,9 @@ package com.songoda.epichoppers.hopper.levels.modules;
import com.bgsoftware.wildstacker.api.WildStackerAPI;
import com.songoda.epichoppers.EpicHoppers;
import com.songoda.epichoppers.hopper.Hopper;
import com.songoda.epichoppers.utils.Methods;
import com.songoda.epichoppers.utils.ServerVersion;
import com.songoda.epichoppers.utils.StorageContainerCache;
import com.songoda.ultimatestacker.utils.Methods;
import org.apache.commons.lang.StringUtils;
import org.bukkit.Bukkit;
import org.bukkit.Material;
@ -48,12 +48,16 @@ public class ModuleSuction extends Module {
Set<Item> itemsToSuck = 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.isDead()
&& entity.getTicksLived() >= ((Item) entity).getPickupDelay()
&& entity.getLocation().getBlock().getType() != Material.HOPPER)
.map(entity -> (Item) entity)
.collect(Collectors.toSet());
boolean filterEndpoint = hopper.getFilter().getEndPoint() != null;
for (Item item : itemsToSuck) {
ItemStack itemStack = item.getItemStack();
if (item.getPickupDelay() == 0) {
@ -72,6 +76,26 @@ public class ModuleSuction extends Module {
if (blacklist.contains(item.getUniqueId()))
return;
// respect filter if no endpoint
if (!filterEndpoint
&& !(hopper.getFilter().getWhiteList().isEmpty() && hopper.getFilter().getBlackList().isEmpty())) {
// this hopper has a filter with no rejection endpoint, so don't absorb disalowed items
// whitelist has priority
if (!hopper.getFilter().getWhiteList().isEmpty()) {
// is this item on the whitelist?
if (!hopper.getFilter().getWhiteList().stream().anyMatch(filterItem -> Methods.isSimilarMaterial(itemStack, filterItem))) {
// nope!
continue;
}
} else {
// check the blacklist
if (hopper.getFilter().getBlackList().stream().anyMatch(filterItem -> Methods.isSimilarMaterial(itemStack, filterItem))) {
// don't grab this, then
continue;
}
}
}
// try to add the items to the hopper
int toAdd, added = hopperCache.addAny(itemStack, toAdd = getActualItemAmount(item));
if (added == 0)
@ -101,7 +125,7 @@ public class ModuleSuction extends Module {
private int getActualItemAmount(Item item) {
if (ultimateStacker) {
return Methods.getActualItemAmount(item);
return com.songoda.ultimatestacker.utils.Methods.getActualItemAmount(item);
} else if (wildStacker)
return WildStackerAPI.getItemAmount(item);
else
@ -111,7 +135,7 @@ public class ModuleSuction extends Module {
private void updateAmount(Item item, int amount) {
if (ultimateStacker)
Methods.updateItemAmount(item, amount);
com.songoda.ultimatestacker.utils.Methods.updateItemAmount(item, amount);
else if (wildStacker)
WildStackerAPI.getStackedItem(item).setStackAmount(amount, true);
else

View File

@ -32,6 +32,8 @@ public class HopperListeners implements Listener {
this.instance = instance;
}
// todo: InventoryMoveItemEvent for filters
@EventHandler(ignoreCancelled = true)
public void onHop(InventoryMoveItemEvent event) {
Inventory source = event.getSource();
@ -63,7 +65,8 @@ public class HopperListeners implements Listener {
// Special cases when a hopper is picking up items
if (destination.getHolder() instanceof org.bukkit.block.Hopper) {
Hopper toHopper = instance.getHopperManager().getHopper(destination.getLocation());
// minecraft 1.8 doesn't have a method to get the hopper's location from the inventory, so we use the holder instead
Hopper toHopper = instance.getHopperManager().getHopper(((org.bukkit.block.Hopper) destination.getHolder()).getLocation());
final ItemStack toMove = event.getItem();
// Don't fill the last inventory slot on crafting hoppers (fixes crafters getting stuck)
@ -72,7 +75,7 @@ public class HopperListeners implements Listener {
// if we're not moving the item that we're trying to craft, we need to verify that we're not trying to fill the last slot
// (filling every slot leaves no room for the crafter to function)
if (toCraft != null && toCraft.getType() != Material.AIR
&& !Methods.isSimilar(toMove, toCraft)
&& !Methods.isSimilarMaterial(toMove, toCraft)
&& !Methods.canMoveReserved(destination, toMove)) {
event.setCancelled(true);
return;
@ -88,14 +91,14 @@ public class HopperListeners implements Listener {
// whitelist has priority
if (!toHopper.getFilter().getWhiteList().isEmpty()) {
// is this item on the whitelist?
allowItem = toHopper.getFilter().getWhiteList().stream().anyMatch(item -> Methods.isSimilar(toMove, item));
allowItem = toHopper.getFilter().getWhiteList().stream().anyMatch(item -> Methods.isSimilarMaterial(toMove, item));
if(!allowItem) {
// can we change the item to something else?
searchReplacement:
for(ItemStack sourceItem : source.getContents()) {
if(sourceItem != null && Methods.canMove(destination, sourceItem)) {
for(ItemStack item : toHopper.getFilter().getWhiteList()) {
if(Methods.isSimilar(sourceItem, item)) {
if(Methods.isSimilarMaterial(sourceItem, item)) {
moveInstead = new ItemStack(sourceItem);
moveInstead.setAmount(1);
break searchReplacement;
@ -106,13 +109,13 @@ public class HopperListeners implements Listener {
}
} else {
// check the blacklist
allowItem = !toHopper.getFilter().getBlackList().stream().anyMatch(item -> Methods.isSimilar(toMove, item));
allowItem = !toHopper.getFilter().getBlackList().stream().anyMatch(item -> Methods.isSimilarMaterial(toMove, item));
if (!allowItem) {
// can we change the item to something else?
searchReplacement:
for (ItemStack sourceItem : source.getContents()) {
if (sourceItem != null && Methods.canMove(destination, sourceItem)) {
boolean blacklisted = toHopper.getFilter().getBlackList().stream().anyMatch(item -> Methods.isSimilar(sourceItem, item));
boolean blacklisted = toHopper.getFilter().getBlackList().stream().anyMatch(item -> Methods.isSimilarMaterial(sourceItem, item));
if (!blacklisted) {
moveInstead = new ItemStack(sourceItem);
moveInstead.setAmount(1);

View File

@ -132,7 +132,7 @@ public class HopTask extends BukkitRunnable {
|| (hopperCache.cacheChanged[i] && item.getAmount() - hopperCache.cacheAdded[i] < maxToMove)
// skip if blocked or voidlisted
|| blockedMaterials.contains(item.getType())
|| hopper.getFilter().getVoidList().stream().anyMatch(itemStack -> Methods.isSimilar(itemStack, item)))
|| hopper.getFilter().getVoidList().stream().anyMatch(itemStack -> Methods.isSimilarMaterial(itemStack, item)))
continue;
doProcess = true;
@ -262,7 +262,7 @@ public class HopTask extends BukkitRunnable {
// if we're not moving the item that we're trying to craft, we need to verify that we're not trying to fill the last slot
// (filling every slot leaves no room for the crafter to function)
if (toCraft != null && !Methods.isSimilar(toMove, toCraft) && !Methods.canMoveReserved(hopperCache.cachedInventory, toMove))
if (toCraft != null && !Methods.isSimilarMaterial(toMove, toCraft) && !Methods.canMoveReserved(hopperCache.cachedInventory, toMove))
continue;
// respect whitelist/blacklist filters
@ -272,13 +272,13 @@ public class HopTask extends BukkitRunnable {
// whitelist has priority
if (!toHopper.getFilter().getWhiteList().isEmpty()) {
// is this item on the whitelist?
if (!toHopper.getFilter().getWhiteList().stream().anyMatch(item -> Methods.isSimilar(toMove, item))) {
if (!toHopper.getFilter().getWhiteList().stream().anyMatch(item -> Methods.isSimilarMaterial(toMove, item))) {
// nope!
continue;
}
} else {
// check the blacklist
if (toHopper.getFilter().getBlackList().stream().anyMatch(item -> Methods.isSimilar(toMove, item))) {
if (toHopper.getFilter().getBlackList().stream().anyMatch(item -> Methods.isSimilarMaterial(toMove, item))) {
// don't grab this, then
continue;
}
@ -407,7 +407,7 @@ public class HopTask extends BukkitRunnable {
|| (hopperCache.cacheChanged[i] && item.getAmount() - hopperCache.cacheAdded[i] < maxToMove)
// skip if blocked or voidlisted
|| blockedMaterials.contains(item.getType())
|| hopper.getFilter().getVoidList().stream().anyMatch(itemStack -> Methods.isSimilar(itemStack, item)))
|| hopper.getFilter().getVoidList().stream().anyMatch(itemStack -> Methods.isSimilarMaterial(itemStack, item)))
continue;
// Create item that will be moved.
@ -443,7 +443,7 @@ public class HopTask extends BukkitRunnable {
ItemStack[] hopperContents = hopperCache.cachedInventory;
for (int i = 0; i < hopperContents.length; i++) {
final ItemStack item = hopperContents[i];
if (item != null && hopper.getFilter().getVoidList().stream().anyMatch(itemStack -> Methods.isSimilar(itemStack, item))) {
if (item != null && hopper.getFilter().getVoidList().stream().anyMatch(itemStack -> Methods.isSimilarMaterial(itemStack, item))) {
int amt = Math.min(0, item.getAmount() - maxToMove);
if (amt == 0) {
hopperCache.removeItem(i);

View File

@ -145,11 +145,11 @@ public class Methods {
return glass;
}
public static boolean isSimilar(ItemStack is1, ItemStack is2) {
public static boolean isSimilarMaterial(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();
return is1.getType() == is2.getType() && (is1.getDurability() == -1 || is2.getDurability() == -1 || is1.getDurability() == is2.getDurability());
}
}
@ -159,7 +159,7 @@ public class Methods {
final ItemMeta itemMeta = item.getItemMeta();
for (ItemStack stack : inventory) {
final ItemMeta stackMeta;
if (isSimilar(stack, item) && (stack.getAmount() + item.getAmount()) < stack.getMaxStackSize()
if (isSimilarMaterial(stack, item) && (stack.getAmount() + item.getAmount()) < stack.getMaxStackSize()
&& ((itemMeta == null) == ((stackMeta = stack.getItemMeta()) == null))
&& (itemMeta == null || Bukkit.getItemFactory().equals(itemMeta, stackMeta))) {
return true;
@ -175,7 +175,7 @@ public class Methods {
if (stack == null || stack.getAmount() == 0)
return true;
final ItemMeta stackMeta;
if (isSimilar(stack, item) && (stack.getAmount() + item.getAmount()) < stack.getMaxStackSize()
if (isSimilarMaterial(stack, item) && (stack.getAmount() + item.getAmount()) < stack.getMaxStackSize()
&& ((itemMeta == null) == ((stackMeta = stack.getItemMeta()) == null))
&& (itemMeta == null || Bukkit.getItemFactory().equals(itemMeta, stackMeta))) {
return true;
@ -192,7 +192,7 @@ public class Methods {
for (int i = 0; i < 4; i++) {
final ItemStack stack = contents[i];
final ItemMeta stackMeta;
if (isSimilar(stack, item) && (stack.getAmount() + item.getAmount()) < stack.getMaxStackSize()
if (isSimilarMaterial(stack, item) && (stack.getAmount() + item.getAmount()) < stack.getMaxStackSize()
&& ((itemMeta == null) == ((stackMeta = stack.getItemMeta()) == null))
&& (itemMeta == null || Bukkit.getItemFactory().equals(itemMeta, stackMeta))) {
return true;
@ -208,7 +208,7 @@ public class Methods {
if (stack == null || stack.getAmount() == 0)
return true;
final ItemMeta stackMeta;
if (isSimilar(stack, item) && (stack.getAmount() + item.getAmount()) < stack.getMaxStackSize()
if (isSimilarMaterial(stack, item) && (stack.getAmount() + item.getAmount()) < stack.getMaxStackSize()
&& ((itemMeta == null) == ((stackMeta = stack.getItemMeta()) == null))
&& (itemMeta == null || Bukkit.getItemFactory().equals(itemMeta, stackMeta))) {
return true;

View File

@ -111,7 +111,7 @@ public class StorageContainerCache {
int toRemove = item.getAmount();
for (int i = 0; toRemove > 0 && i < cachedInventory.length; i++) {
final ItemStack cacheItem = cachedInventory[i];
if (cacheItem != null && cacheItem.getAmount() != 0 && Methods.isSimilar(item, cacheItem)) {
if (cacheItem != null && cacheItem.getAmount() != 0 && item.isSimilar(cacheItem)) {
int have = cacheItem.getAmount();
if (have > toRemove) {
cachedInventory[i].setAmount(have - toRemove);
@ -155,7 +155,7 @@ public class StorageContainerCache {
cacheAdded[i] = toAdd;
totalAdded += toAdd;
amountToAdd -= toAdd;
} else if (maxStack > cacheItem.getAmount() && Methods.isSimilar(item, cacheItem)) {
} else if (maxStack > cacheItem.getAmount() && item.isSimilar(cacheItem)) {
// free space!
int toAdd = Math.min(maxStack - cacheItem.getAmount(), amountToAdd);
cachedInventory[i].setAmount(toAdd + cacheItem.getAmount());

View File

@ -113,6 +113,8 @@ public abstract class AbstractGUI implements Listener {
}
protected void init(String title, int slots) {
if(title.length() > 32)
title = title.substring(0, 31);
if (inventory == null
|| inventory.getSize() != slots
|| !ChatColor.translateAlternateColorCodes('&', title).equals(player.getOpenInventory().getTitle())) {

View File

@ -54,6 +54,11 @@ public enum Setting {
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."),
AUTOCRAFT_JAM_EJECT("Main.AutoCraft Jam Eject", false,
"AutoCraft module needs a free slot to craft items with.",
"Normally, crafting hoppers won't grab items that would fill that slot.",
"This option ejects items if that last slot is forcibly filled"),
AUTOSELL_PRICES("Main.AutoSell Prices",
Arrays.asList("STONE,0.50", "COBBLESTONE,0.20", "IRON_ORE,0.35", "COAL_ORE,0.20"),
"These are the prices used by the auto sell module."),