Version 0.9

Compatible with Bukkit 1.8.7
Improved items name display, especially for very long names.
pull/3/HEAD
KaiKikuchi 2015-07-05 16:05:58 +02:00
commit 3755036137
40 changed files with 6189 additions and 0 deletions

19
README.md Normal file
View 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
View 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>

View 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");
}
}

View File

@ -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);
}
}

View 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();
}
}

View File

@ -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();
}

View File

@ -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);
}
}

View 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
}
}

View 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();
}
}

View 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);
}
}

View 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);
}

View 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);
}
}

View File

@ -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());
}
}

View File

@ -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);
}
}

View File

@ -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();
}
}
}

View File

@ -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);
}
}

View 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);
}
}
}
}

View File

@ -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.
}
}

View File

@ -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();
}
}
}
}

View 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;
}
}

View File

@ -0,0 +1 @@
/Shop.java

View 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();
}
}

View 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;
}
}

View 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;
}
}

View File

@ -0,0 +1,5 @@
package org.maxgamer.quickshop.Shop;
public enum ShopAction {
BUY(), CREATE(), CANCELLED();
}

View 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;
}
}

View File

@ -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;
}
}

View 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();
}
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View 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;
}
}

View 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;
}
}

View 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;
}
}

View 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);
}
}

View 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;
}
}
}

View File

@ -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();
}
}
}

View 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();
}
}

View 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

View 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"

View 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