Implement /shopinfo command/chest click for shop info (Resolves #203)

The shop information can include the owner, available stock, the buy/
 sell prices and detailed item information from the ItemInfoEvent.
This also includes an item component in both /iteminfo and /shopinfo if
 ShowItem is installed.
This commit is contained in:
Phoenix616 2021-02-15 17:28:33 +01:00
parent 2f0e7b81a8
commit ebdebb81df
No known key found for this signature in database
GPG Key ID: 40E2321E71738EB0
12 changed files with 291 additions and 14 deletions

View File

@ -3,6 +3,7 @@ package com.Acrobot.ChestShop;
import com.Acrobot.Breeze.Configuration.Configuration;
import com.Acrobot.ChestShop.Commands.Give;
import com.Acrobot.ChestShop.Commands.ItemInfo;
import com.Acrobot.ChestShop.Commands.ShopInfo;
import com.Acrobot.ChestShop.Commands.Toggle;
import com.Acrobot.ChestShop.Commands.Version;
import com.Acrobot.ChestShop.Commands.AccessToggle;
@ -23,6 +24,7 @@ import com.Acrobot.ChestShop.Listeners.ItemInfoListener;
import com.Acrobot.ChestShop.Listeners.Modules.ItemAliasModule;
import com.Acrobot.ChestShop.Listeners.Modules.MetricsModule;
import com.Acrobot.ChestShop.Listeners.Modules.StockCounterModule;
import com.Acrobot.ChestShop.Listeners.ShopInfoListener;
import com.Acrobot.ChestShop.Listeners.SignParseListener;
import com.Acrobot.ChestShop.Listeners.Modules.DiscountModule;
import com.Acrobot.ChestShop.Listeners.Modules.PriceRestrictionModule;
@ -130,6 +132,7 @@ public class ChestShop extends JavaPlugin {
}
registerCommand("iteminfo", new ItemInfo(), Permission.ITEMINFO);
registerCommand("shopinfo", new ShopInfo(), Permission.SHOPINFO);
registerCommand("csVersion", new Version(), Permission.ADMIN);
registerCommand("csMetrics", new com.Acrobot.ChestShop.Commands.Metrics(), Permission.ADMIN);
registerCommand("csGive", new Give(), Permission.ADMIN);
@ -333,6 +336,7 @@ public class ChestShop extends JavaPlugin {
registerEvent(new SignParseListener());
registerEvent(new ItemStringListener());
registerEvent(new ItemInfoListener());
registerEvent(new ShopInfoListener());
registerEvent(new GarbageTextListener());
Plugin authMe = getServer().getPluginManager().getPlugin("AuthMe");

View File

@ -3,21 +3,25 @@ package com.Acrobot.ChestShop.Commands;
import com.Acrobot.Breeze.Utils.MaterialUtil;
import com.Acrobot.Breeze.Utils.StringUtil;
import com.Acrobot.ChestShop.ChestShop;
import com.Acrobot.ChestShop.Configuration.Messages;
import com.Acrobot.ChestShop.Configuration.Properties;
import com.Acrobot.ChestShop.Events.ItemInfoEvent;
import com.Acrobot.ChestShop.Events.ItemParseEvent;
import com.Acrobot.ChestShop.Utils.ItemUtil;
import com.google.common.collect.ImmutableMap;
import org.bukkit.Bukkit;
import org.bukkit.ChatColor;
import org.bukkit.command.Command;
import org.bukkit.command.CommandExecutor;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.HumanEntity;
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;
import java.util.Map;
import java.util.logging.Level;
import static com.Acrobot.ChestShop.Configuration.Messages.iteminfo;
import static com.Acrobot.ChestShop.Configuration.Messages.iteminfo_fullname;
import static com.Acrobot.ChestShop.Configuration.Messages.iteminfo_shopname;
/**
@ -45,7 +49,11 @@ public class ItemInfo implements CommandExecutor {
iteminfo.send(sender);
try {
iteminfo_fullname.send(sender, "item", ItemUtil.getName(item));
Map<String, String> replacementMap = ImmutableMap.of("item", ItemUtil.getName(item));
if (!Properties.SHOWITEM_MESSAGE || !(sender instanceof Player)
|| !MaterialUtil.Show.sendMessage((Player) sender, Messages.iteminfo_fullname, new ItemStack[]{item}, replacementMap)) {
Messages.iteminfo_fullname.send(sender, replacementMap);
}
} catch (IllegalArgumentException e) {
sender.sendMessage(ChatColor.RED + "Error while generating full name. Please contact an admin or take a look at the console/log!");
ChestShop.getPlugin().getLogger().log(Level.SEVERE, "Error while generating full item name", e);

View File

@ -0,0 +1,43 @@
package com.Acrobot.ChestShop.Commands;
import com.Acrobot.ChestShop.ChestShop;
import com.Acrobot.ChestShop.Configuration.Messages;
import com.Acrobot.ChestShop.Events.ShopInfoEvent;
import com.Acrobot.ChestShop.Signs.ChestShopSign;
import com.Acrobot.ChestShop.Utils.uBlock;
import org.bukkit.ChatColor;
import org.bukkit.block.Block;
import org.bukkit.block.Sign;
import org.bukkit.command.Command;
import org.bukkit.command.CommandExecutor;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
/**
* @author Phoenix616
*/
public class ShopInfo implements CommandExecutor {
public boolean onCommand(CommandSender sender, Command cmd, String label, String[] args) {
if (sender instanceof Player) {
Block target = ((Player) sender).getTargetBlock(5);
if (target != null) {
Sign sign = null;
if (ChestShopSign.isValid(target)) {
sign = (Sign) target.getState();
} else if (uBlock.couldBeShopContainer(target)) {
sign = uBlock.getConnectedSign(target);
}
if (sign != null) {
ShopInfoEvent event = new ShopInfoEvent((Player) sender, sign);
ChestShop.callEvent(event);
} else {
Messages.NO_SHOP_FOUND.sendWithPrefix(sender);
}
}
} else {
sender.sendMessage(ChatColor.RED + "Command must be run by a player!");
}
return true;
}
}

View File

@ -23,6 +23,11 @@ import java.util.logging.Level;
*/
public class Messages {
public static Message prefix;
public static Message shopinfo;
public static Message shopinfo_buy;
public static Message shopinfo_sell;
public static Message iteminfo;
public static Message iteminfo_fullname;
public static Message iteminfo_shopname;
@ -91,6 +96,7 @@ public class Messages {
public static Message INCORRECT_ITEM_ID;
public static Message INVALID_CLIENT_NAME;
public static Message NOT_ENOUGH_PROTECTIONS;
public static Message NO_SHOP_FOUND;
public static Message CANNOT_CREATE_SHOP_HERE;
@ -172,6 +178,10 @@ public class Messages {
ChestShop.getAudiences().sender(sender).sendMessage(getComponent(sender, false, Collections.emptyMap(), replacements));
}
public void send(CommandSender sender, Map<String, String> replacements) {
ChestShop.getAudiences().sender(sender).sendMessage(getComponent(sender, false, replacements));
}
@Deprecated
public BaseComponent[] getComponents(CommandSender sender, boolean prefixSuffix, Map<String, String> replacementMap, String... replacements) {
return BungeeComponentSerializer.get().serialize(getComponent(sender, prefixSuffix, replacementMap, replacements));

View File

@ -0,0 +1,58 @@
package com.Acrobot.ChestShop.Events;
import org.bukkit.block.Sign;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import org.bukkit.event.Cancellable;
import org.bukkit.event.Event;
import org.bukkit.event.HandlerList;
/**
* Represents a /shopinfo call or middle click on a sign
*
* @author Phoenix616
*/
public class ShopInfoEvent extends Event implements Cancellable {
private static final HandlerList handlers = new HandlerList();
private Player sender;
private Sign sign;
private boolean cancelled = false;
public ShopInfoEvent(Player sender, Sign sign) {
this.sender = sender;
this.sign = sign;
}
/**
* @return The Player who initiated the call
*/
public Player getSender() {
return sender;
}
/**
* @return The shop sign
*/
public Sign getSign() {
return sign;
}
public HandlerList getHandlers() {
return handlers;
}
public static HandlerList getHandlerList() {
return handlers;
}
@Override
public boolean isCancelled() {
return cancelled;
}
@Override
public void setCancelled(boolean cancel) {
cancelled = cancel;
}
}

View File

@ -1,7 +1,9 @@
package com.Acrobot.ChestShop.Listeners;
import com.Acrobot.Breeze.Utils.StringUtil;
import com.Acrobot.ChestShop.ChestShop;
import com.Acrobot.ChestShop.Events.ItemInfoEvent;
import com.Acrobot.ChestShop.Utils.ItemUtil;
import org.bukkit.ChatColor;
import org.bukkit.Material;
import org.bukkit.command.CommandSender;
@ -17,14 +19,18 @@ import org.bukkit.potion.Potion;
import org.bukkit.potion.PotionEffect;
import java.util.Map;
import java.util.logging.Level;
import static com.Acrobot.Breeze.Utils.NumberUtil.toRoman;
import static com.Acrobot.Breeze.Utils.NumberUtil.toTime;
import static com.Acrobot.Breeze.Utils.StringUtil.capitalizeFirstLetter;
import static com.Acrobot.ChestShop.Configuration.Messages.iteminfo;
import static com.Acrobot.ChestShop.Configuration.Messages.iteminfo_book;
import static com.Acrobot.ChestShop.Configuration.Messages.iteminfo_book_generation;
import static com.Acrobot.ChestShop.Configuration.Messages.iteminfo_fullname;
import static com.Acrobot.ChestShop.Configuration.Messages.iteminfo_lore;
import static com.Acrobot.ChestShop.Configuration.Messages.iteminfo_repaircost;
import static com.Acrobot.ChestShop.Configuration.Messages.iteminfo_shopname;
/**
* @author Acrobot

View File

@ -1,6 +1,7 @@
package com.Acrobot.ChestShop.Listeners.Player;
import com.Acrobot.Breeze.Utils.*;
import com.Acrobot.ChestShop.ChestShop;
import com.Acrobot.ChestShop.Commands.AccessToggle;
import com.Acrobot.ChestShop.Configuration.Messages;
import com.Acrobot.ChestShop.Configuration.Properties;
@ -10,6 +11,7 @@ import com.Acrobot.ChestShop.Events.AccountQueryEvent;
import com.Acrobot.ChestShop.Events.Economy.AccountCheckEvent;
import com.Acrobot.ChestShop.Events.ItemParseEvent;
import com.Acrobot.ChestShop.Events.PreTransactionEvent;
import com.Acrobot.ChestShop.Events.ShopInfoEvent;
import com.Acrobot.ChestShop.Events.TransactionEvent;
import com.Acrobot.ChestShop.Permission;
import com.Acrobot.ChestShop.Security;
@ -61,17 +63,24 @@ public class PlayerInteract implements Listener {
Action action = event.getAction();
Player player = event.getPlayer();
if (Properties.USE_BUILT_IN_PROTECTION && isShopBlock(block)) {
if (Properties.TURN_OFF_DEFAULT_PROTECTION_WHEN_PROTECTED_EXTERNALLY) {
if (Properties.USE_BUILT_IN_PROTECTION && uBlock.couldBeShopContainer(block)) {
Sign sign = uBlock.getConnectedSign(block);
if (sign != null) {
if (Properties.TURN_OFF_DEFAULT_PROTECTION_WHEN_PROTECTED_EXTERNALLY) {
return;
}
if (!Security.canAccess(player, block)) {
event.setCancelled(true);
if (Permission.has(player, Permission.SHOPINFO)) {
ChestShop.callEvent(new ShopInfoEvent(player, sign));
} else {
Messages.ACCESS_DENIED.send(player);
}
}
return;
}
if (!Security.canAccess(player, block)) {
event.setCancelled(true);
Messages.ACCESS_DENIED.sendWithPrefix(player);
}
return;
}
if (!isSign(block) || player.getInventory().getItemInMainHand().getType().name().contains("SIGN")) // Blocking accidental sign edition

View File

@ -1,12 +1,17 @@
package com.Acrobot.ChestShop.Listeners.Player;
import com.Acrobot.ChestShop.ChestShop;
import com.Acrobot.ChestShop.Configuration.Messages;
import com.Acrobot.ChestShop.Configuration.Properties;
import com.Acrobot.ChestShop.Events.ShopInfoEvent;
import com.Acrobot.ChestShop.Permission;
import com.Acrobot.ChestShop.Security;
import com.Acrobot.ChestShop.Signs.ChestShopSign;
import com.Acrobot.ChestShop.Utils.uBlock;
import org.bukkit.block.Block;
import org.bukkit.block.BlockState;
import org.bukkit.block.DoubleChest;
import org.bukkit.block.Sign;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
@ -59,7 +64,16 @@ public class PlayerInventory implements Listener {
}
if (!canAccess) {
Messages.ACCESS_DENIED.sendWithPrefix(player);
if (Permission.has(player, Permission.SHOPINFO)) {
for (Block container : containers) {
Sign sign = uBlock.getConnectedSign(container);
if (sign != null) {
ChestShop.callEvent(new ShopInfoEvent((Player) event.getPlayer(), sign));
}
}
} else {
Messages.ACCESS_DENIED.sendWithPrefix(event.getPlayer());
}
event.setCancelled(true);
}
}

View File

@ -0,0 +1,108 @@
package com.Acrobot.ChestShop.Listeners;
import com.Acrobot.Breeze.Utils.InventoryUtil;
import com.Acrobot.Breeze.Utils.MaterialUtil;
import com.Acrobot.Breeze.Utils.PriceUtil;
import com.Acrobot.Breeze.Utils.QuantityUtil;
import com.Acrobot.ChestShop.ChestShop;
import com.Acrobot.ChestShop.Configuration.Messages;
import com.Acrobot.ChestShop.Configuration.Properties;
import com.Acrobot.ChestShop.Economy.Economy;
import com.Acrobot.ChestShop.Events.AccountQueryEvent;
import com.Acrobot.ChestShop.Events.Economy.CurrencyFormatEvent;
import com.Acrobot.ChestShop.Events.ItemInfoEvent;
import com.Acrobot.ChestShop.Events.ItemParseEvent;
import com.Acrobot.ChestShop.Events.ShopInfoEvent;
import com.Acrobot.ChestShop.Signs.ChestShopSign;
import com.Acrobot.ChestShop.Utils.ItemUtil;
import com.Acrobot.ChestShop.Utils.uBlock;
import com.google.common.collect.ImmutableMap;
import org.bukkit.block.Container;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.inventory.ItemStack;
import java.math.BigDecimal;
import java.util.LinkedHashMap;
import java.util.Map;
/**
* @author Acrobot
*/
public class ShopInfoListener implements Listener {
@EventHandler(ignoreCancelled = true)
public static void showShopInfo(ShopInfoEvent event) {
if (ChestShopSign.isValid(event.getSign())) {
String nameLine = event.getSign().getLine(ChestShopSign.NAME_LINE);
int amount;
try {
amount = QuantityUtil.parseQuantity(event.getSign().getLine(ChestShopSign.QUANTITY_LINE));
} catch (NumberFormatException notANumber) {
Messages.INVALID_SHOP_DETECTED.sendWithPrefix(event.getSender());
return;
}
String pricesLine = event.getSign().getLine(ChestShopSign.PRICE_LINE);
AccountQueryEvent queryEvent = new AccountQueryEvent(nameLine);
ChestShop.callEvent(queryEvent);
if (queryEvent.getAccount() == null) {
Messages.INVALID_SHOP_DETECTED.sendWithPrefix(event.getSender());
return;
}
String ownerName = queryEvent.getAccount().getName();
ownerName = ownerName != null ? ownerName : nameLine;
ItemParseEvent parseEvent = new ItemParseEvent(event.getSign().getLine(ChestShopSign.ITEM_LINE));
ItemStack item = ChestShop.callEvent(parseEvent).getItem();
if (item == null || amount < 1) {
Messages.INVALID_SHOP_DETECTED.sendWithPrefix(event.getSender());
return;
}
Container shopBlock = uBlock.findConnectedContainer(event.getSign());
String stock;
if (shopBlock != null) {
stock = String.valueOf(InventoryUtil.getAmount(item, shopBlock.getInventory()));
} else {
stock = "\u221e"; // Infinity symbol
}
Map<String, String> replacementMap = ImmutableMap.of(
"item", ItemUtil.getName(item),
"stock", stock,
"owner", ownerName,
"prices", pricesLine,
"quantity", String.valueOf(amount)
);
if (!Properties.SHOWITEM_MESSAGE
|| !MaterialUtil.Show.sendMessage(event.getSender(), Messages.shopinfo, new ItemStack[]{item}, replacementMap)) {
Messages.shopinfo.send(event.getSender(), replacementMap);
}
BigDecimal buyPrice = PriceUtil.getExactBuyPrice(pricesLine);
BigDecimal sellPrice = PriceUtil.getExactSellPrice(pricesLine);
ChestShop.callEvent(new ItemInfoEvent(event.getSender(), item));
if (!buyPrice.equals(PriceUtil.NO_PRICE)) {
CurrencyFormatEvent cfe = ChestShop.callEvent(new CurrencyFormatEvent(buyPrice));
Messages.shopinfo_buy.send(event.getSender(),
"amount", String.valueOf(amount),
"price", cfe.getFormattedAmount()
);
}
if (!sellPrice.equals(PriceUtil.NO_PRICE)) {
CurrencyFormatEvent cfe = ChestShop.callEvent(new CurrencyFormatEvent(sellPrice));
Messages.shopinfo_sell.send(event.getSender(),
"amount", String.valueOf(amount),
"price", cfe.getFormattedAmount()
);
}
} else {
Messages.INVALID_SHOP_DETECTED.sendWithPrefix(event.getSender());
}
}
}

View File

@ -42,7 +42,8 @@ public enum Permission {
NOTIFY_TOGGLE("ChestShop.toggle"),
ACCESS_TOGGLE("ChestShop.accesstoggle"),
ITEMINFO("ChestShop.iteminfo");
ITEMINFO("ChestShop.iteminfo"),
SHOPINFO("ChestShop.shopinfo");
private final String permission;

View File

@ -1,4 +1,13 @@
prefix: "&a[Shop] &r"
shopinfo: |-
&aShop Information:
&fOwner: &7%owner
&fStock: &7%stock
&fItem: &7%item
shopinfo_buy: "&fBuy &7%amount &ffor &7%price"
shopinfo_sell: "&fSell &7%amount &ffor &7%price"
iteminfo: "&aItem Information: &r"
iteminfo_fullname: "&fFull Name: &7%item"
iteminfo_shopname: "&fShop Sign: &7%item"
@ -77,6 +86,7 @@ NO_PERMISSION: "You don't have permissions to do that!"
INCORRECT_ITEM_ID: "You have specified an invalid item id!"
INVALID_CLIENT_NAME: "Your username is not a valid Minecraft username!"
NOT_ENOUGH_PROTECTIONS: "Could not create a protection!"
NO_SHOP_FOUND: "No shop found."
CANNOT_CREATE_SHOP_HERE: "You can't create shop here!"

View File

@ -14,7 +14,10 @@ commands:
usage: |
/<command> §2(what's the item in hand?)
/<command> §7log§f §2(what's the item ID of §7LOG§2?)
shopinfo:
aliases: [sinfo,shop]
description: Shows information about the shop looked at
usage: /<command> §2(information about the looked at shop)
csGive:
description: Gives an item to the appropriate player
usage: /<command> <item code> (amount) (player)
@ -84,6 +87,9 @@ permissions:
ChestShop.iteminfo:
description: Allows user to see item info with the command.
default: true
ChestShop.shopinfo:
description: Allows user to see shop info with the command or middle click on shop sign/container.
default: true
ChestShop.admin:
description: Allows user to modify/destroy other stores and create an Admin Shops
default: op