PageRenderTime 57ms CodeModel.GetById 28ms RepoModel.GetById 1ms app.codeStats 0ms

/src/com/aehdev/commandshops/CommandShops.java

https://github.com/aeheathc/CommandShops
Java | 279 lines | 173 code | 42 blank | 64 comment | 27 complexity | 9b72165c9c5e5658b358b1ecf4531667 MD5 | raw file
  1. package com.aehdev.commandshops;
  2. import java.sql.ResultSet;
  3. import java.util.HashMap;
  4. import java.util.Locale;
  5. import java.util.logging.Logger;
  6. import org.bukkit.ChatColor;
  7. import org.bukkit.plugin.Plugin;
  8. import org.bukkit.plugin.PluginDescriptionFile;
  9. import org.bukkit.plugin.RegisteredServiceProvider;
  10. import org.bukkit.plugin.java.JavaPlugin;
  11. import cuboidLocale.QuadTree;
  12. import net.milkbowl.vault.economy.Economy;
  13. import com.sk89q.worldguard.bukkit.WorldGuardPlugin;
  14. import com.sk89q.worldguard.protection.GlobalRegionManager;
  15. import com.aehdev.commandshops.commands.ShopCommandExecutor;
  16. import com.aehdev.commandshops.lib.multiDB.Database;
  17. import com.aehdev.commandshops.lib.multiDB.MySQL;
  18. import com.aehdev.commandshops.lib.multiDB.SQLite;
  19. import com.aehdev.commandshops.threads.NotificationThread;
  20. /**
  21. * The main Bukkit plugin class for CommandShops.
  22. */
  23. public class CommandShops extends JavaPlugin
  24. {
  25. /** Our class implementing Listener that handles player interaction */
  26. public ShopsPlayerListener playerListener = new ShopsPlayerListener();
  27. /** General plugin info that comes directly from the private
  28. * {@link JavaPlugin#description} in the parent. Effectively all we're
  29. * doing here is converting it to public. */
  30. public static PluginDescriptionFile pdfFile = null;
  31. /** Thread that notifies managers of transactions. */
  32. protected NotificationThread notificationThread = null;
  33. /** Abstracts supported economies. */
  34. public Economy econ = null;
  35. /** Abstracts supported databases. */
  36. public static Database db = null;
  37. /** Main logger with which we write to the server log. */
  38. private final Logger log = Logger.getLogger("Minecraft");
  39. /** Plugin-identifying string that prefixes every message we show to players */
  40. public static final String CHAT_PREFIX = ChatColor.DARK_AQUA + "["
  41. + ChatColor.WHITE + "Shop" + ChatColor.DARK_AQUA + "] ";
  42. /** All shop locations stored in a cuboid tree for fast chacking. */
  43. private static QuadTree cuboidTree = new QuadTree();
  44. /** Path to all our data files. */
  45. static String folderPath = "plugins/CommandShops/";
  46. /** Reference to WorldGuard region manager. */
  47. public static GlobalRegionManager worldguard = null;
  48. /**
  49. * Setup method for when this plugin is enabled by Bukkit
  50. */
  51. public void onEnable()
  52. {
  53. if(Search.materials == null || Search.materials.isEmpty())
  54. {
  55. //if reflection failed in getting material ids, bail. It will have already been logged in the code that catches the exception.
  56. getServer().getPluginManager().disablePlugin(this);
  57. return;
  58. }
  59. pdfFile = getDescription(); //cache plugin info
  60. Config.loadProperties(this);//Get the configuration via Bukkit's builtin method
  61. Search.reload(this); //Compile item data
  62. if(!setupEconomy())
  63. {
  64. log.severe(String.format((Locale)null,"[%s] - Shutting down: Vault economy hook not found!", pdfFile.getName()));
  65. getServer().getPluginManager().disablePlugin(this);
  66. return;
  67. }
  68. //Connect to database, do any necessary setup
  69. if(Config.STORAGE_SYSTEM.equalsIgnoreCase("sqlite"))
  70. {
  71. db = new SQLite(log, "CommandShops","commandshops",folderPath);
  72. if(!setupDB()) return;
  73. }else if(Config.STORAGE_SYSTEM.equalsIgnoreCase("mysql")){
  74. db = new MySQL(log, "CommandShops", Config.DB_HOST, ""+Config.DB_PORT,
  75. Config.DB_NAME, Config.DB_USER, Config.DB_PASS);
  76. if(!setupDB()) return;
  77. }else{
  78. log.severe(String.format((Locale)null,"[%s] Shutting down: Unrecognized storage system.", pdfFile.getName()));
  79. getServer().getPluginManager().disablePlugin(this);
  80. return;
  81. }
  82. long totalShops = 0;
  83. try{
  84. ResultSet shoptot = db.query("SELECT COUNT(*) FROM shops"); shoptot.next();
  85. totalShops = shoptot.getLong(1);
  86. shoptot.close();
  87. }catch(Exception e){
  88. log.severe(String.format((Locale)null,"[%s] - Shutting down: Can't select from DB.", pdfFile.getName()));
  89. getServer().getPluginManager().disablePlugin(this);
  90. return;
  91. }
  92. //optional -- try to hook to worldguard
  93. Plugin plugin = getServer().getPluginManager().getPlugin("WorldGuard");
  94. if(plugin == null || !(plugin instanceof WorldGuardPlugin))
  95. {
  96. plugin = null;
  97. if(Config.REQUIRE_OWNER)
  98. {
  99. log.warning(String.format((Locale)null,"[%s] %s", pdfFile.getName(), "No supported region plugin found, but config says to require owned regions! Existing shops will work but no shops can be created/moved like this."));
  100. }else{
  101. log.info(String.format((Locale)null,"[%s] %s", pdfFile.getName(), "No supported region plugin found, using free selection only."));
  102. }
  103. }else{
  104. worldguard = ((WorldGuardPlugin)plugin).getGlobalRegionManager();
  105. log.info(String.format((Locale)null,"[%s] %s", pdfFile.getName(), "WorldGuard support enabled."));
  106. Shop.refreshRegionLocations();
  107. Shop.refreshRegionMessages();
  108. }
  109. // Register our events
  110. getServer().getPluginManager().registerEvents(playerListener, this);
  111. // Register Commands
  112. getCommand("shop").setExecutor(new ShopCommandExecutor(this));
  113. // update the console that we've started
  114. log.info(String.format((Locale)null,"[%s] %s", pdfFile.getName(), "Loaded with " + totalShops + " shop(s)"));
  115. log.info(String.format((Locale)null,"[%s] %s", pdfFile.getName(), "Version " + pdfFile.getVersion() + " is enabled: "));
  116. // Start Notification thread
  117. if(Config.NOTIFY_INTERVAL > 0)
  118. {
  119. notificationThread = new NotificationThread(this);
  120. notificationThread.start();
  121. }
  122. }
  123. /**
  124. * Shut down the plugin.
  125. * Called by Bukkit when the server is shutting down, plugins are being reloaded,
  126. * or we voluntarily shutdown due to errors.
  127. */
  128. public void onDisable()
  129. {
  130. // Stop notification thread
  131. if((Config.NOTIFY_INTERVAL > 0) && notificationThread != null
  132. && notificationThread.isAlive())
  133. {
  134. try
  135. {
  136. notificationThread.setRun(false);
  137. notificationThread.join(2000);
  138. }catch(InterruptedException e){
  139. // hmm, thread didn't die
  140. log.warning(String.format((Locale)null,"[%s] %s", pdfFile.getName(),
  141. "NotificationThread did not exit"));
  142. }
  143. }
  144. //reset selections
  145. ShopsPlayerListener.selectingPlayers = new HashMap<String,Selection>();
  146. ShopsPlayerListener.playerRegions = new HashMap<String,RegionSelection>();
  147. //drop Worldguard hook
  148. CommandShops.worldguard = null;
  149. //drop Vault hook
  150. econ = null;
  151. //close database connection
  152. db.close(); db = null;
  153. // update the console that we've stopped
  154. log.info(String.format((Locale)null,"[%s] %s", pdfFile.getName(), "Version "
  155. + pdfFile.getVersion() + " is disabled!"));
  156. }
  157. /**
  158. * Sets the structure of shop coords.
  159. * @param cuboidTree
  160. * the new cuboid tree
  161. */
  162. public static void setCuboidTree(QuadTree cuboidTree)
  163. {
  164. CommandShops.cuboidTree = cuboidTree;
  165. }
  166. /**
  167. * Gets the structure of shop coords.
  168. * @return the cuboid tree
  169. */
  170. public static QuadTree getCuboidTree() {return cuboidTree;}
  171. /**
  172. * Attach to Vault's Economy support
  173. * @return true on success
  174. */
  175. private boolean setupEconomy()
  176. {
  177. if (getServer().getPluginManager().getPlugin("Vault") == null) return false;
  178. RegisteredServiceProvider<Economy> rsp = getServer().getServicesManager().getRegistration(Economy.class);
  179. if (rsp == null) return false;
  180. econ = rsp.getProvider();
  181. return econ != null;
  182. }
  183. /**
  184. * Ensure that the database contains the right schema.
  185. * @return true on success
  186. */
  187. private boolean setupDB()
  188. {
  189. String tables,addedcolumns,indexes;
  190. //Start with MySQL version
  191. tables = "CREATE TABLE IF NOT EXISTS `shops` (`id` INTEGER PRIMARY KEY AUTO_INCREMENT NOT NULL,`name` TEXT NOT NULL,`owner` TEXT NOT NULL,`creator` TEXT NOT NULL,`x` INTEGER NOT NULL,`y` INTEGER NOT NULL,`z` INTEGER NOT NULL,`x2` INTEGER NOT NULL,`y2` INTEGER NOT NULL,`z2` INTEGER NOT NULL,`world` TEXT NOT NULL,`minbalance` REAL DEFAULT '0' NOT NULL,`unlimitedMoney` INTEGER DEFAULT '0' NOT NULL,`unlimitedStock` INTEGER DEFAULT '0' NOT NULL,`notify` INTEGER DEFAULT '1' NOT NULL,`service_repair` INTEGER DEFAULT '1' NOT NULL,`service_disenchant` INTEGER DEFAULT '1' NOT NULL);CREATE TABLE IF NOT EXISTS `shop_items` (`id` INTEGER PRIMARY KEY AUTO_INCREMENT NOT NULL,`shop` INTEGER NOT NULL,`itemid` INTEGER NOT NULL,`itemdamage` INTEGER NOT NULL,`stock` REAL DEFAULT '0' NOT NULL,`maxstock` INTEGER DEFAULT '10' NOT NULL,`buy` REAL NULL,`sell` REAL NULL,FOREIGN KEY(shop) REFERENCES shops(id) ON DELETE CASCADE ON UPDATE CASCADE);CREATE TABLE IF NOT EXISTS `managers` (`shop` INTEGER NOT NULL, `manager` VARCHAR(255) NOT NULL, FOREIGN KEY(shop) REFERENCES shops(id) ON DELETE CASCADE ON UPDATE CASCADE);CREATE TABLE IF NOT EXISTS `log` (`id` INTEGER PRIMARY KEY AUTO_INCREMENT NOT NULL, `datetime` TEXT NOT NULL, `user` TEXT NOT NULL, `shop` INTEGER, `action` TEXT NOT NULL, `itemid` INTEGER, `itemdamage` INTEGER, `amount` INTEGER, `cost` REAL, `total` REAL, `comment` TEXT,FOREIGN KEY(shop) REFERENCES shops(id) ON DELETE CASCADE ON UPDATE CASCADE);";
  192. addedcolumns = "ALTER TABLE `shops` ADD COLUMN `region` VARCHAR(63) DEFAULT NULL;ALTER TABLE `shops` ADD COLUMN `lastNotify` DATETIME NULL;";
  193. indexes = "CREATE INDEX `IDX_LOG_DATETIME` ON `log`(`datetime`(22) ASC);CREATE INDEX `IDX_LOG_SHOP` ON `log`(`shop` ASC);CREATE INDEX `IDX_LOG_ACTION` ON `log`(`action`(22) ASC);CREATE INDEX `IDX_MANAGERS_MANAGER` ON `managers`(`manager`(22) ASC);CREATE INDEX `IDX_SHOP_ITEMS_SHOP` ON `shop_items`(`shop` ASC);CREATE INDEX `IDX_SHOP_ITEMS_ITEMID` ON `shop_items`(`itemid` ASC);CREATE INDEX `IDX_SHOP_ITEMS_ITEMDAMAGE` ON `shop_items`(`itemdamage` ASC);CREATE INDEX `IDX_SHOPS_X` ON `shops`(`x` ASC);CREATE INDEX `IDX_SHOPS_Y` ON `shops`(`y` ASC);CREATE INDEX `IDX_SHOPS_Z` ON `shops`(`z` ASC);CREATE INDEX `IDX_SHOPS_X2` ON `shops`(`x2` ASC);CREATE INDEX `IDX_SHOPS_Y2` ON `shops`(`y2` ASC);CREATE INDEX `IDX_SHOPS_Z2` ON `shops`(`z2` ASC);CREATE INDEX `IDX_SHOPS_OWNER` ON `shops`(`owner`(22) ASC);CREATE INDEX `IDX_SHOPS_WORLD` ON `shops`(`world`(22) ASC);CREATE UNIQUE INDEX IDX_MANAGERS_ ON managers(manager,shop);CREATE UNIQUE INDEX IDX_SHOP_ITEMS_ ON shop_items(shop,itemid,itemdamage);CREATE UNIQUE INDEX `IDX_SHOPS_REGION` ON `shops`(`region` ASC);";
  194. if(db instanceof com.aehdev.commandshops.lib.multiDB.SQLite)
  195. {
  196. //convert to SQLite compatible.
  197. tables = tables.replaceAll("AUTO_INCREMENT","AUTOINCREMENT");
  198. tables = tables.replaceAll("VARCHAR\\((\\d+)\\)","TEXT");
  199. indexes = indexes.replaceAll("`\\((\\d+)\\)","`");
  200. }
  201. //Run create table statements only if the tables do not exist. Any errors here will be very bad.
  202. try{
  203. for(String query : tables.split(";")) db.query(query);
  204. }catch(Exception e){
  205. log.severe(String.format((Locale)null,"[%s] [MultiDB] - %s", pdfFile.getName(),e));
  206. log.severe(String.format((Locale)null,"[%s] - Shutting down: Problem checking schema.", pdfFile.getName()));
  207. getServer().getPluginManager().disablePlugin(this);
  208. return false;
  209. }
  210. /* Additional column queries are expected to cause errors if the columns already exist because
  211. * querying for existence of individual columns is not worth the trouble
  212. */
  213. for(String query : addedcolumns.split(";")) try{db.query(query,true);}catch(Exception e){}
  214. /* Index creation queries are expected to cause errors if the indexes already exist because
  215. * MySQL does not support "CREATE INDEX IF NOT EXISTS"
  216. */
  217. for(String query : indexes.split(";")) try{db.query(query,true);}catch(Exception e){}
  218. //trim log
  219. try{
  220. ResultSet reslog = db.query("SELECT COUNT(*) FROM `log`");
  221. reslog.next();
  222. long count = reslog.getLong(1);
  223. reslog.close();
  224. if(count>Config.LOG_LIMIT)
  225. {
  226. ResultSet resPivot = db.query("SELECT `datetime` FROM `log` ORDER BY `datetime` DESC LIMIT " + Config.LOG_LIMIT + ",1");
  227. resPivot.next();
  228. String pivot = resPivot.getString("datetime");
  229. resPivot.close();
  230. db.query("DELETE FROM `log` WHERE `datetime`<='" + pivot + "'");
  231. }
  232. }catch(Exception e){
  233. log.warning(String.format((Locale)null,"[%s] - Couldn't trim log. Beware of ballooning log table. %s", pdfFile.getName(), e));
  234. }
  235. return true;
  236. }
  237. }