Browse Source

Version 0.9

Compatible with Bukkit 1.8.7
Improved items name display, especially for very long names.
pull/3/HEAD
KaiKikuchi 7 years ago
commit
3755036137
  1. 19
      README.md
  2. 56
      pom.xml
  3. 573
      src/main/java/org/maxgamer/quickshop/Command/QS.java
  4. 74
      src/main/java/org/maxgamer/quickshop/Database/BufferStatement.java
  5. 199
      src/main/java/org/maxgamer/quickshop/Database/Database.java
  6. 13
      src/main/java/org/maxgamer/quickshop/Database/DatabaseCore.java
  7. 62
      src/main/java/org/maxgamer/quickshop/Database/DatabaseHelper.java
  8. 85
      src/main/java/org/maxgamer/quickshop/Database/MySQLCore.java
  9. 110
      src/main/java/org/maxgamer/quickshop/Database/SQLiteCore.java
  10. 119
      src/main/java/org/maxgamer/quickshop/Economy/Economy.java
  11. 127
      src/main/java/org/maxgamer/quickshop/Economy/EconomyCore.java
  12. 108
      src/main/java/org/maxgamer/quickshop/Economy/Economy_Vault.java
  13. 124
      src/main/java/org/maxgamer/quickshop/Listeners/BlockListener.java
  14. 28
      src/main/java/org/maxgamer/quickshop/Listeners/ChatListener.java
  15. 45
      src/main/java/org/maxgamer/quickshop/Listeners/ChunkListener.java
  16. 30
      src/main/java/org/maxgamer/quickshop/Listeners/HeroChatListener.java
  17. 144
      src/main/java/org/maxgamer/quickshop/Listeners/LockListener.java
  18. 189
      src/main/java/org/maxgamer/quickshop/Listeners/PlayerListener.java
  19. 79
      src/main/java/org/maxgamer/quickshop/Listeners/WorldListener.java
  20. 400
      src/main/java/org/maxgamer/quickshop/QuickShop.java
  21. 1
      src/main/java/org/maxgamer/quickshop/Shop/.gitignore
  22. 659
      src/main/java/org/maxgamer/quickshop/Shop/ContainerShop.java
  23. 120
      src/main/java/org/maxgamer/quickshop/Shop/DisplayItem.java
  24. 98
      src/main/java/org/maxgamer/quickshop/Shop/Info.java
  25. 5
      src/main/java/org/maxgamer/quickshop/Shop/ShopAction.java
  26. 44
      src/main/java/org/maxgamer/quickshop/Shop/ShopChunk.java
  27. 55
      src/main/java/org/maxgamer/quickshop/Shop/ShopCreateEvent.java
  28. 627
      src/main/java/org/maxgamer/quickshop/Shop/ShopManager.java
  29. 61
      src/main/java/org/maxgamer/quickshop/Shop/ShopPreCreateEvent.java
  30. 76
      src/main/java/org/maxgamer/quickshop/Shop/ShopPurchaseEvent.java
  31. 26
      src/main/java/org/maxgamer/quickshop/Shop/ShopType.java
  32. 12
      src/main/java/org/maxgamer/quickshop/Util/Converter.java
  33. 283
      src/main/java/org/maxgamer/quickshop/Util/MsgUtil.java
  34. 260
      src/main/java/org/maxgamer/quickshop/Util/NMS.java
  35. 937
      src/main/java/org/maxgamer/quickshop/Util/Util.java
  36. 52
      src/main/java/org/maxgamer/quickshop/Watcher/ItemWatcher.java
  37. 53
      src/main/java/org/maxgamer/quickshop/Watcher/LogWatcher.java
  38. 78
      src/main/resources/config.yml
  39. 104
      src/main/resources/messages.yml
  40. 54
      src/main/resources/plugin.yml

19
README.md

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

@ -0,0 +1,56 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>net.kaikk.mc</groupId>
<artifactId>${name}</artifactId>
<version>${version}</version>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.7</source>
<target>1.7</target>
</configuration>
</plugin>
</plugins>
<resources>
<resource>
<directory>src/main/resources</directory>
<filtering>true</filtering>
</resource>
</resources>
</build>
<repositories>
<repository>
<id>bukkit-repo</id>
<!-- <url>http://repo.bukkit.org/content/groups/public/</url> -->
<url>https://hub.spigotmc.org/nexus/content/groups/public/</url>
</repository>
<repository>
<id>sk89q-repo</id>
<url>http://maven.sk89q.com/repo/</url>
</repository>
</repositories>
<dependencies>
<dependency>
<groupId>org.bukkit</groupId>
<artifactId>bukkit</artifactId>
<!-- <version>1.6.4-R2.0</version> -->
<!-- <version>1.7.9-R0.2</version> -->
<version>1.8-R0.1-SNAPSHOT</version>
<type>jar</type>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.sk89q</groupId>
<artifactId>worldguard</artifactId>
<version>6.0.0-SNAPSHOT</version>
</dependency>
</dependencies>
<properties>
<version>0.9</version>
<package>org.maxgamer.quickshop</package>
<name>QuickShop</name>
</properties>
</project>

