PageRenderTime 75ms CodeModel.GetById 41ms RepoModel.GetById 0ms app.codeStats 0ms

/src/com/aehdev/commandshops/commands/Command.java

https://github.com/aeheathc/CommandShops
Java | 541 lines | 312 code | 59 blank | 170 comment | 72 complexity | d370573e1352ef04903dedcc7ab91c8b MD5 | raw file
  1. package com.aehdev.commandshops.commands;
  2. import java.sql.ResultSet;
  3. import java.util.Locale;
  4. import java.util.logging.Logger;
  5. import org.bukkit.Bukkit;
  6. import org.bukkit.ChatColor;
  7. import org.bukkit.Material;
  8. import org.bukkit.command.CommandSender;
  9. import org.bukkit.entity.Player;
  10. import org.bukkit.inventory.ItemStack;
  11. import org.bukkit.inventory.PlayerInventory;
  12. import com.aehdev.commandshops.CommandShops;
  13. import com.aehdev.commandshops.Config;
  14. import com.aehdev.commandshops.ItemInfo;
  15. import com.aehdev.commandshops.Search;
  16. import com.aehdev.commandshops.lib.multiDB.Database;
  17. import com.sk89q.worldguard.protection.regions.ProtectedRegion;
  18. import cuboidLocale.BookmarkedResult;
  19. import cuboidLocale.PrimitiveCuboid;
  20. /**
  21. * Represents a CommandShops command. That is, each object is a sub-command of
  22. * /shop. For example, "/shop buy"
  23. */
  24. public abstract class Command
  25. {
  26. /** Reference to the main plugin object. */
  27. protected CommandShops plugin = null;
  28. /** Reference to database handler */
  29. protected Database db = CommandShops.db;
  30. /** The command name that the user typed (might be an alias). */
  31. protected String commandLabel = null;
  32. /** The sender/origin of the command. We will almost always need this to be
  33. * a player, especially for commands that aren't read-only. */
  34. protected CommandSender sender = null;
  35. /** The actual backend command being executed. */
  36. protected String command = null;
  37. /** Matches valid numbers. */
  38. protected static String DECIMAL_REGEX = "(\\d+\\.\\d+)|(\\d+\\.)|(\\.\\d+)|(\\d+)";
  39. /** The logging object with which we write to the server log. */
  40. protected static final Logger log = Logger.getLogger("Minecraft");
  41. /**
  42. * Groups commands by the permission needed to execute them.
  43. */
  44. public static enum CommandTypes
  45. {
  46. /** Includes all other types as well as admin-only actions. */
  47. ADMIN(0, "commandshops.admin"),
  48. /** Adding items to shops. */
  49. ADD(1, "commandshops.manager.add"),
  50. /** Buying from shops */
  51. BUY(2, "commandshops.user.buy"),
  52. /** Creation of new shops. */
  53. CREATE(3, "commandshops.manager.create"),
  54. /** Bypass the fee for creating new shops. */
  55. CREATE_FREE(4, "commandshops.free.create"),
  56. /** Delete shops. */
  57. DESTROY(5, "commandshops.manager.destroy"),
  58. /** Browsing contents of shops. */
  59. BROWSE(6, "commandshops.user.browse"),
  60. /** Relocating existing shops. */
  61. MOVE(7, "commandshops.manager.move"),
  62. /** Bypass the fee for moving shops. */
  63. MOVE_FREE(8, "commandshops.free.move"),
  64. //9 was "remove"
  65. /** Selling to shops. */
  66. SELL(10, "commandshops.user.sell"),
  67. /** Changing the owner of shops currently under one's authority. */
  68. SET_OWNER(11,"commandshops.manager.set.owner"),
  69. /** Set shop parameters like prices. */
  70. SET(12, "commandshops.manager.set");
  71. /** Identifies this CommandType. */
  72. int id = -1;
  73. /** List of all permissions needed to use a certain type of command. */
  74. String permission = null;
  75. /**
  76. * Define a command type.
  77. * @param id
  78. * the id
  79. */
  80. CommandTypes(int id)
  81. {
  82. this.id = id;
  83. }
  84. /**
  85. * Define a command type.
  86. * @param id
  87. * the id
  88. * @param permission
  89. * Permission needed for this command type.
  90. */
  91. CommandTypes(int id, String permission)
  92. {
  93. this(id);
  94. this.permission = permission;
  95. }
  96. }
  97. /**
  98. * Define a new command.
  99. * @param plugin
  100. * Reference back to main plugin object.
  101. * @param commandLabel
  102. * Alias typed by user for this command.
  103. * @param sender
  104. * Who sent the command. Should be a player, but might be console.
  105. * @param command
  106. * Name of command being run.
  107. */
  108. public Command(CommandShops plugin, String commandLabel,
  109. CommandSender sender, String command)
  110. {
  111. this.plugin = plugin;
  112. this.commandLabel = commandLabel;
  113. this.sender = sender;
  114. this.command = command.trim();
  115. }
  116. /**
  117. * Define a new command.
  118. * @param plugin
  119. * Reference back to main plugin object.
  120. * @param commandLabel
  121. * Alias typed by user for this command.
  122. * @param sender
  123. * Who sent the command. Should be a player, but might be console.
  124. * @param args
  125. * Arguments passed to the command.
  126. */
  127. public Command(CommandShops plugin, String commandLabel,
  128. CommandSender sender, String[] args)
  129. {
  130. this(plugin, commandLabel, sender, Search.join(args, " ").trim());
  131. }
  132. /**
  133. * Gets the name of the command represented by this object.
  134. * @return the command
  135. */
  136. public String getCommand()
  137. {
  138. return command;
  139. }
  140. /**
  141. * Run the command.
  142. * @return true, if successful
  143. */
  144. public abstract boolean process();
  145. /**
  146. * Determine if the sender of this command has permissions for commands of a given type.
  147. * @param type
  148. * Command type being checked for.
  149. * @return true if the sender can access the given command type
  150. */
  151. protected boolean canUseCommand(CommandTypes type)
  152. {
  153. if(sender instanceof Player)
  154. {
  155. Player player = (Player)sender;
  156. if(player.hasPermission(CommandTypes.ADMIN.permission) || player.hasPermission(type.permission))
  157. return true;
  158. return false;
  159. }else{
  160. return true;
  161. }
  162. }
  163. /**
  164. * Check if the given player has permission to create shops.
  165. * @param playerName
  166. * Name of the player in question.
  167. * @return true if the given player has permission to create shops.
  168. */
  169. protected boolean canCreateShop(String playerName)
  170. {
  171. if(canUseCommand(CommandTypes.ADMIN))
  172. {
  173. return true;
  174. }else if(canUseCommand(CommandTypes.CREATE)){
  175. int owned = 0;
  176. try{
  177. ResultSet rsTotal = CommandShops.db.query("SELECT COUNT(*) FROM shops WHERE owner='" + playerName + "'");
  178. rsTotal.next();
  179. owned = rsTotal.getInt(1);
  180. rsTotal.close();
  181. }catch(Exception e){
  182. log.warning(String.format((Locale)null,"[%s] - DB lost trying to check owned shop total.", CommandShops.pdfFile.getName()));
  183. return false;
  184. }
  185. if(owned <= Config.MAX_SHOPS_PER_PLAYER || Config.MAX_SHOPS_PER_PLAYER < 0)
  186. return true;
  187. }
  188. return false;
  189. }
  190. /**
  191. * Check if a theoretical shop position would be acceptable.
  192. * To meet this criteria, it must not overlap any existing shops.
  193. * @param xyzA
  194. * First of 2 points defining the cuboid
  195. * @param xyzB
  196. * Second of 2 points defining the cuboid
  197. * @param worldName
  198. * The world to check in
  199. * @return true if the position is fine
  200. */
  201. protected boolean shopPositionOk(double[] xyzA, double[] xyzB, String worldName)
  202. {
  203. BookmarkedResult res = new BookmarkedResult();
  204. // make sure coords are in right order
  205. for(int i = 0; i < 3; i++)
  206. {
  207. if(xyzA[i] > xyzB[i])
  208. {
  209. double temp = xyzA[i];
  210. xyzA[i] = xyzB[i];
  211. xyzB[i] = temp;
  212. }
  213. }
  214. // Test for overlap with other shops
  215. for(double x = xyzA[0]; x <= xyzB[0]; x++)
  216. {
  217. for(double z = xyzA[2]; z <= xyzB[2]; z++)
  218. {
  219. for(double y = xyzA[1]; y <= xyzB[1]; y++)
  220. {
  221. res = CommandShops.getCuboidTree().relatedSearch(res.bookmark, x, y, z);
  222. if(shopOverlaps(res, worldName)) return false;
  223. }
  224. }
  225. }
  226. return true;
  227. }
  228. /**
  229. * Check if a theoretical shop position would be in a market.
  230. * To meet this criteria, it must overlap a market if one exists.
  231. * @param xyzA
  232. * First of 2 points defining the cuboid
  233. * @param xyzB
  234. * Second of 2 points defining the cuboid
  235. * @param worldName
  236. * The world to check in
  237. * @return true if the position is fine
  238. */
  239. protected boolean shopInMarket(double[] xyzA, double[] xyzB, String worldName)
  240. {
  241. if(Config.MARKETS.length > 0 && CommandShops.worldguard != null)
  242. {
  243. boolean good = false;
  244. for(String market : Config.MARKETS)
  245. {
  246. ProtectedRegion region = CommandShops.worldguard.get(Bukkit.getWorld(worldName)).getRegion(market);
  247. if(region == null)
  248. {
  249. log.warning(String.format((Locale)null,"[%s] - Invalid region name specified for market - '%s'.", CommandShops.pdfFile.getName(), market));
  250. continue;
  251. }
  252. if(region.contains((int)xyzA[0],(int)xyzA[1],(int)xyzA[2]) && region.contains((int)xyzB[0],(int)xyzB[1],(int)xyzB[2]))
  253. {
  254. good = true;
  255. break;
  256. }
  257. }
  258. if(!good) return false;
  259. }
  260. return true;
  261. }
  262. /**
  263. * Check if a list of numerically-overlapping shops are actually in the same world
  264. * as the one we were checking against.
  265. * @param res
  266. * the set of shops found by the numeric overlap algorithm
  267. * @param worldName
  268. * name of the world to check in
  269. * @return true if the overlap is real
  270. */
  271. protected boolean shopOverlaps(BookmarkedResult res, String worldName)
  272. {
  273. if(res.results.size() != 0)
  274. {
  275. for(PrimitiveCuboid cuboid: res.results)
  276. {
  277. if(cuboid.world.equalsIgnoreCase(worldName))
  278. {
  279. sender.sendMessage(CommandShops.CHAT_PREFIX
  280. + ChatColor.DARK_AQUA
  281. + "Could not create shop, it overlaps with shop "
  282. + ChatColor.WHITE + cuboid.id);
  283. return true;
  284. }
  285. }
  286. }
  287. return false;
  288. }
  289. /**
  290. * Give some amount of an item to a player.
  291. * @param item
  292. * the item type
  293. * @param amount
  294. * how many
  295. */
  296. protected void givePlayerItem(ItemInfo item, int amount)
  297. {
  298. Player player = (Player)sender;
  299. int maxStackSize = item.getMaxStackSize();
  300. // fill all the existing slots first
  301. for(int i: player.getInventory().all(item.material).keySet())
  302. {
  303. if(amount == 0) continue;
  304. ItemStack thisStack = player.getInventory().getItem(i);
  305. if(thisStack.getType().equals(item.material) && thisStack.getDurability() == item.subTypeId)
  306. {
  307. if(thisStack.getAmount() < maxStackSize)
  308. {
  309. int remainder = maxStackSize - thisStack.getAmount();
  310. if(remainder <= amount)
  311. {
  312. amount -= remainder;
  313. thisStack.setAmount(maxStackSize);
  314. }else{
  315. thisStack.setAmount(maxStackSize - remainder + amount);
  316. amount = 0;
  317. }
  318. }
  319. }
  320. }
  321. //add item to new slots
  322. for(int i = 0; i < 36; i++)
  323. {
  324. ItemStack thisSlot = player.getInventory().getItem(i);
  325. if(thisSlot == null || thisSlot.getType() == Material.AIR)
  326. {
  327. if(amount == 0) continue;
  328. if(amount >= maxStackSize)
  329. {
  330. player.getInventory().setItem(i, item.toStack(maxStackSize));
  331. amount -= maxStackSize;
  332. }else{
  333. player.getInventory().setItem(i, item.toStack(amount));
  334. amount = 0;
  335. }
  336. }
  337. }
  338. /* If any items remain, drop them on the ground.
  339. * This shouldn't normally happen because we usually limit the amount we give based on their available space.
  340. */
  341. while(amount > 0)
  342. {
  343. ItemStack stack = null;
  344. if(amount >= maxStackSize)
  345. {
  346. stack = item.toStack(maxStackSize);
  347. amount -= maxStackSize;
  348. }else{
  349. stack = item.toStack(amount);
  350. amount = 0;
  351. }
  352. player.getWorld().dropItemNaturally(player.getLocation(), stack);
  353. }
  354. }
  355. /**
  356. * Returns true if the sender is the owner or a manager of a given shop.
  357. * @param shop
  358. * the shop to check against
  359. * @return true, if the sender can control the shop
  360. */
  361. protected boolean isShopController(long shop)
  362. {
  363. if(!(sender instanceof Player)) return false;
  364. Player player = (Player)sender;
  365. try{
  366. String ownQuery = String.format((Locale)null,"SELECT id FROM shops WHERE id=%d AND owner='%s' LIMIT 1",shop,db.escape(player.getName()));
  367. ResultSet resOwn = CommandShops.db.query(ownQuery);
  368. boolean owner = resOwn.next();
  369. resOwn.close();
  370. if(owner) return true;
  371. String manQuery = String.format((Locale)null,"SELECT shop FROM managers WHERE shop=%d AND manager='%s' LIMIT 1",shop,db.escape(player.getName()));
  372. ResultSet resMan = CommandShops.db.query(manQuery);
  373. boolean manager = resMan.next();
  374. resMan.close();
  375. if(manager) return true;
  376. }catch(Exception e){
  377. log.warning(String.format((Locale)null,"[%s] - Problem reading shop controllers: "+e, CommandShops.pdfFile.getName()));
  378. }
  379. return false;
  380. }
  381. /**
  382. * Get how many of a certain item is in a player's inventory.
  383. * @param inventory
  384. * player's inventory
  385. * @param item
  386. * item type to count
  387. * @return the total
  388. */
  389. protected int countItemsInInventory(PlayerInventory inventory, ItemInfo item)
  390. {
  391. int totalAmount = 0;
  392. for(ItemStack thisStack : inventory.all(item.material).values())
  393. {
  394. if(item.isDurable())
  395. {
  396. int damage = calcDurabilityPercentage(thisStack);
  397. if(damage > Config.MAX_DAMAGE
  398. && Config.MAX_DAMAGE != 0) continue;
  399. }else{
  400. if(thisStack.getDurability() != item.subTypeId) continue;
  401. }
  402. totalAmount += thisStack.getAmount();
  403. }
  404. return totalAmount;
  405. }
  406. /**
  407. * Compute the durability percentage of an item based on it's current and max.
  408. * @param item
  409. * the item to inspect
  410. * @return the item's durability as a percentage
  411. */
  412. protected static int calcDurabilityPercentage(ItemStack item)
  413. {
  414. // calc durability prcnt
  415. return (short)((double)item.getDurability()
  416. / (double)item.getType().getMaxDurability() * 100);
  417. }
  418. /**
  419. * Take items from a player.
  420. * @param inventory
  421. * player's inventory
  422. * @param item
  423. * item type to take
  424. * @param amount
  425. * how many of the item to take
  426. * @return remaining number of items that should have been taken but were not
  427. * because the player ran out. Will be zero on success.
  428. */
  429. protected int removeItemsFromInventory(PlayerInventory inventory, ItemInfo item, int amount)
  430. {
  431. // remove number of items from player adding stock
  432. for(int i: inventory.all(item.material).keySet())
  433. {
  434. if(amount == 0) continue;
  435. ItemStack thisStack = inventory.getItem(i);
  436. if(item.isDurable())
  437. {
  438. int damage = calcDurabilityPercentage(thisStack);
  439. if(damage > Config.MAX_DAMAGE
  440. && Config.MAX_DAMAGE != 0) continue;
  441. }else{
  442. if(thisStack.getDurability() != item.subTypeId) continue;
  443. }
  444. int foundAmount = thisStack.getAmount();
  445. if(amount >= foundAmount)
  446. {
  447. amount -= foundAmount;
  448. inventory.setItem(i, null);
  449. }else{
  450. thisStack.setAmount(foundAmount - amount);
  451. inventory.setItem(i, thisStack);
  452. amount = 0;
  453. }
  454. }
  455. return amount;
  456. }
  457. /**
  458. * Figure out how many of a particular item type the player has space for.
  459. * @param inventory
  460. * player's inventory
  461. * @param item
  462. * item type
  463. * @return how many
  464. */
  465. protected int countAvailableSpaceForItemInInventory(PlayerInventory inventory, ItemInfo item)
  466. {
  467. int count = 0;
  468. for(ItemStack thisSlot: inventory.getContents())
  469. {
  470. if(thisSlot == null || thisSlot.getType() == Material.AIR)
  471. {
  472. count += item.getMaxStackSize();
  473. continue;
  474. }
  475. if(Search.materials.inverse().get(thisSlot.getType()) == item.typeId && thisSlot.getDurability() == item.subTypeId)
  476. {
  477. count += Math.max(item.getMaxStackSize() - thisSlot.getAmount(), 0);
  478. }
  479. }
  480. return count;
  481. }
  482. }