[Feature] Withdraw from trade signs without dropping items as overflow. [EXPERIMENTAL]

This commit is contained in:
KHobbits 2013-05-06 06:40:22 +01:00
parent ed5743147b
commit 5f6cca83ce
8 changed files with 172 additions and 60 deletions

View File

@ -2,6 +2,7 @@ package com.earth2me.essentials;
import static com.earth2me.essentials.I18n._;
import static com.earth2me.essentials.I18n.capitalCase;
import com.earth2me.essentials.Trade.OverflowType;
import com.earth2me.essentials.commands.NoChargeException;
import com.earth2me.essentials.craftbukkit.InventoryWorkaround;
import com.earth2me.essentials.textreader.IText;
@ -137,7 +138,7 @@ public class Kit
{
BigDecimal value = new BigDecimal(kitItem.substring(ess.getSettings().getCurrencySymbol().length()).trim());
Trade t = new Trade(value, ess);
t.pay(user);
t.pay(user, OverflowType.DROP);
continue;
}

View File

@ -35,6 +35,14 @@ public class Trade
ITEM
}
public enum OverflowType
{
ABORT,
DROP,
RETURN
}
public Trade(final String command, final IEssentials ess)
{
this(command, null, null, null, null, ess);
@ -112,14 +120,13 @@ public class Trade
}
}
public void pay(final IUser user)
public boolean pay(final IUser user)
{
pay(user, true);
return pay(user, OverflowType.ABORT) == null;
}
public boolean pay(final IUser user, final boolean dropItems)
public Map<Integer, ItemStack> pay(final IUser user, final OverflowType type)
{
boolean success = true;
if (getMoney() != null && getMoney().signum() > 0)
{
if (ess.getSettings().isDebug())
@ -130,33 +137,65 @@ public class Trade
}
if (getItemStack() != null)
{
if (dropItems)
// This stores the would be overflow
Map<Integer, ItemStack> overFlow = InventoryWorkaround.addAllItems(user.getInventory(), getItemStack());
if (overFlow != null)
{
final Map<Integer, ItemStack> leftOver = InventoryWorkaround.addItems(user.getInventory(), getItemStack());
final Location loc = user.getLocation();
for (ItemStack itemStack : leftOver.values())
switch (type)
{
final int maxStackSize = itemStack.getType().getMaxStackSize();
final int stacks = itemStack.getAmount() / maxStackSize;
final int leftover = itemStack.getAmount() % maxStackSize;
final Item[] itemStacks = new Item[stacks + (leftover > 0 ? 1 : 0)];
for (int i = 0; i < stacks; i++)
case ABORT:
if (ess.getSettings().isDebug())
{
final ItemStack stack = itemStack.clone();
stack.setAmount(maxStackSize);
itemStacks[i] = loc.getWorld().dropItem(loc, stack);
ess.getLogger().log(Level.INFO, "abort paying " + user.getName() + " itemstack " + getItemStack().toString() + " due to lack of inventory space ");
}
if (leftover > 0)
return overFlow;
case RETURN:
// Pay the user the items, and return overflow
final Map<Integer, ItemStack> returnStack = InventoryWorkaround.addItems(user.getInventory(), getItemStack());
user.updateInventory();
if (ess.getSettings().isDebug())
{
final ItemStack stack = itemStack.clone();
stack.setAmount(leftover);
itemStacks[stacks] = loc.getWorld().dropItem(loc, stack);
ess.getLogger().log(Level.INFO, "paying " + user.getName() + " partial itemstack " + getItemStack().toString() + " with overflow " + returnStack.get(0).toString());
}
return returnStack;
case DROP:
// Pay the users the items directly, and drop the rest, will always return no overflow.
final Map<Integer, ItemStack> leftOver = InventoryWorkaround.addItems(user.getInventory(), getItemStack());
final Location loc = user.getLocation();
for (ItemStack loStack : leftOver.values())
{
final int maxStackSize = loStack.getType().getMaxStackSize();
final int stacks = loStack.getAmount() / maxStackSize;
final int leftover = loStack.getAmount() % maxStackSize;
final Item[] itemStacks = new Item[stacks + (leftover > 0 ? 1 : 0)];
for (int i = 0; i < stacks; i++)
{
final ItemStack stack = loStack.clone();
stack.setAmount(maxStackSize);
itemStacks[i] = loc.getWorld().dropItem(loc, stack);
}
if (leftover > 0)
{
final ItemStack stack = loStack.clone();
stack.setAmount(leftover);
itemStacks[stacks] = loc.getWorld().dropItem(loc, stack);
}
}
if (ess.getSettings().isDebug())
{
ess.getLogger().log(Level.INFO, "paying " + user.getName() + " partial itemstack " + getItemStack().toString() + " and dropping overflow " + leftOver.get(0).toString());
}
}
}
else
else if (ess.getSettings().isDebug())
{
success = InventoryWorkaround.addAllItems(user.getInventory(), getItemStack());
ess.getLogger().log(Level.INFO, "paying " + user.getName() + " itemstack " + getItemStack().toString());
}
user.updateInventory();
}
@ -164,7 +203,7 @@ public class Trade
{
SetExpFix.setTotalExperience(user, SetExpFix.getTotalExperience(user) + getExperience());
}
return success;
return null;
}
public void charge(final IUser user) throws ChargeException