573
src/main/java/org/maxgamer/quickshop/Command/QS.java

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

74
src/main/java/org/maxgamer/quickshop/Database/BufferStatement.java

@ -0,0 +1,74 @@
package org.maxgamer.quickshop.Database;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.Arrays;
public class BufferStatement {
private Object[] values;
private String query;
private Exception stacktrace;
/**
* Represents a PreparedStatement in a state before preparing it (E.g. No
* file I/O Required)
*
* @param query
* The query to execute. E.g. INSERT INTO accounts (user, passwd)
* VALUES (?, ?)
* @param values
* The values to replace <bold>?</bold> with in
* <bold>query</bold>. These are in order.
*/
public BufferStatement(String query, Object... values) {
this.query = query;
this.values = values;
this.stacktrace = new Exception(); // For error handling
this.stacktrace.fillInStackTrace(); // We can declare where this
// statement came from.
}
/**
* Returns a prepared statement using the given connection. Will try to
* return an empty statement if something went wrong. If that fails, returns
* null.
*
* This method escapes everything automatically.
*
* @param con
* The connection to prepare this on using
* con.prepareStatement(..)
* @return The prepared statement, ready for execution.
*/
public PreparedStatement prepareStatement(Connection con) throws SQLException {
PreparedStatement ps;
ps = con.prepareStatement(query);
for (int i = 1; i <= values.length; i++) {
ps.setObject(i, values[i - 1]);
}
return ps;
}
/**
* Used for debugging. This stacktrace is recorded when the statement is
* created, so printing it to the screen will provide useful debugging
* information about where the query came from, if something went wrong
* while executing it.
*
* @return The stacktrace elements.
*/
public StackTraceElement[] getStackTrace() {
return stacktrace.getStackTrace();
}
/**
* @return A string representation of this statement. Returns
* <italic>"Query: " + query + ", values: " +
* Arrays.toString(values).</italic>
*/
@Override
public String toString() {
return "Query: " + query + ", values: " + Arrays.toString(values);
}
}

199
src/main/java/org/maxgamer/quickshop/Database/Database.java

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

13
src/main/java/org/maxgamer/quickshop/Database/DatabaseCore.java

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

62
src/main/java/org/maxgamer/quickshop/Database/DatabaseHelper.java

@ -0,0 +1,62 @@
package org.maxgamer.quickshop.Database;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.sql.Statement;
public class DatabaseHelper {
public static void setup(Database db) throws SQLException {
if (!db.hasTable("shops")) {
createShopsTable(db);
}
if (!db.hasTable("messages")) {
createMessagesTable(db);
}
checkColumns(db);
}
/**
* Verifies that all required columns exist.
*/
public static void checkColumns(Database db) {
PreparedStatement ps = null;
try {
// V3.4.2
ps = db.getConnection().prepareStatement("ALTER TABLE shops MODIFY COLUMN price double(32,2) NOT NULL AFTER owner");
ps.execute();
ps.close();
} catch (SQLException e) {
}
try {
// V3.4.3
ps = db.getConnection().prepareStatement("ALTER TABLE messages MODIFY COLUMN time BIGINT(32) NOT NULL AFTER message");
ps.execute();
ps.close();
} catch (SQLException e) {
}
}
/**
* Creates the database table 'shops'.
*
* @throws SQLException
* If the connection is invalid.
*/
public static void createShopsTable(Database db) throws SQLException {
Statement st = db.getConnection().createStatement();
String createTable = "CREATE TABLE shops (" + "owner TEXT(32) NOT NULL, " + "price double(32, 2) NOT NULL, " + "itemConfig TEXT CHARSET utf8 NOT NULL, " + "x INTEGER(32) NOT NULL, " + "y INTEGER(32) NOT NULL, " + "z INTEGER(32) NOT NULL, " + "world VARCHAR(32) NOT NULL, " + "unlimited boolean, " + "type boolean, " + "PRIMARY KEY (x, y, z, world) " + ");";
st.execute(createTable);
}
/**
* Creates the database table 'messages'
*
* @throws SQLException
* If the connection is invalid
*/
public static void createMessagesTable(Database db) throws SQLException {
Statement st = db.getConnection().createStatement();
String createTable = "CREATE TABLE messages (" + "owner TEXT(32) NOT NULL, " + "message TEXT(200) NOT NULL, " + "time BIGINT(32) NOT NULL " + ");";
st.execute(createTable);
}
}

