mirror of
https://e.coding.net/circlecloud/QuickShop.git
synced 2024-11-24 02:18:54 +00:00
Version 0.9
Compatible with Bukkit 1.8.7 Improved items name display, especially for very long names.
This commit is contained in:
commit
3755036137
19
README.md
Normal file
19
README.md
Normal file
@ -0,0 +1,19 @@
|
||||
#QuickShop
|
||||
QuickShop is a shop plugin, that allows players to sell items from a chest with no commands. It allows players to purchase any number of items easily. In fact, this plugin doesn't even have any commands that a player would ever need!
|
||||
|
||||
##Support
|
||||
Open a new issue here: https://github.com/KaiKikuchi/QuickShop/issues
|
||||
|
||||
##Features
|
||||
- Easy to use
|
||||
- Togglable Display Item on top of chest
|
||||
- NBT Data, Enchants, Tool Damage, Potion and Mob Egg support
|
||||
- Unlimited chest support
|
||||
- Blacklist support & bypass permissions
|
||||
- Shops that buy items and sell items at the same time are possible (Using double chests)
|
||||
- Herochat support
|
||||
- Checks a player can open a chest before letting them create a shop!
|
||||
- UUID support
|
||||
|
||||
##Bukkit page
|
||||
You can find the project page here: http://dev.bukkit.org/bukkit-plugins/quickshop-notlikeme/
|
56
pom.xml
Normal file
56
pom.xml
Normal file
@ -0,0 +1,56 @@
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<groupId>net.kaikk.mc</groupId>
|
||||
<artifactId>${name}</artifactId>
|
||||
<version>${version}</version>
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<configuration>
|
||||
<source>1.7</source>
|
||||
<target>1.7</target>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
<resources>
|
||||
<resource>
|
||||
<directory>src/main/resources</directory>
|
||||
<filtering>true</filtering>
|
||||
</resource>
|
||||
</resources>
|
||||
</build>
|
||||
<repositories>
|
||||
<repository>
|
||||
<id>bukkit-repo</id>
|
||||
<!-- <url>http://repo.bukkit.org/content/groups/public/</url> -->
|
||||
<url>https://hub.spigotmc.org/nexus/content/groups/public/</url>
|
||||
</repository>
|
||||
<repository>
|
||||
<id>sk89q-repo</id>
|
||||
<url>http://maven.sk89q.com/repo/</url>
|
||||
</repository>
|
||||
</repositories>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.bukkit</groupId>
|
||||
<artifactId>bukkit</artifactId>
|
||||
<!-- <version>1.6.4-R2.0</version> -->
|
||||
<!-- <version>1.7.9-R0.2</version> -->
|
||||
<version>1.8-R0.1-SNAPSHOT</version>
|
||||
<type>jar</type>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.sk89q</groupId>
|
||||
<artifactId>worldguard</artifactId>
|
||||
<version>6.0.0-SNAPSHOT</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
<properties>
|
||||
<version>0.9</version>
|
||||
<package>org.maxgamer.quickshop</package>
|
||||
<name>QuickShop</name>
|
||||
</properties>
|
||||
</project>
|
573
src/main/java/org/maxgamer/quickshop/Command/QS.java
Normal file
573
src/main/java/org/maxgamer/quickshop/Command/QS.java
Normal file
@ -0,0 +1,573 @@
|
||||
package org.maxgamer.quickshop.Command;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.ChatColor;
|
||||
import org.bukkit.Chunk;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.OfflinePlayer;
|
||||
import org.bukkit.block.Block;
|
||||
import org.bukkit.command.Command;
|
||||
import org.bukkit.command.CommandExecutor;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.configuration.ConfigurationSection;
|
||||
import org.bukkit.entity.LivingEntity;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.player.PlayerTeleportEvent.TeleportCause;
|
||||
import org.bukkit.util.BlockIterator;
|
||||
import org.maxgamer.quickshop.QuickShop;
|
||||
import org.maxgamer.quickshop.Database.Database;
|
||||
import org.maxgamer.quickshop.Database.MySQLCore;
|
||||
import org.maxgamer.quickshop.Database.SQLiteCore;
|
||||
import org.maxgamer.quickshop.Shop.ContainerShop;
|
||||
import org.maxgamer.quickshop.Shop.Shop;
|
||||
import org.maxgamer.quickshop.Shop.ShopType;
|
||||
import org.maxgamer.quickshop.Shop.ShopChunk;
|
||||
import org.maxgamer.quickshop.Util.MsgUtil;
|
||||
|
||||
public class QS implements CommandExecutor {
|
||||
QuickShop plugin;
|
||||
|
||||
public QS(QuickShop plugin) {
|
||||
this.plugin = plugin;
|
||||
}
|
||||
|
||||
private void setUnlimited(CommandSender sender) {
|
||||
if (sender instanceof Player && sender.hasPermission("quickshop.unlimited")) {
|
||||
BlockIterator bIt = new BlockIterator((Player) sender, 10);
|
||||
while (bIt.hasNext()) {
|
||||
Block b = bIt.next();
|
||||
Shop shop = plugin.getShopManager().getShop(b.getLocation());
|
||||
if (shop != null) {
|
||||
shop.setUnlimited(!shop.isUnlimited());
|
||||
shop.update();
|
||||
sender.sendMessage(MsgUtil.getMessage("command.toggle-unlimited", (shop.isUnlimited() ? "unlimited" : "limited")));
|
||||
return;
|
||||
}
|
||||
}
|
||||
sender.sendMessage(MsgUtil.getMessage("not-looking-at-shop"));
|
||||
return;
|
||||
} else {
|
||||
sender.sendMessage(MsgUtil.getMessage("no-permission"));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
private void remove(CommandSender sender, String[] args) {
|
||||
if (sender instanceof Player == false) {
|
||||
sender.sendMessage(ChatColor.RED + "Only players may use that command.");
|
||||
return;
|
||||
}
|
||||
if (!sender.hasPermission("quickshop.delete")) {
|
||||
sender.sendMessage(ChatColor.RED + "You do not have permission to use that command. Try break the shop instead?");
|
||||
return;
|
||||
}
|
||||
Player p = (Player) sender;
|
||||
BlockIterator bIt = new BlockIterator(p, 10);
|
||||
while (bIt.hasNext()) {
|
||||
Block b = bIt.next();
|
||||
Shop shop = plugin.getShopManager().getShop(b.getLocation());
|
||||
if (shop != null) {
|
||||
if (shop.getOwner().equals(p.getUniqueId())) {
|
||||
shop.delete();
|
||||
sender.sendMessage(ChatColor.GREEN + "Success. Deleted shop.");
|
||||
} else {
|
||||
p.sendMessage(ChatColor.RED + "That's not your shop!");
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
p.sendMessage(ChatColor.RED + "No shop found!");
|
||||
}
|
||||
|
||||
private void export(CommandSender sender, String[] args) {
|
||||
if (args.length < 2) {
|
||||
sender.sendMessage(ChatColor.RED + "Usage: /qs export mysql|sqlite");
|
||||
return;
|
||||
}
|
||||
String type = args[1].toLowerCase();
|
||||
if (type.startsWith("mysql")) {
|
||||
if (plugin.getDB().getCore() instanceof MySQLCore) {
|
||||
sender.sendMessage(ChatColor.RED + "Database is already MySQL");
|
||||
return;
|
||||
}
|
||||
ConfigurationSection cfg = plugin.getConfig().getConfigurationSection("database");
|
||||
String host = cfg.getString("host");
|
||||
String port = cfg.getString("port");
|
||||
String user = cfg.getString("user");
|
||||
String pass = cfg.getString("password");
|
||||
String name = cfg.getString("database");
|
||||
MySQLCore core = new MySQLCore(host, user, pass, name, port);
|
||||
Database target;
|
||||
try {
|
||||
target = new Database(core);
|
||||
QuickShop.instance.getDB().copyTo(target);
|
||||
sender.sendMessage(ChatColor.GREEN + "Success - Exported to MySQL " + user + "@" + host + "." + name);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
sender.sendMessage(ChatColor.RED + "Failed to export to MySQL " + user + "@" + host + "." + name + ChatColor.DARK_RED + " Reason: " + e.getMessage());
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (type.startsWith("sql") || type.contains("file")) {
|
||||
if (plugin.getDB().getCore() instanceof SQLiteCore) {
|
||||
sender.sendMessage(ChatColor.RED + "Database is already SQLite");
|
||||
return;
|
||||
}
|
||||
File file = new File(plugin.getDataFolder(), "shops.db");
|
||||
if (file.exists()) {
|
||||
if (file.delete() == false) {
|
||||
sender.sendMessage(ChatColor.RED + "Warning: Failed to delete old shops.db file. This may cause errors.");
|
||||
}
|
||||
}
|
||||
SQLiteCore core = new SQLiteCore(file);
|
||||
try {
|
||||
Database target = new Database(core);
|
||||
QuickShop.instance.getDB().copyTo(target);
|
||||
sender.sendMessage(ChatColor.GREEN + "Success - Exported to SQLite: " + file.toString());
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
sender.sendMessage(ChatColor.RED + "Failed to export to SQLite: " + file.toString() + " Reason: " + e.getMessage());
|
||||
}
|
||||
return;
|
||||
}
|
||||
sender.sendMessage(ChatColor.RED + "No target given. Usage: /qs export mysql|sqlite");
|
||||
}
|
||||
|
||||
private void setOwner(CommandSender sender, String[] args) {
|
||||
if (sender instanceof Player && sender.hasPermission("quickshop.setowner")) {
|
||||
if (args.length < 2) {
|
||||
sender.sendMessage(MsgUtil.getMessage("command.no-owner-given"));
|
||||
return;
|
||||
}
|
||||
BlockIterator bIt = new BlockIterator((Player) sender, 10);
|
||||
while (bIt.hasNext()) {
|
||||
Block b = bIt.next();
|
||||
Shop shop = plugin.getShopManager().getShop(b.getLocation());
|
||||
if (shop != null) {
|
||||
@SuppressWarnings("deprecation")
|
||||
OfflinePlayer p = this.plugin.getServer().getOfflinePlayer(args[1]);
|
||||
shop.setOwner(p.getUniqueId());
|
||||
shop.update();
|
||||
sender.sendMessage(MsgUtil.getMessage("command.new-owner", this.plugin.getServer().getOfflinePlayer(shop.getOwner()).getName()));
|
||||
return;
|
||||
}
|
||||
}
|
||||
sender.sendMessage(MsgUtil.getMessage("not-looking-at-shop"));
|
||||
return;
|
||||
} else {
|
||||
sender.sendMessage(MsgUtil.getMessage("no-permission"));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
private void refill(CommandSender sender, String[] args) {
|
||||
if (sender instanceof Player && sender.hasPermission("quickshop.refill")) {
|
||||
if (args.length < 2) {
|
||||
sender.sendMessage(MsgUtil.getMessage("command.no-amount-given"));
|
||||
return;
|
||||
}
|
||||
int add;
|
||||
try {
|
||||
add = Integer.parseInt(args[1]);
|
||||
} catch (NumberFormatException e) {
|
||||
sender.sendMessage(MsgUtil.getMessage("thats-not-a-number"));
|
||||
return;
|
||||
}
|
||||
BlockIterator bIt = new BlockIterator((LivingEntity) (Player) sender, 10);
|
||||
while (bIt.hasNext()) {
|
||||
Block b = bIt.next();
|
||||
Shop shop = plugin.getShopManager().getShop(b.getLocation());
|
||||
if (shop != null) {
|
||||
shop.add(shop.getItem(), add);
|
||||
sender.sendMessage(MsgUtil.getMessage("refill-success"));
|
||||
return;
|
||||
}
|
||||
}
|
||||
sender.sendMessage(MsgUtil.getMessage("not-looking-at-shop"));
|
||||
return;
|
||||
} else {
|
||||
sender.sendMessage(MsgUtil.getMessage("no-permission"));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
private void empty(CommandSender sender, String[] args) {
|
||||
if (sender instanceof Player && sender.hasPermission("quickshop.refill")) {
|
||||
BlockIterator bIt = new BlockIterator((LivingEntity) (Player) sender, 10);
|
||||
while (bIt.hasNext()) {
|
||||
Block b = bIt.next();
|
||||
Shop shop = plugin.getShopManager().getShop(b.getLocation());
|
||||
if (shop != null) {
|
||||
if (shop instanceof ContainerShop) {
|
||||
ContainerShop cs = (ContainerShop) shop;
|
||||
cs.getInventory().clear();
|
||||
sender.sendMessage(MsgUtil.getMessage("empty-success"));
|
||||
return;
|
||||
} else {
|
||||
sender.sendMessage(MsgUtil.getMessage("not-looking-at-shop"));
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
sender.sendMessage(MsgUtil.getMessage("not-looking-at-shop"));
|
||||
return;
|
||||
} else {
|
||||
sender.sendMessage(MsgUtil.getMessage("no-permission"));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
private void find(CommandSender sender, String[] args) {
|
||||
if (sender instanceof Player && sender.hasPermission("quickshop.find")) {
|
||||
if (args.length < 2) {
|
||||
sender.sendMessage(MsgUtil.getMessage("command.no-type-given"));
|
||||
return;
|
||||
}
|
||||
StringBuilder sb = new StringBuilder(args[1]);
|
||||
for (int i = 2; i < args.length; i++) {
|
||||
sb.append(" " + args[i]);
|
||||
}
|
||||
String lookFor = sb.toString();
|
||||
lookFor = lookFor.toLowerCase();
|
||||
Player p = (Player) sender;
|
||||
Location loc = p.getEyeLocation().clone();
|
||||
double minDistance = plugin.getConfig().getInt("shop.find-distance");
|
||||
double minDistanceSquared = minDistance * minDistance;
|
||||
int chunkRadius = (int) minDistance / 16 + 1;
|
||||
Shop closest = null;
|
||||
Chunk c = loc.getChunk();
|
||||
for (int x = -chunkRadius + c.getX(); x < chunkRadius + c.getX(); x++) {
|
||||
for (int z = -chunkRadius + c.getZ(); z < chunkRadius + c.getZ(); z++) {
|
||||
Chunk d = c.getWorld().getChunkAt(x, z);
|
||||
HashMap<Location, Shop> inChunk = plugin.getShopManager().getShops(d);
|
||||
if (inChunk == null)
|
||||
continue;
|
||||
for (Shop shop : inChunk.values()) {
|
||||
if (shop.getDataName().toLowerCase().contains(lookFor) && shop.getLocation().distanceSquared(loc) < minDistanceSquared) {
|
||||
closest = shop;
|
||||
minDistanceSquared = shop.getLocation().distanceSquared(loc);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (closest == null) {
|
||||
sender.sendMessage(MsgUtil.getMessage("no-nearby-shop", args[1]));
|
||||
return;
|
||||
}
|
||||
Location lookat = closest.getLocation().clone().add(0.5, 0.5, 0.5);
|
||||
// Hack fix to make /qs find not used by /back
|
||||
p.teleport(this.lookAt(loc, lookat).add(0, -1.62, 0), TeleportCause.UNKNOWN);
|
||||
p.sendMessage(MsgUtil.getMessage("nearby-shop-this-way", "" + (int) Math.floor(Math.sqrt(minDistanceSquared))));
|
||||
return;
|
||||
} else {
|
||||
sender.sendMessage(MsgUtil.getMessage("no-permission"));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
private void setBuy(CommandSender sender) {
|
||||
if (sender instanceof Player && sender.hasPermission("quickshop.create.buy")) {
|
||||
BlockIterator bIt = new BlockIterator((LivingEntity) (Player) sender, 10);
|
||||
while (bIt.hasNext()) {
|
||||
Block b = bIt.next();
|
||||
Shop shop = plugin.getShopManager().getShop(b.getLocation());
|
||||
if (shop != null && shop.getOwner().equals(((Player) sender).getUniqueId())) {
|
||||
shop.setShopType(ShopType.BUYING);
|
||||
shop.setSignText();
|
||||
shop.update();
|
||||
sender.sendMessage(MsgUtil.getMessage("command.now-buying", shop.getDataName()));
|
||||
return;
|
||||
}
|
||||
}
|
||||
sender.sendMessage(MsgUtil.getMessage("not-looking-at-shop"));
|
||||
return;
|
||||
}
|
||||
sender.sendMessage(MsgUtil.getMessage("no-permission"));
|
||||
return;
|
||||
}
|
||||
|
||||
private void setSell(CommandSender sender) {
|
||||
if (sender instanceof Player && sender.hasPermission("quickshop.create.sell")) {
|
||||
BlockIterator bIt = new BlockIterator((LivingEntity) (Player) sender, 10);
|
||||
while (bIt.hasNext()) {
|
||||
Block b = bIt.next();
|
||||
Shop shop = plugin.getShopManager().getShop(b.getLocation());
|
||||
if (shop != null && shop.getOwner().equals(((Player) sender).getUniqueId())) {
|
||||
shop.setShopType(ShopType.SELLING);
|
||||
shop.setSignText();
|
||||
shop.update();
|
||||
sender.sendMessage(MsgUtil.getMessage("command.now-selling", shop.getDataName()));
|
||||
return;
|
||||
}
|
||||
}
|
||||
sender.sendMessage(MsgUtil.getMessage("not-looking-at-shop"));
|
||||
return;
|
||||
}
|
||||
sender.sendMessage(MsgUtil.getMessage("no-permission"));
|
||||
return;
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
private void setPrice(CommandSender sender, String[] args) {
|
||||
if (sender instanceof Player && sender.hasPermission("quickshop.create.changeprice")) {
|
||||
Player p = (Player) sender;
|
||||
if (args.length < 2) {
|
||||
sender.sendMessage(MsgUtil.getMessage("no-price-given"));
|
||||
return;
|
||||
}
|
||||
double price;
|
||||
try {
|
||||
price = Double.parseDouble(args[1]);
|
||||
} catch (NumberFormatException e) {
|
||||
sender.sendMessage(MsgUtil.getMessage("thats-not-a-number"));
|
||||
return;
|
||||
}
|
||||
if (price < 0.01) {
|
||||
sender.sendMessage(MsgUtil.getMessage("price-too-cheap"));
|
||||
return;
|
||||
}
|
||||
double fee = 0;
|
||||
if (plugin.priceChangeRequiresFee) {
|
||||
fee = plugin.getConfig().getDouble("shop.fee-for-price-change");
|
||||
if (fee > 0 && plugin.getEcon().getBalance(p.getUniqueId()) < fee) {
|
||||
sender.sendMessage(MsgUtil.getMessage("you-cant-afford-to-change-price", plugin.getEcon().format(fee)));
|
||||
return;
|
||||
}
|
||||
}
|
||||
BlockIterator bIt = new BlockIterator(p, 10);
|
||||
// Loop through every block they're looking at upto 10 blocks away
|
||||
while (bIt.hasNext()) {
|
||||
Block b = bIt.next();
|
||||
Shop shop = plugin.getShopManager().getShop(b.getLocation());
|
||||
if (shop != null && (shop.getOwner().equals(((Player) sender).getUniqueId()) || sender.hasPermission("quickshop.other.price"))) {
|
||||
if (shop.getPrice() == price) {
|
||||
// Stop here if there isn't a price change
|
||||
sender.sendMessage(MsgUtil.getMessage("no-price-change"));
|
||||
return;
|
||||
}
|
||||
if (fee > 0) {
|
||||
if (!plugin.getEcon().withdraw(p.getUniqueId(), fee)) {
|
||||
sender.sendMessage(MsgUtil.getMessage("you-cant-afford-to-change-price", plugin.getEcon().format(fee)));
|
||||
return;
|
||||
}
|
||||
sender.sendMessage(MsgUtil.getMessage("fee-charged-for-price-change", plugin.getEcon().format(fee)));
|
||||
plugin.getEcon().deposit(plugin.getConfig().getString("tax-account"), fee);
|
||||
}
|
||||
// Update the shop
|
||||
shop.setPrice(price);
|
||||
shop.setSignText();
|
||||
shop.update();
|
||||
sender.sendMessage(MsgUtil.getMessage("price-is-now", plugin.getEcon().format(shop.getPrice())));
|
||||
// Chest shops can be double shops.
|
||||
if (shop instanceof ContainerShop) {
|
||||
ContainerShop cs = (ContainerShop) shop;
|
||||
if (cs.isDoubleShop()) {
|
||||
Shop nextTo = cs.getAttachedShop();
|
||||
if (cs.isSelling()) {
|
||||
if (cs.getPrice() < nextTo.getPrice()) {
|
||||
sender.sendMessage(MsgUtil.getMessage("buying-more-than-selling"));
|
||||
}
|
||||
} else {
|
||||
// Buying
|
||||
if (cs.getPrice() > nextTo.getPrice()) {
|
||||
sender.sendMessage(MsgUtil.getMessage("buying-more-than-selling"));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
sender.sendMessage(MsgUtil.getMessage("not-looking-at-shop"));
|
||||
return;
|
||||
}
|
||||
sender.sendMessage(MsgUtil.getMessage("no-permission"));
|
||||
return;
|
||||
}
|
||||
|
||||
private void clean(CommandSender sender) {
|
||||
if (sender.hasPermission("quickshop.clean")) {
|
||||
sender.sendMessage(MsgUtil.getMessage("command.cleaning"));
|
||||
Iterator<Shop> shIt = plugin.getShopManager().getShopIterator();
|
||||
int i = 0;
|
||||
while (shIt.hasNext()) {
|
||||
Shop shop = shIt.next();
|
||||
if (shop.getLocation().getWorld() != null && shop.isSelling() && shop.getRemainingStock() == 0 && shop instanceof ContainerShop) {
|
||||
ContainerShop cs = (ContainerShop) shop;
|
||||
if (cs.isDoubleShop())
|
||||
continue;
|
||||
shIt.remove(); // Is selling, but has no stock, and is a
|
||||
// chest shop, but is not a double shop. Can
|
||||
// be deleted safely.
|
||||
i++;
|
||||
}
|
||||
}
|
||||
MsgUtil.clean();
|
||||
sender.sendMessage(MsgUtil.getMessage("command.cleaned", "" + i));
|
||||
return;
|
||||
}
|
||||
sender.sendMessage(MsgUtil.getMessage("no-permission"));
|
||||
return;
|
||||
}
|
||||
|
||||
private void reload(CommandSender sender) {
|
||||
if (sender.hasPermission("quickshop.reload")) {
|
||||
sender.sendMessage(MsgUtil.getMessage("command.reloading"));
|
||||
Bukkit.getPluginManager().disablePlugin(plugin);
|
||||
Bukkit.getPluginManager().enablePlugin(plugin);
|
||||
return;
|
||||
}
|
||||
sender.sendMessage(MsgUtil.getMessage("no-permission"));
|
||||
return;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCommand(CommandSender sender, Command cmd, String commandLabel, String[] args) {
|
||||
if (args.length > 0) {
|
||||
String subArg = args[0].toLowerCase();
|
||||
if (subArg.equals("unlimited")) {
|
||||
setUnlimited(sender);
|
||||
return true;
|
||||
} else if (subArg.equals("setowner")) {
|
||||
setOwner(sender, args);
|
||||
return true;
|
||||
} else if (subArg.equals("find")) {
|
||||
find(sender, args);
|
||||
return true;
|
||||
} else if (subArg.startsWith("buy")) {
|
||||
setBuy(sender);
|
||||
return true;
|
||||
} else if (subArg.startsWith("sell")) {
|
||||
setSell(sender);
|
||||
return true;
|
||||
} else if (subArg.startsWith("price")) {
|
||||
setPrice(sender, args);
|
||||
return true;
|
||||
} else if (subArg.equals("remove")) {
|
||||
remove(sender, args);
|
||||
} else if (subArg.equals("refill")) {
|
||||
refill(sender, args);
|
||||
return true;
|
||||
} else if (subArg.equals("empty")) {
|
||||
empty(sender, args);
|
||||
return true;
|
||||
} else if (subArg.equals("clean")) {
|
||||
clean(sender);
|
||||
return true;
|
||||
} else if (subArg.equals("reload")) {
|
||||
reload(sender);
|
||||
return true;
|
||||
} else if (subArg.equals("export")) {
|
||||
export(sender, args);
|
||||
return true;
|
||||
} else if (subArg.equals("info")) {
|
||||
if (sender.hasPermission("quickshop.info")) {
|
||||
int buying, selling, doubles, chunks, worlds;
|
||||
buying = selling = doubles = chunks = worlds = 0;
|
||||
int nostock = 0;
|
||||
for (HashMap<ShopChunk, HashMap<Location, Shop>> inWorld : plugin.getShopManager().getShops().values()) {
|
||||
worlds++;
|
||||
for (HashMap<Location, Shop> inChunk : inWorld.values()) {
|
||||
chunks++;
|
||||
for (Shop shop : inChunk.values()) {
|
||||
if (shop.isBuying()) {
|
||||
buying++;
|
||||
} else if (shop.isSelling()) {
|
||||
selling++;
|
||||
}
|
||||
if (shop instanceof ContainerShop && ((ContainerShop) shop).isDoubleShop()) {
|
||||
doubles++;
|
||||
} else if (shop.isSelling() && shop.getRemainingStock() == 0) {
|
||||
nostock++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
sender.sendMessage(ChatColor.RED + "QuickShop Statistics...");
|
||||
sender.sendMessage(ChatColor.GREEN + "" + (buying + selling) + " shops in " + chunks + " chunks spread over " + worlds + " worlds.");
|
||||
sender.sendMessage(ChatColor.GREEN + "" + doubles + " double shops. ");
|
||||
sender.sendMessage(ChatColor.GREEN + "" + nostock + " selling shops (excluding doubles) which will be removed by /qs clean.");
|
||||
return true;
|
||||
}
|
||||
sender.sendMessage(MsgUtil.getMessage("no-permission"));
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
// Invalid arg given
|
||||
sendHelp(sender);
|
||||
return true;
|
||||
}
|
||||
// No args given
|
||||
sendHelp(sender);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns loc with modified pitch/yaw angles so it faces lookat
|
||||
*
|
||||
* @param loc
|
||||
* The location a players head is
|
||||
* @param lookat
|
||||
* The location they should be looking
|
||||
* @return The location the player should be facing to have their crosshairs
|
||||
* on the location lookAt Kudos to bergerkiller for most of this
|
||||
* function
|
||||
*/
|
||||
public Location lookAt(Location loc, Location lookat) {
|
||||
// Clone the loc to prevent applied changes to the input loc
|
||||
loc = loc.clone();
|
||||
// Values of change in distance (make it relative)
|
||||
double dx = lookat.getX() - loc.getX();
|
||||
double dy = lookat.getY() - loc.getY();
|
||||
double dz = lookat.getZ() - loc.getZ();
|
||||
// Set yaw
|
||||
if (dx != 0) {
|
||||
// Set yaw start value based on dx
|
||||
if (dx < 0) {
|
||||
loc.setYaw((float) (1.5 * Math.PI));
|
||||
} else {
|
||||
loc.setYaw((float) (0.5 * Math.PI));
|
||||
}
|
||||
loc.setYaw((float) loc.getYaw() - (float) Math.atan(dz / dx));
|
||||
} else if (dz < 0) {
|
||||
loc.setYaw((float) Math.PI);
|
||||
}
|
||||
// Get the distance from dx/dz
|
||||
double dxz = Math.sqrt(Math.pow(dx, 2) + Math.pow(dz, 2));
|
||||
float pitch = (float) -Math.atan(dy / dxz);
|
||||
// Set values, convert to degrees
|
||||
// Minecraft yaw (vertical) angles are inverted (negative)
|
||||
loc.setYaw(-loc.getYaw() * 180f / (float) Math.PI + 360);
|
||||
// But pitch angles are normal
|
||||
loc.setPitch(pitch * 180f / (float) Math.PI);
|
||||
return loc;
|
||||
}
|
||||
|
||||
public void sendHelp(CommandSender s) {
|
||||
s.sendMessage(MsgUtil.getMessage("command.description.title"));
|
||||
if (s.hasPermission("quickshop.unlimited"))
|
||||
s.sendMessage(ChatColor.GREEN + "/qs unlimited" + ChatColor.YELLOW + " - " + MsgUtil.getMessage("command.description.unlimited"));
|
||||
if (s.hasPermission("quickshop.setowner"))
|
||||
s.sendMessage(ChatColor.GREEN + "/qs setowner <player>" + ChatColor.YELLOW + " - " + MsgUtil.getMessage("command.description.setowner"));
|
||||
if (s.hasPermission("quickshop.create.buy"))
|
||||
s.sendMessage(ChatColor.GREEN + "/qs buy" + ChatColor.YELLOW + " - " + MsgUtil.getMessage("command.description.buy"));
|
||||
if (s.hasPermission("quickshop.create.sell"))
|
||||
s.sendMessage(ChatColor.GREEN + "/qs sell" + ChatColor.YELLOW + " - " + MsgUtil.getMessage("command.description.sell"));
|
||||
if (s.hasPermission("quickshop.create.changeprice"))
|
||||
s.sendMessage(ChatColor.GREEN + "/qs price" + ChatColor.YELLOW + " - " + MsgUtil.getMessage("command.description.price"));
|
||||
if (s.hasPermission("quickshop.clean"))
|
||||
s.sendMessage(ChatColor.GREEN + "/qs clean" + ChatColor.YELLOW + " - " + MsgUtil.getMessage("command.description.clean"));
|
||||
if (s.hasPermission("quickshop.find"))
|
||||
s.sendMessage(ChatColor.GREEN + "/qs find <item>" + ChatColor.YELLOW + " - " + MsgUtil.getMessage("command.description.find"));
|
||||
if (s.hasPermission("quickshop.refill"))
|
||||
s.sendMessage(ChatColor.GREEN + "/qs refill <amount>" + ChatColor.YELLOW + " - " + MsgUtil.getMessage("command.description.refill"));
|
||||
if (s.hasPermission("quickshop.empty"))
|
||||
s.sendMessage(ChatColor.GREEN + "/qs empty" + ChatColor.YELLOW + " - " + MsgUtil.getMessage("command.description.empty"));
|
||||
if (s.hasPermission("quickshop.export"))
|
||||
s.sendMessage(ChatColor.GREEN + "/qs export mysql|sqlite" + ChatColor.YELLOW + " - Exports the database to SQLite or MySQL");
|
||||
}
|
||||
}
|
@ -0,0 +1,74 @@
|
||||
package org.maxgamer.quickshop.Database;
|
||||
|
||||
import java.sql.Connection;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.SQLException;
|
||||
import java.util.Arrays;
|
||||
|
||||
public class BufferStatement {
|
||||
private Object[] values;
|
||||
private String query;
|
||||
private Exception stacktrace;
|
||||
|
||||
/**
|
||||
* Represents a PreparedStatement in a state before preparing it (E.g. No
|
||||
* file I/O Required)
|
||||
*
|
||||
* @param query
|
||||
* The query to execute. E.g. INSERT INTO accounts (user, passwd)
|
||||
* VALUES (?, ?)
|
||||
* @param values
|
||||
* The values to replace <bold>?</bold> with in
|
||||
* <bold>query</bold>. These are in order.
|
||||
*/
|
||||
public BufferStatement(String query, Object... values) {
|
||||
this.query = query;
|
||||
this.values = values;
|
||||
this.stacktrace = new Exception(); // For error handling
|
||||
this.stacktrace.fillInStackTrace(); // We can declare where this
|
||||
// statement came from.
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a prepared statement using the given connection. Will try to
|
||||
* return an empty statement if something went wrong. If that fails, returns
|
||||
* null.
|
||||
*
|
||||
* This method escapes everything automatically.
|
||||
*
|
||||
* @param con
|
||||
* The connection to prepare this on using
|
||||
* con.prepareStatement(..)
|
||||
* @return The prepared statement, ready for execution.
|
||||
*/
|
||||
public PreparedStatement prepareStatement(Connection con) throws SQLException {
|
||||
PreparedStatement ps;
|
||||
ps = con.prepareStatement(query);
|
||||
for (int i = 1; i <= values.length; i++) {
|
||||
ps.setObject(i, values[i - 1]);
|
||||
}
|
||||
return ps;
|
||||
}
|
||||
|
||||
/**
|
||||
* Used for debugging. This stacktrace is recorded when the statement is
|
||||
* created, so printing it to the screen will provide useful debugging
|
||||
* information about where the query came from, if something went wrong
|
||||
* while executing it.
|
||||
*
|
||||
* @return The stacktrace elements.
|
||||
*/
|
||||
public StackTraceElement[] getStackTrace() {
|
||||
return stacktrace.getStackTrace();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return A string representation of this statement. Returns
|
||||
* <italic>"Query: " + query + ", values: " +
|
||||
* Arrays.toString(values).</italic>
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Query: " + query + ", values: " + Arrays.toString(values);
|
||||
}
|
||||
}
|
199
src/main/java/org/maxgamer/quickshop/Database/Database.java
Normal file
199
src/main/java/org/maxgamer/quickshop/Database/Database.java
Normal file
@ -0,0 +1,199 @@
|
||||
package org.maxgamer.quickshop.Database;
|
||||
|
||||
import java.sql.Connection;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
public class Database {
|
||||
private DatabaseCore core;
|
||||
|
||||
/**
|
||||
* Creates a new database and validates its connection.
|
||||
*
|
||||
* If the connection is invalid, this will throw a ConnectionException.
|
||||
*
|
||||
* @param core
|
||||
* The core for the database, either MySQL or SQLite.
|
||||
* @throws ConnectionException
|
||||
* If the connection was invalid
|
||||
*/
|
||||
public Database(DatabaseCore core) throws ConnectionException {
|
||||
try {
|
||||
try {
|
||||
if (!core.getConnection().isValid(10)) {
|
||||
throw new ConnectionException("Database doesn not appear to be valid!");
|
||||
}
|
||||
} catch (AbstractMethodError e) {
|
||||
// You don't need to validate this core.
|
||||
}
|
||||
} catch (SQLException e) {
|
||||
throw new ConnectionException(e.getMessage());
|
||||
}
|
||||
this.core = core;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the database core object, that this database runs on.
|
||||
*
|
||||
* @return the database core object, that this database runs on.
|
||||
*/
|
||||
public DatabaseCore getCore() {
|
||||
return core;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches the connection to this database for querying. Try to avoid doing
|
||||
* this in the main thread.
|
||||
*
|
||||
* @return Fetches the connection to this database for querying.
|
||||
*/
|
||||
public Connection getConnection() {
|
||||
return core.getConnection();
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes the given statement either immediately, or soon.
|
||||
*
|
||||
* @param query
|
||||
* The query
|
||||
* @param objs
|
||||
* The string values for each ? in the given query.
|
||||
*/
|
||||
public void execute(String query, Object... objs) {
|
||||
BufferStatement bs = new BufferStatement(query, objs);
|
||||
core.queue(bs);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the table exists
|
||||
*
|
||||
* @param table
|
||||
* The table to check for
|
||||
* @return True if the table is found
|
||||
*/
|
||||
public boolean hasTable(String table) throws SQLException {
|
||||
ResultSet rs = getConnection().getMetaData().getTables(null, null, "%", null);
|
||||
while (rs.next()) {
|
||||
if (table.equalsIgnoreCase(rs.getString("TABLE_NAME"))) {
|
||||
rs.close();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
rs.close();
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes the database
|
||||
*/
|
||||
public void close() {
|
||||
this.core.close();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the given table has the given column
|
||||
*
|
||||
* @param table
|
||||
* The table
|
||||
* @param column
|
||||
* The column
|
||||
* @return True if the given table has the given column
|
||||
* @throws SQLException
|
||||
* If the database isn't connected
|
||||
*/
|
||||
public boolean hasColumn(String table, String column) throws SQLException {
|
||||
if (!hasTable(table))
|
||||
return false;
|
||||
String query = "SELECT * FROM " + table + " LIMIT 0,1";
|
||||
try {
|
||||
PreparedStatement ps = this.getConnection().prepareStatement(query);
|
||||
ResultSet rs = ps.executeQuery();
|
||||
while (rs.next()) {
|
||||
rs.getString(column); // Throws an exception if it can't find
|
||||
// that column
|
||||
return true;
|
||||
}
|
||||
} catch (SQLException e) {
|
||||
return false;
|
||||
}
|
||||
return false; // Uh, wtf.
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents a connection error, generally when the server can't connect to
|
||||
* MySQL or something.
|
||||
*/
|
||||
public static class ConnectionException extends Exception {
|
||||
private static final long serialVersionUID = 8348749992936357317L;
|
||||
|
||||
public ConnectionException(String msg) {
|
||||
super(msg);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Copies the contents of this database into the given database. Does not
|
||||
* delete the contents of this database, or change any settings. This may
|
||||
* take a long time, and will print out progress reports to System.out
|
||||
*
|
||||
* This method does not create the tables in the new database. You need to
|
||||
* do that yourself.
|
||||
*
|
||||
* @param db
|
||||
* The database to copy data to
|
||||
* @throws SQLException
|
||||
* if an error occurs.
|
||||
*/
|
||||
public void copyTo(Database db) throws SQLException {
|
||||
ResultSet rs = getConnection().getMetaData().getTables(null, null, "%", null);
|
||||
List<String> tables = new LinkedList<String>();
|
||||
while (rs.next()) {
|
||||
tables.add(rs.getString("TABLE_NAME"));
|
||||
}
|
||||
rs.close();
|
||||
core.flush();
|
||||
// For each table
|
||||
for (String table : tables) {
|
||||
if (table.toLowerCase().startsWith("sqlite_autoindex_"))
|
||||
continue;
|
||||
System.out.println("Copying " + table);
|
||||
// Wipe the old records
|
||||
db.getConnection().prepareStatement("DELETE FROM " + table).execute();
|
||||
// Fetch all the data from the existing database
|
||||
rs = getConnection().prepareStatement("SELECT * FROM " + table).executeQuery();
|
||||
int n = 0;
|
||||
// Build the query
|
||||
String query = "INSERT INTO " + table + " VALUES (";
|
||||
// Append another placeholder for the value
|
||||
query += "?";
|
||||
for (int i = 2; i <= rs.getMetaData().getColumnCount(); i++) {
|
||||
// Add the rest of the placeholders and values. This is so we
|
||||
// have (?, ?, ?) and not (?, ?, ?, ).
|
||||
query += ", ?";
|
||||
}
|
||||
// End the query
|
||||
query += ")";
|
||||
PreparedStatement ps = db.getConnection().prepareStatement(query);
|
||||
while (rs.next()) {
|
||||
n++;
|
||||
for (int i = 1; i <= rs.getMetaData().getColumnCount(); i++) {
|
||||
ps.setObject(i, rs.getObject(i));
|
||||
}
|
||||
ps.addBatch();
|
||||
if (n % 100 == 0) {
|
||||
ps.executeBatch();
|
||||
System.out.println(n + " records copied...");
|
||||
}
|
||||
}
|
||||
ps.executeBatch();
|
||||
// Close the resultset of that table
|
||||
rs.close();
|
||||
}
|
||||
// Success!
|
||||
db.getConnection().close();
|
||||
this.getConnection().close();
|
||||
}
|
||||
}
|
@ -0,0 +1,13 @@
|
||||
package org.maxgamer.quickshop.Database;
|
||||
|
||||
import java.sql.Connection;
|
||||
|
||||
public interface DatabaseCore {
|
||||
public Connection getConnection();
|
||||
|
||||
public void queue(BufferStatement bs);
|
||||
|
||||
public void flush();
|
||||
|
||||
public void close();
|
||||
}
|
@ -0,0 +1,62 @@
|
||||
package org.maxgamer.quickshop.Database;
|
||||
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.Statement;
|
||||
|
||||
public class DatabaseHelper {
|
||||
public static void setup(Database db) throws SQLException {
|
||||
if (!db.hasTable("shops")) {
|
||||
createShopsTable(db);
|
||||
}
|
||||
if (!db.hasTable("messages")) {
|
||||
createMessagesTable(db);
|
||||
}
|
||||
checkColumns(db);
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifies that all required columns exist.
|
||||
*/
|
||||
public static void checkColumns(Database db) {
|
||||
PreparedStatement ps = null;
|
||||
try {
|
||||
// V3.4.2
|
||||
ps = db.getConnection().prepareStatement("ALTER TABLE shops MODIFY COLUMN price double(32,2) NOT NULL AFTER owner");
|
||||
ps.execute();
|
||||
ps.close();
|
||||
} catch (SQLException e) {
|
||||
}
|
||||
try {
|
||||
// V3.4.3
|
||||
ps = db.getConnection().prepareStatement("ALTER TABLE messages MODIFY COLUMN time BIGINT(32) NOT NULL AFTER message");
|
||||
ps.execute();
|
||||
ps.close();
|
||||
} catch (SQLException e) {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the database table 'shops'.
|
||||
*
|
||||
* @throws SQLException
|
||||
* If the connection is invalid.
|
||||
*/
|
||||
public static void createShopsTable(Database db) throws SQLException {
|
||||
Statement st = db.getConnection().createStatement();
|
||||
String createTable = "CREATE TABLE shops (" + "owner TEXT(32) NOT NULL, " + "price double(32, 2) NOT NULL, " + "itemConfig TEXT CHARSET utf8 NOT NULL, " + "x INTEGER(32) NOT NULL, " + "y INTEGER(32) NOT NULL, " + "z INTEGER(32) NOT NULL, " + "world VARCHAR(32) NOT NULL, " + "unlimited boolean, " + "type boolean, " + "PRIMARY KEY (x, y, z, world) " + ");";
|
||||
st.execute(createTable);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the database table 'messages'
|
||||
*
|
||||
* @throws SQLException
|
||||
* If the connection is invalid
|
||||
*/
|
||||
public static void createMessagesTable(Database db) throws SQLException {
|
||||
Statement st = db.getConnection().createStatement();
|
||||
String createTable = "CREATE TABLE messages (" + "owner TEXT(32) NOT NULL, " + "message TEXT(200) NOT NULL, " + "time BIGINT(32) NOT NULL " + ");";
|
||||
st.execute(createTable);
|
||||
}
|
||||
}
|
85
src/main/java/org/maxgamer/quickshop/Database/MySQLCore.java
Normal file
85
src/main/java/org/maxgamer/quickshop/Database/MySQLCore.java
Normal file
@ -0,0 +1,85 @@
|
||||
package org.maxgamer.quickshop.Database;
|
||||
|
||||
import java.sql.Connection;
|
||||
import java.sql.DriverManager;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.SQLException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Properties;
|
||||
|
||||
public class MySQLCore implements DatabaseCore {
|
||||
private String url;
|
||||
/** The connection properties... user, pass, autoReconnect.. */
|
||||
private Properties info;
|
||||
private static final int MAX_CONNECTIONS = 8;
|
||||
private static ArrayList<Connection> pool = new ArrayList<Connection>();
|
||||
|
||||
public MySQLCore(String host, String user, String pass, String database, String port) {
|
||||
info = new Properties();
|
||||
info.put("autoReconnect", "true");
|
||||
info.put("user", user);
|
||||
info.put("password", pass);
|
||||
info.put("useUnicode", "true");
|
||||
info.put("characterEncoding", "utf8");
|
||||
this.url = "jdbc:mysql://" + host + ":" + port + "/" + database;
|
||||
for (int i = 0; i < MAX_CONNECTIONS; i++)
|
||||
pool.add(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the database connection for executing queries on.
|
||||
*
|
||||
* @return The database connection
|
||||
*/
|
||||
public Connection getConnection() {
|
||||
for (int i = 0; i < MAX_CONNECTIONS; i++) {
|
||||
Connection connection = pool.get(i);
|
||||
try {
|
||||
// If we have a current connection, fetch it
|
||||
if (connection != null && !connection.isClosed()) {
|
||||
if (connection.isValid(10)) {
|
||||
return connection;
|
||||
}
|
||||
// Else, it is invalid, so we return another connection.
|
||||
}
|
||||
connection = DriverManager.getConnection(this.url, info);
|
||||
pool.set(i, connection);
|
||||
return connection;
|
||||
} catch (SQLException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void queue(BufferStatement bs) {
|
||||
try {
|
||||
Connection con = this.getConnection();
|
||||
while (con == null) {
|
||||
try {
|
||||
Thread.sleep(15);
|
||||
} catch (InterruptedException e) {
|
||||
}
|
||||
// Try again
|
||||
this.getConnection();
|
||||
}
|
||||
PreparedStatement ps = bs.prepareStatement(con);
|
||||
ps.execute();
|
||||
ps.close();
|
||||
} catch (SQLException e) {
|
||||
e.printStackTrace();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
// Nothing, because queries are executed immediately for MySQL
|
||||
}
|
||||
|
||||
@Override
|
||||
public void flush() {
|
||||
// Nothing, because queries are executed immediately for MySQL
|
||||
}
|
||||
}
|
110
src/main/java/org/maxgamer/quickshop/Database/SQLiteCore.java
Normal file
110
src/main/java/org/maxgamer/quickshop/Database/SQLiteCore.java
Normal file
@ -0,0 +1,110 @@
|
||||
package org.maxgamer.quickshop.Database;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.sql.Connection;
|
||||
import java.sql.DriverManager;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.SQLException;
|
||||
import java.util.LinkedList;
|
||||
|
||||
public class SQLiteCore implements DatabaseCore {
|
||||
private Connection connection;
|
||||
private File dbFile;
|
||||
private volatile Thread watcher;
|
||||
private volatile LinkedList<BufferStatement> queue = new LinkedList<BufferStatement>();
|
||||
|
||||
public SQLiteCore(File dbFile) {
|
||||
this.dbFile = dbFile;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the database connection for executing queries on.
|
||||
*
|
||||
* @return The database connection
|
||||
*/
|
||||
public Connection getConnection() {
|
||||
try {
|
||||
// If we have a current connection, fetch it
|
||||
if (this.connection != null && !this.connection.isClosed()) {
|
||||
return this.connection;
|
||||
}
|
||||
} catch (SQLException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
if (this.dbFile.exists()) {
|
||||
// So we need a new connection
|
||||
try {
|
||||
Class.forName("org.sqlite.JDBC");
|
||||
this.connection = DriverManager.getConnection("jdbc:sqlite:" + this.dbFile);
|
||||
return this.connection;
|
||||
} catch (ClassNotFoundException e) {
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
} catch (SQLException e) {
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
} else {
|
||||
// So we need a new file too.
|
||||
try {
|
||||
// Create the file
|
||||
this.dbFile.createNewFile();
|
||||
// Now we won't need a new file, just a connection.
|
||||
// This will return that new connection.
|
||||
return this.getConnection();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void queue(BufferStatement bs) {
|
||||
synchronized (queue) {
|
||||
queue.add(bs);
|
||||
}
|
||||
if (watcher == null || !watcher.isAlive()) {
|
||||
startWatcher();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void flush() {
|
||||
while (queue.isEmpty() == false) {
|
||||
BufferStatement bs;
|
||||
synchronized (queue) {
|
||||
bs = queue.removeFirst();
|
||||
}
|
||||
synchronized (dbFile) {
|
||||
try {
|
||||
PreparedStatement ps = bs.prepareStatement(getConnection());
|
||||
ps.execute();
|
||||
ps.close();
|
||||
} catch (SQLException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
flush();
|
||||
}
|
||||
|
||||
private void startWatcher() {
|
||||
watcher = new Thread() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
Thread.sleep(30000);
|
||||
} catch (InterruptedException e) {
|
||||
}
|
||||
flush();
|
||||
}
|
||||
};
|
||||
watcher.start();
|
||||
}
|
||||
}
|
119
src/main/java/org/maxgamer/quickshop/Economy/Economy.java
Normal file
119
src/main/java/org/maxgamer/quickshop/Economy/Economy.java
Normal file
@ -0,0 +1,119 @@
|
||||
package org.maxgamer.quickshop.Economy;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
public class Economy implements EconomyCore {
|
||||
private EconomyCore core;
|
||||
|
||||
public Economy(EconomyCore core) {
|
||||
this.core = core;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks that this economy is valid. Returns false if it is not valid.
|
||||
*
|
||||
* @return True if this economy will work, false if it will not.
|
||||
*/
|
||||
public boolean isValid() {
|
||||
return core.isValid();
|
||||
}
|
||||
|
||||
/**
|
||||
* Deposits a given amount of money from thin air to the given username.
|
||||
*
|
||||
* @param name
|
||||
* The exact (case insensitive) username to give money to
|
||||
* @param amount
|
||||
* The amount to give them
|
||||
* @return True if success (Should be almost always)
|
||||
*/
|
||||
@Deprecated
|
||||
public boolean deposit(String name, double amount) {
|
||||
return core.deposit(name, amount);
|
||||
}
|
||||
|
||||
/**
|
||||
* Withdraws a given amount of money from the given username and turns it to
|
||||
* thin air.
|
||||
*
|
||||
* @param name
|
||||
* The exact (case insensitive) username to take money from
|
||||
* @param amount
|
||||
* The amount to take from them
|
||||
* @return True if success, false if they didn't have enough cash
|
||||
*/
|
||||
@Deprecated
|
||||
public boolean withdraw(String name, double amount) {
|
||||
return core.withdraw(name, amount);
|
||||
}
|
||||
|
||||
/**
|
||||
* Transfers the given amount of money from Player1 to Player2
|
||||
*
|
||||
* @param from
|
||||
* The player who is paying money
|
||||
* @param to
|
||||
* The player who is receiving money
|
||||
* @param amount
|
||||
* The amount to transfer
|
||||
* @return true if success (Payer had enough cash, receiver was able to
|
||||
* receive the funds)
|
||||
*/
|
||||
@Deprecated
|
||||
public boolean transfer(String from, String to, double amount) {
|
||||
return core.transfer(from, to, amount);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches the balance of the given account name
|
||||
*
|
||||
* @param name
|
||||
* The name of the account
|
||||
* @return Their current balance.
|
||||
*/
|
||||
@Deprecated
|
||||
public double getBalance(String name) {
|
||||
return core.getBalance(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Formats the given number... E.g. 50.5 becomes $50.5 Dollars, or 50
|
||||
* Dollars 5 Cents
|
||||
*
|
||||
* @param balance
|
||||
* The given number
|
||||
* @return The balance in human readable text.
|
||||
*/
|
||||
public String format(double balance) {
|
||||
return core.format(balance);
|
||||
}
|
||||
@Deprecated
|
||||
public boolean has(String name, double amount) {
|
||||
return core.getBalance(name) >= amount;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return core.getClass().getName().split("_")[1];
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean deposit(UUID name, double amount) {
|
||||
return core.deposit(name,amount);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean withdraw(UUID name, double amount) {
|
||||
return core.withdraw(name, amount);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean transfer(UUID from, UUID to, double amount) {
|
||||
return core.transfer(from, to, amount);
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getBalance(UUID name) {
|
||||
return core.getBalance(name);
|
||||
}
|
||||
}
|
127
src/main/java/org/maxgamer/quickshop/Economy/EconomyCore.java
Normal file
127
src/main/java/org/maxgamer/quickshop/Economy/EconomyCore.java
Normal file
@ -0,0 +1,127 @@
|
||||
package org.maxgamer.quickshop.Economy;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* @author netherfoam Represents an economy.
|
||||
*/
|
||||
public interface EconomyCore {
|
||||
/**
|
||||
* Checks that this economy is valid. Returns false if it is not valid.
|
||||
*
|
||||
* @return True if this economy will work, false if it will not.
|
||||
*/
|
||||
public boolean isValid();
|
||||
|
||||
/**
|
||||
* Deposits a given amount of money from thin air to the given username.
|
||||
*
|
||||
* @param name
|
||||
* The exact (case insensitive) username to give money to
|
||||
* @param amount
|
||||
* The amount to give them
|
||||
* @return True if success (Should be almost always)
|
||||
*/
|
||||
@Deprecated
|
||||
public boolean deposit(String name, double amount);
|
||||
|
||||
/**
|
||||
* Withdraws a given amount of money from the given username and turns it to
|
||||
* thin air.
|
||||
*
|
||||
* @param name
|
||||
* The exact (case insensitive) username to take money from
|
||||
* @param amount
|
||||
* The amount to take from them
|
||||
* @return True if success, false if they didn't have enough cash
|
||||
*/
|
||||
@Deprecated
|
||||
public boolean withdraw(String name, double amount);
|
||||
|
||||
/**
|
||||
* Transfers the given amount of money from Player1 to Player2
|
||||
*
|
||||
* @param from
|
||||
* The player who is paying money
|
||||
* @param to
|
||||
* The player who is receiving money
|
||||
* @param amount
|
||||
* The amount to transfer
|
||||
* @return true if success (Payer had enough cash, receiver was able to
|
||||
* receive the funds)
|
||||
*/
|
||||
@Deprecated
|
||||
public boolean transfer(String from, String to, double amount);
|
||||
|
||||
/**
|
||||
* Fetches the balance of the given account name
|
||||
*
|
||||
* @param name
|
||||
* The name of the account
|
||||
* @return Their current balance.
|
||||
*/
|
||||
@Deprecated
|
||||
public double getBalance(String name);
|
||||
|
||||
/**
|
||||
* Formats the given number... E.g. 50.5 becomes $50.5 Dollars, or 50
|
||||
* Dollars 5 Cents
|
||||
*
|
||||
* @param balance
|
||||
* The given number
|
||||
* @return The balance in human readable text.
|
||||
*/
|
||||
public String format(double balance);
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Deposits a given amount of money from thin air to the given username.
|
||||
*
|
||||
* @param name
|
||||
* The exact (case insensitive) username to give money to
|
||||
* @param amount
|
||||
* The amount to give them
|
||||
* @return True if success (Should be almost always)
|
||||
*/
|
||||
public boolean deposit(UUID name, double amount);
|
||||
|
||||
/**
|
||||
* Withdraws a given amount of money from the given username and turns it to
|
||||
* thin air.
|
||||
*
|
||||
* @param name
|
||||
* The exact (case insensitive) username to take money from
|
||||
* @param amount
|
||||
* The amount to take from them
|
||||
* @return True if success, false if they didn't have enough cash
|
||||
*/
|
||||
public boolean withdraw(UUID name, double amount);
|
||||
|
||||
/**
|
||||
* Transfers the given amount of money from Player1 to Player2
|
||||
*
|
||||
* @param from
|
||||
* The player who is paying money
|
||||
* @param to
|
||||
* The player who is receiving money
|
||||
* @param amount
|
||||
* The amount to transfer
|
||||
* @return true if success (Payer had enough cash, receiver was able to
|
||||
* receive the funds)
|
||||
*/
|
||||
public boolean transfer(UUID from, UUID to, double amount);
|
||||
|
||||
/**
|
||||
* Fetches the balance of the given account name
|
||||
*
|
||||
* @param name
|
||||
* The name of the account
|
||||
* @return Their current balance.
|
||||
*/
|
||||
public double getBalance(UUID name);
|
||||
}
|
108
src/main/java/org/maxgamer/quickshop/Economy/Economy_Vault.java
Normal file
108
src/main/java/org/maxgamer/quickshop/Economy/Economy_Vault.java
Normal file
@ -0,0 +1,108 @@
|
||||
package org.maxgamer.quickshop.Economy;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
import net.milkbowl.vault.economy.Economy;
|
||||
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.OfflinePlayer;
|
||||
import org.bukkit.plugin.RegisteredServiceProvider;
|
||||
|
||||
public class Economy_Vault implements EconomyCore {
|
||||
private Economy vault;
|
||||
|
||||
public Economy_Vault() {
|
||||
setupEconomy();
|
||||
}
|
||||
|
||||
private boolean setupEconomy() {
|
||||
RegisteredServiceProvider<Economy> economyProvider = Bukkit.getServicesManager().getRegistration(Economy.class);
|
||||
if (economyProvider != null) {
|
||||
this.vault = ((Economy) economyProvider.getProvider());
|
||||
}
|
||||
return this.vault != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isValid() {
|
||||
return this.vault != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Deprecated
|
||||
public boolean deposit(String name, double amount) {
|
||||
return this.vault.depositPlayer(name, amount).transactionSuccess();
|
||||
}
|
||||
|
||||
@Override
|
||||
@Deprecated
|
||||
public boolean withdraw(String name, double amount) {
|
||||
return this.vault.withdrawPlayer(name, amount).transactionSuccess();
|
||||
}
|
||||
|
||||
@Override
|
||||
@Deprecated
|
||||
public boolean transfer(String from, String to, double amount) {
|
||||
if (this.vault.getBalance(from) >= amount) {
|
||||
if (this.vault.withdrawPlayer(from, amount).transactionSuccess()) {
|
||||
if (!this.vault.depositPlayer(to, amount).transactionSuccess()) {
|
||||
this.vault.depositPlayer(from, amount);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Deprecated
|
||||
public double getBalance(String name) {
|
||||
return this.vault.getBalance(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String format(double balance) {
|
||||
try {
|
||||
return this.vault.format(balance);
|
||||
} catch (NumberFormatException e) {
|
||||
}
|
||||
return "$" + balance;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean deposit(UUID name, double amount) {
|
||||
OfflinePlayer p = Bukkit.getOfflinePlayer(name);
|
||||
return this.vault.depositPlayer(p, amount).transactionSuccess();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean withdraw(UUID name, double amount) {
|
||||
OfflinePlayer p = Bukkit.getOfflinePlayer(name);
|
||||
return this.vault.withdrawPlayer(p, amount).transactionSuccess();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean transfer(UUID from, UUID to, double amount) {
|
||||
OfflinePlayer pFrom = Bukkit.getOfflinePlayer(from);
|
||||
OfflinePlayer pTo = Bukkit.getOfflinePlayer(to);
|
||||
if (this.vault.getBalance(pFrom) >= amount) {
|
||||
if (this.vault.withdrawPlayer(pFrom, amount).transactionSuccess()) {
|
||||
if (!this.vault.depositPlayer(pTo, amount).transactionSuccess()) {
|
||||
this.vault.depositPlayer(pFrom, amount);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getBalance(UUID name) {
|
||||
OfflinePlayer p = Bukkit.getOfflinePlayer(name);
|
||||
return this.vault.getBalance(p);
|
||||
}
|
||||
}
|
@ -0,0 +1,124 @@
|
||||
package org.maxgamer.quickshop.Listeners;
|
||||
|
||||
import org.bukkit.GameMode;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.block.Block;
|
||||
import org.bukkit.block.BlockState;
|
||||
import org.bukkit.block.DoubleChest;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.EventPriority;
|
||||
import org.bukkit.event.Listener;
|
||||
import org.bukkit.event.block.BlockBreakEvent;
|
||||
import org.bukkit.event.block.BlockPlaceEvent;
|
||||
import org.bukkit.event.entity.EntityExplodeEvent;
|
||||
import org.bukkit.inventory.InventoryHolder;
|
||||
import org.maxgamer.quickshop.QuickShop;
|
||||
import org.maxgamer.quickshop.Shop.Info;
|
||||
import org.maxgamer.quickshop.Shop.Shop;
|
||||
import org.maxgamer.quickshop.Shop.ShopAction;
|
||||
import org.maxgamer.quickshop.Util.MsgUtil;
|
||||
import org.maxgamer.quickshop.Util.Util;
|
||||
|
||||
public class BlockListener implements Listener {
|
||||
private QuickShop plugin;
|
||||
|
||||
public BlockListener(QuickShop plugin) {
|
||||
this.plugin = plugin;
|
||||
}
|
||||
|
||||
/**
|
||||
* Listens for chest placement, so a doublechest shop can't be created.
|
||||
*/
|
||||
@EventHandler(ignoreCancelled = true)
|
||||
public void onPlace(BlockPlaceEvent e) {
|
||||
if (e.isCancelled())
|
||||
return;
|
||||
BlockState bs = e.getBlock().getState();
|
||||
if (bs instanceof DoubleChest == false)
|
||||
return;
|
||||
Block b = e.getBlock();
|
||||
Player p = e.getPlayer();
|
||||
Block chest = Util.getSecondHalf(b);
|
||||
if (chest != null && plugin.getShopManager().getShop(chest.getLocation()) != null && !p.hasPermission("quickshop.create.double")) {
|
||||
e.setCancelled(true);
|
||||
p.sendMessage(MsgUtil.getMessage("no-double-chests"));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes chests when they're destroyed.
|
||||
*/
|
||||
@EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)
|
||||
public void onBreak(BlockBreakEvent e) {
|
||||
Block b = e.getBlock();
|
||||
Player p = e.getPlayer();
|
||||
// If the shop was a chest
|
||||
if (b.getState() instanceof InventoryHolder) {
|
||||
Shop shop = plugin.getShopManager().getShop(b.getLocation());
|
||||
if (shop == null)
|
||||
return;
|
||||
// If they're either survival or the owner, they can break it
|
||||
if (p.getGameMode() == GameMode.CREATIVE && !p.getUniqueId().equals(shop.getOwner())) {
|
||||
e.setCancelled(true);
|
||||
p.sendMessage(MsgUtil.getMessage("no-creative-break"));
|
||||
return;
|
||||
}
|
||||
// Cancel their current menu... Doesnt cancel other's menu's.
|
||||
Info action = plugin.getShopManager().getActions().get(p.getName());
|
||||
if (action != null) {
|
||||
action.setAction(ShopAction.CANCELLED);
|
||||
}
|
||||
shop.delete();
|
||||
p.sendMessage(MsgUtil.getMessage("success-removed-shop"));
|
||||
} else if (b.getType() == Material.WALL_SIGN) {
|
||||
Shop shop = getShopNextTo(b.getLocation());
|
||||
if (shop == null)
|
||||
return;
|
||||
// If they're in creative and not the owner, don't let them
|
||||
// (accidents happen)
|
||||
if (p.getGameMode() == GameMode.CREATIVE && !p.getUniqueId().equals(shop.getOwner())) {
|
||||
e.setCancelled(true);
|
||||
p.sendMessage(MsgUtil.getMessage("no-creative-break"));
|
||||
return;
|
||||
}
|
||||
if (e.isCancelled())
|
||||
return;
|
||||
e.setCancelled(true); // Cancel the event so that the sign does not
|
||||
// drop.. TODO: Find a better way.
|
||||
b.setType(Material.AIR);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles shops breaking through explosions
|
||||
*/
|
||||
@EventHandler(priority = EventPriority.HIGHEST)
|
||||
public void onExplode(EntityExplodeEvent e) {
|
||||
if (e.isCancelled())
|
||||
return;
|
||||
for (int i = 0; i < e.blockList().size(); i++) {
|
||||
Block b = e.blockList().get(i);
|
||||
Shop shop = plugin.getShopManager().getShop(b.getLocation());
|
||||
if (shop != null) {
|
||||
shop.delete();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the shop a sign is attached to
|
||||
*
|
||||
* @param loc
|
||||
* The location of the sign
|
||||
* @return The shop
|
||||
*/
|
||||
private Shop getShopNextTo(Location loc) {
|
||||
Block b = Util.getAttached(loc.getBlock());
|
||||
// Util.getAttached(b)
|
||||
if (b == null)
|
||||
return null;
|
||||
return plugin.getShopManager().getShop(b.getLocation());
|
||||
}
|
||||
}
|
@ -0,0 +1,28 @@
|
||||
package org.maxgamer.quickshop.Listeners;
|
||||
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.EventPriority;
|
||||
import org.bukkit.event.Listener;
|
||||
import org.bukkit.event.player.AsyncPlayerChatEvent;
|
||||
import org.maxgamer.quickshop.QuickShop;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Netherfoam
|
||||
*
|
||||
*/
|
||||
public class ChatListener implements Listener {
|
||||
QuickShop plugin;
|
||||
|
||||
public ChatListener(QuickShop plugin) {
|
||||
this.plugin = plugin;
|
||||
}
|
||||
|
||||
@EventHandler(ignoreCancelled = true, priority = EventPriority.LOWEST)
|
||||
public void onChat(final AsyncPlayerChatEvent e) {
|
||||
if (!plugin.getShopManager().getActions().containsKey(e.getPlayer().getUniqueId()))
|
||||
return;
|
||||
plugin.getShopManager().handleChat(e.getPlayer(), e.getMessage());
|
||||
e.setCancelled(true);
|
||||
}
|
||||
}
|
@ -0,0 +1,45 @@
|
||||
package org.maxgamer.quickshop.Listeners;
|
||||
|
||||
import java.util.HashMap;
|
||||
|
||||
import org.bukkit.Chunk;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.EventPriority;
|
||||
import org.bukkit.event.Listener;
|
||||
import org.bukkit.event.world.ChunkLoadEvent;
|
||||
import org.bukkit.event.world.ChunkUnloadEvent;
|
||||
import org.maxgamer.quickshop.QuickShop;
|
||||
import org.maxgamer.quickshop.Shop.Shop;
|
||||
|
||||
public class ChunkListener implements Listener {
|
||||
private QuickShop plugin;
|
||||
|
||||
public ChunkListener(QuickShop plugin) {
|
||||
this.plugin = plugin;
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.HIGHEST)
|
||||
public void onChunkLoad(ChunkLoadEvent e) {
|
||||
Chunk c = e.getChunk();
|
||||
if (plugin.getShopManager().getShops() == null)
|
||||
return;
|
||||
HashMap<Location, Shop> inChunk = plugin.getShopManager().getShops(c);
|
||||
if (inChunk == null)
|
||||
return;
|
||||
for (Shop shop : inChunk.values()) {
|
||||
shop.onLoad();
|
||||
}
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.HIGHEST)
|
||||
public void onChunkUnload(ChunkUnloadEvent e) {
|
||||
Chunk c = e.getChunk();
|
||||
HashMap<Location, Shop> inChunk = plugin.getShopManager().getShops(c);
|
||||
if (inChunk == null)
|
||||
return;
|
||||
for (Shop shop : inChunk.values()) {
|
||||
shop.onUnload();
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,30 @@
|
||||
package org.maxgamer.quickshop.Listeners;
|
||||
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.EventPriority;
|
||||
import org.bukkit.event.Listener;
|
||||
import org.maxgamer.quickshop.QuickShop;
|
||||
|
||||
import com.dthielke.herochat.ChannelChatEvent;
|
||||
import com.dthielke.herochat.Chatter.Result;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Netherfoam
|
||||
*
|
||||
*/
|
||||
public class HeroChatListener implements Listener {
|
||||
QuickShop plugin;
|
||||
|
||||
public HeroChatListener(QuickShop plugin) {
|
||||
this.plugin = plugin;
|
||||
}
|
||||
|
||||
@EventHandler(ignoreCancelled = true, priority = EventPriority.LOWEST)
|
||||
public void onHeroChat(ChannelChatEvent e) {
|
||||
if (!plugin.getShopManager().getActions().containsKey(e.getSender().getName()))
|
||||
return;
|
||||
plugin.getShopManager().handleChat(e.getSender().getPlayer(), e.getMessage());
|
||||
e.setResult(Result.FAIL);
|
||||
}
|
||||
}
|
144
src/main/java/org/maxgamer/quickshop/Listeners/LockListener.java
Normal file
144
src/main/java/org/maxgamer/quickshop/Listeners/LockListener.java
Normal file
@ -0,0 +1,144 @@
|
||||
package org.maxgamer.quickshop.Listeners;
|
||||
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.block.Block;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.EventPriority;
|
||||
import org.bukkit.event.Listener;
|
||||
import org.bukkit.event.block.Action;
|
||||
import org.bukkit.event.block.BlockBreakEvent;
|
||||
import org.bukkit.event.block.BlockPlaceEvent;
|
||||
import org.bukkit.event.entity.EntityExplodeEvent;
|
||||
import org.bukkit.event.player.PlayerInteractEvent;
|
||||
import org.maxgamer.quickshop.QuickShop;
|
||||
import org.maxgamer.quickshop.Shop.Shop;
|
||||
import org.maxgamer.quickshop.Util.MsgUtil;
|
||||
import org.maxgamer.quickshop.Util.Util;
|
||||
|
||||
public class LockListener implements Listener {
|
||||
private QuickShop plugin;
|
||||
|
||||
public LockListener(QuickShop plugin) {
|
||||
this.plugin = plugin;
|
||||
}
|
||||
|
||||
@EventHandler(ignoreCancelled = true)
|
||||
public void onClick(PlayerInteractEvent e) {
|
||||
Block b = e.getClickedBlock();
|
||||
Player p = e.getPlayer();
|
||||
if (e.getAction() != Action.RIGHT_CLICK_BLOCK)
|
||||
return; // Didn't right click it, we dont care.
|
||||
if (!Util.canBeShop(b))
|
||||
return; // Interacted with air
|
||||
Shop shop = plugin.getShopManager().getShop(b.getLocation());
|
||||
// Make sure they're not using the non-shop half of a double chest.
|
||||
if (shop == null) {
|
||||
b = Util.getSecondHalf(b);
|
||||
if (b == null)
|
||||
return;
|
||||
shop = plugin.getShopManager().getShop(b.getLocation());
|
||||
if (shop == null)
|
||||
return;
|
||||
}
|
||||
if (!shop.getOwner().equals(p.getUniqueId())) {
|
||||
if (p.hasPermission("quickshop.other.open")) {
|
||||
p.sendMessage(MsgUtil.getMessage("bypassing-lock"));
|
||||
return;
|
||||
}
|
||||
p.sendMessage(MsgUtil.getMessage("that-is-locked"));
|
||||
e.setCancelled(true);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles hopper placement
|
||||
*/
|
||||
@EventHandler(priority = EventPriority.LOW)
|
||||
public void onPlace(BlockPlaceEvent e) {
|
||||
Block b = e.getBlock();
|
||||
try {
|
||||
if (b.getType() != Material.HOPPER)
|
||||
return;
|
||||
} catch (NoSuchFieldError er) {
|
||||
return; // Your server doesn't have hoppers
|
||||
}
|
||||
Block c = e.getBlockAgainst();
|
||||
if (Util.canBeShop(c) == false)
|
||||
return;
|
||||
Player p = e.getPlayer();
|
||||
Shop shop = plugin.getShopManager().getShop(c.getLocation());
|
||||
if (shop == null) {
|
||||
c = Util.getSecondHalf(c);
|
||||
if (c == null)
|
||||
return; // You didn't place a hopper on a shop. Meh.
|
||||
else
|
||||
shop = plugin.getShopManager().getShop(c.getLocation());
|
||||
if (shop == null)
|
||||
return;
|
||||
}
|
||||
if (p.getUniqueId().equals(shop.getOwner()) == false) {
|
||||
if (p.hasPermission("quickshop.other.open")) {
|
||||
p.sendMessage(MsgUtil.getMessage("bypassing-lock"));
|
||||
return;
|
||||
}
|
||||
p.sendMessage(MsgUtil.getMessage("that-is-locked"));
|
||||
e.setCancelled(true);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes chests when they're destroyed.
|
||||
*/
|
||||
@EventHandler(priority = EventPriority.LOW, ignoreCancelled = true)
|
||||
public void onBreak(BlockBreakEvent e) {
|
||||
Block b = e.getBlock();
|
||||
Player p = e.getPlayer();
|
||||
// If the chest was a chest
|
||||
if (Util.canBeShop(b)) {
|
||||
Shop shop = plugin.getShopManager().getShop(b.getLocation());
|
||||
if (shop == null)
|
||||
return; // Wasn't a shop
|
||||
// If they owned it or have bypass perms, they can destroy it
|
||||
if (!shop.getOwner().equals(p.getUniqueId()) && !p.hasPermission("quickshop.other.destroy")) {
|
||||
e.setCancelled(true);
|
||||
p.sendMessage(MsgUtil.getMessage("no-permission"));
|
||||
return;
|
||||
}
|
||||
} else if (b.getType() == Material.WALL_SIGN) {
|
||||
b = Util.getAttached(b);
|
||||
if (b == null)
|
||||
return;
|
||||
Shop shop = plugin.getShopManager().getShop(b.getLocation());
|
||||
if (shop == null)
|
||||
return;
|
||||
// If they're the shop owner or have bypass perms, they can destroy
|
||||
// it.
|
||||
if (!shop.getOwner().equals(p.getUniqueId()) && !p.hasPermission("quickshop.other.destroy")) {
|
||||
e.setCancelled(true);
|
||||
p.sendMessage(MsgUtil.getMessage("no-permission"));
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles shops breaking through explosions
|
||||
*/
|
||||
@EventHandler(priority = EventPriority.LOW)
|
||||
public void onExplode(EntityExplodeEvent e) {
|
||||
if (e.isCancelled())
|
||||
return;
|
||||
for (int i = 0; i < e.blockList().size(); i++) {
|
||||
Block b = e.blockList().get(i);
|
||||
Shop shop = plugin.getShopManager().getShop(b.getLocation());
|
||||
if (shop != null) {
|
||||
// ToDo: Shouldn't I be decrementing 1 here? Concurrency and
|
||||
// all..
|
||||
e.blockList().remove(b);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,189 @@
|
||||
package org.maxgamer.quickshop.Listeners;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.UUID;
|
||||
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.ChatColor;
|
||||
import org.bukkit.GameMode;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.block.Block;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.EventPriority;
|
||||
import org.bukkit.event.Listener;
|
||||
import org.bukkit.event.block.Action;
|
||||
import org.bukkit.event.player.PlayerInteractEvent;
|
||||
import org.bukkit.event.player.PlayerJoinEvent;
|
||||
import org.bukkit.event.player.PlayerMoveEvent;
|
||||
import org.bukkit.event.player.PlayerPickupItemEvent;
|
||||
import org.bukkit.event.player.PlayerQuitEvent;
|
||||
import org.bukkit.event.player.PlayerTeleportEvent;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.bukkit.util.BlockIterator;
|
||||
import org.maxgamer.quickshop.QuickShop;
|
||||
import org.maxgamer.quickshop.Shop.Info;
|
||||
import org.maxgamer.quickshop.Shop.Shop;
|
||||
import org.maxgamer.quickshop.Shop.ShopAction;
|
||||
import org.maxgamer.quickshop.Util.MsgUtil;
|
||||
import org.maxgamer.quickshop.Util.Util;
|
||||
|
||||
public class PlayerListener implements Listener {
|
||||
private QuickShop plugin;
|
||||
|
||||
public PlayerListener(QuickShop plugin) {
|
||||
this.plugin = plugin;
|
||||
}
|
||||
|
||||
/*
|
||||
* Could be useful one day private LinkedList<String> getParents(Class<?>
|
||||
* clazz){ LinkedList<String> classes = new LinkedList<String>();
|
||||
*
|
||||
* while(clazz != null){ classes.add("Extends " + ChatColor.GREEN +
|
||||
* clazz.getCanonicalName()); for(Class<?> iface : clazz.getInterfaces()){
|
||||
* classes.add("Implements " + ChatColor.RED + iface.getCanonicalName());
|
||||
* classes.addAll(getParents(iface)); }
|
||||
*
|
||||
* clazz = clazz.getSuperclass(); } return classes; }
|
||||
*/
|
||||
/**
|
||||
* Handles players left clicking a chest. Left click a NORMAL chest with
|
||||
* item : Send creation menu Left click a SHOP chest : Send purchase menu
|
||||
*/
|
||||
@SuppressWarnings("deprecation")
|
||||
@EventHandler(priority = EventPriority.MONITOR)
|
||||
public void onClick(PlayerInteractEvent e) {
|
||||
if (e.getAction() != Action.LEFT_CLICK_BLOCK)
|
||||
return;
|
||||
Block b = e.getClickedBlock();
|
||||
if (!Util.canBeShop(b) && b.getType() != Material.WALL_SIGN)
|
||||
return;
|
||||
Player p = e.getPlayer();
|
||||
if (plugin.sneak && !p.isSneaking()) {
|
||||
// Sneak only
|
||||
return;
|
||||
}
|
||||
Location loc = b.getLocation();
|
||||
ItemStack item = e.getItem();
|
||||
// Get the shop
|
||||
Shop shop = plugin.getShopManager().getShop(loc);
|
||||
// If that wasn't a shop, search nearby shops
|
||||
if (shop == null && b.getType() == Material.WALL_SIGN) {
|
||||
Block attached = Util.getAttached(b);
|
||||
if (attached != null) {
|
||||
shop = plugin.getShopManager().getShop(attached.getLocation());
|
||||
}
|
||||
}
|
||||
// Purchase handling
|
||||
if (shop != null && p.hasPermission("quickshop.use") && (plugin.sneakTrade == false || p.isSneaking())) {
|
||||
shop.onClick();
|
||||
// Text menu
|
||||
MsgUtil.sendShopInfo(p, shop);
|
||||
if (shop.isSelling()) {
|
||||
p.sendMessage(MsgUtil.getMessage("how-many-buy"));
|
||||
} else {
|
||||
int items = Util.countItems(p.getInventory(), shop.getItem());
|
||||
p.sendMessage(MsgUtil.getMessage("how-many-sell", "" + items));
|
||||
}
|
||||
// Add the new action
|
||||
HashMap<UUID, Info> actions = plugin.getShopManager().getActions();
|
||||
Info info = new Info(shop.getLocation(), ShopAction.BUY, null, null, shop);
|
||||
actions.put(p.getUniqueId(), info);
|
||||
return;
|
||||
}
|
||||
// Handles creating shops
|
||||
else if (!e.isCancelled() && shop == null && item != null && item.getType() != Material.AIR && p.hasPermission("quickshop.create.sell") && Util.canBeShop(b) && p.getGameMode() != GameMode.CREATIVE && (plugin.sneakCreate == false || p.isSneaking())) {
|
||||
if (!plugin.getShopManager().canBuildShop(p, b, e.getBlockFace())) {
|
||||
// As of the new checking system, most plugins will tell the
|
||||
// player why they can't create a shop there.
|
||||
// So telling them a message would cause spam etc.
|
||||
return;
|
||||
}
|
||||
if (Util.getSecondHalf(b) != null && !p.hasPermission("quickshop.create.double")) {
|
||||
p.sendMessage(MsgUtil.getMessage("no-double-chests"));
|
||||
return;
|
||||
}
|
||||
if (Util.isBlacklisted(item.getType()) && !p.hasPermission("quickshop.bypass." + item.getTypeId())) {
|
||||
p.sendMessage(MsgUtil.getMessage("blacklisted-item"));
|
||||
return;
|
||||
}
|
||||
// Finds out where the sign should be placed for the shop
|
||||
Block last = null;
|
||||
Location from = p.getLocation().clone();
|
||||
from.setY(b.getY());
|
||||
from.setPitch(0);
|
||||
BlockIterator bIt = new BlockIterator(from, 0, 7);
|
||||
while (bIt.hasNext()) {
|
||||
Block n = bIt.next();
|
||||
if (n.equals(b))
|
||||
break;
|
||||
last = n;
|
||||
}
|
||||
// Send creation menu.
|
||||
Info info = new Info(b.getLocation(), ShopAction.CREATE, e.getItem(), last);
|
||||
plugin.getShopManager().getActions().put(p.getUniqueId(), info);
|
||||
p.sendMessage(MsgUtil.getMessage("how-much-to-trade-for", Util.getName(info.getItem())));
|
||||
}
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.HIGH)
|
||||
/**
|
||||
* Waits for a player to move too far from a shop, then cancels the menu.
|
||||
*/
|
||||
public void onMove(PlayerMoveEvent e) {
|
||||
if (e.isCancelled())
|
||||
return;
|
||||
Info info = plugin.getShopManager().getActions().get(e.getPlayer().getUniqueId());
|
||||
if (info != null) {
|
||||
Player p = e.getPlayer();
|
||||
Location loc1 = info.getLocation();
|
||||
Location loc2 = p.getLocation();
|
||||
if (loc1.getWorld() != loc2.getWorld() || loc1.distanceSquared(loc2) > 25) {
|
||||
if (info.getAction() == ShopAction.CREATE) {
|
||||
p.sendMessage(MsgUtil.getMessage("shop-creation-cancelled"));
|
||||
} else if (info.getAction() == ShopAction.BUY) {
|
||||
p.sendMessage(MsgUtil.getMessage("shop-purchase-cancelled"));
|
||||
}
|
||||
plugin.getShopManager().getActions().remove(p.getUniqueId());
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
public void onTeleport(PlayerTeleportEvent e) {
|
||||
PlayerMoveEvent me = new PlayerMoveEvent(e.getPlayer(), e.getFrom(), e.getTo());
|
||||
onMove(me);
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
public void onJoin(final PlayerJoinEvent e) {
|
||||
// Notify the player any messages they were sent
|
||||
Bukkit.getScheduler().runTaskLater(QuickShop.instance, new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
MsgUtil.flush(e.getPlayer());
|
||||
}
|
||||
}, 60);
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
public void onPlayerQuit(PlayerQuitEvent e) {
|
||||
// Remove them from the menu
|
||||
plugin.getShopManager().getActions().remove(e.getPlayer().getUniqueId());
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
public void onPlayerPickup(PlayerPickupItemEvent e) {
|
||||
ItemStack stack = e.getItem().getItemStack();
|
||||
try {
|
||||
if (stack.getItemMeta().getDisplayName().startsWith(ChatColor.RED + "QuickShop ")) {
|
||||
e.setCancelled(true);
|
||||
// You shouldn't be able to pick up that...
|
||||
}
|
||||
} catch (NullPointerException ex) {
|
||||
} // if meta/displayname/stack is null. We don't really care in that
|
||||
// case.
|
||||
}
|
||||
}
|
@ -0,0 +1,79 @@
|
||||
package org.maxgamer.quickshop.Listeners;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
import org.bukkit.Chunk;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.Listener;
|
||||
import org.bukkit.event.world.WorldLoadEvent;
|
||||
import org.bukkit.event.world.WorldUnloadEvent;
|
||||
import org.maxgamer.quickshop.QuickShop;
|
||||
import org.maxgamer.quickshop.Shop.Shop;
|
||||
import org.maxgamer.quickshop.Shop.ShopChunk;
|
||||
|
||||
public class WorldListener implements Listener {
|
||||
QuickShop plugin;
|
||||
|
||||
public WorldListener(QuickShop plugin) {
|
||||
this.plugin = plugin;
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
public void onWorldLoad(WorldLoadEvent e) {
|
||||
/* *************************************
|
||||
* This listener fixes any broken world references. Such as hashmap
|
||||
* lookups will fail, because the World reference is different, but the
|
||||
* world value is the same. ************************************
|
||||
*/
|
||||
World world = e.getWorld();
|
||||
// New world data
|
||||
HashMap<ShopChunk, HashMap<Location, Shop>> inWorld = new HashMap<ShopChunk, HashMap<Location, Shop>>(1);
|
||||
// Old world data
|
||||
HashMap<ShopChunk, HashMap<Location, Shop>> oldInWorld = plugin.getShopManager().getShops(world.getName());
|
||||
// Nothing in the old world, therefore we don't care. No locations to
|
||||
// update.
|
||||
if (oldInWorld == null)
|
||||
return;
|
||||
for (Entry<ShopChunk, HashMap<Location, Shop>> oldInChunk : oldInWorld.entrySet()) {
|
||||
HashMap<Location, Shop> inChunk = new HashMap<Location, Shop>(1);
|
||||
// Put the new chunk were the old chunk was
|
||||
inWorld.put(oldInChunk.getKey(), inChunk);
|
||||
for (Entry<Location, Shop> entry : oldInChunk.getValue().entrySet()) {
|
||||
Shop shop = entry.getValue();
|
||||
shop.getLocation().setWorld(world);
|
||||
inChunk.put(shop.getLocation(), shop);
|
||||
}
|
||||
}
|
||||
// Done - Now we can store the new world dataz!
|
||||
plugin.getShopManager().getShops().put(world.getName(), inWorld);
|
||||
// This is a workaround, because I don't get parsed chunk events when a
|
||||
// world first loads....
|
||||
// So manually tell all of these shops they're loaded.
|
||||
for (Chunk chunk : world.getLoadedChunks()) {
|
||||
HashMap<Location, Shop> inChunk = plugin.getShopManager().getShops(chunk);
|
||||
if (inChunk == null)
|
||||
continue;
|
||||
for (Shop shop : inChunk.values()) {
|
||||
shop.onLoad();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
public void onWorldUnload(WorldUnloadEvent e) {
|
||||
// This is a workaround, because I don't get parsed chunk events when a
|
||||
// world unloads, I think...
|
||||
// So manually tell all of these shops they're unloaded.
|
||||
for (Chunk chunk : e.getWorld().getLoadedChunks()) {
|
||||
HashMap<Location, Shop> inChunk = plugin.getShopManager().getShops(chunk);
|
||||
if (inChunk == null)
|
||||
continue;
|
||||
for (Shop shop : inChunk.values()) {
|
||||
shop.onUnload();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
400
src/main/java/org/maxgamer/quickshop/QuickShop.java
Normal file
400
src/main/java/org/maxgamer/quickshop/QuickShop.java
Normal file
@ -0,0 +1,400 @@
|
||||
package org.maxgamer.quickshop;
|
||||
|
||||
import java.io.File;
|
||||
import java.sql.Connection;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.Timestamp;
|
||||
import java.util.Calendar;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.UUID;
|
||||
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.ChatColor;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.configuration.ConfigurationSection;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.inventory.InventoryHolder;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
import org.bukkit.scheduler.BukkitTask;
|
||||
import org.maxgamer.quickshop.Command.QS;
|
||||
import org.maxgamer.quickshop.Database.Database;
|
||||
import org.maxgamer.quickshop.Database.Database.ConnectionException;
|
||||
import org.maxgamer.quickshop.Database.DatabaseCore;
|
||||
import org.maxgamer.quickshop.Database.DatabaseHelper;
|
||||
import org.maxgamer.quickshop.Database.MySQLCore;
|
||||
import org.maxgamer.quickshop.Database.SQLiteCore;
|
||||
import org.maxgamer.quickshop.Economy.Economy;
|
||||
import org.maxgamer.quickshop.Economy.EconomyCore;
|
||||
import org.maxgamer.quickshop.Economy.Economy_Vault;
|
||||
import org.maxgamer.quickshop.Listeners.BlockListener;
|
||||
import org.maxgamer.quickshop.Listeners.ChatListener;
|
||||
import org.maxgamer.quickshop.Listeners.ChunkListener;
|
||||
import org.maxgamer.quickshop.Listeners.HeroChatListener;
|
||||
import org.maxgamer.quickshop.Listeners.LockListener;
|
||||
import org.maxgamer.quickshop.Listeners.PlayerListener;
|
||||
import org.maxgamer.quickshop.Listeners.WorldListener;
|
||||
import org.maxgamer.quickshop.Shop.ContainerShop;
|
||||
import org.maxgamer.quickshop.Shop.Shop;
|
||||
import org.maxgamer.quickshop.Shop.ShopManager;
|
||||
import org.maxgamer.quickshop.Shop.ShopType;
|
||||
import org.maxgamer.quickshop.Util.Converter;
|
||||
import org.maxgamer.quickshop.Util.MsgUtil;
|
||||
import org.maxgamer.quickshop.Util.Util;
|
||||
import org.maxgamer.quickshop.Watcher.ItemWatcher;
|
||||
import org.maxgamer.quickshop.Watcher.LogWatcher;
|
||||
|
||||
|
||||
public class QuickShop extends JavaPlugin {
|
||||
/** The active instance of QuickShop */
|
||||
public static QuickShop instance;
|
||||
/** The economy we hook into for transactions */
|
||||
private Economy economy;
|
||||
/** The Shop Manager used to store shops */
|
||||
private ShopManager shopManager;
|
||||
/**
|
||||
* A set of players who have been warned
|
||||
* ("Your shop isn't automatically locked")
|
||||
*/
|
||||
public HashSet<String> warnings = new HashSet<String>();
|
||||
/** The database for storing all our data for persistence */
|
||||
private Database database;
|
||||
// Listeners - We decide which one to use at runtime
|
||||
private ChatListener chatListener;
|
||||
private HeroChatListener heroChatListener;
|
||||
// Listeners (These don't)
|
||||
private BlockListener blockListener = new BlockListener(this);
|
||||
private PlayerListener playerListener = new PlayerListener(this);
|
||||
private ChunkListener chunkListener = new ChunkListener(this);
|
||||
private WorldListener worldListener = new WorldListener(this);
|
||||
private BukkitTask itemWatcherTask;
|
||||
private LogWatcher logWatcher;
|
||||
/** Whether players are required to sneak to create/buy from a shop */
|
||||
public boolean sneak;
|
||||
/** Whether players are required to sneak to create a shop */
|
||||
public boolean sneakCreate;
|
||||
/** Whether players are required to sneak to trade with a shop */
|
||||
public boolean sneakTrade;
|
||||
/** Whether we should use display items or not */
|
||||
public boolean display = true;
|
||||
/**
|
||||
* Whether we players are charged a fee to change the price on their shop
|
||||
* (To help deter endless undercutting
|
||||
*/
|
||||
public boolean priceChangeRequiresFee = false;
|
||||
/** Whether or not to limit players shop amounts */
|
||||
public boolean limit = false;
|
||||
private HashMap<String, Integer> limits = new HashMap<String, Integer>();
|
||||
/** Use SpoutPlugin to get item / block names */
|
||||
public boolean useSpout = false;
|
||||
// private Metrics metrics;
|
||||
/** Whether debug info should be shown in the console */
|
||||
public static boolean debug = false;
|
||||
|
||||
/** The plugin metrics from Hidendra */
|
||||
// public Metrics getMetrics(){ return metrics; }
|
||||
public int getShopLimit(Player p) {
|
||||
int max = getConfig().getInt("limits.default");
|
||||
for (Entry<String, Integer> entry : limits.entrySet()) {
|
||||
if (entry.getValue() > max && p.hasPermission(entry.getKey()))
|
||||
max = entry.getValue();
|
||||
}
|
||||
return max;
|
||||
}
|
||||
|
||||
public void onEnable() {
|
||||
instance = this;
|
||||
saveDefaultConfig(); // Creates the config folder and copies config.yml
|
||||
// (If one doesn't exist) as required.
|
||||
reloadConfig(); // Reloads messages.yml too, aswell as config.yml and
|
||||
// others.
|
||||
getConfig().options().copyDefaults(true); // Load defaults.
|
||||
if (getConfig().contains("debug"))
|
||||
debug = true;
|
||||
if (loadEcon() == false)
|
||||
return;
|
||||
// Create the shop manager.
|
||||
this.shopManager = new ShopManager(this);
|
||||
if (this.display) {
|
||||
// Display item handler thread
|
||||
getLogger().info("Starting item scheduler");
|
||||
ItemWatcher itemWatcher = new ItemWatcher(this);
|
||||
itemWatcherTask = Bukkit.getScheduler().runTaskTimer(this, itemWatcher, 600, 600);
|
||||
}
|
||||
if (this.getConfig().getBoolean("log-actions")) {
|
||||
// Logger Handler
|
||||
this.logWatcher = new LogWatcher(this, new File(this.getDataFolder(), "qs.log"));
|
||||
logWatcher.task = Bukkit.getScheduler().runTaskTimerAsynchronously(this, this.logWatcher, 150, 150);
|
||||
}
|
||||
if (getConfig().getBoolean("shop.lock")) {
|
||||
LockListener ll = new LockListener(this);
|
||||
getServer().getPluginManager().registerEvents(ll, this);
|
||||
}
|
||||
ConfigurationSection limitCfg = this.getConfig().getConfigurationSection("limits");
|
||||
if (limitCfg != null) {
|
||||
getLogger().info("Limit cfg found...");
|
||||
this.limit = limitCfg.getBoolean("use", false);
|
||||
getLogger().info("Limits.use: " + limit);
|
||||
limitCfg = limitCfg.getConfigurationSection("ranks");
|
||||
for (String key : limitCfg.getKeys(true)) {
|
||||
limits.put(key, limitCfg.getInt(key));
|
||||
}
|
||||
getLogger().info(limits.toString());
|
||||
}
|
||||
try {
|
||||
ConfigurationSection dbCfg = getConfig().getConfigurationSection("database");
|
||||
DatabaseCore dbCore;
|
||||
if (dbCfg.getBoolean("mysql")) {
|
||||
// MySQL database - Required database be created first.
|
||||
String user = dbCfg.getString("user");
|
||||
String pass = dbCfg.getString("password");
|
||||
String host = dbCfg.getString("host");
|
||||
String port = dbCfg.getString("port");
|
||||
String database = dbCfg.getString("database");
|
||||
dbCore = new MySQLCore(host, user, pass, database, port);
|
||||
} else {
|
||||
// SQLite database - Doing this handles file creation
|
||||
dbCore = new SQLiteCore(new File(this.getDataFolder(), "shops.db"));
|
||||
}
|
||||
this.database = new Database(dbCore);
|
||||
// Make the database up to date
|
||||
DatabaseHelper.setup(getDB());
|
||||
} catch (ConnectionException e) {
|
||||
e.printStackTrace();
|
||||
getLogger().severe("Error connecting to database. Aborting plugin load.");
|
||||
getServer().getPluginManager().disablePlugin(this);
|
||||
return;
|
||||
} catch (SQLException e) {
|
||||
e.printStackTrace();
|
||||
getLogger().severe("Error setting up database. Aborting plugin load.");
|
||||
getServer().getPluginManager().disablePlugin(this);
|
||||
return;
|
||||
}
|
||||
/* Load shops from database to memory */
|
||||
int count = 0; // Shops count
|
||||
Connection con;
|
||||
try {
|
||||
getLogger().info("Loading shops from database...");
|
||||
int res = Converter.convert();
|
||||
if (res < 0) {
|
||||
System.out.println("Could not convert shops. Exitting.");
|
||||
return;
|
||||
}
|
||||
if (res > 0) {
|
||||
System.out.println("Conversion success. Continuing...");
|
||||
}
|
||||
con = database.getConnection();
|
||||
PreparedStatement ps = con.prepareStatement("SELECT * FROM shops");
|
||||
ResultSet rs = ps.executeQuery();
|
||||
int errors = 0;
|
||||
while (rs.next()) {
|
||||
int x = 0;
|
||||
int y = 0;
|
||||
int z = 0;
|
||||
String worldName = null;
|
||||
try {
|
||||
x = rs.getInt("x");
|
||||
y = rs.getInt("y");
|
||||
z = rs.getInt("z");
|
||||
worldName = rs.getString("world");
|
||||
World world = Bukkit.getWorld(worldName);
|
||||
ItemStack item = Util.deserialize(rs.getString("itemConfig"));
|
||||
String owner = rs.getString("owner");
|
||||
double price = rs.getDouble("price");
|
||||
Location loc = new Location(world, x, y, z);
|
||||
/* Skip invalid shops, if we know of any */
|
||||
if (world != null && (loc.getBlock().getState() instanceof InventoryHolder) == false) {
|
||||
getLogger().info("Shop is not an InventoryHolder in " + rs.getString("world") + " at: " + x + ", " + y + ", " + z + ". Deleting.");
|
||||
PreparedStatement delps = getDB().getConnection().prepareStatement("DELETE FROM shops WHERE x = ? AND y = ? and z = ? and world = ?");
|
||||
delps.setInt(1, x);
|
||||
delps.setInt(2, y);
|
||||
delps.setInt(3, z);
|
||||
delps.setString(4, worldName);
|
||||
delps.execute();
|
||||
continue;
|
||||
}
|
||||
int type = rs.getInt("type");
|
||||
Shop shop = new ContainerShop(loc, price, item, UUID.fromString(owner));
|
||||
shop.setUnlimited(rs.getBoolean("unlimited"));
|
||||
shop.setShopType(ShopType.fromID(type));
|
||||
shopManager.loadShop(rs.getString("world"), shop);
|
||||
if (loc.getWorld() != null && loc.getChunk().isLoaded()) {
|
||||
shop.onLoad();
|
||||
}
|
||||
count++;
|
||||
} catch (Exception e) {
|
||||
errors++;
|
||||
e.printStackTrace();
|
||||
getLogger().severe("Error loading a shop! Coords: " + worldName + " (" + x + ", " + y + ", " + z + ")...");
|
||||
if (errors < 3) {
|
||||
getLogger().info("Deleting the shop...");
|
||||
PreparedStatement delps = getDB().getConnection().prepareStatement("DELETE FROM shops WHERE x = ? AND y = ? and z = ? and world = ?");
|
||||
delps.setInt(1, x);
|
||||
delps.setInt(2, y);
|
||||
delps.setInt(3, z);
|
||||
delps.setString(4, worldName);
|
||||
delps.execute();
|
||||
} else {
|
||||
getLogger().severe("Multiple errors in shops - Something seems to be wrong with your shops database! Please check it out immediately!");
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (SQLException e) {
|
||||
e.printStackTrace();
|
||||
getLogger().severe("Could not load shops.");
|
||||
}
|
||||
getLogger().info("Loaded " + count + " shops.");
|
||||
MsgUtil.loadTransactionMessages();
|
||||
MsgUtil.clean();
|
||||
// Register events
|
||||
getLogger().info("Registering Listeners");
|
||||
Bukkit.getServer().getPluginManager().registerEvents(blockListener, this);
|
||||
Bukkit.getServer().getPluginManager().registerEvents(playerListener, this);
|
||||
if (this.display) {
|
||||
Bukkit.getServer().getPluginManager().registerEvents(chunkListener, this);
|
||||
}
|
||||
Bukkit.getServer().getPluginManager().registerEvents(worldListener, this);
|
||||
if (Bukkit.getPluginManager().getPlugin("Herochat") != null) {
|
||||
this.getLogger().info("Found Herochat... Hooking!");
|
||||
this.heroChatListener = new HeroChatListener(this);
|
||||
Bukkit.getServer().getPluginManager().registerEvents(heroChatListener, this);
|
||||
} else {
|
||||
this.chatListener = new ChatListener(this);
|
||||
Bukkit.getServer().getPluginManager().registerEvents(chatListener, this);
|
||||
}
|
||||
// Command handlers
|
||||
QS commandExecutor = new QS(this);
|
||||
getCommand("qs").setExecutor(commandExecutor);
|
||||
if (getConfig().getInt("shop.find-distance") > 100) {
|
||||
getLogger().severe("Shop.find-distance is too high! Pick a number under 100!");
|
||||
}
|
||||
/**
|
||||
* If the server has Spout we can get the names of custom items. Latest
|
||||
* SpoutPlugin http://get.spout.org/1412/SpoutPlugin.jar
|
||||
* http://build.spout.org/view/Legacy/job/SpoutPlugin/1412/
|
||||
*/
|
||||
// if (Bukkit.getPluginManager().getPlugin("Spout") != null) {
|
||||
// this.getLogger().info("Found Spout...");
|
||||
// this.useSpout = true;
|
||||
// } else {
|
||||
// this.useSpout = false;
|
||||
// }
|
||||
getLogger().info("QuickShop loaded!");
|
||||
}
|
||||
|
||||
/** Reloads QuickShops config */
|
||||
@Override
|
||||
public void reloadConfig() {
|
||||
super.reloadConfig();
|
||||
// Load quick variables
|
||||
this.display = this.getConfig().getBoolean("shop.display-items");
|
||||
this.sneak = this.getConfig().getBoolean("shop.sneak-only");
|
||||
this.sneakCreate = this.getConfig().getBoolean("shop.sneak-to-create");
|
||||
this.sneakTrade = this.getConfig().getBoolean("shop.sneak-to-trade");
|
||||
this.priceChangeRequiresFee = this.getConfig().getBoolean("shop.price-change-requires-fee");
|
||||
MsgUtil.loadCfgMessages();
|
||||
}
|
||||
|
||||
/**
|
||||
* Tries to load the economy and its core. If this fails, it will try to use
|
||||
* vault. If that fails, it will return false.
|
||||
*
|
||||
* @return true if successful, false if the core is invalid or is not found,
|
||||
* and vault cannot be used.
|
||||
*/
|
||||
public boolean loadEcon() {
|
||||
EconomyCore core = new Economy_Vault();
|
||||
if (core == null || !core.isValid()) {
|
||||
// getLogger().severe("Economy is not valid!");
|
||||
getLogger().severe("QuickShop could not hook an economy!");
|
||||
getLogger().severe("QuickShop CANNOT start!");
|
||||
this.getPluginLoader().disablePlugin(this);
|
||||
// if(econ.equals("Vault"))
|
||||
// getLogger().severe("(Does Vault have an Economy to hook into?!)");
|
||||
return false;
|
||||
} else {
|
||||
this.economy = new Economy(core);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public void onDisable() {
|
||||
if (itemWatcherTask != null) {
|
||||
itemWatcherTask.cancel();
|
||||
}
|
||||
if (logWatcher != null) {
|
||||
logWatcher.task.cancel();
|
||||
logWatcher.close(); // Closes the file
|
||||
}
|
||||
/* Remove all display items, and any dupes we can find */
|
||||
shopManager.clear();
|
||||
/* Empty the buffer */
|
||||
database.close();
|
||||
try {
|
||||
this.database.getConnection().close();
|
||||
} catch (SQLException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
this.warnings.clear();
|
||||
this.reloadConfig();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the economy for moving currency around
|
||||
*
|
||||
* @return The economy for moving currency around
|
||||
*/
|
||||
public EconomyCore getEcon() {
|
||||
return economy;
|
||||
}
|
||||
|
||||
/**
|
||||
* Logs the given string to qs.log, if QuickShop is configured to do so.
|
||||
*
|
||||
* @param s
|
||||
* The string to log. It will be prefixed with the date and time.
|
||||
*/
|
||||
public void log(String s) {
|
||||
if (this.logWatcher == null)
|
||||
return;
|
||||
Date date = Calendar.getInstance().getTime();
|
||||
Timestamp time = new Timestamp(date.getTime());
|
||||
this.logWatcher.add("[" + time.toString() + "] " + s);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Returns the database handler for queries etc.
|
||||
*/
|
||||
public Database getDB() {
|
||||
return this.database;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prints debug information if QuickShop is configured to do so.
|
||||
*
|
||||
* @param s
|
||||
* The string to print.
|
||||
*/
|
||||
public void debug(String s) {
|
||||
if (!debug)
|
||||
return;
|
||||
this.getLogger().info(ChatColor.YELLOW + "[Debug] " + s);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the ShopManager. This is used for fetching, adding and removing
|
||||
* shops.
|
||||
*
|
||||
* @return The ShopManager.
|
||||
*/
|
||||
public ShopManager getShopManager() {
|
||||
return this.shopManager;
|
||||
}
|
||||
}
|
1
src/main/java/org/maxgamer/quickshop/Shop/.gitignore
vendored
Normal file
1
src/main/java/org/maxgamer/quickshop/Shop/.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
||||
/Shop.java
|
659
src/main/java/org/maxgamer/quickshop/Shop/ContainerShop.java
Normal file
659
src/main/java/org/maxgamer/quickshop/Shop/ContainerShop.java
Normal file
@ -0,0 +1,659 @@
|
||||
package org.maxgamer.quickshop.Shop;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
import java.util.logging.Level;
|
||||
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.ChatColor;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.block.Block;
|
||||
import org.bukkit.block.Sign;
|
||||
import org.bukkit.enchantments.Enchantment;
|
||||
import org.bukkit.entity.Item;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.player.PlayerTeleportEvent.TeleportCause;
|
||||
import org.bukkit.inventory.Inventory;
|
||||
import org.bukkit.inventory.InventoryHolder;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.maxgamer.quickshop.QuickShop;
|
||||
import org.maxgamer.quickshop.Util.MsgUtil;
|
||||
import org.maxgamer.quickshop.Util.Util;
|
||||
|
||||
public class ContainerShop implements Shop {
|
||||
private Location loc;
|
||||
private double price;
|
||||
private UUID owner;
|
||||
private ItemStack item;
|
||||
private DisplayItem displayItem;
|
||||
private boolean unlimited;
|
||||
private ShopType shopType;
|
||||
private QuickShop plugin;
|
||||
|
||||
/**
|
||||
* Returns a clone of this shop. References to the same display item,
|
||||
* itemstack, location and owner as this shop does. Do not modify them or
|
||||
* you will modify this shop.
|
||||
*
|
||||
* **NOT A DEEP CLONE**
|
||||
*/
|
||||
public ContainerShop clone() {
|
||||
return new ContainerShop(this);
|
||||
}
|
||||
|
||||
private ContainerShop(ContainerShop s) {
|
||||
this.displayItem = s.displayItem;
|
||||
this.shopType = s.shopType;
|
||||
this.item = s.item;
|
||||
this.loc = s.loc;
|
||||
this.plugin = s.plugin;
|
||||
this.unlimited = s.unlimited;
|
||||
this.owner = s.owner;
|
||||
this.price = s.price;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a new shop.
|
||||
*
|
||||
* @param loc
|
||||
* The location of the chest block
|
||||
* @param price
|
||||
* The cost per item
|
||||
* @param item
|
||||
* The itemstack with the properties we want. This is .cloned, no
|
||||
* need to worry about references
|
||||
* @param owner
|
||||
* The player who owns this shop.
|
||||
*/
|
||||
public ContainerShop(Location loc, double price, ItemStack item, UUID owner) {
|
||||
this.loc = loc;
|
||||
this.price = price;
|
||||
this.owner = owner;
|
||||
this.item = item.clone();
|
||||
this.plugin = (QuickShop) Bukkit.getPluginManager().getPlugin("QuickShop");
|
||||
this.item.setAmount(1);
|
||||
if (plugin.display) {
|
||||
this.displayItem = new DisplayItem(this, this.item);
|
||||
}
|
||||
this.shopType = ShopType.SELLING;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of items this shop has in stock.
|
||||
*
|
||||
* @return The number of items available for purchase.
|
||||
*/
|
||||
public int getRemainingStock() {
|
||||
if (this.unlimited)
|
||||
return 10000;
|
||||
return Util.countItems(this.getInventory(), this.getItem());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of free spots in the chest for the particular item.
|
||||
*
|
||||
* @param stackSize
|
||||
* @return
|
||||
*/
|
||||
public int getRemainingSpace() {
|
||||
if (this.unlimited)
|
||||
return 10000;
|
||||
return Util.countSpace(this.getInventory(), item);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the ItemStack matches what this shop is selling/buying
|
||||
*
|
||||
* @param item
|
||||
* The ItemStack
|
||||
* @return True if the ItemStack is the same (Excludes amounts)
|
||||
*/
|
||||
public boolean matches(ItemStack item) {
|
||||
return Util.matches(this.item, item);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the shop that shares it's inventory with this one.
|
||||
*
|
||||
* @return the shop that shares it's inventory with this one. Will return
|
||||
* null if this shop is not attached to another.
|
||||
*/
|
||||
public ContainerShop getAttachedShop() {
|
||||
Block c = Util.getSecondHalf(this.getLocation().getBlock());
|
||||
if (c == null)
|
||||
return null;
|
||||
Shop shop = plugin.getShopManager().getShop(c.getLocation());
|
||||
return shop == null ? null : (ContainerShop) shop;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if this shop is a double chest, and the other half is
|
||||
* selling/buying the same as this is buying/selling.
|
||||
*
|
||||
* @return true if this shop is a double chest, and the other half is
|
||||
* selling/buying the same as this is buying/selling.
|
||||
*/
|
||||
public boolean isDoubleShop() {
|
||||
ContainerShop nextTo = this.getAttachedShop();
|
||||
if (nextTo == null) {
|
||||
return false;
|
||||
}
|
||||
if (nextTo.matches(this.getItem())) {
|
||||
// They're both trading the same item
|
||||
if (this.getShopType() == nextTo.getShopType()) {
|
||||
// They're both buying or both selling => Not a double shop,
|
||||
// just two shops.
|
||||
return false;
|
||||
} else {
|
||||
// One is buying, one is selling.
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The location of the shops chest
|
||||
*/
|
||||
public Location getLocation() {
|
||||
return this.loc;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The price per item this shop is selling
|
||||
*/
|
||||
public double getPrice() {
|
||||
return this.price;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the price of the shop. Does not update it in the database. Use
|
||||
* shop.update() for that.
|
||||
*
|
||||
* @param price
|
||||
* The new price of the shop.
|
||||
*/
|
||||
public void setPrice(double price) {
|
||||
this.price = price;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The ItemStack type of this shop
|
||||
*/
|
||||
public Material getMaterial() {
|
||||
return this.item.getType();
|
||||
}
|
||||
|
||||
/**
|
||||
* Upates the shop into the database.
|
||||
*/
|
||||
public void update() {
|
||||
int x = this.getLocation().getBlockX();
|
||||
int y = this.getLocation().getBlockY();
|
||||
int z = this.getLocation().getBlockZ();
|
||||
String world = this.getLocation().getWorld().getName();
|
||||
int unlimited = this.isUnlimited() ? 1 : 0;
|
||||
String q = "UPDATE shops SET owner = ?, itemConfig = ?, unlimited = ?, type = ?, price = ? WHERE x = ? AND y = ? and z = ? and world = ?";
|
||||
try {
|
||||
plugin.getDB().execute(q, this.getOwner().toString(), Util.serialize(this.getItem()), unlimited, shopType.toID(), this.getPrice(), x, y, z, world);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
System.out.println("Could not update shop in database! Changes will revert after a reboot!");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The durability of the item
|
||||
*/
|
||||
public short getDurability() {
|
||||
return this.item.getDurability();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The chest this shop is based on.
|
||||
*/
|
||||
public Inventory getInventory() {
|
||||
InventoryHolder container = (InventoryHolder) this.loc.getBlock().getState();
|
||||
return container.getInventory();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The name of the player who owns the shop.
|
||||
*/
|
||||
public UUID getOwner() {
|
||||
return this.owner;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The enchantments the shop has on its items.
|
||||
*/
|
||||
public Map<Enchantment, Integer> getEnchants() {
|
||||
return this.item.getItemMeta().getEnchants();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Returns a dummy itemstack of the item this shop is selling.
|
||||
*/
|
||||
public ItemStack getItem() {
|
||||
return item;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes an item from the shop.
|
||||
*
|
||||
* @param item
|
||||
* The itemstack. The amount does not matter, just everything
|
||||
* else
|
||||
* @param amount
|
||||
* The amount to remove from the shop.
|
||||
*/
|
||||
public void remove(ItemStack item, int amount) {
|
||||
if (this.unlimited)
|
||||
return;
|
||||
Inventory inv = this.getInventory();
|
||||
int remains = amount;
|
||||
while (remains > 0) {
|
||||
int stackSize = Math.min(remains, item.getMaxStackSize());
|
||||
item.setAmount(stackSize);
|
||||
inv.removeItem(item);
|
||||
remains = remains - stackSize;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add an item to shops chest.
|
||||
*
|
||||
* @param item
|
||||
* The itemstack. The amount does not matter, just everything
|
||||
* else
|
||||
* @param amount
|
||||
* The amount to add to the shop.
|
||||
*/
|
||||
public void add(ItemStack item, int amount) {
|
||||
if (this.unlimited)
|
||||
return;
|
||||
Inventory inv = this.getInventory();
|
||||
int remains = amount;
|
||||
while (remains > 0) {
|
||||
int stackSize = Math.min(remains, item.getMaxStackSize());
|
||||
item.setAmount(stackSize);
|
||||
inv.addItem(item);
|
||||
remains = remains - stackSize;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sells amount of item to Player p. Does NOT check our inventory, or
|
||||
* balances
|
||||
*
|
||||
* @param p
|
||||
* The player to sell to
|
||||
* @param amount
|
||||
* The amount to sell
|
||||
*/
|
||||
public void sell(Player p, int amount) {
|
||||
if (amount < 0)
|
||||
this.buy(p, -amount);
|
||||
// Items to drop on floor
|
||||
ArrayList<ItemStack> floor = new ArrayList<ItemStack>(5);
|
||||
Inventory pInv = p.getInventory();
|
||||
if (this.isUnlimited()) {
|
||||
ItemStack item = this.item.clone();
|
||||
while (amount > 0) {
|
||||
int stackSize = Math.min(amount, this.item.getMaxStackSize());
|
||||
item.setAmount(stackSize);
|
||||
pInv.addItem(item);
|
||||
amount -= stackSize;
|
||||
}
|
||||
} else {
|
||||
ItemStack[] chestContents = this.getInventory().getContents();
|
||||
for (int i = 0; amount > 0 && i < chestContents.length; i++) {
|
||||
// Can't clone it here, it could be null
|
||||
ItemStack item = chestContents[i];
|
||||
if (item != null && this.matches(item)) {
|
||||
// Copy it, we don't want to interfere
|
||||
item = item.clone();
|
||||
// Amount = total, item.getAmount() = how many items in the
|
||||
// stack
|
||||
int stackSize = Math.min(amount, item.getAmount());
|
||||
// If Amount is item.getAmount(), then this sets the amount
|
||||
// to 0
|
||||
// Else it sets it to the remainder
|
||||
chestContents[i].setAmount(chestContents[i].getAmount() - stackSize);
|
||||
// We can modify this, it is a copy.
|
||||
item.setAmount(stackSize);
|
||||
// Add the items to the players inventory
|
||||
floor.addAll(pInv.addItem(item).values());
|
||||
amount -= stackSize;
|
||||
}
|
||||
}
|
||||
// We now have to update the chests inventory manually.
|
||||
this.getInventory().setContents(chestContents);
|
||||
}
|
||||
for (int i = 0; i < floor.size(); i++) {
|
||||
p.getWorld().dropItem(p.getLocation(), floor.get(i));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Buys amount of item from Player p. Does NOT check our inventory, or
|
||||
* balances
|
||||
*
|
||||
* @param p
|
||||
* The player to buy from
|
||||
* @param item
|
||||
* The itemStack to buy
|
||||
* @param amount
|
||||
* The amount to buy
|
||||
*/
|
||||
public void buy(Player p, int amount) {
|
||||
if (amount < 0)
|
||||
this.sell(p, -amount);
|
||||
if (this.isUnlimited()) {
|
||||
ItemStack[] contents = p.getInventory().getContents();
|
||||
for (int i = 0; amount > 0 && i < contents.length; i++) {
|
||||
ItemStack stack = contents[i];
|
||||
if (stack == null)
|
||||
continue; // No item
|
||||
if (matches(stack)) {
|
||||
int stackSize = Math.min(amount, stack.getAmount());
|
||||
stack.setAmount(stack.getAmount() - stackSize);
|
||||
amount -= stackSize;
|
||||
}
|
||||
}
|
||||
// Send the players new inventory to them
|
||||
p.getInventory().setContents(contents);
|
||||
// This should not happen.
|
||||
if (amount > 0) {
|
||||
plugin.getLogger().log(Level.WARNING, "Could not take all items from a players inventory on purchase! " + p.getName() + ", missing: " + amount + ", item: " + this.getDataName() + "!");
|
||||
}
|
||||
} else {
|
||||
ItemStack[] playerContents = p.getInventory().getContents();
|
||||
Inventory chestInv = this.getInventory();
|
||||
for (int i = 0; amount > 0 && i < playerContents.length; i++) {
|
||||
ItemStack item = playerContents[i];
|
||||
if (item != null && this.matches(item)) {
|
||||
// Copy it, we don't want to interfere
|
||||
item = item.clone();
|
||||
// Amount = total, item.getAmount() = how many items in the
|
||||
// stack
|
||||
int stackSize = Math.min(amount, item.getAmount());
|
||||
// If Amount is item.getAmount(), then this sets the amount
|
||||
// to 0
|
||||
// Else it sets it to the remainder
|
||||
playerContents[i].setAmount(playerContents[i].getAmount() - stackSize);
|
||||
// We can modify this, it is a copy.
|
||||
item.setAmount(stackSize);
|
||||
// Add the items to the players inventory
|
||||
chestInv.addItem(item);
|
||||
amount -= stackSize;
|
||||
}
|
||||
}
|
||||
// Now update the players inventory.
|
||||
p.getInventory().setContents(playerContents);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Changes the owner of this shop to the given player.
|
||||
*
|
||||
* @param owner
|
||||
* The name of the owner. You must do shop.update() after to save
|
||||
* it after a reboot.
|
||||
*/
|
||||
public void setOwner(UUID owner) {
|
||||
this.owner = owner;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the display item associated with this shop.
|
||||
*
|
||||
* @return The display item associated with this shop.
|
||||
*/
|
||||
public DisplayItem getDisplayItem() {
|
||||
return this.displayItem;
|
||||
}
|
||||
|
||||
public void setUnlimited(boolean unlimited) {
|
||||
this.unlimited = unlimited;
|
||||
}
|
||||
|
||||
public boolean isUnlimited() {
|
||||
return this.unlimited;
|
||||
}
|
||||
|
||||
public ShopType getShopType() {
|
||||
return this.shopType;
|
||||
}
|
||||
|
||||
public boolean isBuying() {
|
||||
return this.shopType == ShopType.BUYING;
|
||||
}
|
||||
|
||||
public boolean isSelling() {
|
||||
return this.shopType == ShopType.SELLING;
|
||||
}
|
||||
|
||||
/**
|
||||
* Changes a shop type to Buying or Selling. Also updates the signs nearby.
|
||||
*
|
||||
* @param shopType
|
||||
* The new type (ShopType.BUYING or ShopType.SELLING)
|
||||
*/
|
||||
public void setShopType(ShopType shopType) {
|
||||
this.shopType = shopType;
|
||||
this.setSignText();
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates signs attached to the shop
|
||||
*/
|
||||
public void setSignText() {
|
||||
if (Util.isLoaded(this.getLocation()) == false)
|
||||
return;
|
||||
String[] lines = new String[4];
|
||||
lines[0] = ChatColor.RED + "[QuickShop]";
|
||||
if (this.isBuying()) {
|
||||
lines[1] = MsgUtil.getMessage("signs.buying", "" + this.getRemainingSpace());
|
||||
}
|
||||
if (this.isSelling()) {
|
||||
lines[1] = MsgUtil.getMessage("signs.selling", "" + this.getRemainingStock());
|
||||
}
|
||||
lines[2] = Util.getNameForSign(this.item);
|
||||
lines[3] = MsgUtil.getMessage("signs.price", "" + this.getPrice());
|
||||
this.setSignText(lines);
|
||||
}
|
||||
|
||||
/**
|
||||
* Changes all lines of text on a sign near the shop
|
||||
*
|
||||
* @param lines
|
||||
* The array of lines to change. Index is line number.
|
||||
*/
|
||||
public void setSignText(String[] lines) {
|
||||
if (Util.isLoaded(this.getLocation()) == false)
|
||||
return;
|
||||
for (Sign sign : this.getSigns()) {
|
||||
for (int i = 0; i < lines.length; i++) {
|
||||
sign.setLine(i, lines[i]);
|
||||
}
|
||||
sign.update();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of signs that are attached to this shop (QuickShop and
|
||||
* blank signs only)
|
||||
*
|
||||
* @return a list of signs that are attached to this shop (QuickShop and
|
||||
* blank signs only)
|
||||
*/
|
||||
public List<Sign> getSigns() {
|
||||
ArrayList<Sign> signs = new ArrayList<Sign>(1);
|
||||
if (this.getLocation().getWorld() == null)
|
||||
return signs;
|
||||
Block[] blocks = new Block[4];
|
||||
blocks[0] = loc.getBlock().getRelative(1, 0, 0);
|
||||
blocks[1] = loc.getBlock().getRelative(-1, 0, 0);
|
||||
blocks[2] = loc.getBlock().getRelative(0, 0, 1);
|
||||
blocks[3] = loc.getBlock().getRelative(0, 0, -1);
|
||||
for (Block b : blocks) {
|
||||
if (b.getType() != Material.WALL_SIGN)
|
||||
continue;
|
||||
if (!isAttached(b))
|
||||
continue;
|
||||
Sign sign = (Sign) b.getState();
|
||||
if (sign.getLine(0).contains("[QuickShop")) {
|
||||
signs.add(sign);
|
||||
} else {
|
||||
boolean text = false;
|
||||
for (String s : sign.getLines()) {
|
||||
if (!s.isEmpty()) {
|
||||
text = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!text) {
|
||||
signs.add(sign);
|
||||
}
|
||||
}
|
||||
}
|
||||
return signs;
|
||||
}
|
||||
|
||||
public boolean isAttached(Block b) {
|
||||
if (b.getType() != Material.WALL_SIGN)
|
||||
new IllegalArgumentException(b + " Is not a sign!").printStackTrace();
|
||||
return this.getLocation().getBlock().equals(Util.getAttached(b));
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenience method. Equivilant to
|
||||
* org.maxgamer.quickshop.Util.getName(shop.getItem()).
|
||||
*
|
||||
* @return The name of this shops item
|
||||
*/
|
||||
public String getDataName() {
|
||||
return Util.getName(this.getItem());
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes the shop from the list of shops and queues it for database
|
||||
* deletion *DOES* delete it from memory
|
||||
*/
|
||||
public void delete() {
|
||||
delete(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes the shop from the list of shops and queues it for database
|
||||
* deletion
|
||||
*
|
||||
* @param fromMemory
|
||||
* True if you are *NOT* iterating over this currently, *false if
|
||||
* you are iterating*
|
||||
*/
|
||||
public void delete(boolean fromMemory) {
|
||||
// Delete the display item
|
||||
if (this.getDisplayItem() != null) {
|
||||
this.getDisplayItem().remove();
|
||||
}
|
||||
// Delete the signs around it
|
||||
for (Sign s : this.getSigns()) {
|
||||
s.getBlock().setType(Material.AIR);
|
||||
}
|
||||
// Delete it from the database
|
||||
int x = this.getLocation().getBlockX();
|
||||
int y = this.getLocation().getBlockY();
|
||||
int z = this.getLocation().getBlockZ();
|
||||
String world = this.getLocation().getWorld().getName();
|
||||
plugin.getDB().execute("DELETE FROM shops WHERE x = '" + x + "' AND y = '" + y + "' AND z = '" + z + "' AND world = '" + world + "'");
|
||||
// Refund if necessary
|
||||
if (plugin.getConfig().getBoolean("shop.refund")) {
|
||||
plugin.getEcon().deposit(this.getOwner(), plugin.getConfig().getDouble("shop.cost"));
|
||||
}
|
||||
if (fromMemory) {
|
||||
// Delete it from memory
|
||||
plugin.getShopManager().removeShop(this);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isValid() {
|
||||
checkDisplay();
|
||||
return Util.canBeShop(this.getLocation().getBlock());
|
||||
}
|
||||
|
||||
private void checkDisplay() {
|
||||
if (plugin.display == false)
|
||||
return;
|
||||
if (getLocation().getWorld() == null)
|
||||
return; // not loaded
|
||||
boolean trans = Util.isTransparent(getLocation().clone().add(0.5, 1.2, 0.5).getBlock().getType());
|
||||
if (trans && this.getDisplayItem() == null) {
|
||||
this.displayItem = new DisplayItem(this, this.getItem());
|
||||
this.getDisplayItem().spawn();
|
||||
}
|
||||
if (this.getDisplayItem() != null) {
|
||||
if (!trans) { // We have a display item in a block... delete it
|
||||
this.getDisplayItem().remove();
|
||||
this.displayItem = null;
|
||||
return;
|
||||
}
|
||||
DisplayItem disItem = this.getDisplayItem();
|
||||
Location dispLoc = disItem.getDisplayLocation();
|
||||
if (dispLoc.getBlock() != null && dispLoc.getBlock().getType() == Material.WATER) { // Flowing
|
||||
// water.
|
||||
// Stationery
|
||||
// water
|
||||
// does
|
||||
// not
|
||||
// move
|
||||
// items.
|
||||
disItem.remove();
|
||||
return;
|
||||
}
|
||||
if (disItem.getItem() == null) {
|
||||
disItem.removeDupe();
|
||||
disItem.spawn();
|
||||
return;
|
||||
}
|
||||
Item item = disItem.getItem();
|
||||
if (item.getTicksLived() > 5000 || !item.isValid() || item.isDead()) {
|
||||
disItem.respawn();
|
||||
disItem.removeDupe();
|
||||
} else if (item.getLocation().distanceSquared(dispLoc) > 1) {
|
||||
item.teleport(dispLoc, TeleportCause.PLUGIN);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void onUnload() {
|
||||
if (this.getDisplayItem() != null) {
|
||||
this.getDisplayItem().remove();
|
||||
this.displayItem = null;
|
||||
}
|
||||
}
|
||||
|
||||
public void onLoad() {
|
||||
checkDisplay();
|
||||
}
|
||||
|
||||
public void onClick() {
|
||||
this.setSignText();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder sb = new StringBuilder("Shop " + (loc.getWorld() == null ? "unloaded world" : loc.getWorld().getName()) + "(" + loc.getBlockX() + ", " + loc.getBlockY() + ", " + loc.getBlockZ() + ")");
|
||||
sb.append(" Owner: " + getOwner().toString());
|
||||
if (isUnlimited())
|
||||
sb.append(" Unlimited: true");
|
||||
sb.append(" Price: " + getPrice());
|
||||
sb.append("Item: " + getItem().toString());
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
120
src/main/java/org/maxgamer/quickshop/Shop/DisplayItem.java
Normal file
120
src/main/java/org/maxgamer/quickshop/Shop/DisplayItem.java
Normal file
@ -0,0 +1,120 @@
|
||||
package org.maxgamer.quickshop.Shop;
|
||||
|
||||
import org.bukkit.Chunk;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.entity.Entity;
|
||||
import org.bukkit.entity.Item;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.bukkit.util.Vector;
|
||||
import org.maxgamer.quickshop.QuickShop;
|
||||
import org.maxgamer.quickshop.Util.NMS;
|
||||
|
||||
/**
|
||||
* @author Netherfoam A display item, that spawns a block above the chest and
|
||||
* cannot be interacted with.
|
||||
*/
|
||||
public class DisplayItem {
|
||||
private Shop shop;
|
||||
private ItemStack iStack;
|
||||
private Item item;
|
||||
|
||||
// private Location displayLoc;
|
||||
/**
|
||||
* Creates a new display item.
|
||||
*
|
||||
* @param shop
|
||||
* The shop (See Shop)
|
||||
* @param iStack
|
||||
* The item stack to clone properties of the display item from.
|
||||
*/
|
||||
public DisplayItem(Shop shop, ItemStack iStack) {
|
||||
this.shop = shop;
|
||||
this.iStack = iStack.clone();
|
||||
// this.displayLoc = shop.getLocation().clone().add(0.5, 1.2, 0.5);
|
||||
}
|
||||
|
||||
/**
|
||||
* Spawns the dummy item on top of the shop.
|
||||
*/
|
||||
public void spawn() {
|
||||
if (shop.getLocation().getWorld() == null)
|
||||
return;
|
||||
Location dispLoc = this.getDisplayLocation();
|
||||
this.item = shop.getLocation().getWorld().dropItem(dispLoc, this.iStack);
|
||||
this.item.setVelocity(new Vector(0, 0.1, 0));
|
||||
if (QuickShop.debug) {
|
||||
System.out.println("Spawned item. Safeguarding.");
|
||||
}
|
||||
try {
|
||||
NMS.safeGuard(this.item);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
System.out.println("QuickShop version mismatch! This version of QuickShop is incompatible with this version of bukkit! Try update?");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Spawns the new display item. Does not remove duplicate items.
|
||||
*/
|
||||
public void respawn() {
|
||||
remove();
|
||||
spawn();
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes all items floating ontop of the chest that aren't the display
|
||||
* item.
|
||||
*/
|
||||
public boolean removeDupe() {
|
||||
if (shop.getLocation().getWorld() == null)
|
||||
return false;
|
||||
// QuickShop qs = (QuickShop)
|
||||
// Bukkit.getPluginManager().getPlugin("QuickShop");
|
||||
Location displayLoc = shop.getLocation().getBlock().getRelative(0, 1, 0).getLocation();
|
||||
boolean removed = false;
|
||||
Chunk c = displayLoc.getChunk();
|
||||
for (Entity e : c.getEntities()) {
|
||||
if (!(e instanceof Item))
|
||||
continue;
|
||||
if (this.item != null && e.getEntityId() == this.item.getEntityId())
|
||||
continue;
|
||||
Location eLoc = e.getLocation().getBlock().getLocation();
|
||||
if (eLoc.equals(displayLoc) || eLoc.equals(shop.getLocation())) {
|
||||
ItemStack near = ((Item) e).getItemStack();
|
||||
// if its the same its a dupe
|
||||
if (this.shop.matches(near)) {
|
||||
e.remove();
|
||||
removed = true;
|
||||
if (QuickShop.debug) {
|
||||
System.out.println("Removed rogue item: " + near.getType());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return removed;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the display item.
|
||||
*/
|
||||
public void remove() {
|
||||
if (this.item == null)
|
||||
return;
|
||||
this.item.remove();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Returns the exact location of the display item. (1 above shop
|
||||
* block, in the center)
|
||||
*/
|
||||
public Location getDisplayLocation() {
|
||||
return this.shop.getLocation().clone().add(0.5, 1.2, 0.5);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the reference to this shops item. Do not modify.
|
||||
*/
|
||||
public Item getItem() {
|
||||
return this.item;
|
||||
}
|
||||
}
|
98
src/main/java/org/maxgamer/quickshop/Shop/Info.java
Normal file
98
src/main/java/org/maxgamer/quickshop/Shop/Info.java
Normal file
@ -0,0 +1,98 @@
|
||||
package org.maxgamer.quickshop.Shop;
|
||||
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.block.Block;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
|
||||
public class Info {
|
||||
private Location loc;
|
||||
private ShopAction action;
|
||||
private ItemStack item;
|
||||
private Block last;
|
||||
private Shop shop;
|
||||
|
||||
/**
|
||||
* Stores info for the players last shop interact.
|
||||
*
|
||||
* @param loc
|
||||
* The location they clicked (Block.getLocation())
|
||||
* @param action
|
||||
* The action (ShopAction.*)
|
||||
* @param material
|
||||
* The material they were holding
|
||||
* @param data
|
||||
* The data value of the material
|
||||
*/
|
||||
public Info(Location loc, ShopAction action, ItemStack item, Block last) {
|
||||
this.loc = loc;
|
||||
this.action = action;
|
||||
this.last = last;
|
||||
if (item != null)
|
||||
this.item = item.clone();
|
||||
}
|
||||
|
||||
/**
|
||||
* Stores info for the players last shop interact.
|
||||
*
|
||||
* @param loc
|
||||
* The location they clicked (Block.getLocation())
|
||||
* @param action
|
||||
* The action (ShopAction.*)
|
||||
* @param material
|
||||
* The material they were holding
|
||||
* @param data
|
||||
* The data value of the material
|
||||
* @param shop
|
||||
* The shop they interacted with, or null if none
|
||||
*/
|
||||
public Info(Location loc, ShopAction action, ItemStack item, Block last, Shop shop) {
|
||||
this.loc = loc;
|
||||
this.action = action;
|
||||
this.last = last;
|
||||
if (item != null)
|
||||
this.item = item.clone();
|
||||
if (shop != null) {
|
||||
this.shop = shop.clone();
|
||||
}
|
||||
}
|
||||
|
||||
public boolean hasChanged(Shop shop) {
|
||||
if (this.shop.isUnlimited() != shop.isUnlimited())
|
||||
return true;
|
||||
if (this.shop.getShopType() != shop.getShopType())
|
||||
return true;
|
||||
if (!this.shop.getOwner().equals(shop.getOwner()))
|
||||
return true;
|
||||
if (this.shop.getPrice() != shop.getPrice())
|
||||
return true;
|
||||
if (!this.shop.getLocation().equals(shop.getLocation()))
|
||||
return true;
|
||||
if (!this.shop.matches(shop.getItem()))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
public ShopAction getAction() {
|
||||
return this.action;
|
||||
}
|
||||
|
||||
public Location getLocation() {
|
||||
return this.loc;
|
||||
}
|
||||
|
||||
/*
|
||||
* public Material getMaterial(){ return this.item.getType(); } public byte
|
||||
* getData(){ return this.getData(); }
|
||||
*/
|
||||
public ItemStack getItem() {
|
||||
return this.item;
|
||||
}
|
||||
|
||||
public void setAction(ShopAction action) {
|
||||
this.action = action;
|
||||
}
|
||||
|
||||
public Block getSignBlock() {
|
||||
return this.last;
|
||||
}
|
||||
}
|
@ -0,0 +1,5 @@
|
||||
package org.maxgamer.quickshop.Shop;
|
||||
|
||||
public enum ShopAction {
|
||||
BUY(), CREATE(), CANCELLED();
|
||||
}
|
44
src/main/java/org/maxgamer/quickshop/Shop/ShopChunk.java
Normal file
44
src/main/java/org/maxgamer/quickshop/Shop/ShopChunk.java
Normal file
@ -0,0 +1,44 @@
|
||||
package org.maxgamer.quickshop.Shop;
|
||||
|
||||
public class ShopChunk {
|
||||
private String world;
|
||||
private int x;
|
||||
private int z;
|
||||
private int hash = 0;
|
||||
|
||||
public ShopChunk(String world, int x, int z) {
|
||||
this.world = world;
|
||||
this.x = x;
|
||||
this.z = z;
|
||||
this.hash = this.x * this.z; // We don't need to use the world's hash,
|
||||
// as these are seperated by world in
|
||||
// memory
|
||||
}
|
||||
|
||||
public int getX() {
|
||||
return this.x;
|
||||
}
|
||||
|
||||
public int getZ() {
|
||||
return this.z;
|
||||
}
|
||||
|
||||
public String getWorld() {
|
||||
return this.world;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (obj.getClass() != this.getClass()) {
|
||||
return false;
|
||||
} else {
|
||||
ShopChunk shopChunk = (ShopChunk) obj;
|
||||
return (this.getWorld().equals(shopChunk.getWorld()) && this.getX() == shopChunk.getX() && this.getZ() == shopChunk.getZ());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return hash;
|
||||
}
|
||||
}
|
@ -0,0 +1,55 @@
|
||||
package org.maxgamer.quickshop.Shop;
|
||||
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.Cancellable;
|
||||
import org.bukkit.event.Event;
|
||||
import org.bukkit.event.HandlerList;
|
||||
|
||||
public class ShopCreateEvent extends Event implements Cancellable {
|
||||
private static final HandlerList handlers = new HandlerList();
|
||||
private Shop shop;
|
||||
private boolean cancelled;
|
||||
private Player p;
|
||||
|
||||
public ShopCreateEvent(Shop shop, Player p) {
|
||||
this.shop = shop;
|
||||
this.p = p;
|
||||
}
|
||||
|
||||
/**
|
||||
* The shop to be created
|
||||
*
|
||||
* @return The shop to be created
|
||||
*/
|
||||
public Shop getShop() {
|
||||
return this.shop;
|
||||
}
|
||||
|
||||
/**
|
||||
* The player who is creating this shop
|
||||
*
|
||||
* @return The player who is creating this shop
|
||||
*/
|
||||
public Player getPlayer() {
|
||||
return p;
|
||||
}
|
||||
|
||||
@Override
|
||||
public HandlerList getHandlers() {
|
||||
return handlers;
|
||||
}
|
||||
|
||||
public static HandlerList getHandlerList() {
|
||||
return handlers;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCancelled() {
|
||||
return this.cancelled;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setCancelled(boolean cancel) {
|
||||
this.cancelled = cancel;
|
||||
}
|
||||
}
|
627
src/main/java/org/maxgamer/quickshop/Shop/ShopManager.java
Normal file
627
src/main/java/org/maxgamer/quickshop/Shop/ShopManager.java
Normal file
@ -0,0 +1,627 @@
|
||||
package org.maxgamer.quickshop.Shop;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.NoSuchElementException;
|
||||
import java.util.UUID;
|
||||
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.ChatColor;
|
||||
import org.bukkit.Chunk;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.block.Block;
|
||||
import org.bukkit.block.BlockFace;
|
||||
import org.bukkit.block.BlockState;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.block.Action;
|
||||
import org.bukkit.event.player.PlayerInteractEvent;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.bukkit.material.Sign;
|
||||
import org.maxgamer.quickshop.QuickShop;
|
||||
import org.maxgamer.quickshop.Database.Database;
|
||||
import org.maxgamer.quickshop.Util.MsgUtil;
|
||||
import org.maxgamer.quickshop.Util.Util;
|
||||
|
||||
public class ShopManager {
|
||||
private QuickShop plugin;
|
||||
private HashMap<UUID, Info> actions = new HashMap<UUID, Info>();
|
||||
private HashMap<String, HashMap<ShopChunk, HashMap<Location, Shop>>> shops = new HashMap<String, HashMap<ShopChunk, HashMap<Location, Shop>>>();
|
||||
|
||||
public ShopManager(QuickShop plugin) {
|
||||
this.plugin = plugin;
|
||||
}
|
||||
|
||||
public Database getDatabase() {
|
||||
return plugin.getDB();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Returns the HashMap<Player name, shopInfo>. Info contains what
|
||||
* their last question etc was.
|
||||
*/
|
||||
public HashMap<UUID, Info> getActions() {
|
||||
return this.actions;
|
||||
}
|
||||
|
||||
public void createShop(Shop shop) {
|
||||
Location loc = shop.getLocation();
|
||||
ItemStack item = shop.getItem();
|
||||
try {
|
||||
// Write it to the database
|
||||
String q = "INSERT INTO shops (owner, price, itemConfig, x, y, z, world, unlimited, type) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)";
|
||||
plugin.getDB().execute(q, shop.getOwner().toString(), shop.getPrice(), Util.serialize(item), loc.getBlockX(), loc.getBlockY(), loc.getBlockZ(), loc.getWorld().getName(), (shop.isUnlimited() ? 1 : 0), shop.getShopType().toID());
|
||||
// Add it to the world
|
||||
addShop(loc.getWorld().getName(), shop);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
System.out.println("Could not create shop! Changes will revert after a reboot!");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the given shop into storage. This method is used for loading data
|
||||
* from the database. Do not use this method to create a shop.
|
||||
*
|
||||
* @param world
|
||||
* The world the shop is in
|
||||
* @param shop
|
||||
* The shop to load
|
||||
*/
|
||||
public void loadShop(String world, Shop shop) {
|
||||
this.addShop(world, shop);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a hashmap of World -> Chunk -> Shop
|
||||
*
|
||||
* @return a hashmap of World -> Chunk -> Shop
|
||||
*/
|
||||
public HashMap<String, HashMap<ShopChunk, HashMap<Location, Shop>>> getShops() {
|
||||
return this.shops;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a hashmap of Chunk -> Shop
|
||||
*
|
||||
* @param world
|
||||
* The name of the world (case sensitive) to get the list of
|
||||
* shops from
|
||||
* @return a hashmap of Chunk -> Shop
|
||||
*/
|
||||
public HashMap<ShopChunk, HashMap<Location, Shop>> getShops(String world) {
|
||||
return this.shops.get(world);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a hashmap of Shops
|
||||
*
|
||||
* @param c
|
||||
* The chunk to search. Referencing doesn't matter, only
|
||||
* coordinates and world are used.
|
||||
* @return
|
||||
*/
|
||||
public HashMap<Location, Shop> getShops(Chunk c) {
|
||||
// long start = System.nanoTime();
|
||||
HashMap<Location, Shop> shops = getShops(c.getWorld().getName(), c.getX(), c.getZ());
|
||||
// long end = System.nanoTime();
|
||||
// System.out.println("Chunk lookup in " + ((end - start)/1000000.0) +
|
||||
// "ms.");
|
||||
return shops;
|
||||
}
|
||||
|
||||
public HashMap<Location, Shop> getShops(String world, int chunkX, int chunkZ) {
|
||||
HashMap<ShopChunk, HashMap<Location, Shop>> inWorld = this.getShops(world);
|
||||
if (inWorld == null) {
|
||||
return null;
|
||||
}
|
||||
ShopChunk shopChunk = new ShopChunk(world, chunkX, chunkZ);
|
||||
return inWorld.get(shopChunk);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a shop in a specific location
|
||||
*
|
||||
* @param loc
|
||||
* The location to get the shop from
|
||||
* @return The shop at that location
|
||||
*/
|
||||
public Shop getShop(Location loc) {
|
||||
HashMap<Location, Shop> inChunk = getShops(loc.getChunk());
|
||||
if (inChunk == null) {
|
||||
return null;
|
||||
}
|
||||
// We can do this because WorldListener updates the world reference so
|
||||
// the world in loc is the same as world in inChunk.get(loc)
|
||||
return inChunk.get(loc);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a shop to the world. Does NOT require the chunk or world to be
|
||||
* loaded
|
||||
*
|
||||
* @param world
|
||||
* The name of the world
|
||||
* @param shop
|
||||
* The shop to add
|
||||
*/
|
||||
private void addShop(String world, Shop shop) {
|
||||
HashMap<ShopChunk, HashMap<Location, Shop>> inWorld = this.getShops().get(world);
|
||||
// There's no world storage yet. We need to create that hashmap.
|
||||
if (inWorld == null) {
|
||||
inWorld = new HashMap<ShopChunk, HashMap<Location, Shop>>(3);
|
||||
// Put it in the data universe
|
||||
this.getShops().put(world, inWorld);
|
||||
}
|
||||
// Calculate the chunks coordinates. These are 1,2,3 for each chunk, NOT
|
||||
// location rounded to the nearest 16.
|
||||
int x = (int) Math.floor((shop.getLocation().getBlockX()) / 16.0);
|
||||
int z = (int) Math.floor((shop.getLocation().getBlockZ()) / 16.0);
|
||||
// Get the chunk set from the world info
|
||||
ShopChunk shopChunk = new ShopChunk(world, x, z);
|
||||
HashMap<Location, Shop> inChunk = inWorld.get(shopChunk);
|
||||
// That chunk data hasn't been created yet - Create it!
|
||||
if (inChunk == null) {
|
||||
inChunk = new HashMap<Location, Shop>(1);
|
||||
// Put it in the world
|
||||
inWorld.put(shopChunk, inChunk);
|
||||
}
|
||||
// Put the shop in its location in the chunk list.
|
||||
inChunk.put(shop.getLocation(), shop);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a shop from the world. Does NOT remove it from the database. *
|
||||
* REQUIRES * the world to be loaded
|
||||
*
|
||||
* @param shop
|
||||
* The shop to remove
|
||||
*/
|
||||
public void removeShop(Shop shop) {
|
||||
Location loc = shop.getLocation();
|
||||
String world = loc.getWorld().getName();
|
||||
HashMap<ShopChunk, HashMap<Location, Shop>> inWorld = this.getShops().get(world);
|
||||
int x = (int) Math.floor((shop.getLocation().getBlockX()) / 16.0);
|
||||
int z = (int) Math.floor((shop.getLocation().getBlockZ()) / 16.0);
|
||||
ShopChunk shopChunk = new ShopChunk(world, x, z);
|
||||
HashMap<Location, Shop> inChunk = inWorld.get(shopChunk);
|
||||
inChunk.remove(loc);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes all shops from memory and the world. Does not delete them from
|
||||
* the database. Call this on plugin disable ONLY.
|
||||
*/
|
||||
public void clear() {
|
||||
if (plugin.display) {
|
||||
for (World world : Bukkit.getWorlds()) {
|
||||
for (Chunk chunk : world.getLoadedChunks()) {
|
||||
HashMap<Location, Shop> inChunk = this.getShops(chunk);
|
||||
if (inChunk == null)
|
||||
continue;
|
||||
for (Shop shop : inChunk.values()) {
|
||||
shop.onUnload();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
this.actions.clear();
|
||||
this.shops.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks other plugins to make sure they can use the chest they're making a
|
||||
* shop.
|
||||
*
|
||||
* @param p
|
||||
* The player to check
|
||||
* @param b
|
||||
* The block to check
|
||||
* @return True if they're allowed to place a shop there.
|
||||
*/
|
||||
public boolean canBuildShop(Player p, Block b, BlockFace bf) {
|
||||
if (plugin.limit) {
|
||||
int owned = 0;
|
||||
Iterator<Shop> it = getShopIterator();
|
||||
while (it.hasNext()) {
|
||||
if (it.next().getOwner().equals(p.getUniqueId())) {
|
||||
owned++;
|
||||
}
|
||||
}
|
||||
int max = plugin.getShopLimit(p);
|
||||
if (owned + 1 > max) {
|
||||
p.sendMessage(ChatColor.RED + "You have already created a maximum of " + owned + "/" + max + " shops!");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
PlayerInteractEvent pie = new PlayerInteractEvent(p, Action.RIGHT_CLICK_BLOCK, new ItemStack(Material.AIR), b, bf); // PIE
|
||||
// =
|
||||
// PlayerInteractEvent
|
||||
// -
|
||||
// What
|
||||
// else?
|
||||
Bukkit.getPluginManager().callEvent(pie);
|
||||
pie.getPlayer().closeInventory(); // If the player has chat open, this
|
||||
// will close their chat.
|
||||
if (pie.isCancelled()) {
|
||||
return false;
|
||||
}
|
||||
ShopPreCreateEvent spce = new ShopPreCreateEvent(p, b.getLocation());
|
||||
Bukkit.getPluginManager().callEvent(spce);
|
||||
if (spce.isCancelled()) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public void handleChat(final Player p, String msg) {
|
||||
final String message = ChatColor.stripColor(msg);
|
||||
// Use from the main thread, because Bukkit hates life
|
||||
Bukkit.getScheduler().scheduleSyncDelayedTask(plugin, new Runnable() {
|
||||
@SuppressWarnings("deprecation")
|
||||
@Override
|
||||
public void run() {
|
||||
HashMap<UUID, Info> actions = getActions();
|
||||
// They wanted to do something.
|
||||
Info info = actions.remove(p.getUniqueId());
|
||||
if (info == null)
|
||||
return; // multithreaded means this can happen
|
||||
if (info.getLocation().getWorld() != p.getLocation().getWorld()) {
|
||||
p.sendMessage(MsgUtil.getMessage("shop-creation-cancelled"));
|
||||
return;
|
||||
}
|
||||
if (info.getLocation().distanceSquared(p.getLocation()) > 25) {
|
||||
p.sendMessage(MsgUtil.getMessage("shop-creation-cancelled"));
|
||||
return;
|
||||
}
|
||||
/* Creation handling */
|
||||
if (info.getAction() == ShopAction.CREATE) {
|
||||
try {
|
||||
// Checking the shop can be created
|
||||
if (plugin.getShopManager().getShop(info.getLocation()) != null) {
|
||||
p.sendMessage(MsgUtil.getMessage("shop-already-owned"));
|
||||
return;
|
||||
}
|
||||
if (Util.getSecondHalf(info.getLocation().getBlock()) != null && !p.hasPermission("quickshop.create.double")) {
|
||||
p.sendMessage(MsgUtil.getMessage("no-double-chests"));
|
||||
return;
|
||||
}
|
||||
if (Util.canBeShop(info.getLocation().getBlock()) == false) {
|
||||
p.sendMessage(MsgUtil.getMessage("chest-was-removed"));
|
||||
return;
|
||||
}
|
||||
// Price per item
|
||||
double price;
|
||||
if (plugin.getConfig().getBoolean("whole-number-prices-only")) {
|
||||
price = Integer.parseInt(message);
|
||||
} else {
|
||||
price = Double.parseDouble(message);
|
||||
}
|
||||
if (price < 0.01) {
|
||||
p.sendMessage(MsgUtil.getMessage("price-too-cheap"));
|
||||
return;
|
||||
}
|
||||
double tax = plugin.getConfig().getDouble("shop.cost");
|
||||
// Tax refers to the cost to create a shop. Not actual
|
||||
// tax, that would be silly
|
||||
if (tax != 0 && plugin.getEcon().getBalance(p.getName()) < tax) {
|
||||
p.sendMessage(MsgUtil.getMessage("you-cant-afford-a-new-shop", format(tax)));
|
||||
return;
|
||||
}
|
||||
// Create the sample shop.
|
||||
Shop shop = new ContainerShop(info.getLocation(), price, info.getItem(), p.getUniqueId());
|
||||
shop.onLoad();
|
||||
ShopCreateEvent e = new ShopCreateEvent(shop, p);
|
||||
Bukkit.getPluginManager().callEvent(e);
|
||||
if (e.isCancelled()) {
|
||||
shop.onUnload();
|
||||
return;
|
||||
}
|
||||
// This must be called after the event has been called.
|
||||
// Else, if the event is cancelled, they won't get their
|
||||
// money back.
|
||||
if (tax != 0) {
|
||||
if (!plugin.getEcon().withdraw(p.getName(), tax)) {
|
||||
p.sendMessage(MsgUtil.getMessage("you-cant-afford-a-new-shop", format(tax)));
|
||||
shop.onUnload();
|
||||
return;
|
||||
}
|
||||
plugin.getEcon().deposit(plugin.getConfig().getString("tax-account"), tax);
|
||||
}
|
||||
/* The shop has hereforth been successfully created */
|
||||
createShop(shop);
|
||||
Location loc = shop.getLocation();
|
||||
plugin.log(p.getName() + " created a " + shop.getDataName() + " shop at (" + loc.getWorld().getName() + " - " + loc.getX() + "," + loc.getY() + "," + loc.getZ() + ")");
|
||||
if (!plugin.getConfig().getBoolean("shop.lock")) {
|
||||
// Warn them if they haven't been warned since
|
||||
// reboot
|
||||
if (!plugin.warnings.contains(p.getName())) {
|
||||
p.sendMessage(MsgUtil.getMessage("shops-arent-locked"));
|
||||
plugin.warnings.add(p.getName());
|
||||
}
|
||||
}
|
||||
// Figures out which way we should put the sign on and
|
||||
// sets its text.
|
||||
if (info.getSignBlock() != null && info.getSignBlock().getType() == Material.AIR && plugin.getConfig().getBoolean("shop.auto-sign")) {
|
||||
BlockState bs = info.getSignBlock().getState();
|
||||
BlockFace bf = info.getLocation().getBlock().getFace(info.getSignBlock());
|
||||
bs.setType(Material.WALL_SIGN);
|
||||
Sign sign = (Sign) bs.getData();
|
||||
sign.setFacingDirection(bf);
|
||||
bs.update(true);
|
||||
shop.setSignText();
|
||||
/*
|
||||
* Block b = shop.getLocation().getBlock();
|
||||
* ItemFrame iFrame = (ItemFrame)
|
||||
* b.getWorld().spawnEntity(b.getLocation(),
|
||||
* EntityType.ITEM_FRAME);
|
||||
*
|
||||
* BlockFace[] faces = new
|
||||
* BlockFace[]{BlockFace.NORTH, BlockFace.EAST,
|
||||
* BlockFace.SOUTH, BlockFace.WEST}; for(BlockFace
|
||||
* face : faces){ if(face == bf) continue; //This is
|
||||
* the sign's location iFrame.setFacingDirection(bf,
|
||||
* true); //iFrame.setItem(shop.getItem());
|
||||
* ItemStack iStack = shop.getItem().clone();
|
||||
* iStack.setAmount(0); iFrame.setItem(iStack); /*
|
||||
* Field handleField =
|
||||
* iFrame.getClass().getField("entity");
|
||||
* handleField.setAccessible(true); Object handle =
|
||||
* handleField.get(iFrame);
|
||||
*
|
||||
* ItemStack bukkitStack = shop.getItem();
|
||||
*
|
||||
* Field itemStackHandle =
|
||||
*
|
||||
* Method setItemStack =
|
||||
* handle.getClass().getMethod("a", Object.class);
|
||||
* setItemStack.
|
||||
*/
|
||||
// }
|
||||
}
|
||||
if (shop instanceof ContainerShop) {
|
||||
ContainerShop cs = (ContainerShop) shop;
|
||||
if (cs.isDoubleShop()) {
|
||||
Shop nextTo = cs.getAttachedShop();
|
||||
if (nextTo.getPrice() > shop.getPrice()) {
|
||||
// The one next to it must always be a
|
||||
// buying shop.
|
||||
p.sendMessage(MsgUtil.getMessage("buying-more-than-selling"));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
/* They didn't enter a number. */
|
||||
catch (NumberFormatException ex) {
|
||||
p.sendMessage(MsgUtil.getMessage("shop-creation-cancelled"));
|
||||
return;
|
||||
}
|
||||
}
|
||||
/* Purchase Handling */
|
||||
else if (info.getAction() == ShopAction.BUY) {
|
||||
int amount = 0;
|
||||
try {
|
||||
amount = Integer.parseInt(message);
|
||||
} catch (NumberFormatException e) {
|
||||
p.sendMessage(MsgUtil.getMessage("shop-purchase-cancelled"));
|
||||
return;
|
||||
}
|
||||
// Get the shop they interacted with
|
||||
Shop shop = plugin.getShopManager().getShop(info.getLocation());
|
||||
// It's not valid anymore
|
||||
if (shop == null || Util.canBeShop(info.getLocation().getBlock()) == false) {
|
||||
p.sendMessage(MsgUtil.getMessage("chest-was-removed"));
|
||||
return;
|
||||
}
|
||||
if (info.hasChanged(shop)) {
|
||||
p.sendMessage(MsgUtil.getMessage("shop-has-changed"));
|
||||
return;
|
||||
}
|
||||
if (shop.isSelling()) {
|
||||
int stock = shop.getRemainingStock();
|
||||
if (stock < amount) {
|
||||
p.sendMessage(MsgUtil.getMessage("shop-stock-too-low", "" + shop.getRemainingStock(), shop.getDataName()));
|
||||
return;
|
||||
}
|
||||
if (amount == 0) {
|
||||
// Dumb.
|
||||
MsgUtil.sendPurchaseSuccess(p, shop, amount);
|
||||
return;
|
||||
} else if (amount < 0) {
|
||||
// & Dumber
|
||||
p.sendMessage(MsgUtil.getMessage("negative-amount"));
|
||||
return;
|
||||
}
|
||||
int pSpace = Util.countSpace(p.getInventory(), shop.getItem());
|
||||
if (amount > pSpace) {
|
||||
p.sendMessage(MsgUtil.getMessage("not-enough-space", "" + pSpace));
|
||||
return;
|
||||
}
|
||||
ShopPurchaseEvent e = new ShopPurchaseEvent(shop, p, amount);
|
||||
Bukkit.getPluginManager().callEvent(e);
|
||||
if (e.isCancelled())
|
||||
return; // Cancelled
|
||||
// Money handling
|
||||
if (!p.getUniqueId().equals(shop.getOwner())) {
|
||||
// Check their balance. Works with *most* economy
|
||||
// plugins*
|
||||
if (plugin.getEcon().getBalance(p.getName()) < amount * shop.getPrice()) {
|
||||
p.sendMessage(MsgUtil.getMessage("you-cant-afford-to-buy", format(amount * shop.getPrice()), format(plugin.getEcon().getBalance(p.getName()))));
|
||||
return;
|
||||
}
|
||||
// Don't tax them if they're purchasing from
|
||||
// themselves.
|
||||
// Do charge an amount of tax though.
|
||||
double tax = plugin.getConfig().getDouble("tax");
|
||||
double total = amount * shop.getPrice();
|
||||
if (!plugin.getEcon().withdraw(p.getUniqueId(), total)) {
|
||||
p.sendMessage(MsgUtil.getMessage("you-cant-afford-to-buy", format(amount * shop.getPrice()), format(plugin.getEcon().getBalance(p.getName()))));
|
||||
return;
|
||||
}
|
||||
if (!shop.isUnlimited() || plugin.getConfig().getBoolean("shop.pay-unlimited-shop-owners")) {
|
||||
plugin.getEcon().deposit(shop.getOwner(), total * (1 - tax));
|
||||
if (tax != 0) {
|
||||
plugin.getEcon().deposit(plugin.getConfig().getString("tax-account"), total * tax);
|
||||
}
|
||||
}
|
||||
// Notify the shop owner
|
||||
if (plugin.getConfig().getBoolean("show-tax")) {
|
||||
String msg = MsgUtil.getMessage("player-bought-from-your-store-tax", p.getName(), "" + amount, shop.getDataName(), Util.format((tax * total)));
|
||||
if (stock == amount)
|
||||
msg += "\n" + MsgUtil.getMessage("shop-out-of-stock", "" + shop.getLocation().getBlockX(), "" + shop.getLocation().getBlockY(), "" + shop.getLocation().getBlockZ(), shop.getDataName());
|
||||
MsgUtil.send(shop.getOwner(), msg);
|
||||
} else {
|
||||
String msg = MsgUtil.getMessage("player-bought-from-your-store", p.getName(), "" + amount, shop.getDataName());
|
||||
if (stock == amount)
|
||||
msg += "\n" + MsgUtil.getMessage("shop-out-of-stock", "" + shop.getLocation().getBlockX(), "" + shop.getLocation().getBlockY(), "" + shop.getLocation().getBlockZ(), shop.getDataName());
|
||||
MsgUtil.send(shop.getOwner(), msg);
|
||||
}
|
||||
}
|
||||
// Transfers the item from A to B
|
||||
shop.sell(p, amount);
|
||||
MsgUtil.sendPurchaseSuccess(p, shop, amount);
|
||||
plugin.log(p.getName() + " bought " + amount + " for " + (shop.getPrice() * amount) + " from " + shop.toString());
|
||||
} else if (shop.isBuying()) {
|
||||
int space = shop.getRemainingSpace();
|
||||
if (space < amount) {
|
||||
p.sendMessage(MsgUtil.getMessage("shop-has-no-space", "" + space, shop.getDataName()));
|
||||
return;
|
||||
}
|
||||
int count = Util.countItems(p.getInventory(), shop.getItem());
|
||||
// Not enough items
|
||||
if (amount > count) {
|
||||
p.sendMessage(MsgUtil.getMessage("you-dont-have-that-many-items", "" + count, shop.getDataName()));
|
||||
return;
|
||||
}
|
||||
if (amount == 0) {
|
||||
// Dumb.
|
||||
MsgUtil.sendPurchaseSuccess(p, shop, amount);
|
||||
return;
|
||||
} else if (amount < 0) {
|
||||
// & Dumber
|
||||
p.sendMessage(MsgUtil.getMessage("negative-amount"));
|
||||
return;
|
||||
}
|
||||
// Money handling
|
||||
if (!p.getUniqueId().equals(shop.getOwner())) {
|
||||
// Don't tax them if they're purchasing from
|
||||
// themselves.
|
||||
// Do charge an amount of tax though.
|
||||
double tax = plugin.getConfig().getDouble("tax");
|
||||
double total = amount * shop.getPrice();
|
||||
if (!shop.isUnlimited() || plugin.getConfig().getBoolean("shop.pay-unlimited-shop-owners")) {
|
||||
// Tries to check their balance nicely to see if
|
||||
// they can afford it.
|
||||
if (plugin.getEcon().getBalance(shop.getOwner()) < amount * shop.getPrice()) {
|
||||
p.sendMessage(MsgUtil.getMessage("the-owner-cant-afford-to-buy-from-you", format(amount * shop.getPrice()), format(plugin.getEcon().getBalance(shop.getOwner()))));
|
||||
return;
|
||||
}
|
||||
// Check for plugins faking econ.has(amount)
|
||||
if (!plugin.getEcon().withdraw(shop.getOwner(), total)) {
|
||||
p.sendMessage(MsgUtil.getMessage("the-owner-cant-afford-to-buy-from-you", format(amount * shop.getPrice()), format(plugin.getEcon().getBalance(shop.getOwner()))));
|
||||
return;
|
||||
}
|
||||
if (tax != 0) {
|
||||
plugin.getEcon().deposit(plugin.getConfig().getString("tax-account"), total * tax);
|
||||
}
|
||||
}
|
||||
// Give them the money after we know we succeeded
|
||||
plugin.getEcon().deposit(p.getName(), total * (1 - tax));
|
||||
// Notify the owner of the purchase.
|
||||
String msg = MsgUtil.getMessage("player-sold-to-your-store", p.getName(), "" + amount, shop.getDataName());
|
||||
if (space == amount)
|
||||
msg += "\n" + MsgUtil.getMessage("shop-out-of-space", "" + shop.getLocation().getBlockX(), "" + shop.getLocation().getBlockY(), "" + shop.getLocation().getBlockZ());
|
||||
MsgUtil.send(shop.getOwner(), msg);
|
||||
}
|
||||
shop.buy(p, amount);
|
||||
MsgUtil.sendSellSuccess(p, shop, amount);
|
||||
plugin.log(p.getName() + " sold " + amount + " for " + (shop.getPrice() * amount) + " to " + shop.toString());
|
||||
}
|
||||
shop.setSignText(); // Update the signs count
|
||||
}
|
||||
/* If it was already cancelled (from destroyed) */
|
||||
else {
|
||||
return; // It was cancelled, go away.
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a new shop iterator object, allowing iteration over shops easily,
|
||||
* instead of sorting through a 3D hashmap.
|
||||
*
|
||||
* @return a new shop iterator object.
|
||||
*/
|
||||
public Iterator<Shop> getShopIterator() {
|
||||
return new ShopIterator();
|
||||
}
|
||||
|
||||
public String format(double d) {
|
||||
return plugin.getEcon().format(d);
|
||||
}
|
||||
|
||||
public class ShopIterator implements Iterator<Shop> {
|
||||
private Iterator<Shop> shops;
|
||||
private Iterator<HashMap<Location, Shop>> chunks;
|
||||
private Iterator<HashMap<ShopChunk, HashMap<Location, Shop>>> worlds;
|
||||
private Shop current;
|
||||
|
||||
public ShopIterator() {
|
||||
worlds = getShops().values().iterator();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if there is still more shops to iterate over.
|
||||
*/
|
||||
@Override
|
||||
public boolean hasNext() {
|
||||
if (shops == null || !shops.hasNext()) {
|
||||
if (chunks == null || !chunks.hasNext()) {
|
||||
if (!worlds.hasNext()) {
|
||||
return false;
|
||||
} else {
|
||||
chunks = worlds.next().values().iterator();
|
||||
return hasNext();
|
||||
}
|
||||
} else {
|
||||
shops = chunks.next().values().iterator();
|
||||
return hasNext();
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches the next shop. Throws NoSuchElementException if there are no
|
||||
* more shops.
|
||||
*/
|
||||
@Override
|
||||
public Shop next() {
|
||||
if (shops == null || !shops.hasNext()) {
|
||||
if (chunks == null || !chunks.hasNext()) {
|
||||
if (!worlds.hasNext()) {
|
||||
throw new NoSuchElementException("No more shops to iterate over!");
|
||||
}
|
||||
chunks = worlds.next().values().iterator();
|
||||
}
|
||||
shops = chunks.next().values().iterator();
|
||||
}
|
||||
if (!shops.hasNext())
|
||||
return this.next(); // Skip to the next one (Empty iterator?)
|
||||
current = shops.next();
|
||||
return current;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the current shop. This method will delete the shop from
|
||||
* memory and the database.
|
||||
*/
|
||||
@Override
|
||||
public void remove() {
|
||||
current.delete(false);
|
||||
shops.remove();
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,61 @@
|
||||
package org.maxgamer.quickshop.Shop;
|
||||
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.Cancellable;
|
||||
import org.bukkit.event.Event;
|
||||
import org.bukkit.event.HandlerList;
|
||||
|
||||
/**
|
||||
* This event is called before the shop creation request is sent. E.g. A player
|
||||
* clicks a chest, this event is thrown, if successful, the player is asked how
|
||||
* much they wish to trade for.
|
||||
*/
|
||||
public class ShopPreCreateEvent extends Event implements Cancellable {
|
||||
private static final HandlerList handlers = new HandlerList();
|
||||
private boolean cancelled;
|
||||
private Player p;
|
||||
private Location loc;
|
||||
|
||||
public ShopPreCreateEvent(Player p, Location loc) {
|
||||
this.loc = loc;
|
||||
this.p = p;
|
||||
}
|
||||
|
||||
/**
|
||||
* The location of the shop that will be created.
|
||||
*
|
||||
* @return The location of the shop that will be created.
|
||||
*/
|
||||
public Location getLocation() {
|
||||
return loc;
|
||||
}
|
||||
|
||||
/**
|
||||
* The player who is creating this shop
|
||||
*
|
||||
* @return The player who is creating this shop
|
||||
*/
|
||||
public Player getPlayer() {
|
||||
return p;
|
||||
}
|
||||
|
||||
@Override
|
||||
public HandlerList getHandlers() {
|
||||
return handlers;
|
||||
}
|
||||
|
||||
public static HandlerList getHandlerList() {
|
||||
return handlers;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCancelled() {
|
||||
return this.cancelled;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setCancelled(boolean cancel) {
|
||||
this.cancelled = cancel;
|
||||
}
|
||||
}
|
@ -0,0 +1,76 @@
|
||||
package org.maxgamer.quickshop.Shop;
|
||||
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.Cancellable;
|
||||
import org.bukkit.event.Event;
|
||||
import org.bukkit.event.HandlerList;
|
||||
|
||||
public class ShopPurchaseEvent extends Event implements Cancellable {
|
||||
private static final HandlerList handlers = new HandlerList();
|
||||
private Shop shop;
|
||||
private Player p;
|
||||
private int amount;
|
||||
private boolean cancelled;
|
||||
|
||||
/**
|
||||
* Builds a new shop purchase event
|
||||
*
|
||||
* @param shop
|
||||
* The shop bought from
|
||||
* @param p
|
||||
* The player buying
|
||||
* @param amount
|
||||
* The amount they're buying
|
||||
*/
|
||||
public ShopPurchaseEvent(Shop shop, Player p, int amount) {
|
||||
this.shop = shop;
|
||||
this.p = p;
|
||||
this.amount = amount;
|
||||
}
|
||||
|
||||
/**
|
||||
* The shop used in this event
|
||||
*
|
||||
* @return The shop used in this event
|
||||
*/
|
||||
public Shop getShop() {
|
||||
return this.shop;
|
||||
}
|
||||
|
||||
/**
|
||||
* The player trading with the shop
|
||||
*
|
||||
* @return The player trading with the shop
|
||||
*/
|
||||
public Player getPlayer() {
|
||||
return this.p;
|
||||
}
|
||||
|
||||
/**
|
||||
* The amount the purchase was for
|
||||
*
|
||||
* @return The amount the purchase was for
|
||||
*/
|
||||
public int getAmount() {
|
||||
return this.amount;
|
||||
}
|
||||
|
||||
@Override
|
||||
public HandlerList getHandlers() {
|
||||
return handlers;
|
||||
}
|
||||
|
||||
public static HandlerList getHandlerList() {
|
||||
return handlers;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCancelled() {
|
||||
return this.cancelled;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setCancelled(boolean cancel) {
|
||||
this.cancelled = cancel;
|
||||
}
|
||||
}
|
26
src/main/java/org/maxgamer/quickshop/Shop/ShopType.java
Normal file
26
src/main/java/org/maxgamer/quickshop/Shop/ShopType.java
Normal file
@ -0,0 +1,26 @@
|
||||
package org.maxgamer.quickshop.Shop;
|
||||
|
||||
public enum ShopType {
|
||||
SELLING(0), BUYING(1);
|
||||
private int id;
|
||||
private ShopType(int id){
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public static ShopType fromID(int id) {
|
||||
for(ShopType type:ShopType.values()){
|
||||
if(type.id==id){
|
||||
return type;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static int toID(ShopType shopType) {
|
||||
return shopType.id;
|
||||
}
|
||||
|
||||
public int toID() {
|
||||
return id;
|
||||
}
|
||||
}
|
12
src/main/java/org/maxgamer/quickshop/Util/Converter.java
Normal file
12
src/main/java/org/maxgamer/quickshop/Util/Converter.java
Normal file
@ -0,0 +1,12 @@
|
||||
package org.maxgamer.quickshop.Util;
|
||||
|
||||
public class Converter {
|
||||
/**
|
||||
* Attempts to convert the quickshop database, if necessary.
|
||||
*
|
||||
* @return -1 for failure, 0 for no changes, 1 for success converting.
|
||||
*/
|
||||
public static int convert() {
|
||||
return 0;
|
||||
}
|
||||
}
|
283
src/main/java/org/maxgamer/quickshop/Util/MsgUtil.java
Normal file
283
src/main/java/org/maxgamer/quickshop/Util/MsgUtil.java
Normal file
@ -0,0 +1,283 @@
|
||||
package org.maxgamer.quickshop.Util;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.InputStream;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedList;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.UUID;
|
||||
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.ChatColor;
|
||||
import org.bukkit.OfflinePlayer;
|
||||
import org.bukkit.configuration.file.YamlConfiguration;
|
||||
import org.bukkit.enchantments.Enchantment;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.bukkit.inventory.meta.EnchantmentStorageMeta;
|
||||
import org.maxgamer.quickshop.QuickShop;
|
||||
import org.maxgamer.quickshop.Shop.Shop;
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
public class MsgUtil {
|
||||
private static QuickShop plugin;
|
||||
private static YamlConfiguration messages;
|
||||
private static HashMap<UUID, LinkedList<String>> player_messages = new HashMap<UUID, LinkedList<String>>();
|
||||
static {
|
||||
plugin = QuickShop.instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads all the messages from messages.yml
|
||||
*/
|
||||
public static void loadCfgMessages() {
|
||||
// Load messages.yml
|
||||
File messageFile = new File(plugin.getDataFolder(), "messages.yml");
|
||||
if (!messageFile.exists()) {
|
||||
plugin.getLogger().info("Creating messages.yml");
|
||||
plugin.saveResource("messages.yml", true);
|
||||
}
|
||||
// Store it
|
||||
messages = YamlConfiguration.loadConfiguration(messageFile);
|
||||
messages.options().copyDefaults(true);
|
||||
// Load default messages
|
||||
InputStream defMessageStream = plugin.getResource("messages.yml");
|
||||
YamlConfiguration defMessages = YamlConfiguration.loadConfiguration(defMessageStream);
|
||||
messages.setDefaults(defMessages);
|
||||
// Parse colour codes
|
||||
Util.parseColours(messages);
|
||||
}
|
||||
|
||||
/**
|
||||
* loads all player purchase messages from the database.
|
||||
*/
|
||||
public static void loadTransactionMessages() { //TODO Converted to UUID
|
||||
player_messages.clear(); // Delete old messages
|
||||
try {
|
||||
ResultSet rs = plugin.getDB().getConnection().prepareStatement("SELECT * FROM messages").executeQuery();
|
||||
while (rs.next()) {
|
||||
UUID owner = UUID.fromString(rs.getString("owner"));
|
||||
String message = rs.getString("message");
|
||||
LinkedList<String> msgs = player_messages.get(owner);
|
||||
if (msgs == null) {
|
||||
msgs = new LinkedList<String>();
|
||||
player_messages.put(owner, msgs);
|
||||
}
|
||||
msgs.add(message);
|
||||
}
|
||||
} catch (SQLException e) {
|
||||
e.printStackTrace();
|
||||
System.out.println("Could not load transaction messages from database. Skipping.");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param player
|
||||
* The name of the player to message
|
||||
* @param message
|
||||
* The message to send them Sends the given player a message if
|
||||
* they're online. Else, if they're not online, queues it for
|
||||
* them in the database.
|
||||
*/
|
||||
public static void send(UUID player, String message) { //TODO Converted to UUID
|
||||
OfflinePlayer p = Bukkit.getOfflinePlayer(player);
|
||||
if (p == null || !p.isOnline()) {
|
||||
LinkedList<String> msgs = player_messages.get(player);
|
||||
if (msgs == null) {
|
||||
msgs = new LinkedList<String>();
|
||||
player_messages.put(player, msgs);
|
||||
}
|
||||
msgs.add(message);
|
||||
String q = "INSERT INTO messages (owner, message, time) VALUES (?, ?, ?)";
|
||||
plugin.getDB().execute(q, player.toString(), message, System.currentTimeMillis());
|
||||
} else {
|
||||
p.getPlayer().sendMessage(message);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes any messages that are older than a week in the database, to save
|
||||
* on space.
|
||||
*/
|
||||
public static void clean() {
|
||||
System.out.println("Cleaning purchase messages from database that are over a week old...");
|
||||
// 604800,000 msec = 1 week.
|
||||
long weekAgo = System.currentTimeMillis() - 604800000;
|
||||
plugin.getDB().execute("DELETE FROM messages WHERE time < ?", weekAgo);
|
||||
}
|
||||
|
||||
/**
|
||||
* Empties the queue of messages a player has and sends them to the player.
|
||||
*
|
||||
* @param p
|
||||
* The player to message
|
||||
* @return true if success, false if the player is offline or null
|
||||
*/
|
||||
public static boolean flush(OfflinePlayer p) { //TODO Changed to UUID
|
||||
if (p != null && p.isOnline()) {
|
||||
UUID pName = p.getUniqueId();
|
||||
LinkedList<String> msgs = player_messages.get(pName);
|
||||
if (msgs != null) {
|
||||
for (String msg : msgs) {
|
||||
p.getPlayer().sendMessage(msg);
|
||||
}
|
||||
plugin.getDB().execute("DELETE FROM messages WHERE owner = ?", pName.toString());
|
||||
msgs.clear();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static void sendShopInfo(Player p, Shop shop) {
|
||||
sendShopInfo(p, shop, shop.getRemainingStock());
|
||||
}
|
||||
|
||||
public static void sendShopInfo(Player p, Shop shop, int stock) {
|
||||
// Potentially faster with an array?
|
||||
ItemStack items = shop.getItem();
|
||||
p.sendMessage("");
|
||||
p.sendMessage("");
|
||||
p.sendMessage(ChatColor.DARK_PURPLE + "+---------------------------------------------------+");
|
||||
p.sendMessage(ChatColor.DARK_PURPLE + "| " + MsgUtil.getMessage("menu.shop-information"));
|
||||
p.sendMessage(ChatColor.DARK_PURPLE + "| " + MsgUtil.getMessage("menu.owner", Bukkit.getOfflinePlayer(shop.getOwner()).getName()));
|
||||
p.sendMessage(ChatColor.DARK_PURPLE + "| " + MsgUtil.getMessage("menu.item", shop.getDataName()));
|
||||
if (Util.isTool(items.getType())) {
|
||||
p.sendMessage(ChatColor.DARK_PURPLE + "| " + MsgUtil.getMessage("menu.damage-percent-remaining", Util.getToolPercentage(items)));
|
||||
}
|
||||
if (shop.isSelling()) {
|
||||
p.sendMessage(ChatColor.DARK_PURPLE + "| " + MsgUtil.getMessage("menu.stock", "" + stock));
|
||||
} else {
|
||||
int space = shop.getRemainingSpace();
|
||||
p.sendMessage(ChatColor.DARK_PURPLE + "| " + MsgUtil.getMessage("menu.space", "" + space));
|
||||
}
|
||||
p.sendMessage(ChatColor.DARK_PURPLE + "| " + MsgUtil.getMessage("menu.price-per", shop.getDataName(), Util.format(shop.getPrice())));
|
||||
if (shop.isBuying()) {
|
||||
p.sendMessage(ChatColor.DARK_PURPLE + "| " + MsgUtil.getMessage("menu.this-shop-is-buying"));
|
||||
} else {
|
||||
p.sendMessage(ChatColor.DARK_PURPLE + "| " + MsgUtil.getMessage("menu.this-shop-is-selling"));
|
||||
}
|
||||
Map<Enchantment, Integer> enchs = items.getItemMeta().getEnchants();
|
||||
if (enchs != null && !enchs.isEmpty()) {
|
||||
p.sendMessage(ChatColor.DARK_PURPLE + "+--------------------" + MsgUtil.getMessage("menu.enchants") + "-----------------------+");
|
||||
for (Entry<Enchantment, Integer> entries : enchs.entrySet()) {
|
||||
p.sendMessage(ChatColor.DARK_PURPLE + "| " + ChatColor.YELLOW + entries.getKey().getName() + " " + entries.getValue());
|
||||
}
|
||||
}
|
||||
try {
|
||||
Class.forName("org.bukkit.inventory.meta.EnchantmentStorageMeta");
|
||||
if (items.getItemMeta() instanceof EnchantmentStorageMeta) {
|
||||
EnchantmentStorageMeta stor = (EnchantmentStorageMeta) items.getItemMeta();
|
||||
stor.getStoredEnchants();
|
||||
enchs = stor.getStoredEnchants();
|
||||
if (enchs != null && !enchs.isEmpty()) {
|
||||
p.sendMessage(ChatColor.DARK_PURPLE + "+-----------------" + MsgUtil.getMessage("menu.stored-enchants") + "--------------------+");
|
||||
for (Entry<Enchantment, Integer> entries : enchs.entrySet()) {
|
||||
p.sendMessage(ChatColor.DARK_PURPLE + "| " + ChatColor.YELLOW + entries.getKey().getName() + " " + entries.getValue());
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (ClassNotFoundException e) {
|
||||
// They don't have an up to date enough build of CB to do this.
|
||||
// TODO: Remove this when it becomes redundant
|
||||
}
|
||||
p.sendMessage(ChatColor.DARK_PURPLE + "+---------------------------------------------------+");
|
||||
}
|
||||
|
||||
public static void sendPurchaseSuccess(Player p, Shop shop, int amount) {
|
||||
p.sendMessage(ChatColor.DARK_PURPLE + "+---------------------------------------------------+");
|
||||
p.sendMessage(ChatColor.DARK_PURPLE + "| " + MsgUtil.getMessage("menu.successful-purchase"));
|
||||
p.sendMessage(ChatColor.DARK_PURPLE + "| " + MsgUtil.getMessage("menu.item-name-and-price", "" + amount, shop.getDataName(), Util.format((amount * shop.getPrice()))));
|
||||
Map<Enchantment, Integer> enchs = shop.getItem().getItemMeta().getEnchants();
|
||||
if (enchs != null && !enchs.isEmpty()) {
|
||||
p.sendMessage(ChatColor.DARK_PURPLE + "+--------------------" + MsgUtil.getMessage("menu.enchants") + "-----------------------+");
|
||||
for (Entry<Enchantment, Integer> entries : enchs.entrySet()) {
|
||||
p.sendMessage(ChatColor.DARK_PURPLE + "| " + ChatColor.YELLOW + entries.getKey().getName() + " " + entries.getValue());
|
||||
}
|
||||
}
|
||||
enchs = shop.getItem().getItemMeta().getEnchants();
|
||||
if (enchs != null && !enchs.isEmpty()) {
|
||||
p.sendMessage(ChatColor.DARK_PURPLE + "+-----------------" + MsgUtil.getMessage("menu.stored-enchants") + "--------------------+");
|
||||
for (Entry<Enchantment, Integer> entries : enchs.entrySet()) {
|
||||
p.sendMessage(ChatColor.DARK_PURPLE + "| " + ChatColor.YELLOW + entries.getKey().getName() + " " + entries.getValue());
|
||||
}
|
||||
}
|
||||
try {
|
||||
Class.forName("org.bukkit.inventory.meta.EnchantmentStorageMeta");
|
||||
if (shop.getItem().getItemMeta() instanceof EnchantmentStorageMeta) {
|
||||
EnchantmentStorageMeta stor = (EnchantmentStorageMeta) shop.getItem().getItemMeta();
|
||||
stor.getStoredEnchants();
|
||||
enchs = stor.getStoredEnchants();
|
||||
if (enchs != null && !enchs.isEmpty()) {
|
||||
p.sendMessage(ChatColor.DARK_PURPLE + "+-----------------" + MsgUtil.getMessage("menu.stored-enchants") + "--------------------+");
|
||||
for (Entry<Enchantment, Integer> entries : enchs.entrySet()) {
|
||||
p.sendMessage(ChatColor.DARK_PURPLE + "| " + ChatColor.YELLOW + entries.getKey().getName() + " " + entries.getValue());
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (ClassNotFoundException e) {
|
||||
// They don't have an up to date enough build of CB to do this.
|
||||
// TODO: Remove this when it becomes redundant
|
||||
}
|
||||
p.sendMessage(ChatColor.DARK_PURPLE + "+---------------------------------------------------+");
|
||||
}
|
||||
|
||||
public static void sendSellSuccess(Player p, Shop shop, int amount) {
|
||||
p.sendMessage(ChatColor.DARK_PURPLE + "+---------------------------------------------------+");
|
||||
p.sendMessage(ChatColor.DARK_PURPLE + "| " + MsgUtil.getMessage("menu.successfully-sold"));
|
||||
p.sendMessage(ChatColor.DARK_PURPLE + "| " + MsgUtil.getMessage("menu.item-name-and-price", "" + amount, shop.getDataName(), Util.format((amount * shop.getPrice()))));
|
||||
if (plugin.getConfig().getBoolean("show-tax")) {
|
||||
double tax = plugin.getConfig().getDouble("tax");
|
||||
double total = amount * shop.getPrice();
|
||||
if (tax != 0) {
|
||||
if (!p.getUniqueId().equals(shop.getOwner())) {
|
||||
p.sendMessage(ChatColor.DARK_PURPLE + "| " + MsgUtil.getMessage("menu.sell-tax", "" + Util.format((tax * total))));
|
||||
} else {
|
||||
p.sendMessage(ChatColor.DARK_PURPLE + "| " + MsgUtil.getMessage("menu.sell-tax-self"));
|
||||
}
|
||||
}
|
||||
}
|
||||
Map<Enchantment, Integer> enchs = shop.getItem().getItemMeta().getEnchants();
|
||||
if (enchs != null && !enchs.isEmpty()) {
|
||||
p.sendMessage(ChatColor.DARK_PURPLE + "+--------------------" + MsgUtil.getMessage("menu.enchants") + "-----------------------+");
|
||||
for (Entry<Enchantment, Integer> entries : enchs.entrySet()) {
|
||||
p.sendMessage(ChatColor.DARK_PURPLE + "| " + ChatColor.YELLOW + entries.getKey().getName() + " " + entries.getValue());
|
||||
}
|
||||
}
|
||||
try {
|
||||
Class.forName("org.bukkit.inventory.meta.EnchantmentStorageMeta");
|
||||
if (shop.getItem().getItemMeta() instanceof EnchantmentStorageMeta) {
|
||||
EnchantmentStorageMeta stor = (EnchantmentStorageMeta) shop.getItem().getItemMeta();
|
||||
stor.getStoredEnchants();
|
||||
enchs = stor.getStoredEnchants();
|
||||
if (enchs != null && !enchs.isEmpty()) {
|
||||
p.sendMessage(ChatColor.DARK_PURPLE + "+--------------------" + MsgUtil.getMessage("menu.stored-enchants") + "-----------------------+");
|
||||
for (Entry<Enchantment, Integer> entries : enchs.entrySet()) {
|
||||
p.sendMessage(ChatColor.DARK_PURPLE + "| " + ChatColor.YELLOW + entries.getKey().getName() + " " + entries.getValue());
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (ClassNotFoundException e) {
|
||||
// They don't have an up to date enough build of CB to do this.
|
||||
// TODO: Remove this when it becomes redundant
|
||||
}
|
||||
p.sendMessage(ChatColor.DARK_PURPLE + "+---------------------------------------------------+");
|
||||
}
|
||||
|
||||
public static String getMessage(String loc, String... args) {
|
||||
String raw = messages.getString(loc);
|
||||
if (raw == null || raw.isEmpty()) {
|
||||
return "Invalid message: " + loc;
|
||||
}
|
||||
if (args == null) {
|
||||
return raw;
|
||||
}
|
||||
for (int i = 0; i < args.length; i++) {
|
||||
raw = raw.replace("{" + i + "}", args[i]); //TODO Nullpointer?
|
||||
}
|
||||
return raw;
|
||||
}
|
||||
}
|
260
src/main/java/org/maxgamer/quickshop/Util/NMS.java
Normal file
260
src/main/java/org/maxgamer/quickshop/Util/NMS.java
Normal file
@ -0,0 +1,260 @@
|
||||
package org.maxgamer.quickshop.Util;
|
||||
|
||||
import java.io.BufferedInputStream;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.DataInput;
|
||||
import java.io.DataInputStream;
|
||||
import java.io.DataOutput;
|
||||
import java.io.DataOutputStream;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.ArrayList;
|
||||
import java.util.zip.GZIPInputStream;
|
||||
import java.util.zip.GZIPOutputStream;
|
||||
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.ChatColor;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.entity.Item;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.bukkit.inventory.meta.ItemMeta;
|
||||
import org.maxgamer.quickshop.QuickShop;
|
||||
|
||||
public class NMS {
|
||||
private static ArrayList<NMSDependent> dependents = new ArrayList<NMSDependent>();
|
||||
private static int nextId = 0;
|
||||
private static NMSDependent nms;
|
||||
|
||||
static {
|
||||
NMSDependent dep;
|
||||
dep = new NMSDependent("v1_6_R3") {
|
||||
@Override
|
||||
public void safeGuard(Item item) {
|
||||
org.bukkit.inventory.ItemStack iStack = item.getItemStack();
|
||||
net.minecraft.server.v1_6_R3.ItemStack nmsI = org.bukkit.craftbukkit.v1_6_R3.inventory.CraftItemStack.asNMSCopy(iStack);
|
||||
nmsI.count = 0;
|
||||
iStack = org.bukkit.craftbukkit.v1_6_R3.inventory.CraftItemStack.asBukkitCopy(nmsI);
|
||||
item.setItemStack(iStack);
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] getNBTBytes(org.bukkit.inventory.ItemStack iStack) {
|
||||
net.minecraft.server.v1_6_R3.ItemStack is = org.bukkit.craftbukkit.v1_6_R3.inventory.CraftItemStack.asNMSCopy(iStack);
|
||||
net.minecraft.server.v1_6_R3.NBTTagCompound itemCompound = new net.minecraft.server.v1_6_R3.NBTTagCompound();
|
||||
itemCompound = is.save(itemCompound);
|
||||
return net.minecraft.server.v1_6_R3.NBTCompressedStreamTools.a(itemCompound);
|
||||
}
|
||||
|
||||
@Override
|
||||
public org.bukkit.inventory.ItemStack getItemStack(byte[] bytes) {
|
||||
net.minecraft.server.v1_6_R3.NBTTagCompound c = net.minecraft.server.v1_6_R3.NBTCompressedStreamTools.a(bytes);
|
||||
net.minecraft.server.v1_6_R3.ItemStack is = net.minecraft.server.v1_6_R3.ItemStack.createStack(c);
|
||||
return org.bukkit.craftbukkit.v1_6_R3.inventory.CraftItemStack.asBukkitCopy(is);
|
||||
}
|
||||
};
|
||||
dependents.add(dep);
|
||||
dep = new NMSDependent("v1_7_R1") {
|
||||
@Override
|
||||
public void safeGuard(Item item) {
|
||||
if(QuickShop.debug)System.out.println("safeGuard");
|
||||
org.bukkit.inventory.ItemStack iStack = item.getItemStack();
|
||||
net.minecraft.server.v1_7_R1.ItemStack nmsI = org.bukkit.craftbukkit.v1_7_R1.inventory.CraftItemStack.asNMSCopy(iStack);
|
||||
nmsI.count = 0;
|
||||
iStack = org.bukkit.craftbukkit.v1_7_R1.inventory.CraftItemStack.asBukkitCopy(nmsI);
|
||||
item.setItemStack(iStack);
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] getNBTBytes(org.bukkit.inventory.ItemStack iStack) {
|
||||
if(QuickShop.debug)System.out.println("getNBTBytes");
|
||||
net.minecraft.server.v1_7_R1.ItemStack is = org.bukkit.craftbukkit.v1_7_R1.inventory.CraftItemStack.asNMSCopy(iStack);
|
||||
net.minecraft.server.v1_7_R1.NBTTagCompound itemCompound = new net.minecraft.server.v1_7_R1.NBTTagCompound();
|
||||
itemCompound = is.save(itemCompound);
|
||||
return net.minecraft.server.v1_7_R1.NBTCompressedStreamTools.a(itemCompound);
|
||||
}
|
||||
|
||||
@Override
|
||||
public org.bukkit.inventory.ItemStack getItemStack(byte[] bytes) {
|
||||
if(QuickShop.debug)System.out.println("getItemStack");
|
||||
net.minecraft.server.v1_7_R1.NBTTagCompound c = net.minecraft.server.v1_7_R1.NBTCompressedStreamTools.a(bytes);
|
||||
net.minecraft.server.v1_7_R1.ItemStack is = net.minecraft.server.v1_7_R1.ItemStack.createStack(c);
|
||||
return org.bukkit.craftbukkit.v1_7_R1.inventory.CraftItemStack.asBukkitCopy(is);
|
||||
}
|
||||
};
|
||||
dependents.add(dep);
|
||||
dep = new NMSDependent("v1_7_R3") {
|
||||
@Override
|
||||
public void safeGuard(Item item) {
|
||||
if(QuickShop.debug)System.out.println("safeGuard");
|
||||
org.bukkit.inventory.ItemStack iStack = item.getItemStack();
|
||||
net.minecraft.server.v1_7_R3.ItemStack nmsI = org.bukkit.craftbukkit.v1_7_R3.inventory.CraftItemStack.asNMSCopy(iStack);
|
||||
nmsI.count = 0;
|
||||
iStack = org.bukkit.craftbukkit.v1_7_R3.inventory.CraftItemStack.asBukkitCopy(nmsI);
|
||||
item.setItemStack(iStack);
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] getNBTBytes(org.bukkit.inventory.ItemStack iStack) {
|
||||
if(QuickShop.debug)System.out.println("getNBTBytes");
|
||||
net.minecraft.server.v1_7_R3.ItemStack is = org.bukkit.craftbukkit.v1_7_R3.inventory.CraftItemStack.asNMSCopy(iStack);
|
||||
net.minecraft.server.v1_7_R3.NBTTagCompound itemCompound = new net.minecraft.server.v1_7_R3.NBTTagCompound();
|
||||
itemCompound = is.save(itemCompound);
|
||||
return net.minecraft.server.v1_7_R3.NBTCompressedStreamTools.a(itemCompound);
|
||||
}
|
||||
|
||||
@Override
|
||||
public org.bukkit.inventory.ItemStack getItemStack(byte[] bytes) {
|
||||
if(QuickShop.debug)System.out.println("getItemStack");
|
||||
net.minecraft.server.v1_7_R3.NBTTagCompound c = net.minecraft.server.v1_7_R3.NBTCompressedStreamTools.a(bytes, null);
|
||||
net.minecraft.server.v1_7_R3.ItemStack is = net.minecraft.server.v1_7_R3.ItemStack.createStack(c);
|
||||
return org.bukkit.craftbukkit.v1_7_R3.inventory.CraftItemStack.asBukkitCopy(is);
|
||||
}
|
||||
};
|
||||
dependents.add(dep);
|
||||
dep = new NMSDependent("v1_8") {
|
||||
@Override
|
||||
public void safeGuard(Item item) {
|
||||
if(QuickShop.debug)System.out.println("safeGuard");
|
||||
org.bukkit.inventory.ItemStack iStack = item.getItemStack();
|
||||
net.minecraft.server.v1_8_R1.ItemStack nmsI = org.bukkit.craftbukkit.v1_8_R1.inventory.CraftItemStack.asNMSCopy(iStack);
|
||||
nmsI.count = 0;
|
||||
iStack = org.bukkit.craftbukkit.v1_8_R1.inventory.CraftItemStack.asBukkitCopy(nmsI);
|
||||
item.setItemStack(iStack);
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] getNBTBytes(org.bukkit.inventory.ItemStack iStack) {
|
||||
try{
|
||||
if(QuickShop.debug)System.out.println("getNBTBytes");
|
||||
net.minecraft.server.v1_8_R1.ItemStack is = org.bukkit.craftbukkit.v1_8_R1.inventory.CraftItemStack.asNMSCopy(iStack);
|
||||
net.minecraft.server.v1_8_R1.NBTTagCompound itemCompound = new net.minecraft.server.v1_8_R1.NBTTagCompound();
|
||||
itemCompound = is.save(itemCompound);
|
||||
ByteArrayOutputStream bytearrayoutputstream = new ByteArrayOutputStream();
|
||||
DataOutputStream dataoutputstream = new DataOutputStream(new GZIPOutputStream(bytearrayoutputstream));
|
||||
try {
|
||||
net.minecraft.server.v1_8_R1.NBTCompressedStreamTools.a(itemCompound, (DataOutput) dataoutputstream);
|
||||
} finally {
|
||||
dataoutputstream.close();
|
||||
}
|
||||
return bytearrayoutputstream.toByteArray();
|
||||
}catch(Exception e){
|
||||
return new byte[0];
|
||||
}
|
||||
//return net.minecraft.server.v1_8_R1.NBTCompressedStreamTools.a(itemCompound);
|
||||
}
|
||||
|
||||
@Override
|
||||
public org.bukkit.inventory.ItemStack getItemStack(byte[] bytes) {
|
||||
try{
|
||||
if(QuickShop.debug)System.out.println("getItemStack");
|
||||
DataInputStream datainputstream = new DataInputStream(new BufferedInputStream(new GZIPInputStream(new ByteArrayInputStream(bytes))));
|
||||
net.minecraft.server.v1_8_R1.NBTTagCompound nbttagcompound;
|
||||
try {
|
||||
nbttagcompound = net.minecraft.server.v1_8_R1.NBTCompressedStreamTools.a((DataInput) datainputstream, null);
|
||||
} finally {
|
||||
datainputstream.close();
|
||||
}
|
||||
//net.minecraft.server.v1_8_R1.NBTTagCompound c = net.minecraft.server.v1_8_R1.NBTCompressedStreamTools.a(bytes, null);
|
||||
net.minecraft.server.v1_8_R1.ItemStack is = net.minecraft.server.v1_8_R1.ItemStack.createStack(nbttagcompound);
|
||||
return org.bukkit.craftbukkit.v1_8_R1.inventory.CraftItemStack.asBukkitCopy(is);
|
||||
}catch(Exception e){
|
||||
return new ItemStack(Material.AIR);
|
||||
}
|
||||
}
|
||||
};
|
||||
dependents.add(dep);
|
||||
}
|
||||
|
||||
public static void safeGuard(Item item) throws ClassNotFoundException {
|
||||
if(QuickShop.debug)System.out.println("Renaming");
|
||||
rename(item.getItemStack());
|
||||
if(QuickShop.debug)System.out.println("Protecting");
|
||||
protect(item);
|
||||
if(QuickShop.debug)System.out.println("Seting pickup delay");
|
||||
item.setPickupDelay(2147483647);
|
||||
}
|
||||
|
||||
private static void rename(ItemStack iStack) {
|
||||
ItemMeta meta = iStack.getItemMeta();
|
||||
meta.setDisplayName(ChatColor.RED + "QuickShop " + Util.getName(iStack) + " " + nextId++);
|
||||
iStack.setItemMeta(meta);
|
||||
}
|
||||
|
||||
public static byte[] getNBTBytes(org.bukkit.inventory.ItemStack iStack) throws ClassNotFoundException {
|
||||
validate();
|
||||
return nms.getNBTBytes(iStack);
|
||||
}
|
||||
|
||||
public static ItemStack getItemStack(byte[] bytes) throws ClassNotFoundException {
|
||||
validate();
|
||||
return nms.getItemStack(bytes);
|
||||
}
|
||||
|
||||
private static void protect(Item item) {
|
||||
try {
|
||||
Field itemField = item.getClass().getDeclaredField("item");
|
||||
itemField.setAccessible(true);
|
||||
Object nmsEntityItem = itemField.get(item);
|
||||
Method getItemStack;
|
||||
try {
|
||||
getItemStack = nmsEntityItem.getClass().getMethod("getItemStack", new Class[0]);
|
||||
} catch (NoSuchMethodException e) {
|
||||
try {
|
||||
getItemStack = nmsEntityItem.getClass().getMethod("d", new Class[0]);
|
||||
} catch (NoSuchMethodException e2) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
Object itemStack = getItemStack.invoke(nmsEntityItem, new Object[0]);
|
||||
Field countField;
|
||||
try {
|
||||
countField = itemStack.getClass().getDeclaredField("count");
|
||||
} catch (NoSuchFieldException e) {
|
||||
countField = itemStack.getClass().getDeclaredField("a");
|
||||
}
|
||||
countField.setAccessible(true);
|
||||
countField.set(itemStack, Integer.valueOf(0));
|
||||
} catch (NoSuchFieldException e) {
|
||||
e.printStackTrace();
|
||||
System.out.println("[QuickShop] Could not protect item from pickup properly! Dupes are now possible.");
|
||||
} catch (Exception e) {
|
||||
System.out.println("Other error");
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
private static void validate() throws ClassNotFoundException {
|
||||
if (nms != null) {
|
||||
return;
|
||||
}
|
||||
String packageName = Bukkit.getServer().getClass().getPackage().getName();
|
||||
packageName = packageName.substring(packageName.lastIndexOf(".") + 1);
|
||||
// System.out.println("Package: " + packageName);
|
||||
for (NMSDependent dep : dependents) {
|
||||
if ((packageName.startsWith(dep.getVersion())) || ((dep.getVersion().isEmpty()) && ((packageName.equals("bukkit")) || (packageName.equals("craftbukkit"))))) {
|
||||
nms = dep;
|
||||
return;
|
||||
}
|
||||
}
|
||||
throw new ClassNotFoundException("This version of QuickShop is incompatible.");
|
||||
}
|
||||
|
||||
private static abstract class NMSDependent {
|
||||
private String version;
|
||||
|
||||
public String getVersion() {
|
||||
return this.version;
|
||||
}
|
||||
|
||||
public NMSDependent(String version) {
|
||||
this.version = version;
|
||||
}
|
||||
|
||||
public abstract void safeGuard(Item paramItem);
|
||||
|
||||
public abstract byte[] getNBTBytes(org.bukkit.inventory.ItemStack paramItemStack);
|
||||
|
||||
public abstract org.bukkit.inventory.ItemStack getItemStack(byte[] paramArrayOfByte);
|
||||
}
|
||||
}
|
937
src/main/java/org/maxgamer/quickshop/Util/Util.java
Normal file
937
src/main/java/org/maxgamer/quickshop/Util/Util.java
Normal file
@ -0,0 +1,937 @@
|
||||
package org.maxgamer.quickshop.Util;
|
||||
|
||||
import java.text.DecimalFormat;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.bukkit.ChatColor;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.block.Block;
|
||||
import org.bukkit.block.BlockFace;
|
||||
import org.bukkit.block.BlockState;
|
||||
import org.bukkit.configuration.InvalidConfigurationException;
|
||||
import org.bukkit.configuration.file.YamlConfiguration;
|
||||
import org.bukkit.enchantments.Enchantment;
|
||||
import org.bukkit.inventory.Inventory;
|
||||
import org.bukkit.inventory.InventoryHolder;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.bukkit.inventory.meta.EnchantmentStorageMeta;
|
||||
import org.bukkit.material.MaterialData;
|
||||
import org.bukkit.material.Sign;
|
||||
import org.bukkit.potion.Potion;
|
||||
import org.bukkit.potion.PotionEffect;
|
||||
import org.maxgamer.quickshop.QuickShop;
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
public class Util {
|
||||
private static HashSet<Material> tools = new HashSet<Material>();
|
||||
private static HashSet<Material> blacklist = new HashSet<Material>();
|
||||
private static HashSet<Material> shoppables = new HashSet<Material>();
|
||||
private static HashSet<Material> transparent = new HashSet<Material>();
|
||||
private static QuickShop plugin;
|
||||
static {
|
||||
plugin = QuickShop.instance;
|
||||
for (String s : plugin.getConfig().getStringList("shop-blocks")) {
|
||||
Material mat = Material.getMaterial(s.toUpperCase());
|
||||
if (mat == null) {
|
||||
try {
|
||||
mat = Material.getMaterial(Integer.parseInt(s));
|
||||
} catch (NumberFormatException e) {
|
||||
}
|
||||
}
|
||||
if (mat == null) {
|
||||
plugin.getLogger().info("Invalid shop-block: " + s);
|
||||
} else {
|
||||
shoppables.add(mat);
|
||||
}
|
||||
}
|
||||
tools.add(Material.BOW);
|
||||
tools.add(Material.SHEARS);
|
||||
tools.add(Material.FISHING_ROD);
|
||||
tools.add(Material.FLINT_AND_STEEL);
|
||||
tools.add(Material.CHAINMAIL_BOOTS);
|
||||
tools.add(Material.CHAINMAIL_CHESTPLATE);
|
||||
tools.add(Material.CHAINMAIL_HELMET);
|
||||
tools.add(Material.CHAINMAIL_LEGGINGS);
|
||||
tools.add(Material.WOOD_AXE);
|
||||
tools.add(Material.WOOD_HOE);
|
||||
tools.add(Material.WOOD_PICKAXE);
|
||||
tools.add(Material.WOOD_SPADE);
|
||||
tools.add(Material.WOOD_SWORD);
|
||||
tools.add(Material.LEATHER_BOOTS);
|
||||
tools.add(Material.LEATHER_CHESTPLATE);
|
||||
tools.add(Material.LEATHER_HELMET);
|
||||
tools.add(Material.LEATHER_LEGGINGS);
|
||||
tools.add(Material.DIAMOND_AXE);
|
||||
tools.add(Material.DIAMOND_HOE);
|
||||
tools.add(Material.DIAMOND_PICKAXE);
|
||||
tools.add(Material.DIAMOND_SPADE);
|
||||
tools.add(Material.DIAMOND_SWORD);
|
||||
tools.add(Material.DIAMOND_BOOTS);
|
||||
tools.add(Material.DIAMOND_CHESTPLATE);
|
||||
tools.add(Material.DIAMOND_HELMET);
|
||||
tools.add(Material.DIAMOND_LEGGINGS);
|
||||
tools.add(Material.STONE_AXE);
|
||||
tools.add(Material.STONE_HOE);
|
||||
tools.add(Material.STONE_PICKAXE);
|
||||
tools.add(Material.STONE_SPADE);
|
||||
tools.add(Material.STONE_SWORD);
|
||||
tools.add(Material.GOLD_AXE);
|
||||
tools.add(Material.GOLD_HOE);
|
||||
tools.add(Material.GOLD_PICKAXE);
|
||||
tools.add(Material.GOLD_SPADE);
|
||||
tools.add(Material.GOLD_SWORD);
|
||||
tools.add(Material.GOLD_BOOTS);
|
||||
tools.add(Material.GOLD_CHESTPLATE);
|
||||
tools.add(Material.GOLD_HELMET);
|
||||
tools.add(Material.GOLD_LEGGINGS);
|
||||
tools.add(Material.IRON_AXE);
|
||||
tools.add(Material.IRON_HOE);
|
||||
tools.add(Material.IRON_PICKAXE);
|
||||
tools.add(Material.IRON_SPADE);
|
||||
tools.add(Material.IRON_SWORD);
|
||||
tools.add(Material.IRON_BOOTS);
|
||||
tools.add(Material.IRON_CHESTPLATE);
|
||||
tools.add(Material.IRON_HELMET);
|
||||
tools.add(Material.IRON_LEGGINGS);
|
||||
List<String> configBlacklist = plugin.getConfig().getStringList("blacklist");
|
||||
for (String s : configBlacklist) {
|
||||
Material mat = Material.getMaterial(s.toUpperCase());
|
||||
if (mat == null) {
|
||||
mat = Material.getMaterial(Integer.parseInt(s));
|
||||
if (mat == null) {
|
||||
plugin.getLogger().info(s + " is not a valid material. Check your spelling or ID");
|
||||
continue;
|
||||
}
|
||||
}
|
||||
blacklist.add(mat);
|
||||
}
|
||||
transparent.clear();
|
||||
// ToDo: add extras to config file
|
||||
addTransparentBlock(Material.AIR);
|
||||
/* Misc */
|
||||
addTransparentBlock(Material.CAKE_BLOCK);
|
||||
/* Redstone Material */
|
||||
addTransparentBlock(Material.REDSTONE_WIRE);
|
||||
/* Redstone Torches */
|
||||
addTransparentBlock(Material.REDSTONE_TORCH_OFF);
|
||||
addTransparentBlock(Material.REDSTONE_TORCH_ON);
|
||||
/* Diodes (Repeaters) */
|
||||
addTransparentBlock(Material.DIODE_BLOCK_OFF);
|
||||
addTransparentBlock(Material.DIODE_BLOCK_ON);
|
||||
/* Power Sources */
|
||||
addTransparentBlock(Material.DETECTOR_RAIL);
|
||||
addTransparentBlock(Material.LEVER);
|
||||
addTransparentBlock(Material.STONE_BUTTON);
|
||||
addTransparentBlock(Material.WOOD_BUTTON);
|
||||
addTransparentBlock(Material.STONE_PLATE);
|
||||
addTransparentBlock(Material.WOOD_PLATE);
|
||||
/* Nature Material */
|
||||
addTransparentBlock(Material.RED_MUSHROOM);
|
||||
addTransparentBlock(Material.BROWN_MUSHROOM);
|
||||
addTransparentBlock(Material.RED_ROSE);
|
||||
addTransparentBlock(Material.YELLOW_FLOWER);
|
||||
addTransparentBlock(Material.FLOWER_POT);
|
||||
/* Greens */
|
||||
addTransparentBlock(Material.LONG_GRASS);
|
||||
addTransparentBlock(Material.VINE);
|
||||
addTransparentBlock(Material.WATER_LILY);
|
||||
/* Seedy things */
|
||||
addTransparentBlock(Material.MELON_STEM);
|
||||
addTransparentBlock(Material.PUMPKIN_STEM);
|
||||
addTransparentBlock(Material.CROPS);
|
||||
addTransparentBlock(Material.NETHER_WARTS);
|
||||
/* Semi-nature */
|
||||
addTransparentBlock(Material.SNOW);
|
||||
addTransparentBlock(Material.FIRE);
|
||||
addTransparentBlock(Material.WEB);
|
||||
addTransparentBlock(Material.TRIPWIRE);
|
||||
addTransparentBlock(Material.TRIPWIRE_HOOK);
|
||||
/* Stairs */
|
||||
addTransparentBlock(Material.COBBLESTONE_STAIRS);
|
||||
addTransparentBlock(Material.BRICK_STAIRS);
|
||||
addTransparentBlock(Material.SANDSTONE_STAIRS);
|
||||
addTransparentBlock(Material.NETHER_BRICK_STAIRS);
|
||||
addTransparentBlock(Material.SMOOTH_STAIRS);
|
||||
/* Wood Stairs */
|
||||
addTransparentBlock(Material.BIRCH_WOOD_STAIRS);
|
||||
addTransparentBlock(Material.WOOD_STAIRS);
|
||||
addTransparentBlock(Material.JUNGLE_WOOD_STAIRS);
|
||||
addTransparentBlock(Material.SPRUCE_WOOD_STAIRS);
|
||||
/* Lava & Water */
|
||||
addTransparentBlock(Material.LAVA);
|
||||
addTransparentBlock(Material.STATIONARY_LAVA);
|
||||
addTransparentBlock(Material.WATER);
|
||||
addTransparentBlock(Material.STATIONARY_WATER);
|
||||
/* Saplings and bushes */
|
||||
addTransparentBlock(Material.SAPLING);
|
||||
addTransparentBlock(Material.DEAD_BUSH);
|
||||
/* Construction Material */
|
||||
/* Fences */
|
||||
addTransparentBlock(Material.FENCE);
|
||||
addTransparentBlock(Material.FENCE_GATE);
|
||||
addTransparentBlock(Material.IRON_FENCE);
|
||||
addTransparentBlock(Material.NETHER_FENCE);
|
||||
/* Ladders, Signs */
|
||||
addTransparentBlock(Material.LADDER);
|
||||
addTransparentBlock(Material.SIGN_POST);
|
||||
addTransparentBlock(Material.WALL_SIGN);
|
||||
/* Bed */
|
||||
addTransparentBlock(Material.BED_BLOCK);
|
||||
/* Pistons */
|
||||
addTransparentBlock(Material.PISTON_EXTENSION);
|
||||
addTransparentBlock(Material.PISTON_MOVING_PIECE);
|
||||
addTransparentBlock(Material.RAILS);
|
||||
/* Torch & Trapdoor */
|
||||
addTransparentBlock(Material.TORCH);
|
||||
addTransparentBlock(Material.TRAP_DOOR);
|
||||
/* New */
|
||||
addTransparentBlock(Material.BREWING_STAND);
|
||||
addTransparentBlock(Material.WOODEN_DOOR);
|
||||
addTransparentBlock(Material.WOOD_STEP);
|
||||
}
|
||||
|
||||
public static boolean isTransparent(Material m) {
|
||||
boolean trans = transparent.contains(m);
|
||||
return trans;
|
||||
}
|
||||
|
||||
public static void addTransparentBlock(Material m) {
|
||||
if (transparent.add(m) == false) {
|
||||
System.out.println("Already added as transparent: " + m.toString());
|
||||
}
|
||||
if (!m.isBlock())
|
||||
System.out.println(m + " is not a block!");
|
||||
}
|
||||
|
||||
public static void parseColours(YamlConfiguration config) {
|
||||
Set<String> keys = config.getKeys(true);
|
||||
for (String key : keys) {
|
||||
String filtered = config.getString(key);
|
||||
if (filtered.startsWith("MemorySection")) {
|
||||
continue;
|
||||
}
|
||||
filtered = ChatColor.translateAlternateColorCodes('&', filtered);
|
||||
config.set(key, filtered);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the given block could be used to make a shop out of.
|
||||
*
|
||||
* @param b
|
||||
* The block to check. Possibly a chest, dispenser, etc.
|
||||
* @return True if it can be made into a shop, otherwise false.
|
||||
*/
|
||||
public static boolean canBeShop(Block b) {
|
||||
BlockState bs = b.getState();
|
||||
if (bs instanceof InventoryHolder == false)
|
||||
return false;
|
||||
return shoppables.contains(bs.getType());
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the percentage (Without trailing %) damage on a tool.
|
||||
*
|
||||
* @param item
|
||||
* The ItemStack of tools to check
|
||||
* @return The percentage 'health' the tool has. (Opposite of total damage)
|
||||
*/
|
||||
public static String getToolPercentage(ItemStack item) {
|
||||
double dura = item.getDurability();
|
||||
double max = item.getType().getMaxDurability();
|
||||
DecimalFormat formatter = new DecimalFormat("0");
|
||||
return formatter.format((1 - dura / max) * 100.0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the chest attached to the given chest. The given block must be a
|
||||
* chest.
|
||||
*
|
||||
* @param b
|
||||
* The chest to check.
|
||||
* @return the block which is also a chest and connected to b.
|
||||
*/
|
||||
public static Block getSecondHalf(Block b) {
|
||||
if (b.getType().toString().contains("CHEST") == false)
|
||||
return null;
|
||||
Block[] blocks = new Block[4];
|
||||
blocks[0] = b.getRelative(1, 0, 0);
|
||||
blocks[1] = b.getRelative(-1, 0, 0);
|
||||
blocks[2] = b.getRelative(0, 0, 1);
|
||||
blocks[3] = b.getRelative(0, 0, -1);
|
||||
for (Block c : blocks) {
|
||||
if (c.getType() == b.getType()) {
|
||||
return c;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a string into an item from the database.
|
||||
*
|
||||
* @param itemString
|
||||
* The database string. Is the result of makeString(ItemStack
|
||||
* item).
|
||||
* @return A new itemstack, with the properties given in the string
|
||||
*/
|
||||
public static ItemStack makeItem(String itemString) {
|
||||
String[] itemInfo = itemString.split(":");
|
||||
ItemStack item = new ItemStack(Material.getMaterial(itemInfo[0]));
|
||||
MaterialData data = new MaterialData(Integer.parseInt(itemInfo[1]));
|
||||
item.setData(data);
|
||||
item.setDurability(Short.parseShort(itemInfo[2]));
|
||||
item.setAmount(Integer.parseInt(itemInfo[3]));
|
||||
for (int i = 4; i < itemInfo.length; i = i + 2) {
|
||||
int level = Integer.parseInt(itemInfo[i + 1]);
|
||||
Enchantment ench = Enchantment.getByName(itemInfo[i]);
|
||||
if (ench == null)
|
||||
continue; // Invalid
|
||||
if (ench.canEnchantItem(item)) {
|
||||
if (level <= 0)
|
||||
continue;
|
||||
level = Math.min(ench.getMaxLevel(), level);
|
||||
item.addEnchantment(ench, level);
|
||||
}
|
||||
}
|
||||
return item;
|
||||
}
|
||||
|
||||
public static String serialize(ItemStack iStack) {
|
||||
YamlConfiguration cfg = new YamlConfiguration();
|
||||
cfg.set("item", iStack);
|
||||
return cfg.saveToString();
|
||||
}
|
||||
|
||||
public static ItemStack deserialize(String config) throws InvalidConfigurationException {
|
||||
YamlConfiguration cfg = new YamlConfiguration();
|
||||
cfg.loadFromString(config);
|
||||
ItemStack stack = cfg.getItemStack("item");
|
||||
return stack;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches an ItemStack's name - For example, converting INK_SAC:11 to
|
||||
* Dandellion Yellow, or WOOL:14 to Red Wool
|
||||
*
|
||||
* @param i
|
||||
* The itemstack to fetch the name of
|
||||
* @return The human readable item name.
|
||||
*/
|
||||
public static String getName(ItemStack i) {
|
||||
String vanillaName = getDataName(i.getType(), i.getDurability());
|
||||
return prettifyText(vanillaName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a name like IRON_INGOT into Iron Ingot to improve readability
|
||||
*
|
||||
* @param ugly
|
||||
* The string such as IRON_INGOT
|
||||
* @return A nicer version, such as Iron Ingot
|
||||
*
|
||||
*/
|
||||
public static String prettifyText(String ugly) {
|
||||
String[] nameParts = ugly.split("_");
|
||||
if (nameParts.length==1) {
|
||||
return firstUppercase(ugly);
|
||||
}
|
||||
|
||||
StringBuilder sb=new StringBuilder();
|
||||
for (String part : nameParts) {
|
||||
sb.append(firstUppercase(part)+" ");
|
||||
}
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
// Let's make very long names shorter for our sign
|
||||
public static String getNameForSign(ItemStack itemStack) {
|
||||
String name = getDataName(itemStack.getType(), itemStack.getDurability());
|
||||
|
||||
String[] nameParts = name.split("_");
|
||||
if (nameParts.length==1) {
|
||||
return firstUppercase(nameParts[0]);
|
||||
}
|
||||
|
||||
for (int i=0; i<nameParts.length-1; i++) {
|
||||
int length = StringUtils.join(nameParts).length();
|
||||
if (length>16) {
|
||||
nameParts[i] = nameParts[i].substring(0, 1)+".";
|
||||
} else {
|
||||
nameParts[i] = firstUppercase(nameParts[i]);
|
||||
}
|
||||
}
|
||||
|
||||
nameParts[nameParts.length-1] = firstUppercase(nameParts[nameParts.length-1]);
|
||||
|
||||
return StringUtils.join(nameParts);
|
||||
}
|
||||
|
||||
public static String firstUppercase(String string) {
|
||||
if (string.length()>1) {
|
||||
return Character.toUpperCase(string.charAt(0))+string.substring(1).toLowerCase();
|
||||
} else {
|
||||
return string.toUpperCase();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static String toRomain(Integer value) {
|
||||
return toRoman(value.intValue());
|
||||
}
|
||||
|
||||
private static final String[] ROMAN = { "X", "IX", "V", "IV", "I" };
|
||||
private static final int[] DECIMAL = { 10, 9, 5, 4, 1 };
|
||||
|
||||
/**
|
||||
* Converts the given number to roman numerals. If the number is >= 40 or <=
|
||||
* 0, it will just return the number as a string.
|
||||
*
|
||||
* @param n
|
||||
* The number to convert
|
||||
* @return The roman numeral representation of this number, or the number in
|
||||
* decimal form as a string if n <= 0 || n >= 40.
|
||||
*/
|
||||
public static String toRoman(int n) {
|
||||
if (n <= 0 || n >= 40)
|
||||
return "" + n;
|
||||
String roman = "";
|
||||
for (int i = 0; i < ROMAN.length; i++) {
|
||||
while (n >= DECIMAL[i]) {
|
||||
n -= DECIMAL[i];
|
||||
roman += ROMAN[i];
|
||||
}
|
||||
}
|
||||
return roman;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a given material and data value into a format similar to
|
||||
* Material.<?>.toString(). Upper case, with underscores. Includes material
|
||||
* name in result.
|
||||
*
|
||||
* @param mat
|
||||
* The base material.
|
||||
* @param damage
|
||||
* The durability/damage of the item.
|
||||
* @return A string with the name of the item.
|
||||
*/
|
||||
private static String getDataName(Material mat, short damage) {
|
||||
int id = mat.getId();
|
||||
switch (id) {
|
||||
case 35:
|
||||
switch ((int) damage) {
|
||||
case 0:
|
||||
return "WHITE_WOOL";
|
||||
case 1:
|
||||
return "ORANGE_WOOL";
|
||||
case 2:
|
||||
return "MAGENTA_WOOL";
|
||||
case 3:
|
||||
return "LIGHT_BLUE_WOOL";
|
||||
case 4:
|
||||
return "YELLOW_WOOL";
|
||||
case 5:
|
||||
return "LIME_WOOL";
|
||||
case 6:
|
||||
return "PINK_WOOL";
|
||||
case 7:
|
||||
return "GRAY_WOOL";
|
||||
case 8:
|
||||
return "LIGHT_GRAY_WOOL";
|
||||
case 9:
|
||||
return "CYAN_WOOL";
|
||||
case 10:
|
||||
return "PURPLE_WOOL";
|
||||
case 11:
|
||||
return "BLUE_WOOL";
|
||||
case 12:
|
||||
return "BROWN_WOOL";
|
||||
case 13:
|
||||
return "GREEN_WOOL";
|
||||
case 14:
|
||||
return "RED_WOOL";
|
||||
case 15:
|
||||
return "BLACK_WOOL";
|
||||
}
|
||||
return mat.toString();
|
||||
case 351:
|
||||
switch ((int) damage) {
|
||||
case 0:
|
||||
return "INK_SAC";
|
||||
case 1:
|
||||
return "ROSE_RED";
|
||||
case 2:
|
||||
return "CACTUS_GREEN";
|
||||
case 3:
|
||||
return "COCOA_BEANS";
|
||||
case 4:
|
||||
return "LAPIS_LAZULI";
|
||||
case 5:
|
||||
return "PURPLE_DYE";
|
||||
case 6:
|
||||
return "CYAN_DYE";
|
||||
case 7:
|
||||
return "LIGHT_GRAY_DYE";
|
||||
case 8:
|
||||
return "GRAY_DYE";
|
||||
case 9:
|
||||
return "PINK_DYE";
|
||||
case 10:
|
||||
return "LIME_DYE";
|
||||
case 11:
|
||||
return "DANDELION_YELLOW";
|
||||
case 12:
|
||||
return "LIGHT_BLUE_DYE";
|
||||
case 13:
|
||||
return "MAGENTA_DYE";
|
||||
case 14:
|
||||
return "ORANGE_DYE";
|
||||
case 15:
|
||||
return "BONE_MEAL";
|
||||
}
|
||||
return mat.toString();
|
||||
case 98:
|
||||
switch ((int) damage) {
|
||||
case 0:
|
||||
return "STONE_BRICKS";
|
||||
case 1:
|
||||
return "MOSSY_STONE_BRICKS";
|
||||
case 2:
|
||||
return "CRACKED_STONE_BRICKS";
|
||||
case 3:
|
||||
return "CHISELED_STONE_BRICKS";
|
||||
}
|
||||
return mat.toString();
|
||||
case 373:
|
||||
// Special case,.. Why?
|
||||
if (damage == 0)
|
||||
return "WATER_BOTTLE";
|
||||
Potion pot;
|
||||
try {
|
||||
pot = Potion.fromDamage(damage);
|
||||
} catch (Exception e) {
|
||||
return "CUSTOM_POTION";
|
||||
}
|
||||
String prefix = "";
|
||||
String suffix = "";
|
||||
if (pot.getLevel() > 0)
|
||||
suffix += "_" + pot.getLevel();
|
||||
if (pot.hasExtendedDuration())
|
||||
prefix += "EXTENDED_";
|
||||
if (pot.isSplash())
|
||||
prefix += "SPLASH_";
|
||||
if (pot.getEffects().isEmpty()) {
|
||||
switch ((int) pot.getNameId()) {
|
||||
case 0:
|
||||
return prefix + "MUNDANE_POTION" + suffix;
|
||||
case 7:
|
||||
return prefix + "CLEAR_POTION" + suffix;
|
||||
case 11:
|
||||
return prefix + "DIFFUSE_POTION" + suffix;
|
||||
case 13:
|
||||
return prefix + "ARTLESS_POTION" + suffix;
|
||||
case 15:
|
||||
return prefix + "THIN_POTION" + suffix;
|
||||
case 16:
|
||||
return prefix + "AWKWARD_POTION" + suffix;
|
||||
case 32:
|
||||
return prefix + "THICK_POTION" + suffix;
|
||||
}
|
||||
} else {
|
||||
String effects = "";
|
||||
for (PotionEffect effect : pot.getEffects()) {
|
||||
effects += effect.toString().split(":")[0];
|
||||
}
|
||||
return prefix + effects + suffix;
|
||||
}
|
||||
return mat.toString();
|
||||
case 6:
|
||||
switch ((int) damage) {
|
||||
case 0:
|
||||
return "OAK_SAPLING";
|
||||
case 1:
|
||||
return "PINE_SAPLING";
|
||||
case 2:
|
||||
return "BIRCH_SAPLING";
|
||||
case 3:
|
||||
return "JUNGLE_TREE_SAPLING";
|
||||
}
|
||||
return mat.toString();
|
||||
case 5:
|
||||
switch ((int) damage) {
|
||||
case 0:
|
||||
return "OAK_PLANKS";
|
||||
case 1:
|
||||
return "PINE_PLANKS";
|
||||
case 2:
|
||||
return "BIRCH_PLANKS";
|
||||
case 3:
|
||||
return "JUNGLE_PLANKS";
|
||||
}
|
||||
return mat.toString();
|
||||
case 17:
|
||||
switch (damage) {
|
||||
case 0:
|
||||
return "OAK_LOG";
|
||||
case 1:
|
||||
return "PINE_LOG";
|
||||
case 2:
|
||||
return "BIRCH_LOG";
|
||||
case 3:
|
||||
return "JUNGLE_LOG";
|
||||
}
|
||||
return mat.toString();
|
||||
case 18:
|
||||
damage = (short) (damage % 4);
|
||||
switch (damage) {
|
||||
case 0:
|
||||
return "OAK_LEAVES";
|
||||
case 1:
|
||||
return "PINE_LEAVES";
|
||||
case 2:
|
||||
return "BIRCH_LEAVES";
|
||||
case 3:
|
||||
return "JUNGLE_LEAVES";
|
||||
}
|
||||
case 263:
|
||||
switch (damage) {
|
||||
case 0:
|
||||
return "COAL";
|
||||
case 1:
|
||||
return "CHARCOAL";
|
||||
}
|
||||
return mat.toString();
|
||||
case 24:
|
||||
switch ((int) damage) {
|
||||
case 0:
|
||||
return "SANDSTONE";
|
||||
case 1:
|
||||
return "CHISELED_SANDSTONE";
|
||||
case 2:
|
||||
return "SMOOTH_SANDSTONE";
|
||||
}
|
||||
return mat.toString();
|
||||
case 31:
|
||||
switch ((int) damage) {
|
||||
case 0:
|
||||
return "DEAD_SHRUB";
|
||||
case 1:
|
||||
return "TALL_GRASS";
|
||||
case 2:
|
||||
return "FERN";
|
||||
}
|
||||
return mat.toString();
|
||||
case 44:
|
||||
switch ((int) damage) {
|
||||
case 0:
|
||||
return "STONE_SLAB";
|
||||
case 1:
|
||||
return "SANDSTONE_SLAB";
|
||||
case 2:
|
||||
return "WOODEN_SLAB";
|
||||
case 3:
|
||||
return "COBBLESTONE_SLAB";
|
||||
case 4:
|
||||
return "BRICK_SLAB";
|
||||
case 5:
|
||||
return "STONE_BRICK_SLAB";
|
||||
}
|
||||
return mat.toString();
|
||||
case 383:
|
||||
switch ((int) damage) {
|
||||
case 50:
|
||||
return "CREEPER_EGG";
|
||||
case 51:
|
||||
return "SKELETON_EGG";
|
||||
case 52:
|
||||
return "SPIDER_EGG";
|
||||
case 53:
|
||||
return "GIANT_EGG";
|
||||
case 54:
|
||||
return "ZOMBIE_EGG";
|
||||
case 55:
|
||||
return "SLIME_EGG";
|
||||
case 56:
|
||||
return "GHAST_EGG";
|
||||
case 57:
|
||||
return "ZOMBIE_PIGMAN_EGG";
|
||||
case 58:
|
||||
return "ENDERMAN_EGG";
|
||||
case 59:
|
||||
return "CAVE_SPIDER_EGG";
|
||||
case 60:
|
||||
return "SILVERFISH_EGG";
|
||||
case 61:
|
||||
return "BLAZE_EGG";
|
||||
case 62:
|
||||
return "MAGMA_CUBE_EGG";
|
||||
case 63:
|
||||
return "ENDER_DRAGON_EGG";
|
||||
case 90:
|
||||
return "PIG_EGG";
|
||||
case 91:
|
||||
return "SHEEP_EGG";
|
||||
case 92:
|
||||
return "COW_EGG";
|
||||
case 93:
|
||||
return "CHICKEN_EGG";
|
||||
case 94:
|
||||
return "SQUID_EGG";
|
||||
case 95:
|
||||
return "WOLF_EGG";
|
||||
case 96:
|
||||
return "MOOSHROOM_EGG";
|
||||
case 97:
|
||||
return "SNOW_GOLEM_EGG";
|
||||
case 98:
|
||||
return "OCELOT_EGG";
|
||||
case 99:
|
||||
return "IRON_GOLEM_EGG";
|
||||
case 120:
|
||||
return "VILLAGER_EGG";
|
||||
case 200:
|
||||
return "ENDER_CRYSTAL_EGG";
|
||||
case 14:
|
||||
return "PRIMED_TNT_EGG";
|
||||
case 66:
|
||||
return "WITCH_EGG";
|
||||
case 65:
|
||||
return "BAT_EGG";
|
||||
}
|
||||
return mat.toString();
|
||||
case 397:
|
||||
switch ((int) damage) {
|
||||
case 0:
|
||||
return "SKELETON_SKULL";
|
||||
case 1:
|
||||
return "WITHER_SKULL";
|
||||
case 2:
|
||||
return "ZOMBIE_HEAD";
|
||||
case 3:
|
||||
return "PLAYER_HEAD";
|
||||
case 4:
|
||||
return "CREEPER_HEAD";
|
||||
}
|
||||
break;
|
||||
case 76:
|
||||
return "REDSTONE_TORCH";
|
||||
case 115:
|
||||
return "NETHER_WART";
|
||||
case 30:
|
||||
return "COBWEB";
|
||||
case 102:
|
||||
return "GLASS_PANE";
|
||||
case 101:
|
||||
return "IRON_BARS";
|
||||
case 58:
|
||||
return "CRAFTING_TABLE";
|
||||
case 123:
|
||||
return "REDSTONE_LAMP";
|
||||
case 392:
|
||||
return "POTATO";
|
||||
case 289:
|
||||
return "GUNPOWDER";
|
||||
case 391:
|
||||
return "CARROT";
|
||||
case 322:
|
||||
switch ((int) damage) {
|
||||
case 0:
|
||||
return "GOLDEN_APPLE";
|
||||
case 1:
|
||||
return "ENCHANTED_GOLDEN_APPLE";
|
||||
}
|
||||
break;
|
||||
case 390:
|
||||
return "FLOWER_POT";
|
||||
case 145:
|
||||
switch ((int) damage) {
|
||||
case 0:
|
||||
return "ANVIL";
|
||||
case 1:
|
||||
return "SLIGHTLY_DAMAGED_ANVIL";
|
||||
case 2:
|
||||
return "VERY_DAMAGED:ANVIL";
|
||||
}
|
||||
break;
|
||||
case 384:
|
||||
return "BOTTLE_O'_ENCHANTING";
|
||||
case 402:
|
||||
return "FIREWORK_STAR";
|
||||
case 385:
|
||||
return "FIREWORK_CHARGE";
|
||||
}
|
||||
if (damage == 0 || isTool(mat))
|
||||
return mat.toString();
|
||||
return mat.toString() + ":" + damage;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mat
|
||||
* The material to check
|
||||
* @return Returns true if the item is a tool (Has durability) or false if
|
||||
* it doesn't.
|
||||
*/
|
||||
public static boolean isTool(Material mat) {
|
||||
return tools.contains(mat);
|
||||
}
|
||||
|
||||
/**
|
||||
* Compares two items to each other. Returns true if they match.
|
||||
*
|
||||
* @param stack1
|
||||
* The first item stack
|
||||
* @param stack2
|
||||
* The second item stack
|
||||
* @return true if the itemstacks match. (Material, durability, enchants)
|
||||
*/
|
||||
public static boolean matches(ItemStack stack1, ItemStack stack2) {
|
||||
if (stack1 == stack2)
|
||||
return true; // Referring to the same thing, or both are null.
|
||||
if (stack1 == null || stack2 == null)
|
||||
return false; // One of them is null (Can't be both, see above)
|
||||
if (stack1.getType() != stack2.getType())
|
||||
return false; // Not the same material
|
||||
if (stack1.getDurability() != stack2.getDurability())
|
||||
return false; // Not the same durability
|
||||
if (!stack1.getEnchantments().equals(stack2.getEnchantments()))
|
||||
return false; // They have the same enchants
|
||||
try {
|
||||
Class.forName("org.bukkit.inventory.meta.EnchantmentStorageMeta");
|
||||
boolean book1 = stack1.getItemMeta() instanceof EnchantmentStorageMeta;
|
||||
boolean book2 = stack2.getItemMeta() instanceof EnchantmentStorageMeta;
|
||||
if (book1 != book2)
|
||||
return false;// One has enchantment meta, the other does not.
|
||||
if (book1 == true) { // They are the same here (both true or both
|
||||
// false). So if one is true, the other is
|
||||
// true.
|
||||
Map<Enchantment, Integer> ench1 = ((EnchantmentStorageMeta) stack1.getItemMeta()).getStoredEnchants();
|
||||
Map<Enchantment, Integer> ench2 = ((EnchantmentStorageMeta) stack2.getItemMeta()).getStoredEnchants();
|
||||
if (!ench1.equals(ench2))
|
||||
return false; // Enchants aren't the same.
|
||||
}
|
||||
} catch (ClassNotFoundException e) {
|
||||
// Nothing. They dont have a build high enough to support this.
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Formats the given number according to how vault would like it. E.g. $50
|
||||
* or 5 dollars.
|
||||
*
|
||||
* @return The formatted string.
|
||||
*/
|
||||
public static String format(double n) {
|
||||
try {
|
||||
return plugin.getEcon().format(n);
|
||||
} catch (NumberFormatException e) {
|
||||
return "$" + n;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param m
|
||||
* The material to check if it is blacklisted
|
||||
* @return true if the material is black listed. False if not.
|
||||
*/
|
||||
public static boolean isBlacklisted(Material m) {
|
||||
return blacklist.contains(m);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches the block which the given sign is attached to
|
||||
*
|
||||
* @param sign
|
||||
* The sign which is attached
|
||||
* @return The block the sign is attached to
|
||||
*/
|
||||
public static Block getAttached(Block b) {
|
||||
try {
|
||||
Sign sign = (Sign) b.getState().getData(); // Throws a NPE
|
||||
// sometimes??
|
||||
BlockFace attached = sign.getAttachedFace();
|
||||
if (attached == null)
|
||||
return null;
|
||||
return b.getRelative(attached);
|
||||
} catch (NullPointerException e) {
|
||||
return null; // /Not sure what causes this.
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Counts the number of items in the given inventory where
|
||||
* Util.matches(inventory item, item) is true.
|
||||
*
|
||||
* @param inv
|
||||
* The inventory to search
|
||||
* @param item
|
||||
* The ItemStack to search for
|
||||
* @return The number of items that match in this inventory.
|
||||
*/
|
||||
public static int countItems(Inventory inv, ItemStack item) {
|
||||
int items = 0;
|
||||
for (ItemStack iStack : inv.getContents()) {
|
||||
if (iStack == null)
|
||||
continue;
|
||||
if (Util.matches(item, iStack)) {
|
||||
items += iStack.getAmount();
|
||||
}
|
||||
}
|
||||
return items;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of items that can be given to the inventory safely.
|
||||
*
|
||||
* @param inv
|
||||
* The inventory to count
|
||||
* @param item
|
||||
* The item prototype. Material, durabiltiy and enchants must
|
||||
* match for 'stackability' to occur.
|
||||
* @return The number of items that can be given to the inventory safely.
|
||||
*/
|
||||
public static int countSpace(Inventory inv, ItemStack item) {
|
||||
int space = 0;
|
||||
for (ItemStack iStack : inv.getContents()) {
|
||||
if (iStack == null || iStack.getType() == Material.AIR) {
|
||||
space += item.getMaxStackSize();
|
||||
} else if (matches(item, iStack)) {
|
||||
space += item.getMaxStackSize() - iStack.getAmount();
|
||||
}
|
||||
}
|
||||
return space;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the given location is loaded or not.
|
||||
*
|
||||
* @param loc
|
||||
* The location
|
||||
* @return true if the given location is loaded or not.
|
||||
*/
|
||||
public static boolean isLoaded(Location loc) {
|
||||
// System.out.println("Checking isLoaded(Location loc)");
|
||||
if (loc.getWorld() == null) {
|
||||
// System.out.println("Is not loaded. (No world)");
|
||||
return false;
|
||||
}
|
||||
// Calculate the chunks coordinates. These are 1,2,3 for each chunk, NOT
|
||||
// location rounded to the nearest 16.
|
||||
int x = (int) Math.floor((loc.getBlockX()) / 16.0);
|
||||
int z = (int) Math.floor((loc.getBlockZ()) / 16.0);
|
||||
if (loc.getWorld().isChunkLoaded(x, z)) {
|
||||
// System.out.println("Chunk is loaded " + x + ", " + z);
|
||||
return true;
|
||||
} else {
|
||||
// System.out.println("Chunk is NOT loaded " + x + ", " + z);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,52 @@
|
||||
package org.maxgamer.quickshop.Watcher;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.World;
|
||||
import org.maxgamer.quickshop.QuickShop;
|
||||
import org.maxgamer.quickshop.Shop.Shop;
|
||||
import org.maxgamer.quickshop.Shop.ShopChunk;
|
||||
|
||||
/**
|
||||
* @author Netherfoam Maintains the display items, restoring them when needed.
|
||||
* Also deletes invalid items.
|
||||
*/
|
||||
public class ItemWatcher implements Runnable {
|
||||
private QuickShop plugin;
|
||||
|
||||
public ItemWatcher(QuickShop plugin) {
|
||||
this.plugin = plugin;
|
||||
}
|
||||
|
||||
public void run() {
|
||||
List<Shop> toRemove = new ArrayList<Shop>(1);
|
||||
for (Entry<String, HashMap<ShopChunk, HashMap<Location, Shop>>> inWorld : plugin.getShopManager().getShops().entrySet()) {
|
||||
// This world
|
||||
World world = Bukkit.getWorld(inWorld.getKey());
|
||||
if (world == null)
|
||||
continue; // world not loaded.
|
||||
for (Entry<ShopChunk, HashMap<Location, Shop>> inChunk : inWorld.getValue().entrySet()) {
|
||||
if (!world.isChunkLoaded(inChunk.getKey().getX(), inChunk.getKey().getZ())) {
|
||||
// If the chunk is not loaded, next chunk!
|
||||
continue;
|
||||
}
|
||||
for (Shop shop : inChunk.getValue().values()) {
|
||||
// Validate the shop.
|
||||
if (!shop.isValid()) {
|
||||
toRemove.add(shop);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// Now we can remove it.
|
||||
for (Shop shop : toRemove) {
|
||||
shop.delete();
|
||||
}
|
||||
}
|
||||
}
|
53
src/main/java/org/maxgamer/quickshop/Watcher/LogWatcher.java
Normal file
53
src/main/java/org/maxgamer/quickshop/Watcher/LogWatcher.java
Normal file
@ -0,0 +1,53 @@
|
||||
package org.maxgamer.quickshop.Watcher;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.PrintStream;
|
||||
import java.util.ArrayList;
|
||||
|
||||
import org.bukkit.scheduler.BukkitTask;
|
||||
import org.maxgamer.quickshop.QuickShop;
|
||||
|
||||
public class LogWatcher implements Runnable {
|
||||
private PrintStream ps;
|
||||
private ArrayList<String> logs = new ArrayList<String>(5);
|
||||
public BukkitTask task;
|
||||
|
||||
public LogWatcher(QuickShop plugin, File log) {
|
||||
try {
|
||||
if (!log.exists()) {
|
||||
log.createNewFile();
|
||||
}
|
||||
FileOutputStream fos = new FileOutputStream(log, true);
|
||||
this.ps = new PrintStream(fos);
|
||||
} catch (FileNotFoundException e) {
|
||||
e.printStackTrace();
|
||||
plugin.getLogger().severe("Log file not found!");
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
plugin.getLogger().severe("Could not create log file!");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
synchronized (logs) {
|
||||
for (String s : logs) {
|
||||
ps.println(s);
|
||||
}
|
||||
logs.clear();
|
||||
}
|
||||
}
|
||||
|
||||
public void add(String s) {
|
||||
synchronized (logs) {
|
||||
logs.add(s);
|
||||
}
|
||||
}
|
||||
|
||||
public void close() {
|
||||
this.ps.close();
|
||||
}
|
||||
}
|
78
src/main/resources/config.yml
Normal file
78
src/main/resources/config.yml
Normal file
@ -0,0 +1,78 @@
|
||||
#Tax amount (decimal) - Eg, P1 buys $50 worth of stuff from P2. Therefore, P1 loses $50, P2 gains $(1-0.05)*50, and tax-account gains $(0.05)*50.
|
||||
tax: 0.05
|
||||
#The fake player who money from taxing people goes to
|
||||
tax-account: tax
|
||||
#Whether or not to show taxes paid when selling to a shop
|
||||
show-tax: false
|
||||
|
||||
#Should we log transactions/creations to Bukkit\Plugins\QuickShop\qs.log?
|
||||
log-actions: true
|
||||
|
||||
#For item-item based economies that don't use virtual coins.
|
||||
whole-number-prices-only: false
|
||||
|
||||
database:
|
||||
mysql: false
|
||||
host: localhost
|
||||
port: 3306
|
||||
database: quickshop
|
||||
user: root
|
||||
password: passwd
|
||||
|
||||
#Limits the number of shops a person can create and own at a single time.
|
||||
limits:
|
||||
#Disable these if you're not using them! If this is false, the rest of this section is ignored
|
||||
use: false
|
||||
#The default number of shops players can make
|
||||
default: 10
|
||||
#Players with these permissions can create these amounts of shops.
|
||||
ranks:
|
||||
#Anyone with 'quickshop.vip' permissions, can create 20 shops instead of 10.
|
||||
quickshop.vip: 20 #Players with quickshop.vip can make 20 shops.
|
||||
|
||||
#A list of block (materials) which can be used to create shops.
|
||||
#By default, chests are added to this list.
|
||||
#This will only work for blocks which implement InventoryHolder
|
||||
#in other words, no enderchest shops, no shops on dirt blocks, etc.
|
||||
#May cause unexpected behaviour with some blocks... Eg don't make a
|
||||
#shop on a hopper (It sucks the display item in) and furnace shops
|
||||
#allow players to put whatever item they want in all 3 slots and
|
||||
#dispenser shops aren't protected from redstone... Etc.
|
||||
shop-blocks:
|
||||
- CHEST
|
||||
- TRAPPED_CHEST
|
||||
|
||||
shop:
|
||||
#Cost to make a stall
|
||||
cost: 10
|
||||
#Should we refund when their shops are deleted/removed/broken?
|
||||
refund: false
|
||||
|
||||
#Is there a fee for changing prices on a shop (Help deter endless undercutting)
|
||||
price-change-requires-fee: true
|
||||
#If price changes require a fee, how much is the fee
|
||||
fee-for-price-change: 50
|
||||
|
||||
#Should we try and lock their shops from other players, so people can't steal from them?
|
||||
lock: true
|
||||
#Should we require players be sneaking to create and use shops?
|
||||
sneak-to-create: false
|
||||
sneak-to-trade: false
|
||||
|
||||
#Should we automatically create the sign for the chest?
|
||||
auto-sign: true
|
||||
#If a player owns an unlimited shop, should they receive the cash from it or not?
|
||||
#If you buy from YOUR unlimited shop, you will NEVER be charged $$, regardless of this setting
|
||||
pay-unlimited-shop-owners: false
|
||||
#Should we place display items on the chests?
|
||||
display-items: true
|
||||
#Should we place item frames on chests?
|
||||
frame-items: true
|
||||
#When someone uses /qs find <item>, how far should we search in blocks?
|
||||
#This command lets users shop quickly without wasting time searching
|
||||
#Settings > 100 WILL cause lag. Don't do it, or don't cry when your server lags.
|
||||
find-distance: 45
|
||||
|
||||
#List of items to disallow selling of. Anyone with quickshop.bypass.<itemID> can bypass it
|
||||
blacklist:
|
||||
- 7 #Bedrock
|
104
src/main/resources/messages.yml
Normal file
104
src/main/resources/messages.yml
Normal file
@ -0,0 +1,104 @@
|
||||
# Colors:
|
||||
# &0-9, &a-f
|
||||
# {0}, {1}, {2}, etc are variables. You can swap them around, but adding a new variable won't work. Removing them will work
|
||||
|
||||
not-looking-at-shop: "&cNo QuickShop found. You must be looking at one."
|
||||
no-permission: "&cYou do not have permission to do that."
|
||||
no-creative-break: "&cYou cannot break other players shops in creative mode. Use survival instead."
|
||||
no-double-chests: "&cYou don't have permission to create a double-chest shop."
|
||||
shop-already-owned: "&cThat is already a shop."
|
||||
chest-was-removed: "&cThat chest was removed."
|
||||
price-too-cheap: "&cPrice must be greater than &e$0.01"
|
||||
no-price-change: "&cThat wouldn't result in a price change!"
|
||||
you-cant-afford-a-new-shop: "&cIt costs {0} to create a new shop."
|
||||
player-bought-from-your-store-tax: "&c{0} purchased {1} {2} from your store, and you paid {3} in taxes."
|
||||
you-cant-afford-to-change-price: "&cIt costs {0} to change the price in your shop."
|
||||
success-created-shop: "&aCreated shop."
|
||||
success-removed-shop: "&aShop removed."
|
||||
shops-arent-locked: "&cRemember, shops are NOT protected from theft! If you want to stop thieves, lock it with LWC, Lockette, etc!"
|
||||
shop-creation-cancelled: "&cCancelled Shop Creation."
|
||||
shop-purchase-cancelled: "&cCancelled Shop Purchase."
|
||||
shop-stock-too-low: "&cThe shop only has {0} {1} left"
|
||||
you-cant-afford-to-buy: "&cThat costs {0}, but you only have {1}"
|
||||
negative-amount: "&cDerp, can't trade negative amounts"
|
||||
player-bought-from-your-store: "&c{0} purchased {1} {2} from your store."
|
||||
shop-out-of-stock: "&5Your shop at {0}, {1}, {2}, has run out of {3}"
|
||||
shop-has-no-space: "&cThe shop only has room for {0} more {1}."
|
||||
you-dont-have-that-many-items: "&cYou only have {0} {1}."
|
||||
the-owner-cant-afford-to-buy-from-you: "&cThat costs {0} but the owner only has {1}"
|
||||
player-sold-to-your-store: "&a{0} sold {1} {2} to your store."
|
||||
shop-out-of-space: "&5Your shop at {0}, {1}, {2}, is now full."
|
||||
fee-charged-for-price-change: "&aYou pay &c{0}&a to change the price."
|
||||
price-is-now: "&aThe shops new price is &e{0}"
|
||||
thats-not-a-number: "&cInvalid number"
|
||||
no-price-given: "&cPlease give a valid price."
|
||||
average-price-nearby: "&aAverage Price Nearby: &e{0}"
|
||||
shop-has-changed: "&cThe shop you tried to use has changed since you clicked it!"
|
||||
nearby-shop-this-way: "&aShop is {0} blocks away from you."
|
||||
no-nearby-shop: "&cNo shops matching {0} nearby."
|
||||
buying-more-than-selling: "&cWARNING: You are buying items for more than you are selling them!"
|
||||
not-enough-space: "&cYou only have room for {0} more of that!"
|
||||
refill-success: "&aRefill success"
|
||||
empty-success: "&aEmpty success"
|
||||
|
||||
menu:
|
||||
successful-purchase: "&aSuccessfully Purchased:"
|
||||
successfully-sold: "&aSuccessfully Sold:"
|
||||
item-name-and-price: "&e{0} {1} &afor &e{2}"
|
||||
sell-tax: "&aYou paid &e{0} &ain taxes."
|
||||
sell-tax-self: "&aYou own this shop so you don't pay taxes."
|
||||
enchants: "&5Enchants"
|
||||
stored-enchants: "&5Stored Enchants"
|
||||
|
||||
shop-information: "&aShop Information:"
|
||||
owner: "&aOwner: {0}"
|
||||
item: "&aItem: &e{0}"
|
||||
space: "&aSpace: &e{0}"
|
||||
stock: "&aStock &e{0}"
|
||||
price-per: "&aPrice per &e{0} &a- &e{1}"
|
||||
total-value-of-chest: "&aTotal value of Chest: &e{0}"
|
||||
damage-percent-remaining: "&e{0}% &aRemaining."
|
||||
this-shop-is-buying: "&aThis shop is &dBUYING&a items."
|
||||
this-shop-is-selling: "&aThis shop is &bSELLING&a items."
|
||||
|
||||
|
||||
bypassing-lock: "&cBypassing a QuickShop lock!"
|
||||
that-is-locked: "&cThat shop is locked."
|
||||
|
||||
how-many-buy: "&aEnter how many you wish to &bBUY&a in chat."
|
||||
how-many-sell: "&aEnter how many you wish to &dSELL&a in chat. You have &e{0}&a available"
|
||||
|
||||
not-allowed-to-create: "&cYou may not create a shop here."
|
||||
blacklisted-item: "&cThat item is blacklisted. You may not sell it"
|
||||
how-much-to-trade-for: "&aEnter how much you wish to trade one &e{0}&a for in chat."
|
||||
|
||||
command:
|
||||
#success-created-unlimited: "&aUnlimited QuickShop created."
|
||||
toggle-unlimited: "&aShop is now {0}"
|
||||
no-owner-given: "&cNo owner given. Use &a/qs setowner <player>&c"
|
||||
new-owner: "&aNew owner: &e{0}"
|
||||
now-buying: "&aNow &dBUYING&a &e{0}"
|
||||
now-selling: "&aNow &bSELLING &e{0}"
|
||||
cleaning: "&aCleaning up shops with 0 stock..."
|
||||
reloading: "&aReloading..."
|
||||
cleaned: "&aCleaned &e{0}&a shops"
|
||||
no-type-given: "&cUsage: /qs find <item>"
|
||||
description:
|
||||
title: "&aQuickShop Help"
|
||||
unlimited: "&eMakes a shop unlimited"
|
||||
setowner: "&eChanges who owns a shop"
|
||||
buy: "&eChanges a shop to &dBUY&e mode"
|
||||
sell: "&eChanges a shop to &bSELL&e mode"
|
||||
clean: "&eRemoves all (loaded) shops with 0 stock"
|
||||
price: "&eChanges the buy/selling price of one of your shops"
|
||||
find: "&eLocates the nearest shop of a specific type."
|
||||
reload: "&eReloads QuickShop from config.yml"
|
||||
refill: "&eAdds a given number of items to a shop"
|
||||
empty: "&eRemoves all stock from a shop"
|
||||
|
||||
signs:
|
||||
#Line 1 is used as an identifier at the moment, so I kind of need it to work, thats why I won't let you change it
|
||||
#Line 3 is the item name... There really isnt anything to change.
|
||||
selling: "Selling {0}"
|
||||
buying: "Buying {0}"
|
||||
price: "For {0} each"
|
54
src/main/resources/plugin.yml
Normal file
54
src/main/resources/plugin.yml
Normal file
@ -0,0 +1,54 @@
|
||||
name: ${name}
|
||||
main: ${package}.${name}
|
||||
version: ${version}
|
||||
author: Netherfoam, Timtower, KaiNoMood
|
||||
|
||||
description: Economy Shops plugin
|
||||
softdepend: [Herochat, Vault]
|
||||
|
||||
commands:
|
||||
qs:
|
||||
description: QuickShop command
|
||||
usage: /qs
|
||||
aliases: [shop]
|
||||
|
||||
permissions:
|
||||
quickshop.create.sell:
|
||||
description: Allows a player to sell from a shop
|
||||
default: op
|
||||
quickshop.create.buy:
|
||||
description: Allows a player to buy from a shop
|
||||
default: op
|
||||
quickshop.create.double:
|
||||
description: Allows a player to create a double shop
|
||||
default: op
|
||||
quickshop.use:
|
||||
description: Allows a player to buy/sell using other players shops
|
||||
default: true
|
||||
quickshop.unlimited:
|
||||
description: Allows a Staff Member to use /qs unlimited and make a shop infinite
|
||||
quickshop.bypass.<itemID>:
|
||||
description: Allows a player to sell <itemID>, even if its blacklisted
|
||||
quickshop.other.destroy:
|
||||
description: Allows a Staff Member to destroy other players shops if they are locked in the config
|
||||
quickshop.other.open:
|
||||
description: Allows a Staff Member to open someone elses shop if they are locked in the config
|
||||
quickshop.other.price:
|
||||
description: Allows a Staff Member to change the price of someone elses shop
|
||||
quickshop.setowner:
|
||||
description: Allows a Staff Member to change the owner of any shop
|
||||
quickshop.find:
|
||||
description: Allows a player to locate the nearest shop of a specific item type. Works in a 3 chunk radius.
|
||||
default: true
|
||||
quickshop.refill:
|
||||
description: Allows a Staff Member to refill the shop theyre looking at with the given number of items.
|
||||
default: op
|
||||
quickshop.empty:
|
||||
description: Allows a Staff Member to empty the shop theyre looking at of all items.
|
||||
default: op
|
||||
quickshop.debug:
|
||||
description: Enables debug info to console
|
||||
default: op
|
||||
quickshop.export:
|
||||
description: Allows exporting database to mysql or sqlite
|
||||
default: op
|
Loading…
Reference in New Issue
Block a user