View File

@ -222,6 +222,7 @@ public class Commandessentials extends EssentialsCommand
{
sender.sendMessage("This sub-command will delete users who havent logged in in the last <days> days.");
sender.sendMessage("Optional parameters define the minium amount required to prevent deletion.");
sender.sendMessage("Unless you define larger default values, this command wil ignore people who have more than 0 money/homes/bans.");
throw new Exception("/<command> cleanup <days> [money] [homes] [ban count]");
}
sender.sendMessage(_("cleaning"));

View File

@ -34,23 +34,29 @@ public final class InventoryWorkaround
return -1;
}
public static boolean addAllItems(final Inventory inventory, final ItemStack... items)
// Returns what it couldnt store
// This will will abort if it couldn't store all items
public static Map<Integer, ItemStack> addAllItems(final Inventory inventory, final ItemStack... items)
{
final Inventory fakeInventory = Bukkit.getServer().createInventory(null, inventory.getType());
fakeInventory.setContents(inventory.getContents());
if (addItems(fakeInventory, items).isEmpty())
Map<Integer, ItemStack> overFlow = addItems(fakeInventory, items);
if (overFlow.isEmpty())
{
addItems(inventory, items);
return true;
return null;
}
return false;
return addItems(fakeInventory, items);
}
// Returns what it couldnt store
public static Map<Integer, ItemStack> addItems(final Inventory inventory, final ItemStack... items)
{
return addOversizedItems(inventory, 0, items);
}
// Returns what it couldnt store
// Set oversizedStack to below normal stack size to disable oversized stacks
public static Map<Integer, ItemStack> addOversizedItems(final Inventory inventory, final int oversizedStacks, final ItemStack... items)
{
final Map<Integer, ItemStack> leftover = new HashMap<Integer, ItemStack>();

View File

@ -27,7 +27,7 @@ public class SignBuy extends EssentialsSign
final Trade items = getTrade(sign, 1, 2, player, ess);
final Trade charge = getTrade(sign, 3, ess);
charge.isAffordableFor(player);
if (!items.pay(player, false))
if (!items.pay(player))
{
throw new ChargeException("Inventory full"); //TODO: TL
}

View File

@ -2,6 +2,7 @@ package com.earth2me.essentials.signs;
import static com.earth2me.essentials.I18n._;
import com.earth2me.essentials.*;
import com.earth2me.essentials.Trade.OverflowType;
import java.util.*;
import org.bukkit.Location;
import org.bukkit.Material;
@ -10,6 +11,7 @@ import org.bukkit.block.BlockFace;
import org.bukkit.block.Sign;
import org.bukkit.inventory.ItemStack;
@Deprecated // This sign will be removed soon
public class SignProtection extends EssentialsSign
{
@ -81,7 +83,7 @@ public class SignProtection extends EssentialsSign
{
block.setType(Material.AIR);
final Trade trade = new Trade(new ItemStack(Material.SIGN, 1), ess);
trade.pay(player);
trade.pay(player, OverflowType.DROP);
}
}
}
@ -241,7 +243,7 @@ public class SignProtection extends EssentialsSign
{
return protectedBlocks;
}
@Override
public boolean areHeavyEventRequired()
{

View File

@ -3,6 +3,7 @@ package com.earth2me.essentials.signs;
import com.earth2me.essentials.ChargeException;
import com.earth2me.essentials.IEssentials;
import com.earth2me.essentials.Trade;
import com.earth2me.essentials.Trade.OverflowType;
import com.earth2me.essentials.User;
@ -27,7 +28,7 @@ public class SignSell extends EssentialsSign
final Trade charge = getTrade(sign, 1, 2, player, ess);
final Trade money = getTrade(sign, 3, ess);
charge.isAffordableFor(player);
money.pay(player);
money.pay(player, OverflowType.DROP);
charge.charge(player);
Trade.log("Sign", "Sell", "Interact", username, charge, username, money, sign.getBlock().getLocation(), ess);
return true;

View File

@ -3,7 +3,9 @@ package com.earth2me.essentials.signs;
import static com.earth2me.essentials.I18n._;
import com.earth2me.essentials.Trade.TradeType;
import com.earth2me.essentials.*;
import com.earth2me.essentials.Trade.OverflowType;
import java.math.BigDecimal;
import java.util.Map;
import org.bukkit.inventory.ItemStack;
//TODO: TL exceptions
@ -14,13 +16,21 @@ public class SignTrade extends EssentialsSign
super("Trade");
}
public enum AmountType
{
TOTAL,
ROUNDED,
COST
}
@Override
protected boolean onSignCreate(final ISign sign, final User player, final String username, final IEssentials ess) throws SignException, ChargeException
{
validateTrade(sign, 1, false, ess);
validateTrade(sign, 2, true, ess);
final Trade trade = getTrade(sign, 2, true, true, ess);
final Trade charge = getTrade(sign, 1, true, false, ess);
final Trade trade = getTrade(sign, 2, AmountType.ROUNDED, true, ess);
final Trade charge = getTrade(sign, 1, AmountType.ROUNDED, false, ess);
if (trade.getType() == charge.getType() && (trade.getType() != TradeType.ITEM || trade.getItemStack().getType().equals(charge.getItemStack().getType())))
{
throw new SignException("You cannot trade for the same item type.");
@ -41,9 +51,20 @@ public class SignTrade extends EssentialsSign
Trade stored = null;
try
{
stored = getTrade(sign, 1, true, true, ess);
stored = getTrade(sign, 1, AmountType.TOTAL, true, ess);
subtractAmount(sign, 1, stored, ess);
stored.pay(player);
Map<Integer, ItemStack> withdraw = stored.pay(player, OverflowType.RETURN);
if (withdraw == null)
{
Trade.log("Sign", "Trade", "Withdraw", username, store, username, null, sign.getBlock().getLocation(), ess);
}
else
{
setAmount(sign, 1, BigDecimal.valueOf(withdraw.get(0).getAmount()), ess);
Trade.log("Sign", "Trade", "Withdraw", username, stored, username, new Trade(withdraw.get(0), ess), sign.getBlock().getLocation(), ess);
}
}
catch (SignException e)
{
@ -52,16 +73,16 @@ public class SignTrade extends EssentialsSign
throw new SignException(_("tradeSignEmptyOwner"), e);
}
}
Trade.log("Sign", "Trade", "OwnerInteract", username, store, username, stored, sign.getBlock().getLocation(), ess);
Trade.log("Sign", "Trade", "Deposit", username, store, username, null, sign.getBlock().getLocation(), ess);
}
else
{
final Trade charge = getTrade(sign, 1, false, false, ess);
final Trade trade = getTrade(sign, 2, false, true, ess);
final Trade charge = getTrade(sign, 1, AmountType.COST, false, ess);
final Trade trade = getTrade(sign, 2, AmountType.COST, true, ess);
charge.isAffordableFor(player);
addAmount(sign, 1, charge, ess);
subtractAmount(sign, 2, trade, ess);
if (!trade.pay(player, false))
if (!trade.pay(player))
{
subtractAmount(sign, 1, charge, ess);
addAmount(sign, 2, trade, ess);
@ -76,7 +97,7 @@ public class SignTrade extends EssentialsSign
private Trade rechargeSign(final ISign sign, final IEssentials ess, final User player) throws SignException, ChargeException
{
final Trade trade = getTrade(sign, 2, false, false, ess);
final Trade trade = getTrade(sign, 2, AmountType.COST, false, ess);
if (trade.getItemStack() != null && player.getItemInHand() != null
&& trade.getItemStack().getTypeId() == player.getItemInHand().getTypeId()
&& trade.getItemStack().getDurability() == player.getItemInHand().getDurability()
@ -105,11 +126,24 @@ public class SignTrade extends EssentialsSign
{
try
{
final Trade stored1 = getTrade(sign, 1, true, false, ess);
final Trade stored2 = getTrade(sign, 2, true, false, ess);
stored1.pay(player);
stored2.pay(player);
Trade.log("Sign", "Trade", "Break", username, stored2, username, stored1, sign.getBlock().getLocation(), ess);
final Trade stored1 = getTrade(sign, 1, AmountType.TOTAL, false, ess);
final Trade stored2 = getTrade(sign, 2, AmountType.TOTAL, false, ess);
Map<Integer, ItemStack> withdraw1 = stored1.pay(player, OverflowType.RETURN);
Map<Integer, ItemStack> withdraw2 = stored2.pay(player, OverflowType.RETURN);
if (withdraw1 == null && withdraw2 == null)
{
Trade.log("Sign", "Trade", "Break", username, stored2, username, stored1, sign.getBlock().getLocation(), ess);
return true;
}
setAmount(sign, 1, BigDecimal.valueOf(withdraw1 == null ? 0L : withdraw1.get(0).getAmount()), ess);
Trade.log("Sign", "Trade", "Withdraw", username, stored1, username, withdraw1 == null ? null : new Trade(withdraw1.get(0), ess), sign.getBlock().getLocation(), ess);
setAmount(sign, 2, BigDecimal.valueOf(withdraw2 == null ? 0L : withdraw2.get(0).getAmount()), ess);
Trade.log("Sign", "Trade", "Withdraw", username, stored2, username, withdraw2 == null ? null : new Trade(withdraw2.get(0), ess), sign.getBlock().getLocation(), ess);
sign.updateSign();
}
catch (SignException e)
{
@ -119,7 +153,7 @@ public class SignTrade extends EssentialsSign
}
throw e;
}
return true;
return false;
}
else
{
@ -208,7 +242,7 @@ public class SignTrade extends EssentialsSign
throw new SignException(_("invalidSignLine", index + 1));
}
protected final Trade getTrade(final ISign sign, final int index, final boolean fullAmount, final boolean notEmpty, final IEssentials ess) throws SignException
protected final Trade getTrade(final ISign sign, final int index, final AmountType amountType, final boolean notEmpty, final IEssentials ess) throws SignException
{
final String line = sign.getLine(index).trim();
if (line.isEmpty())
@ -225,7 +259,7 @@ public class SignTrade extends EssentialsSign
final BigDecimal amount = notEmpty ? getBigDecimalPositive(split[1]) : getBigDecimal(split[1]);
if (money != null && amount != null)
{
return new Trade(fullAmount ? amount : money, ess);
return new Trade(amountType == AmountType.COST ? money : amount, ess);
}
}
catch (SignException e)
@ -240,24 +274,30 @@ public class SignTrade extends EssentialsSign
{
final int stackamount = getIntegerPositive(split[0]);
int amount = getInteger(split[2]);
amount -= amount % stackamount;
if (amountType == AmountType.ROUNDED)
{
amount -= amount % stackamount;
}
if (notEmpty && (amount < 1 || stackamount < 1))
{
throw new SignException(_("tradeSignEmpty"));
}
return new Trade(fullAmount ? amount : stackamount, ess);
return new Trade((amountType == AmountType.COST ? stackamount : amount), ess);
}
else
{
final int stackamount = getIntegerPositive(split[0]);
final ItemStack item = getItemStack(split[1], stackamount, ess);
int amount = getInteger(split[2]);
amount -= amount % stackamount;
if (amountType == AmountType.ROUNDED)
{
amount -= amount % stackamount;
}
if (notEmpty && (amount < 1 || stackamount < 1 || item.getTypeId() == 0))
{
throw new SignException(_("tradeSignEmpty"));
}
item.setAmount(fullAmount ? amount : stackamount);
item.setAmount(amountType == AmountType.COST ? stackamount : amount);
return new Trade(item, ess);
}
}
@ -304,6 +344,31 @@ public class SignTrade extends EssentialsSign
//TODO: Translate these exceptions.
private void changeAmount(final ISign sign, final int index, final BigDecimal value, final IEssentials ess) throws SignException
{
final String line = sign.getLine(index).trim();
if (line.isEmpty())
{
throw new SignException("Empty line");
}
final String[] split = line.split("[ :]+");
if (split.length == 2)
{
final BigDecimal amount = getBigDecimal(split[1]).add(value);
setAmount(sign, index, amount, ess);
return;
}
if (split.length == 3)
{
final BigDecimal amount = getBigDecimal(split[2]).add(value);
setAmount(sign, index, amount, ess);
return;
}
throw new SignException(_("invalidSignLine", index + 1));
}
//TODO: Translate these exceptions.
private void setAmount(final ISign sign, final int index, final BigDecimal value, final IEssentials ess) throws SignException
{
final String line = sign.getLine(index).trim();
@ -319,7 +384,7 @@ public class SignTrade extends EssentialsSign
final BigDecimal amount = getBigDecimal(split[1]);
if (money != null && amount != null)
{
final String newline = Util.shortCurrency(money, ess) + ":" + Util.shortCurrency(amount.add(value), ess).substring(1);
final String newline = Util.shortCurrency(money, ess) + ":" + Util.shortCurrency(value, ess).substring(1);
if (newline.length() > 15)
{
throw new SignException("This sign is full: Line too long!");
@ -334,8 +399,7 @@ public class SignTrade extends EssentialsSign
if (split[1].equalsIgnoreCase("exp") || split[1].equalsIgnoreCase("xp"))
{
final int stackamount = getIntegerPositive(split[0]);
final int amount = getInteger(split[2]);
final String newline = stackamount + " " + split[1] + ":" + (amount + value.intValueExact());
final String newline = stackamount + " " + split[1] + ":" + (value.intValueExact());
if (newline.length() > 15)
{
throw new SignException("This sign is full: Line too long!");
@ -346,10 +410,8 @@ public class SignTrade extends EssentialsSign
else
{
final int stackamount = getIntegerPositive(split[0]);
//TODO: Unused local variable
final ItemStack item = getItemStack(split[1], stackamount, ess);
final int amount = getInteger(split[2]);
final String newline = stackamount + " " + split[1] + ":" + (amount + value.intValueExact());
getItemStack(split[1], stackamount, ess);
final String newline = stackamount + " " + split[1] + ":" + (value.intValueExact());
if (newline.length() > 15)
{
throw new SignException("This sign is full: Line too long!");