85
src/main/java/org/maxgamer/quickshop/Database/MySQLCore.java

@ -0,0 +1,85 @@
package org.maxgamer.quickshop.Database;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Properties;
public class MySQLCore implements DatabaseCore {
private String url;
/** The connection properties... user, pass, autoReconnect.. */
private Properties info;
private static final int MAX_CONNECTIONS = 8;
private static ArrayList<Connection> pool = new ArrayList<Connection>();
public MySQLCore(String host, String user, String pass, String database, String port) {
info = new Properties();
info.put("autoReconnect", "true");
info.put("user", user);
info.put("password", pass);
info.put("useUnicode", "true");
info.put("characterEncoding", "utf8");
this.url = "jdbc:mysql://" + host + ":" + port + "/" + database;
for (int i = 0; i < MAX_CONNECTIONS; i++)
pool.add(null);
}
/**
* Gets the database connection for executing queries on.
*
* @return The database connection
*/
public Connection getConnection() {
for (int i = 0; i < MAX_CONNECTIONS; i++) {
Connection connection = pool.get(i);
try {
// If we have a current connection, fetch it
if (connection != null && !connection.isClosed()) {
if (connection.isValid(10)) {
return connection;
}
// Else, it is invalid, so we return another connection.
}
connection = DriverManager.getConnection(this.url, info);
pool.set(i, connection);
return connection;
} catch (SQLException e) {
e.printStackTrace();
}
}
return null;
}
@Override
public void queue(BufferStatement bs) {
try {
Connection con = this.getConnection();
while (con == null) {
try {
Thread.sleep(15);
} catch (InterruptedException e) {
}
// Try again
this.getConnection();
}
PreparedStatement ps = bs.prepareStatement(con);
ps.execute();
ps.close();
} catch (SQLException e) {
e.printStackTrace();
return;
}
}
@Override
public void close() {
// Nothing, because queries are executed immediately for MySQL
}
@Override
public void flush() {
// Nothing, because queries are executed immediately for MySQL
}
}

110
src/main/java/org/maxgamer/quickshop/Database/SQLiteCore.java

