Weird sign shop stuff.

This commit is contained in:
AppleDash 2017-01-10 17:09:18 -05:00
parent 934d92fbf9
commit 4df22729aa
10 changed files with 271 additions and 27 deletions

View File

@ -39,8 +39,17 @@ public class TransactionResult {
} }
public enum Status { public enum Status {
SUCCESS, SUCCESS("Success."),
ERR_NOT_ENOUGH_FUNDS("Not enough money is available for you to complete that transaction.");
ERR_NOT_ENOUGH_FUNDS private final String message;
Status(String message) {
this.message = message;
}
public String getMessage() {
return message;
}
} }
} }

View File

@ -7,6 +7,7 @@ import org.appledash.saneeconomysignshop.listeners.SignChangeListener;
import org.appledash.saneeconomysignshop.signshop.SignShopManager; import org.appledash.saneeconomysignshop.signshop.SignShopManager;
import org.appledash.saneeconomysignshop.signshop.storage.SignShopStorageFlatfile; import org.appledash.saneeconomysignshop.signshop.storage.SignShopStorageFlatfile;
import org.appledash.saneeconomysignshop.util.ItemDatabase; import org.appledash.saneeconomysignshop.util.ItemDatabase;
import org.appledash.saneeconomysignshop.util.LimitManager;
import org.bukkit.plugin.Plugin; import org.bukkit.plugin.Plugin;
import org.bukkit.plugin.java.JavaPlugin; import org.bukkit.plugin.java.JavaPlugin;
@ -19,6 +20,7 @@ import java.io.File;
public class SaneEconomySignShop extends JavaPlugin { public class SaneEconomySignShop extends JavaPlugin {
private ISaneEconomy saneEconomy; private ISaneEconomy saneEconomy;
private final SignShopManager signShopManager = new SignShopManager(new SignShopStorageFlatfile(new File(getDataFolder(), "shops.db"))); private final SignShopManager signShopManager = new SignShopManager(new SignShopStorageFlatfile(new File(getDataFolder(), "shops.db")));
private final LimitManager limitManager = new LimitManager();
@Override @Override
public void onEnable() { public void onEnable() {
@ -50,4 +52,8 @@ public class SaneEconomySignShop extends JavaPlugin {
public ISaneEconomy getSaneEconomy() { public ISaneEconomy getSaneEconomy() {
return saneEconomy; return saneEconomy;
} }
public LimitManager getLimitManager() {
return limitManager;
}
} }

View File

@ -1,12 +1,11 @@
package org.appledash.saneeconomysignshop.listeners; package org.appledash.saneeconomysignshop.listeners;
import org.appledash.saneeconomy.economy.EconomyManager; import org.appledash.saneeconomy.economy.EconomyManager;
import org.appledash.saneeconomy.economy.economable.Economable;
import org.appledash.saneeconomy.economy.transaction.Transaction; import org.appledash.saneeconomy.economy.transaction.Transaction;
import org.appledash.saneeconomy.economy.transaction.TransactionReason;
import org.appledash.saneeconomy.economy.transaction.TransactionResult; import org.appledash.saneeconomy.economy.transaction.TransactionResult;
import org.appledash.saneeconomy.utils.MessageUtils; import org.appledash.saneeconomy.utils.MessageUtils;
import org.appledash.saneeconomysignshop.SaneEconomySignShop; import org.appledash.saneeconomysignshop.SaneEconomySignShop;
import org.appledash.saneeconomysignshop.signshop.ShopTransaction;
import org.appledash.saneeconomysignshop.signshop.SignShop; import org.appledash.saneeconomysignshop.signshop.SignShop;
import org.bukkit.Material; import org.bukkit.Material;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
@ -84,26 +83,30 @@ public class InteractListener implements Listener {
private void doBuy(SignShop shop, Player player) { private void doBuy(SignShop shop, Player player) {
EconomyManager ecoMan = plugin.getSaneEconomy().getEconomyManager(); EconomyManager ecoMan = plugin.getSaneEconomy().getEconomyManager();
int quantity = player.isSneaking() ? 1 : shop.getQuantity(); int quantity = player.isSneaking() ? 1 : shop.getQuantity();
double price = shop.getBuyPrice(quantity);
if (!ecoMan.hasBalance(Economable.wrap(player), price)) { ShopTransaction shopTransaction = shop.makeTransaction(player, ShopTransaction.TransactionDirection.BUY, quantity);
MessageUtils.sendMessage(player, "You do not have enough money to buy {1} {2}.", quantity, shop.getItem());
if (!plugin.getLimitManager().shouldAllowTransaction(shopTransaction)) {
MessageUtils.sendMessage(player, "You have reached your buying limit for the time being. Try back in an hour or so.");
return; return;
} }
TransactionResult result = ecoMan.transact(new Transaction(Economable.wrap(player), Economable.PLUGIN, price, TransactionReason.PLUGIN_TAKE)); plugin.getLimitManager().setRemainingLimit(player, ShopTransaction.TransactionDirection.BUY, shop.getItem(), plugin.getLimitManager().getRemainingLimit(player, ShopTransaction.TransactionDirection.BUY, shop.getItem()) - quantity);
Transaction ecoTransaction = shopTransaction.makeEconomyTransaction();
TransactionResult result = ecoMan.transact(ecoTransaction);
if (result.getStatus() != TransactionResult.Status.SUCCESS) { if (result.getStatus() != TransactionResult.Status.SUCCESS) {
MessageUtils.sendMessage(player, "An error occurred attempting to perform that transaction: {1}", result.getStatus()); MessageUtils.sendMessage(player, "An error occurred attempting to perform that transaction: {1}", result.getStatus());
return; return;
} }
ItemStack stack = shop.getItem().clone(); ItemStack stack = shop.getItemStack().clone();
stack.setAmount(quantity); stack.setAmount(quantity);
player.getInventory().addItem(stack); player.getInventory().addItem(stack);
MessageUtils.sendMessage(player, "You have bought {1} {2} for {3}.", quantity, shop.getItem(), ecoMan.getCurrency().formatAmount(price));
LOGGER.info(String.format("%s just bought %s for %s.", player.getName(), shop.getItem(), ecoMan.getCurrency().formatAmount(price))); MessageUtils.sendMessage(player, "You have bought {1} {2} for {3}.", quantity, shop.getItemStack().getType().name(), ecoMan.getCurrency().formatAmount(shopTransaction.getPrice()));
LOGGER.info(String.format("%s just bought %s for %s.", player.getName(), shop.getItemStack(), ecoMan.getCurrency().formatAmount(shopTransaction.getPrice())));
} }
private void doSell(SignShop shop, Player player) { // TODO: Selling enchanted items private void doSell(SignShop shop, Player player) { // TODO: Selling enchanted items
@ -111,17 +114,29 @@ public class InteractListener implements Listener {
int quantity = player.isSneaking() ? 1 : shop.getQuantity(); int quantity = player.isSneaking() ? 1 : shop.getQuantity();
double price = shop.getSellPrice(quantity); double price = shop.getSellPrice(quantity);
if (!player.getInventory().containsAtLeast(new ItemStack(shop.getItem()), quantity)) { if (!player.getInventory().containsAtLeast(new ItemStack(shop.getItemStack()), quantity)) {
MessageUtils.sendMessage(player, "You do not have {1} {2}!", quantity, shop.getItem()); MessageUtils.sendMessage(player, "You do not have {1} {2}!", quantity, shop.getItemStack().getType().name());
return; return;
} }
ItemStack stack = shop.getItem().clone(); ShopTransaction shopTransaction = shop.makeTransaction(player, ShopTransaction.TransactionDirection.SELL, quantity);
stack.setAmount(quantity);
if (!plugin.getLimitManager().shouldAllowTransaction(shopTransaction)) {
MessageUtils.sendMessage(player, "You have reached your selling limit for the time being. Try back in an hour or so.");
return;
}
plugin.getLimitManager().setRemainingLimit(player, ShopTransaction.TransactionDirection.SELL, shop.getItem(), plugin.getLimitManager().getRemainingLimit(player, ShopTransaction.TransactionDirection.BUY, shop.getItem()) - quantity);
ItemStack stack = shop.getItemStack().clone();
stack.setAmount(quantity);
player.getInventory().removeItem(stack); // FIXME: This does not remove items with damage values that were detected by contains() player.getInventory().removeItem(stack); // FIXME: This does not remove items with damage values that were detected by contains()
ecoMan.transact(new Transaction(Economable.PLUGIN, Economable.wrap(player), price, TransactionReason.PLUGIN_GIVE));
MessageUtils.sendMessage(player, "You have sold {1} {2} for {3}.", quantity, shop.getItem(), ecoMan.getCurrency().formatAmount(price)); ecoMan.transact(shopTransaction.makeEconomyTransaction());
LOGGER.info(String.format("%s just sold %s for %s.", player.getName(), shop.getItem(), ecoMan.getCurrency().formatAmount(price)));
MessageUtils.sendMessage(player, "You have sold {1} {2} for {3}.", quantity, shop.getItemStack().getType().name(), ecoMan.getCurrency().formatAmount(price));
LOGGER.info(String.format("%s just sold %s for %s.", player.getName(), shop.getItemStack(), ecoMan.getCurrency().formatAmount(price)));
} }
} }

View File

@ -50,7 +50,7 @@ public class SignChangeListener implements Listener {
plugin.getSignShopManager().addSignShop(signShop); plugin.getSignShopManager().addSignShop(signShop);
evt.setLine(0, ChatColor.translateAlternateColorCodes('&', plugin.getConfig().getString("admin-shop-title"))); evt.setLine(0, ChatColor.translateAlternateColorCodes('&', plugin.getConfig().getString("admin-shop-title")));
MessageUtils.sendMessage(evt.getPlayer(), "Sign shop created!"); MessageUtils.sendMessage(evt.getPlayer(), "Sign shop created!");
MessageUtils.sendMessage(evt.getPlayer(), "Item: {1} x {2}", signShop.getQuantity(), signShop.getItem()); MessageUtils.sendMessage(evt.getPlayer(), "Item: {1} x {2}", signShop.getQuantity(), signShop.getItemStack());
if (signShop.canBuy()) { // The player be buying from the shop, not the other way around. if (signShop.canBuy()) { // The player be buying from the shop, not the other way around.
MessageUtils.sendMessage(evt.getPlayer(), "Will sell to players for {!}.", MessageUtils.sendMessage(evt.getPlayer(), "Will sell to players for {!}.",

View File

@ -0,0 +1,60 @@
package org.appledash.saneeconomysignshop.signshop;
import org.appledash.saneeconomy.economy.economable.Economable;
import org.appledash.saneeconomy.economy.transaction.Transaction;
import org.appledash.saneeconomy.economy.transaction.TransactionReason;
import org.appledash.saneeconomysignshop.util.ItemInfo;
import org.bukkit.entity.Player;
/**
* Created by appledash on 1/1/17.
* Blackjack is still best pony.
*/
public class ShopTransaction {
// Direction is always what the player is doing. BUY = player is buying from shop.
private final TransactionDirection direction;
private final Player player;
private final ItemInfo item;
private final int quantity;
private final double price;
public ShopTransaction(TransactionDirection direction, Player player, ItemInfo item, int quantity, double price) {
this.direction = direction;
this.player = player;
this.item = item;
this.quantity = quantity;
this.price = price;
}
public TransactionDirection getDirection() {
return direction;
}
public Player getPlayer() {
return player;
}
public ItemInfo getItem() {
return item;
}
public int getQuantity() {
return quantity;
}
public double getPrice() {
return price;
}
public Transaction makeEconomyTransaction() {
if (direction == TransactionDirection.BUY) {
return new Transaction(Economable.wrap(player), Economable.PLUGIN, price, TransactionReason.PLUGIN_TAKE);
} else {
return new Transaction(Economable.PLUGIN, Economable.wrap(player), price, TransactionReason.PLUGIN_GIVE);
}
}
public enum TransactionDirection {
BUY, SELL
}
}

View File

@ -1,8 +1,10 @@
package org.appledash.saneeconomysignshop.signshop; package org.appledash.saneeconomysignshop.signshop;
import org.appledash.saneeconomysignshop.signshop.ShopTransaction.TransactionDirection;
import org.appledash.saneeconomysignshop.util.ItemInfo; import org.appledash.saneeconomysignshop.util.ItemInfo;
import org.appledash.saneeconomysignshop.util.SerializableLocation; import org.appledash.saneeconomysignshop.util.SerializableLocation;
import org.bukkit.Location; import org.bukkit.Location;
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.ItemStack;
import java.io.Serializable; import java.io.Serializable;
@ -49,10 +51,18 @@ public class SignShop implements Serializable {
* Get the type of item this SignShop is selling * Get the type of item this SignShop is selling
* @return Material representing item/block type * @return Material representing item/block type
*/ */
public ItemStack getItem() { public ItemStack getItemStack() {
return item.toItemStack(); return item.toItemStack();
} }
/**
* Get the ItemInfo for the item this SignShop is selling
* @return ItemInfo representing the type and quantity of item
*/
public ItemInfo getItem() {
return item;
}
/** /**
* Get the price that the player can buy this item from the server for * Get the price that the player can buy this item from the server for
* @return Buy price for this.getQuantity() items * @return Buy price for this.getQuantity() items
@ -120,4 +130,8 @@ public class SignShop implements Serializable {
public int getQuantity() { public int getQuantity() {
return quantity; return quantity;
} }
public ShopTransaction makeTransaction(Player player, TransactionDirection direction, int quantity) {
return new ShopTransaction(direction, player, item, quantity, (direction == TransactionDirection.BUY) ? getBuyPrice(quantity) : getSellPrice(quantity));
}
} }

View File

@ -0,0 +1,32 @@
package org.appledash.saneeconomysignshop.util;
import java.util.HashMap;
import java.util.function.Supplier;
/**
* Created by appledash on 1/1/17.
* Blackjack is still best pony.
*/
public class DefaultHashMap<K, V> extends HashMap<K, V> {
private final Supplier<V> defaultSupplier;
public DefaultHashMap(Supplier<V> defaultSupplier) {
if (defaultSupplier == null) {
throw new NullPointerException("defaultSupplier is null");
}
this.defaultSupplier = defaultSupplier;
}
@Override
public V get(Object k) {
V v = super.get(k);
if (v == null) {
v = defaultSupplier.get();
this.put((K) k, v);
}
return v;
}
}

View File

@ -4,27 +4,48 @@ import org.bukkit.Material;
import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.ItemStack;
import java.io.Serializable; import java.io.Serializable;
import java.util.Objects;
/** /**
* Created by appledash on 11/3/16. * Created by appledash on 11/3/16.
* Blackjack is still best pony. * Blackjack is still best pony.
*/ */
public class ItemInfo implements Serializable { public class ItemInfo implements Serializable {
private Material id; private final Material material;
private short damage; private final short damage;
private int amount; private final int amount;
public ItemInfo(ItemStack stack) { public ItemInfo(ItemStack stack) {
this(stack.getType(), stack.getDurability(), stack.getAmount()); this(stack.getType(), stack.getDurability(), stack.getAmount());
} }
public ItemInfo(Material id, short damage, int amount) { public ItemInfo(Material material, short damage, int amount) {
this.id = id; this.material = material;
this.damage = damage; this.damage = damage;
this.amount = amount; this.amount = amount;
} }
public ItemStack toItemStack() { public ItemStack toItemStack() {
return new ItemStack(id, amount, damage); return new ItemStack(material, amount, damage);
}
public Material getMaterial() {
return material;
}
@Override
public boolean equals(Object o) {
if (!(o instanceof ItemInfo)) {
return false;
}
ItemInfo other = ((ItemInfo) o);
return (other.material == this.material) && (other.damage == this.damage);
}
@Override
public int hashCode() {
return Objects.hash(material, damage);
} }
} }

View File

@ -0,0 +1,26 @@
package org.appledash.saneeconomysignshop.util;
/**
* Created by appledash on 1/1/17.
* Blackjack is still best pony.
*/
public class ItemLimits {
// The default limit for items that have no limit.
public static final ItemLimits DEFAULT = new ItemLimits(10, 1);
private final int limit;
private final int hourlyGain;
public ItemLimits(int limit, int hourlyGain) {
this.limit = limit;
this.hourlyGain = hourlyGain;
}
public int getHourlyGain() {
return hourlyGain;
}
public int getLimit() {
return limit;
}
}

View File

@ -0,0 +1,61 @@
package org.appledash.saneeconomysignshop.util;
import org.appledash.saneeconomysignshop.signshop.ShopTransaction;
import org.appledash.saneeconomysignshop.signshop.ShopTransaction.TransactionDirection;
import org.bukkit.entity.Player;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
/**
* Created by appledash on 1/1/17.
* Blackjack is still best pony.
*/
public class LimitManager {
private final Map<TransactionDirection, Map<ItemInfo, ItemLimits>> itemLimits = new DefaultHashMap<>(() -> new DefaultHashMap<>(() -> ItemLimits.DEFAULT));
// This is a slightly complex data structure. It works like this:
// It's a map of (limit types to (maps of players to (maps of materials to the remaning limit))).
// All the TransactionDirections defaults to an empty map, which defaults to an empty map, which defaults to 0.
@SuppressWarnings("MismatchedQueryAndUpdateOfCollection")
private final Map<TransactionDirection, Map<UUID, Map<ItemInfo, Integer>>> playerLimits = new DefaultHashMap<>(() -> new DefaultHashMap<>(() -> new DefaultHashMap<>(() -> 0)));
public int getRemainingLimit(Player player, TransactionDirection type, ItemInfo stack) {
return playerLimits.get(type).get(player.getUniqueId()).get(stack);
}
public void setRemainingLimit(Player player, TransactionDirection type, ItemInfo stack, int limit) {
if (playerLimits.get(type).get(player.getUniqueId()).get(stack) == -1) {
return;
}
limit = Math.min(limit, itemLimits.get(type).get(stack).getLimit());
limit = Math.max(0, limit);
playerLimits.get(type).get(player.getUniqueId()).put(stack, limit);
}
public boolean shouldAllowTransaction(ShopTransaction transaction) {
return getRemainingLimit(transaction.getPlayer(), transaction.getDirection(), transaction.getItem()) >= transaction.getQuantity();
}
public void incrementLimitsHourly() {
// For every limit type
// For every player
// For every limit
// Increment limit by the limit for the specific direction and item.
playerLimits.forEach((transactionDirection, playerToLimit) -> {
playerToLimit.forEach((playerUuid, itemToLimit) -> {
Map<ItemInfo, Integer> newLimits = new HashMap<>();
itemToLimit.forEach((itemInfo, currentLimit) -> {
newLimits.put(itemInfo, currentLimit + (itemLimits.get(transactionDirection).get(itemInfo).getHourlyGain()));
});
itemToLimit.putAll(newLimits);
});
});
}
}