@ -0,0 +1,110 @@
package org.maxgamer.quickshop.Database;
import java.io.File;
import java.io.IOException;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.LinkedList;
public class SQLiteCore implements DatabaseCore {
private Connection connection;
private File dbFile;
private volatile Thread watcher;
private volatile LinkedList<BufferStatement> queue = new LinkedList<BufferStatement>();
public SQLiteCore(File dbFile) {
this.dbFile = dbFile;
}
/**
* Gets the database connection for executing queries on.
*
* @return The database connection
*/
public Connection getConnection() {
try {
// If we have a current connection, fetch it
if (this.connection != null && !this.connection.isClosed()) {
return this.connection;
}
} catch (SQLException e) {
e.printStackTrace();
}
if (this.dbFile.exists()) {
// So we need a new connection
try {
Class.forName("org.sqlite.JDBC");
this.connection = DriverManager.getConnection("jdbc:sqlite:" + this.dbFile);
return this.connection;
} catch (ClassNotFoundException e) {
e.printStackTrace();
return null;
} catch (SQLException e) {
e.printStackTrace();
return null;
}
} else {
// So we need a new file too.
try {
// Create the file
this.dbFile.createNewFile();
// Now we won't need a new file, just a connection.
// This will return that new connection.
return this.getConnection();
} catch (IOException e) {
e.printStackTrace();
return null;
}
}
}
@Override
public void queue(BufferStatement bs) {
synchronized (queue) {
queue.add(bs);
}
if (watcher == null || !watcher.isAlive()) {
startWatcher();
}
}
@Override
public void flush() {
while (queue.isEmpty() == false) {
BufferStatement bs;
synchronized (queue) {
bs = queue.removeFirst();
}
synchronized (dbFile) {
try {
PreparedStatement ps = bs.prepareStatement(getConnection());
ps.execute();
ps.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
@Override
public void close() {
flush();
}
private void startWatcher() {
watcher = new Thread() {
@Override
public void run() {
try {
Thread.sleep(30000);
} catch (InterruptedException e) {
}
flush();
}
};
watcher.start();
}
}

119
src/main/java/org/maxgamer/quickshop/Economy/Economy.java

@ -0,0 +1,119 @@
package org.maxgamer.quickshop.Economy;
import java.util.UUID;
public class Economy implements EconomyCore {
private EconomyCore core;
public Economy(EconomyCore core) {
this.core = core;
}
/**
* Checks that this economy is valid. Returns false if it is not valid.
*
* @return True if this economy will work, false if it will not.
*/
public boolean isValid() {
return core.isValid();
}
/**
* Deposits a given amount of money from thin air to the given username.
*
* @param name
* The exact (case insensitive) username to give money to
* @param amount
* The amount to give them
* @return True if success (Should be almost always)
*/
@Deprecated
public boolean deposit(String name, double amount) {
return core.deposit(name, amount);
}
/**
* Withdraws a given amount of money from the given username and turns it to
* thin air.
*
* @param name
* The exact (case insensitive) username to take money from
* @param amount
* The amount to take from them
* @return True if success, false if they didn't have enough cash
*/
@Deprecated
public boolean withdraw(String name, double amount) {
return core.withdraw(name, amount);
}
/**
* Transfers the given amount of money from Player1 to Player2
*
* @param from
* The player who is paying money
* @param to
* The player who is receiving money
* @param amount
* The amount to transfer
* @return true if success (Payer had enough cash, receiver was able to
* receive the funds)
*/
@Deprecated
public boolean transfer(String from, String to, double amount) {
return core.transfer(from, to, amount);
}
/**
* Fetches the balance of the given account name
*
* @param name
* The name of the account
* @return Their current balance.
*/
@Deprecated
public double getBalance(String name) {
return core.getBalance(name);
}
/**
* Formats the given number... E.g. 50.5 becomes $50.5 Dollars, or 50
* Dollars 5 Cents
*
* @param balance
* The given number
* @return The balance in human readable text.
*/
public String format(double balance) {
return core.format(balance);
}
@Deprecated
public boolean has(String name, double amount) {
return core.getBalance(name) >= amount;
}
@Override
public String toString() {
return core.getClass().getName().split("_")[1];
}
@Override
public boolean deposit(UUID name, double amount) {
return core.deposit(name,amount);
}
@Override
public boolean withdraw(UUID name, double amount) {
return core.withdraw(name, amount);
}
@Override
public boolean transfer(UUID from, UUID to, double amount) {
return core.transfer(from, to, amount);
}
@Override
public double getBalance(UUID name) {
return core.getBalance(name);
}
}

127
src/main/java/org/maxgamer/quickshop/Economy/EconomyCore.java

@ -0,0 +1,127 @@
package org.maxgamer.quickshop.Economy;
import java.util.UUID;
/**
* @author netherfoam Represents an economy.
*/
public interface EconomyCore {
/**
* Checks that this economy is valid. Returns false if it is not valid.
*
* @return True if this economy will work, false if it will not.
*/
public boolean isValid();
/**
* Deposits a given amount of money from thin air to the given username.
*
* @param name
* The exact (case insensitive) username to give money to
* @param amount
* The amount to give them
* @return True if success (Should be almost always)
*/
@Deprecated
public boolean deposit(String name, double amount);
/**
* Withdraws a given amount of money from the given username and turns it to
* thin air.
*
* @param name
* The exact (case insensitive) username to take money from
* @param amount
* The amount to take from them
* @return True if success, false if they didn't have enough cash
*/
@Deprecated
public boolean withdraw(String name, double amount);
/**
* Transfers the given amount of money from Player1 to Player2
*
* @param from
* The player who is paying money
* @param to
* The player who is receiving money
* @param amount
* The amount to transfer
* @return true if success (Payer had enough cash, receiver was able to
* receive the funds)
*/
@Deprecated
public boolean transfer(String from, String to, double amount);
/**
* Fetches the balance of the given account name
*
* @param name
* The name of the account
* @return Their current balance.
*/
@Deprecated
public double getBalance(String name);
/**
* Formats the given number... E.g. 50.5 becomes $50.5 Dollars, or 50
* Dollars 5 Cents
*
* @param balance
* The given number
* @return The balance in human readable text.
*/
public String format(double balance);
/**
* Deposits a given amount of money from thin air to the given username.
*
* @param name
* The exact (case insensitive) username to give money to
* @param amount
* The amount to give them
* @return True if success (Should be almost always)
*/
public boolean deposit(UUID name, double amount);
/**
* Withdraws a given amount of money from the given username and turns it to
* thin air.
*
* @param name
* The exact (case insensitive) username to take money from
* @param amount
* The amount to take from them
* @return True if success, false if they didn't have enough cash
*/
public boolean withdraw(UUID name, double amount);
/**
* Transfers the given amount of money from Player1 to Player2
*
* @param from
* The player who is paying money
* @param to
* The player who is receiving money
* @param amount
* The amount to transfer
* @return true if success (Payer had enough cash, receiver was able to
* receive the funds)
*/
public boolean transfer(UUID from, UUID to, double amount);
/**
* Fetches the balance of the given account name
*
* @param name
* The name of the account
* @return Their current balance.
*/
public double getBalance(UUID name);
}

108
src/main/java/org/maxgamer/quickshop/Economy/Economy_Vault.java

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