From 01b3944115cdc693b407913ba8415de3ef4dbaf3 Mon Sep 17 00:00:00 2001 From: ArmEagle Date: Wed, 30 Mar 2011 01:56:52 +0200 Subject: [PATCH 001/100] Added prevention of block removal when there's a sign attached to it of a TradeCraft shop the player is not allowed to destroy. --- com/mjmr89/TradeCraft/TradeCraft.java | 47 ++++++++++++++++++- .../TradeCraft/TradeCraftBlockListener.java | 46 +++++++++++------- 2 files changed, 74 insertions(+), 19 deletions(-) diff --git a/com/mjmr89/TradeCraft/TradeCraft.java b/com/mjmr89/TradeCraft/TradeCraft.java index 8ea4cb1..aac7922 100644 --- a/com/mjmr89/TradeCraft/TradeCraft.java +++ b/com/mjmr89/TradeCraft/TradeCraft.java @@ -9,6 +9,7 @@ import org.bukkit.Material; import org.bukkit.Server; import org.bukkit.block.Block; +import org.bukkit.block.BlockFace; import org.bukkit.block.Chest; import org.bukkit.block.Sign; import org.bukkit.command.Command; @@ -17,6 +18,7 @@ import org.bukkit.event.Listener; import org.bukkit.event.Event.Priority; import org.bukkit.event.Event.Type; +import org.bukkit.material.MaterialData; import org.bukkit.plugin.Plugin; import org.bukkit.plugin.PluginDescriptionFile; import org.bukkit.plugin.PluginLoader; @@ -153,7 +155,50 @@ void trace(Player player, String format, Object... args) { sendMessage(player, format, args); } } - + /* + * When a block behind a shop sign is destroyed, the sign would be destroyed too. + * Check all side faces of this block, for a sign attached to this block. Then + * pass that sign block to getShopFromSignBlock. + * + * This should only be used for checking whether a normal block can be destroyed, for there not being any + * signs attached to it, or this block being a chest or sign itself. + * Since one block can + */ + ArrayList getShopsFromBlock(Player player, Block block) { + ArrayList shops = new ArrayList(); + // use other function(s) directly if applicable + if ( block.getType() == Material.CHEST || block.getType() == Material.WALL_SIGN ) { + TradeCraftShop shop = getShopFromSignOrChestBlock(player, block); + if ( shop != null ) { + shops.add(shop); + } + } else { + // go through all 4 faces of this block to check for a sign attached to this block + final BlockFace[] sides = {BlockFace.NORTH, BlockFace.EAST, BlockFace.SOUTH, BlockFace.WEST}; + for ( int index_sides = 0; index_sides < sides.length; index_sides++ ) { + BlockFace side = sides[index_sides]; + // get the block on that side + Block sideBlock = block.getFace(side); + // check for it being a wall sign + if ( sideBlock.getType() == Material.WALL_SIGN ) { + // get the sign (extending MaterialData) for clean attached face checking + org.bukkit.material.Sign materialSign = new org.bukkit.material.Sign(Material.WALL_SIGN, sideBlock.getData()); + /* Now, for easy comparison, we'll compare to the direction the sign is facing. If that's + * the same as the current face (of the 4 we're looping through), then this sign is attached + * to the block that is being destroyed. + */ + if ( materialSign.getFacing() == side ) { + TradeCraftShop shop = this.getShopFromSignBlock(player, sideBlock); + if ( shop != null ) { + shops.add(shop); + } + } + + } + } + } + return shops; + } TradeCraftShop getShopFromSignOrChestBlock(Player player, Block block) { if (block.getType() == Material.CHEST) { block = player.getWorld().getBlockAt(block.getX(), diff --git a/com/mjmr89/TradeCraft/TradeCraftBlockListener.java b/com/mjmr89/TradeCraft/TradeCraftBlockListener.java index ca4ac9b..d34d718 100644 --- a/com/mjmr89/TradeCraft/TradeCraftBlockListener.java +++ b/com/mjmr89/TradeCraft/TradeCraftBlockListener.java @@ -1,5 +1,7 @@ package com.mjmr89.TradeCraft; +import java.util.ArrayList; + import org.bukkit.Material; import org.bukkit.block.Block; import org.bukkit.block.Chest; @@ -42,30 +44,38 @@ public void onBlockRightClick(BlockRightClickEvent e){ public void onBlockBreak(BlockBreakEvent e) { Player player = e.getPlayer(); Block block = e.getBlock(); - - TradeCraftShop shop = plugin.getShopFromSignOrChestBlock(player, block); + ArrayList shops = plugin.getShopsFromBlock(player, block); - if (shop == null) { - + if (shops.size() == 0) { return; } - if (shop.playerCanDestroy(player) || plugin.permissions.canDestroyShops(player)) { - if (!shop.shopCanBeWithdrawnFrom()) { - plugin.data.deleteShop(shop); + // Go through all shops in the list and check whether the player can destroy them all first. + // Only if that is the case proceed to destroy. + for ( TradeCraftShop shop : shops ) { + if (!shop.playerCanDestroy(player) && !plugin.permissions.canDestroyShops(player) || + shop.shopCanBeWithdrawnFrom() ) { + // cannot destroy this shop, so cancel destruction, use distinct error messages + if ( shop.shopCanBeWithdrawnFrom() ) { + plugin.sendMessage(player, "All items and gold must be withdrawn before you can destroy this sign or chest!"); + } else { + if ( block.getType() == Material.WALL_SIGN ) { + plugin.sendMessage(player, "You can't destroy this sign!"); + } else if ( block.getType() == Material.CHEST ) { + plugin.sendMessage(player, "You can't destroy this chest!"); + } else { + plugin.sendMessage(player, "You can't destroy this block because there are signs attached to it!"); + } + } + stopDestruction(block,e); return; - } - - plugin.sendMessage(player, "All items and gold must be withdrawn before you can destroy this sign or chest!"); - - stopDestruction(block,e); + } + } + // player can destroy all shops, so proceed + for ( TradeCraftShop shop : shops ) { + plugin.data.deleteShop(shop); return; } - - plugin.sendMessage(player, "You can't destroy this sign or chest!"); - - stopDestruction(block,e); - return; } public void stopDestruction(Block b, BlockBreakEvent e){ @@ -79,7 +89,7 @@ public void stopDestruction(Block b, BlockBreakEvent e){ sign.update(true); return; - }else if(b.getState() instanceof Chest){ + } else { e.setCancelled(true); } From 758d503baaf49d804b181260e1464d98084fe3ab Mon Sep 17 00:00:00 2001 From: ArmEagle Date: Wed, 30 Mar 2011 02:08:29 +0200 Subject: [PATCH 002/100] Fixed premature return in onBlockBreak (when going through shops for closing) --- com/mjmr89/TradeCraft/TradeCraftBlockListener.java | 1 - 1 file changed, 1 deletion(-) diff --git a/com/mjmr89/TradeCraft/TradeCraftBlockListener.java b/com/mjmr89/TradeCraft/TradeCraftBlockListener.java index d34d718..2d27465 100644 --- a/com/mjmr89/TradeCraft/TradeCraftBlockListener.java +++ b/com/mjmr89/TradeCraft/TradeCraftBlockListener.java @@ -74,7 +74,6 @@ public void onBlockBreak(BlockBreakEvent e) { // player can destroy all shops, so proceed for ( TradeCraftShop shop : shops ) { plugin.data.deleteShop(shop); - return; } } From 9e5ce0f651b735fca828c807bcda98d51a4c2a29 Mon Sep 17 00:00:00 2001 From: ArmEagle Date: Wed, 30 Mar 2011 15:34:17 +0200 Subject: [PATCH 003/100] made CB 602 compatible --- com/mjmr89/TradeCraft/TradeCraft.java | 2 +- .../TradeCraft/TradeCraftBlockListener.java | 15 -------------- .../TradeCraft/TradeCraftPlayerListener.java | 20 ++++++++++++++++++- 3 files changed, 20 insertions(+), 17 deletions(-) diff --git a/com/mjmr89/TradeCraft/TradeCraft.java b/com/mjmr89/TradeCraft/TradeCraft.java index aac7922..7946466 100644 --- a/com/mjmr89/TradeCraft/TradeCraft.java +++ b/com/mjmr89/TradeCraft/TradeCraft.java @@ -71,7 +71,7 @@ public void onEnable() { PluginManager pm = this.getServer().getPluginManager(); - pm.registerEvent(Type.BLOCK_RIGHTCLICKED, blockListener, + pm.registerEvent(Type.PLAYER_INTERACT, playerListener, Priority.Normal, this); pm diff --git a/com/mjmr89/TradeCraft/TradeCraftBlockListener.java b/com/mjmr89/TradeCraft/TradeCraftBlockListener.java index 2d27465..5546c10 100644 --- a/com/mjmr89/TradeCraft/TradeCraftBlockListener.java +++ b/com/mjmr89/TradeCraft/TradeCraftBlockListener.java @@ -9,7 +9,6 @@ import org.bukkit.entity.Player; import org.bukkit.event.block.BlockBreakEvent; import org.bukkit.event.block.BlockListener; -import org.bukkit.event.block.BlockRightClickEvent; import org.bukkit.event.block.SignChangeEvent; import org.bukkit.inventory.Inventory; @@ -26,20 +25,6 @@ public void debug(String str){ plugin.getServer().broadcastMessage(str); } - @Override - public void onBlockRightClick(BlockRightClickEvent e){ - Block blockClicked = e.getBlock(); - Player player = e.getPlayer(); - - TradeCraftShop shop = plugin.getShopFromSignBlock(player, blockClicked); - - if (shop == null) { - return; - } - - shop.handleRightClick(player); - } - @Override public void onBlockBreak(BlockBreakEvent e) { Player player = e.getPlayer(); diff --git a/com/mjmr89/TradeCraft/TradeCraftPlayerListener.java b/com/mjmr89/TradeCraft/TradeCraftPlayerListener.java index 474464b..0cae789 100644 --- a/com/mjmr89/TradeCraft/TradeCraftPlayerListener.java +++ b/com/mjmr89/TradeCraft/TradeCraftPlayerListener.java @@ -1,7 +1,10 @@ package com.mjmr89.TradeCraft; +import org.bukkit.block.Block; import org.bukkit.entity.Player; +import org.bukkit.event.block.Action; import org.bukkit.event.player.PlayerChatEvent; +import org.bukkit.event.player.PlayerInteractEvent; import org.bukkit.event.player.PlayerListener; @@ -12,6 +15,21 @@ public class TradeCraftPlayerListener extends PlayerListener{ TradeCraftPlayerListener(TradeCraft plugin){ this.plugin = plugin; } + @Override + public void onPlayerInteract(PlayerInteractEvent e) { + if ( e.getAction() == Action.RIGHT_CLICK_BLOCK ) { + Block blockClicked = e.getClickedBlock(); + Player player = e.getPlayer(); + + TradeCraftShop shop = plugin.getShopFromSignBlock(player, blockClicked); + + if (shop == null) { + return; + } + + shop.handleRightClick(player); + } + } private void displayItems(Player player) { String[] names = plugin.configuration.getNames(); @@ -30,7 +48,7 @@ private void displayItems(Player player) { plugin.sendMessage(player, sb.toString()); } } - + private void displaySecurity(Player player) { plugin.permissions.debug(player.getName()); } From 3c0e6e315532f40bd45d50c9c6c73ded5d8cfe18 Mon Sep 17 00:00:00 2001 From: ArmEagle Date: Fri, 1 Apr 2011 15:31:02 +0200 Subject: [PATCH 004/100] Changed location of plugin config/state files to plugins/TradeCraft/. If config files (.properties and .txt) do not exist, default files will be copied from the jar file. Removed several warnings (changes across a lot of files) Changed version to 0.81AE to mark it as modified Working with Bukkit#495/CB#617(RB) --- com/mjmr89/TradeCraft/TradeCraft.java | 9 +-- .../TradeCraft/TradeCraftBlockListener.java | 2 - .../TradeCraftConfigurationFile.java | 49 +++++++++++++++-- com/mjmr89/TradeCraft/TradeCraftDataFile.java | 2 +- com/mjmr89/TradeCraft/TradeCraftItemShop.java | 1 - .../TradeCraft/TradeCraftPermissions.java | 3 +- .../TradeCraft/TradeCraftPlayerListener.java | 9 +-- .../TradeCraft/TradeCraftPropertiesFile.java | 55 ++++++++++++++----- .../TradeCraft/TradeCraftRepairShop.java | 3 +- plugin.yml | 2 +- 10 files changed, 95 insertions(+), 40 deletions(-) diff --git a/com/mjmr89/TradeCraft/TradeCraft.java b/com/mjmr89/TradeCraft/TradeCraft.java index 7946466..afa8e3c 100644 --- a/com/mjmr89/TradeCraft/TradeCraft.java +++ b/com/mjmr89/TradeCraft/TradeCraft.java @@ -1,6 +1,5 @@ package com.mjmr89.TradeCraft; -import java.io.File; import java.util.ArrayList; import java.util.logging.Logger; import java.util.regex.Matcher; @@ -15,17 +14,12 @@ import org.bukkit.command.Command; import org.bukkit.command.CommandSender; import org.bukkit.entity.Player; -import org.bukkit.event.Listener; import org.bukkit.event.Event.Priority; import org.bukkit.event.Event.Type; -import org.bukkit.material.MaterialData; -import org.bukkit.plugin.Plugin; import org.bukkit.plugin.PluginDescriptionFile; -import org.bukkit.plugin.PluginLoader; import org.bukkit.plugin.PluginManager; import org.bukkit.plugin.java.JavaPlugin; -import com.nijiko.permissions.PermissionHandler; import com.nijikokun.bukkit.Permissions.Permissions; public class TradeCraft extends JavaPlugin { @@ -108,9 +102,8 @@ public boolean onCommand(CommandSender sender, Command command, p.sendMessage("Currency is set to " + currency); } - } finally { - return true; } + return true; } else if (name.equalsIgnoreCase("displaycurrency") && args.length == 0) { p.sendMessage("Currency is: " + currency); diff --git a/com/mjmr89/TradeCraft/TradeCraftBlockListener.java b/com/mjmr89/TradeCraft/TradeCraftBlockListener.java index 5546c10..7a99418 100644 --- a/com/mjmr89/TradeCraft/TradeCraftBlockListener.java +++ b/com/mjmr89/TradeCraft/TradeCraftBlockListener.java @@ -4,13 +4,11 @@ import org.bukkit.Material; import org.bukkit.block.Block; -import org.bukkit.block.Chest; import org.bukkit.block.Sign; import org.bukkit.entity.Player; import org.bukkit.event.block.BlockBreakEvent; import org.bukkit.event.block.BlockListener; import org.bukkit.event.block.SignChangeEvent; -import org.bukkit.inventory.Inventory; public class TradeCraftBlockListener extends BlockListener{ diff --git a/com/mjmr89/TradeCraft/TradeCraftConfigurationFile.java b/com/mjmr89/TradeCraft/TradeCraftConfigurationFile.java index ac9702c..a096e06 100644 --- a/com/mjmr89/TradeCraft/TradeCraftConfigurationFile.java +++ b/com/mjmr89/TradeCraft/TradeCraftConfigurationFile.java @@ -1,8 +1,11 @@ package com.mjmr89.TradeCraft; import java.io.BufferedReader; +import java.io.File; +import java.io.FileOutputStream; import java.io.FileReader; import java.io.IOException; +import java.io.InputStream; import java.util.Arrays; import java.util.HashMap; import java.util.Map; @@ -10,8 +13,8 @@ import java.util.regex.Pattern; class TradeCraftConfigurationFile { - private static final String fileName = TradeCraft.pluginName + ".txt"; + private static final String filePath = "plugins" + File.separator + TradeCraft.pluginName; private static final Pattern commentPattern = Pattern.compile("^\\s*#.*$"); private static final Pattern infoPattern = Pattern.compile( @@ -28,10 +31,48 @@ class TradeCraftConfigurationFile { } void load() { + // make folder in the plugins dir if it doesn't exist yet + File path = new File(filePath); + if ( !path.exists() ) { + path.mkdirs(); + } + path = null; + + // if file does not exist in this directory, copy it from the jar + File file = new File(filePath + File.separator + fileName); + if ( !file.exists() ) { + InputStream input = this.getClass().getResourceAsStream("/" + fileName); + if ( input != null ) { + FileOutputStream output = null; + + try { + output = new FileOutputStream(file); + byte[] buf = new byte[8192]; + int length = 0; + while ((length = input.read(buf)) > 0) { + output.write(buf, 0, length); + } + } catch (IOException e) { + e.printStackTrace(); + } finally { + try { + if (input != null) { + input.close(); + } + } catch (IOException e) {} + + try { + if (output != null) { + output.close(); + } + } catch (IOException e) {} + } + } + } try { infos.clear(); - BufferedReader configurationFile = new BufferedReader(new FileReader(fileName)); + BufferedReader configurationFile = new BufferedReader(new FileReader(filePath + File.separator + fileName)); int lineNumber = 0; String line; @@ -54,7 +95,7 @@ void load() { if (!infoMatcher.matches()) { plugin.log.warning( "Failed to parse line number " + lineNumber + - " in " + fileName + + " in " + filePath + File.separator + fileName + ": " + line); continue; } @@ -79,7 +120,7 @@ void load() { configurationFile.close(); } catch (IOException e) { - plugin.log.warning("Error reading " + fileName); + plugin.log.warning("Error reading " + filePath + File.separator + fileName); } } diff --git a/com/mjmr89/TradeCraft/TradeCraftDataFile.java b/com/mjmr89/TradeCraft/TradeCraftDataFile.java index feccb34..7a21492 100644 --- a/com/mjmr89/TradeCraft/TradeCraftDataFile.java +++ b/com/mjmr89/TradeCraft/TradeCraftDataFile.java @@ -17,7 +17,7 @@ class TradeCraftDataFile { - private static final String fileName = TradeCraft.pluginName + ".data"; + private static final String fileName = "plugins" + File.separator + TradeCraft.pluginName+ File.separator + TradeCraft.pluginName + ".data"; private static final Pattern infoPattern1 = Pattern.compile( "^\\s*(-?\\d+)\\s*,\\s*(-?\\d+)\\s*,\\s*(-?\\d+)" + // x,y,z "\\s*=\\s*" + diff --git a/com/mjmr89/TradeCraft/TradeCraftItemShop.java b/com/mjmr89/TradeCraft/TradeCraftItemShop.java index 6736564..0b8a9ec 100644 --- a/com/mjmr89/TradeCraft/TradeCraftItemShop.java +++ b/com/mjmr89/TradeCraft/TradeCraftItemShop.java @@ -1,6 +1,5 @@ package com.mjmr89.TradeCraft; -import org.bukkit.Material; import org.bukkit.block.Chest; import org.bukkit.block.Sign; import org.bukkit.entity.Player; diff --git a/com/mjmr89/TradeCraft/TradeCraftPermissions.java b/com/mjmr89/TradeCraft/TradeCraftPermissions.java index e8c701e..5571f63 100644 --- a/com/mjmr89/TradeCraft/TradeCraftPermissions.java +++ b/com/mjmr89/TradeCraft/TradeCraftPermissions.java @@ -64,12 +64,11 @@ public boolean canDestroyShops(Player p){ public void debug(String n){ Player p = plugin.getServer().getPlayer(n); - String name = p.getName(); if(p == null){ plugin.getServer().broadcastMessage("/canPlayer used with a name of player who is not online."); return; } - + String name = p.getName(); plugin.log.info("" + name + " has:"); plugin.log.info("canbuy " + canBuy(p)); plugin.log.info("cansell " + canSell(p)); diff --git a/com/mjmr89/TradeCraft/TradeCraftPlayerListener.java b/com/mjmr89/TradeCraft/TradeCraftPlayerListener.java index 0cae789..3314bf8 100644 --- a/com/mjmr89/TradeCraft/TradeCraftPlayerListener.java +++ b/com/mjmr89/TradeCraft/TradeCraftPlayerListener.java @@ -3,7 +3,6 @@ import org.bukkit.block.Block; import org.bukkit.entity.Player; import org.bukkit.event.block.Action; -import org.bukkit.event.player.PlayerChatEvent; import org.bukkit.event.player.PlayerInteractEvent; import org.bukkit.event.player.PlayerListener; @@ -30,8 +29,9 @@ public void onPlayerInteract(PlayerInteractEvent e) { shop.handleRightClick(player); } } - - private void displayItems(Player player) { + + @SuppressWarnings("unused") + private void displayItems(Player player) { String[] names = plugin.configuration.getNames(); StringBuilder sb = new StringBuilder(); for (String name : names) { @@ -49,7 +49,8 @@ private void displayItems(Player player) { } } - private void displaySecurity(Player player) { + @SuppressWarnings("unused") + private void displaySecurity(Player player) { plugin.permissions.debug(player.getName()); } diff --git a/com/mjmr89/TradeCraft/TradeCraftPropertiesFile.java b/com/mjmr89/TradeCraft/TradeCraftPropertiesFile.java index 1a5f3c3..98e5411 100644 --- a/com/mjmr89/TradeCraft/TradeCraftPropertiesFile.java +++ b/com/mjmr89/TradeCraft/TradeCraftPropertiesFile.java @@ -1,34 +1,59 @@ package com.mjmr89.TradeCraft; import java.io.File; +import java.io.FileOutputStream; import java.io.IOException; +import java.io.InputStream; import org.bukkit.util.config.Configuration; public class TradeCraftPropertiesFile { + private static final String fileName = TradeCraft.pluginName + ".properties"; + private static final String filePath = "plugins" + File.separator + TradeCraft.pluginName; - private File f = new File(TradeCraft.pluginName + ".properties"); private final Configuration properties; public TradeCraftPropertiesFile() { - - if(!f.exists()){ - try { - f.createNewFile(); -// populate(); - } catch (IOException e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } + // make folder in the plugins dir if it doesn't exist yet + File path = new File(filePath); + if ( !path.exists() ) { + path.mkdirs(); } + path = null; - properties = new Configuration(f); - } + // if file does not exist in this directory, copy it from the jar + File file = new File(filePath + File.separator + fileName); + if ( !file.exists() ) { + InputStream input = this.getClass().getResourceAsStream("/" + fileName); + if ( input != null ) { + FileOutputStream output = null; - public void populate(){ - properties.setProperty("infinite-shops-enabled",true); + try { + output = new FileOutputStream(file); + byte[] buf = new byte[8192]; + int length = 0; + while ((length = input.read(buf)) > 0) { + output.write(buf, 0, length); + } + } catch (IOException e) { + e.printStackTrace(); + } finally { + try { + if (input != null) { + input.close(); + } + } catch (IOException e) {} + + try { + if (output != null) { + output.close(); + } + } catch (IOException e) {} + } + } + } - properties.save(); + properties = new Configuration(file); } public int getCurrencyTypeId(){ diff --git a/com/mjmr89/TradeCraft/TradeCraftRepairShop.java b/com/mjmr89/TradeCraft/TradeCraftRepairShop.java index a148304..e369d6d 100644 --- a/com/mjmr89/TradeCraft/TradeCraftRepairShop.java +++ b/com/mjmr89/TradeCraft/TradeCraftRepairShop.java @@ -2,7 +2,6 @@ import java.util.List; -import org.bukkit.Material; import org.bukkit.block.Chest; import org.bukkit.block.Sign; import org.bukkit.entity.Player; @@ -45,7 +44,7 @@ public void handleRightClick(Player player) { chest.add(item.getType().getId(), 1); } - chest.add(plugin.currency.getId(), (gold - actualCost)); + chest.add(TradeCraft.currency.getId(), (gold - actualCost)); chest.update(); diff --git a/plugin.yml b/plugin.yml index a86da38..7a7fefb 100644 --- a/plugin.yml +++ b/plugin.yml @@ -1,6 +1,6 @@ name: TradeCraft main: com.mjmr89.TradeCraft.TradeCraft -version: 0.7 +version: 0.8AE commands: myshops: description: See a printout of any personal shops you have From a4a27a95da5cfd19fecde9cd928e8063da71497c Mon Sep 17 00:00:00 2001 From: ArmEagle Date: Wed, 6 Apr 2011 13:56:33 +0200 Subject: [PATCH 005/100] setcurrency should now follow permissions, using TradeCraft.canSetCurrency permission entry. --- .classpath | 16 ++++++------- com/mjmr89/TradeCraft/TradeCraft.java | 23 +++++++++++-------- .../TradeCraft/TradeCraftPermissions.java | 8 +++++++ plugin.yml | 2 +- 4 files changed, 30 insertions(+), 19 deletions(-) diff --git a/.classpath b/.classpath index 60c8e56..7821aa2 100644 --- a/.classpath +++ b/.classpath @@ -1,8 +1,8 @@ - - - - - - - - + + + + + + + + diff --git a/com/mjmr89/TradeCraft/TradeCraft.java b/com/mjmr89/TradeCraft/TradeCraft.java index afa8e3c..de6265f 100644 --- a/com/mjmr89/TradeCraft/TradeCraft.java +++ b/com/mjmr89/TradeCraft/TradeCraft.java @@ -91,19 +91,22 @@ public boolean onCommand(CommandSender sender, Command command, p = (Player) sender; if (name.equalsIgnoreCase("setcurrency") && args.length == 1) { - try { - int cid = Integer.parseInt(args[0]); - currency = Material.getMaterial(cid); - p.sendMessage("Currency is set to " + currency); - } catch (NumberFormatException nfe) { - Material m = Material.getMaterial(args[0]); - if (m != null) { - currency = m; + if ( !this.permissions.canSell(p) ) { + p.sendMessage("You do not have the permission to set the currency"); + } else { + try { + int cid = Integer.parseInt(args[0]); + currency = Material.getMaterial(cid); p.sendMessage("Currency is set to " + currency); - + } catch (NumberFormatException nfe) { + Material m = Material.getMaterial(args[0]); + if (m != null) { + currency = m; + p.sendMessage("Currency is set to " + currency); + + } } } - return true; } else if (name.equalsIgnoreCase("displaycurrency") && args.length == 0) { p.sendMessage("Currency is: " + currency); diff --git a/com/mjmr89/TradeCraft/TradeCraftPermissions.java b/com/mjmr89/TradeCraft/TradeCraftPermissions.java index 5571f63..94a9e96 100644 --- a/com/mjmr89/TradeCraft/TradeCraftPermissions.java +++ b/com/mjmr89/TradeCraft/TradeCraftPermissions.java @@ -62,6 +62,14 @@ public boolean canDestroyShops(Player p){ return p.isOp(); } + public boolean canSetCurrency(Player p) { + if ( plugin.permEnabled ) { + return permHandler.has(p, "TradeCraft.canSetCurrency"); + } else { + return p.isOp(); + } + } + public void debug(String n){ Player p = plugin.getServer().getPlayer(n); if(p == null){ diff --git a/plugin.yml b/plugin.yml index 7a7fefb..0d9e992 100644 --- a/plugin.yml +++ b/plugin.yml @@ -1,6 +1,6 @@ name: TradeCraft main: com.mjmr89.TradeCraft.TradeCraft -version: 0.8AE +version: 0.81AE commands: myshops: description: See a printout of any personal shops you have From 8af9f4c0e42fa699fdd7dbfe03b1650e8fe4d343 Mon Sep 17 00:00:00 2001 From: ArmEagle Date: Wed, 6 Apr 2011 14:04:48 +0200 Subject: [PATCH 006/100] Use bukkit functionality to get the max stack size for an item type. --- com/mjmr89/TradeCraft/TradeCraft.java | 20 +------------------- 1 file changed, 1 insertion(+), 19 deletions(-) diff --git a/com/mjmr89/TradeCraft/TradeCraft.java b/com/mjmr89/TradeCraft/TradeCraft.java index de6265f..6ded220 100644 --- a/com/mjmr89/TradeCraft/TradeCraft.java +++ b/com/mjmr89/TradeCraft/TradeCraft.java @@ -353,25 +353,7 @@ TradeCraftExchangeRate getExchangeRate(Sign sign, int lineNumber) { } static int getMaxStackSize(int itemType) { - /* - * switch (Material.getMaterial(itemType)) { case APPLE: case - * Golden_Apple: case Pork: case Grilled_Pork: case Bread: case Bucket: - * case Water_Bucket: case Lava_Bucket: case Milk_Bucket: case - * Wood_Sword: case Wood_Spade: case Wood_Pickaxe: case Wood_Axe: case - * Wood_Hoe: case Stone_Sword: case Stone_Spade: case Stone_Pickaxe: - * case Stone_Axe: case Stone_Hoe: case Iron_Sword: case Iron_Spade: - * case Iron_Pickaxe: case Iron_Axe: case Iron_Hoe: case Diamond_Sword: - * case Diamond_Spade: case Diamond_Pickaxe: case Diamond_Axe: case - * Diamond_Hoe: case Gold_Sword: case Gold_Spade: case Gold_Pickaxe: - * case Gold_Axe: case Gold_Hoe: case Leather_Helmet: case - * Leather_Chestplate: case Leather_Leggings: case Leather_Boots: case - * Iron_Helmet: case Iron_Chestplate: case Iron_Leggings: case - * Iron_Boots: case Diamond_Helmet: case Diamond_Chestplate: case - * Diamond_Leggings: case Diamond_Boots: case Gold_Helmet: case - * Gold_Chestplate: case Gold_Leggings: case Gold_Boots: return 1; case - * SnowBall: return 16; } return 64; - */ - return 64; + return Material.getMaterial(itemType).getMaxStackSize(); } public void onLoad() { From 01baa1e3084f5dfcc7afa66474dba22c8fa466c4 Mon Sep 17 00:00:00 2001 From: ArmEagle Date: Wed, 6 Apr 2011 16:07:04 +0200 Subject: [PATCH 007/100] Configuration/.properties file is reformatted and now successfully loaded (code was missing call to .load()). Currency is saving and used correctly now. All references to "gold" are replaced by the name of the current set currency. And probably some other small changes I already forgot about. --- TradeCraft.properties | 17 ++++----- com/mjmr89/TradeCraft/TradeCraft.java | 35 ++++++++++++------- .../TradeCraft/TradeCraftBlockListener.java | 2 +- com/mjmr89/TradeCraft/TradeCraftItemShop.java | 32 ++++++++--------- .../TradeCraft/TradeCraftPropertiesFile.java | 13 +++++-- .../TradeCraft/TradeCraftRepairShop.java | 20 +++++------ 6 files changed, 66 insertions(+), 53 deletions(-) diff --git a/TradeCraft.properties b/TradeCraft.properties index 75eb5dd..690c234 100644 --- a/TradeCraft.properties +++ b/TradeCraft.properties @@ -1,11 +1,6 @@ -infinite-shops-enabled = true -player-owned-shops-enabled = true -repair-shops-enabled = true -group-required-to-create-infinite-shops = * -group-required-to-create-player-owned-shops = * -group-required-to-create-repair-shops = * -group-required-to-buy-from-shops = * -group-required-to-sell-to-shops = * -group-required-to-use-repair-shops = * -repair-cost = 10 -enable-debug-messages = true \ No newline at end of file +infinite-shops-enabled: true +player-owned-shops-enabled: true +repair-shops-enabled: false +repair-cost: 10 +enable-debug-messages: false +currency-id: 266 diff --git a/com/mjmr89/TradeCraft/TradeCraft.java b/com/mjmr89/TradeCraft/TradeCraft.java index 6ded220..eeca034 100644 --- a/com/mjmr89/TradeCraft/TradeCraft.java +++ b/com/mjmr89/TradeCraft/TradeCraft.java @@ -36,10 +36,9 @@ public class TradeCraft extends JavaPlugin { // Objects used by the plugin. static Material currency; - TradeCraftPropertiesFile properties = new TradeCraftPropertiesFile(); - TradeCraftConfigurationFile configuration = new TradeCraftConfigurationFile( - this); - TradeCraftDataFile data = new TradeCraftDataFile(this); + TradeCraftPropertiesFile properties; + TradeCraftConfigurationFile configuration; + TradeCraftDataFile data; private final TradeCraftBlockListener blockListener = new TradeCraftBlockListener( this); @@ -53,8 +52,7 @@ public void onDisable() { } public void onEnable() { - - properties = new TradeCraftPropertiesFile(); + properties = new TradeCraftPropertiesFile(this); configuration = new TradeCraftConfigurationFile(this); data = new TradeCraftDataFile(this); @@ -97,19 +95,18 @@ public boolean onCommand(CommandSender sender, Command command, try { int cid = Integer.parseInt(args[0]); currency = Material.getMaterial(cid); - p.sendMessage("Currency is set to " + currency); } catch (NumberFormatException nfe) { Material m = Material.getMaterial(args[0]); if (m != null) { currency = m; - p.sendMessage("Currency is set to " + currency); - } } + this.properties.setCurrencyTypeId(currency.getId()); + p.sendMessage("Currency is set to " + TradeCraft.getCurrencyName()); } } else if (name.equalsIgnoreCase("displaycurrency") && args.length == 0) { - p.sendMessage("Currency is: " + currency); + p.sendMessage("Currency is: " + TradeCraft.getCurrencyName()); } else if (name.equalsIgnoreCase("canplayer") && args.length == 1) { permissions.debug(args[0]); } else if (name.equalsIgnoreCase("myshops")) { @@ -134,8 +131,8 @@ void displayShops(Player p) { for (TradeCraftDataInfo info : list) { p.sendMessage("Item: " + Material.getMaterial(info.itemType) - + " Amount: " + info.itemAmount + "Gold: " - + info.currencyAmount); + + " Amount: " + info.itemAmount + " " + + TradeCraft.getCurrencyName() +": " + info.currencyAmount); } @@ -360,5 +357,19 @@ public void onLoad() { // TODO Auto-generated method stub } + + public static String getCurrencyName() { + String baseName = TradeCraft.currency.name(); + String[] words = baseName.split("_"); + String name = ""; + for ( int word_ind = 0; word_ind < words.length; word_ind++ ) { + String word = words[word_ind]; + if ( word_ind > 0 ) { + name = name.concat(" "); + } + name = name.concat(word.substring(0, 1).toUpperCase()).concat(word.substring(1).toLowerCase()); + } + return name; + } } \ No newline at end of file diff --git a/com/mjmr89/TradeCraft/TradeCraftBlockListener.java b/com/mjmr89/TradeCraft/TradeCraftBlockListener.java index 7a99418..08e8b0f 100644 --- a/com/mjmr89/TradeCraft/TradeCraftBlockListener.java +++ b/com/mjmr89/TradeCraft/TradeCraftBlockListener.java @@ -40,7 +40,7 @@ public void onBlockBreak(BlockBreakEvent e) { shop.shopCanBeWithdrawnFrom() ) { // cannot destroy this shop, so cancel destruction, use distinct error messages if ( shop.shopCanBeWithdrawnFrom() ) { - plugin.sendMessage(player, "All items and gold must be withdrawn before you can destroy this sign or chest!"); + plugin.sendMessage(player, "All items and currency must be withdrawn before you can destroy this sign or chest!"); } else { if ( block.getType() == Material.WALL_SIGN ) { plugin.sendMessage(player, "You can't destroy this sign!"); diff --git a/com/mjmr89/TradeCraft/TradeCraftItemShop.java b/com/mjmr89/TradeCraft/TradeCraftItemShop.java index 0b8a9ec..1159870 100644 --- a/com/mjmr89/TradeCraft/TradeCraftItemShop.java +++ b/com/mjmr89/TradeCraft/TradeCraftItemShop.java @@ -25,10 +25,10 @@ private void handleOwnerClick(Player player) { } if (getChestItemCount() == 0) { - int goldAmount = withdrawCurrency(); - if (goldAmount > 0) { - populateChest(TradeCraft.currency.getId(), goldAmount); - plugin.sendMessage(player, "Withdrew %1$d gold.", goldAmount); + int currencyAmount = withdrawCurrency(); + if (currencyAmount > 0) { + populateChest(TradeCraft.currency.getId(), currencyAmount); + plugin.sendMessage(player, "Withdrew %1$d "+ TradeCraft.getCurrencyName() +".", currencyAmount); } else { int itemAmount = withdrawItems(); if (itemAmount > 0) { @@ -40,7 +40,7 @@ private void handleOwnerClick(Player player) { } } else if (getChestItemType() == TradeCraft.currency.getId()) { depositCurrency(getChestItemCount()); - plugin.sendMessage(player, "Deposited %1$d gold.", getChestItemCount()); + plugin.sendMessage(player, "Deposited %1$d "+ TradeCraft.getCurrencyName() +".", getChestItemCount()); populateChest(0, 0); int itemAmount = withdrawItems(); if (itemAmount > 0) { @@ -72,7 +72,7 @@ private void handlePatronClick(Player player) { if (getChestItemCount() == 0) { if (playerCanBuy && playerCanBuy()) { plugin.sendMessage(player, - "You can buy %1$d %2$s for %3$d gold.", + "You can buy %1$d %2$s for %3$d "+ TradeCraft.getCurrencyName() +".", getBuyAmount(), getItemName(), getBuyValue()); @@ -80,7 +80,7 @@ private void handlePatronClick(Player player) { if (playerCanSell && playerCanSell()) { plugin.sendMessage(player, - "You can sell %1$d %2$s for %3$d gold.", + "You can sell %1$d %2$s for %3$d "+ TradeCraft.getCurrencyName() +".", getSellAmount(), getItemName(), getSellValue()); @@ -120,7 +120,7 @@ private void playerWantsToBuy(Player player) { if (amountPlayerWantsToBuy == 0) { plugin.sendMessage(player, - "You need to spend at least %1$d gold to get any %2$s.", + "You need to spend at least %1$d "+ TradeCraft.getCurrencyName() +" to get any %2$s.", getBuyValue(), getItemName()); return; @@ -134,20 +134,20 @@ private void playerWantsToBuy(Player player) { return; } - int requiredGoldForThatAmount = amountPlayerWantsToBuy * getBuyValue() / getBuyAmount(); + int requiredCurrencyForThatAmount = amountPlayerWantsToBuy * getBuyValue() / getBuyAmount(); - updateItemAndCurrencyAmounts(-amountPlayerWantsToBuy, requiredGoldForThatAmount); + updateItemAndCurrencyAmounts(-amountPlayerWantsToBuy, requiredCurrencyForThatAmount); chest.clear(); - chest.add(TradeCraft.currency.getId(), currencyPlayerWantsToSpend - requiredGoldForThatAmount); + chest.add(TradeCraft.currency.getId(), currencyPlayerWantsToSpend - requiredCurrencyForThatAmount); chest.add(getItemType(), amountPlayerWantsToBuy); chest.update(); plugin.sendMessage(player, - "You bought %1$d %2$s for %3$d gold.", + "You bought %1$d %2$s for %3$d "+ TradeCraft.getCurrencyName() +".", amountPlayerWantsToBuy, getItemName(), - requiredGoldForThatAmount); + requiredCurrencyForThatAmount); } private void playerWantsToSell(Player player) { @@ -161,7 +161,7 @@ private void playerWantsToSell(Player player) { if (currencyPlayerShouldReceive == 0) { plugin.sendMessage(player, - "You need to sell at least %1$d %2$s to get any gold.", + "You need to sell at least %1$d %2$s to get any "+ TradeCraft.getCurrencyName() +".", getSellAmount(), getItemName()); return; @@ -169,7 +169,7 @@ private void playerWantsToSell(Player player) { if (currencyPlayerShouldReceive > getCurrencyInShop()) { plugin.sendMessage(player, - "Cannot sell. This shop only has %1$d gold.", + "Cannot sell. This shop only has %1$d "+ TradeCraft.getCurrencyName() +".", getCurrencyInShop()); return; } @@ -184,7 +184,7 @@ private void playerWantsToSell(Player player) { chest.update(); plugin.sendMessage(player, - "You sold %1$d %2$s for %3$d gold.", + "You sold %1$d %2$s for %3$d "+ TradeCraft.getCurrencyName() +".", amountThatCanBeSold, getItemName(), currencyPlayerShouldReceive); diff --git a/com/mjmr89/TradeCraft/TradeCraftPropertiesFile.java b/com/mjmr89/TradeCraft/TradeCraftPropertiesFile.java index 98e5411..fd7b035 100644 --- a/com/mjmr89/TradeCraft/TradeCraftPropertiesFile.java +++ b/com/mjmr89/TradeCraft/TradeCraftPropertiesFile.java @@ -4,16 +4,17 @@ import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; - import org.bukkit.util.config.Configuration; public class TradeCraftPropertiesFile { private static final String fileName = TradeCraft.pluginName + ".properties"; private static final String filePath = "plugins" + File.separator + TradeCraft.pluginName; - + + private TradeCraft plugin; private final Configuration properties; - public TradeCraftPropertiesFile() { + public TradeCraftPropertiesFile(TradeCraft plugin) { + this.plugin = plugin; // make folder in the plugins dir if it doesn't exist yet File path = new File(filePath); if ( !path.exists() ) { @@ -24,6 +25,7 @@ public TradeCraftPropertiesFile() { // if file does not exist in this directory, copy it from the jar File file = new File(filePath + File.separator + fileName); if ( !file.exists() ) { + this.plugin.log.info(filePath + File.separator + fileName +" does not exist, creating..."); InputStream input = this.getClass().getResourceAsStream("/" + fileName); if ( input != null ) { FileOutputStream output = null; @@ -54,11 +56,16 @@ public TradeCraftPropertiesFile() { } properties = new Configuration(file); + properties.load(); } public int getCurrencyTypeId(){ return properties.getInt("currency-id",266); } + public void setCurrencyTypeId(int id) { + properties.setProperty("currency-id", id); + properties.save(); + } public boolean getInfiniteShopsEnabled() { return properties.getBoolean("infinite-shops-enabled", true); diff --git a/com/mjmr89/TradeCraft/TradeCraftRepairShop.java b/com/mjmr89/TradeCraft/TradeCraftRepairShop.java index e369d6d..691c166 100644 --- a/com/mjmr89/TradeCraft/TradeCraftRepairShop.java +++ b/com/mjmr89/TradeCraft/TradeCraftRepairShop.java @@ -14,27 +14,27 @@ public TradeCraftRepairShop(TradeCraft plugin, Sign sign, Chest chest) { } public void handleRightClick(Player player) { - int gold = chest.getAmountOfCurrencyInChest(); + int currencyAmount = chest.getAmountOfCurrencyInChest(); List items = chest.getNonCurrencyItems(); int repairCost = plugin.properties.getRepairCost(); - if (gold == 0 && items.size() == 0) { - plugin.sendMessage(player, "It costs %d gold to repair an item.", repairCost); + if (currencyAmount == 0 && items.size() == 0) { + plugin.sendMessage(player, "It costs %d "+ TradeCraft.getCurrencyName() +" to repair an item.", repairCost); return; } int actualCost = items.size() * repairCost; if (items.size() == 0) { - plugin.sendMessage(player, "With this much gold, you can repair %d items.", gold / repairCost); + plugin.sendMessage(player, "With this much "+ TradeCraft.getCurrencyName() +", you can repair %d items.", currencyAmount / repairCost); return; } - if (gold < actualCost) { - if (gold > 0) { - plugin.sendMessage(player, "That's not enough gold."); + if (currencyAmount < actualCost) { + if (currencyAmount > 0) { + plugin.sendMessage(player, "That's not enough "+ TradeCraft.getCurrencyName() +"."); } - plugin.sendMessage(player, "You need %d gold to repair all this.", actualCost); + plugin.sendMessage(player, "You need %d "+ TradeCraft.getCurrencyName() +" to repair all this.", actualCost); return; } @@ -44,11 +44,11 @@ public void handleRightClick(Player player) { chest.add(item.getType().getId(), 1); } - chest.add(TradeCraft.currency.getId(), (gold - actualCost)); + chest.add(TradeCraft.currency.getId(), (currencyAmount - actualCost)); chest.update(); - plugin.sendMessage(player, "You repaired %d items for %d gold.", items.size(), actualCost); + plugin.sendMessage(player, "You repaired %d items for %d "+ TradeCraft.getCurrencyName() +".", items.size(), actualCost); } public boolean playerCanDestroy(Player player) { From e73d5ec41da32880eb94dfc4ba8efb871cf7bdb6 Mon Sep 17 00:00:00 2001 From: ArmEagle Date: Wed, 6 Apr 2011 16:12:05 +0200 Subject: [PATCH 008/100] Wrong test on player cansetcurrency fixed. Prevented invalid object type to be set as currency (not existing name/id). --- com/mjmr89/TradeCraft/TradeCraft.java | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/com/mjmr89/TradeCraft/TradeCraft.java b/com/mjmr89/TradeCraft/TradeCraft.java index eeca034..ae3edbc 100644 --- a/com/mjmr89/TradeCraft/TradeCraft.java +++ b/com/mjmr89/TradeCraft/TradeCraft.java @@ -89,20 +89,23 @@ public boolean onCommand(CommandSender sender, Command command, p = (Player) sender; if (name.equalsIgnoreCase("setcurrency") && args.length == 1) { - if ( !this.permissions.canSell(p) ) { + if ( !this.permissions.canSetCurrency(p) ) { p.sendMessage("You do not have the permission to set the currency"); } else { + Material testCurrency; try { int cid = Integer.parseInt(args[0]); - currency = Material.getMaterial(cid); + testCurrency = Material.getMaterial(cid); } catch (NumberFormatException nfe) { - Material m = Material.getMaterial(args[0]); - if (m != null) { - currency = m; - } + testCurrency = Material.getMaterial(args[0]); + } + if ( testCurrency == null ) { + p.sendMessage(args[0] +" is not a valid value for a currency."); + } else { + currency = testCurrency; + this.properties.setCurrencyTypeId(currency.getId()); + p.sendMessage("Currency is set to " + TradeCraft.getCurrencyName()); } - this.properties.setCurrencyTypeId(currency.getId()); - p.sendMessage("Currency is set to " + TradeCraft.getCurrencyName()); } } else if (name.equalsIgnoreCase("displaycurrency") && args.length == 0) { From 168a904928b34c447e321daed2331febbcc5d31f Mon Sep 17 00:00:00 2001 From: ArmEagle Date: Wed, 6 Apr 2011 16:16:47 +0200 Subject: [PATCH 009/100] fixed some whitespace issues and added brief doc to new function. --- com/mjmr89/TradeCraft/TradeCraft.java | 29 +++++++++++---------------- 1 file changed, 12 insertions(+), 17 deletions(-) diff --git a/com/mjmr89/TradeCraft/TradeCraft.java b/com/mjmr89/TradeCraft/TradeCraft.java index ae3edbc..9327979 100644 --- a/com/mjmr89/TradeCraft/TradeCraft.java +++ b/com/mjmr89/TradeCraft/TradeCraft.java @@ -23,12 +23,10 @@ import com.nijikokun.bukkit.Permissions.Permissions; public class TradeCraft extends JavaPlugin { - // The plugin name. static final String pluginName = "TradeCraft"; - private static final Pattern ratePattern = Pattern - .compile("\\s*(\\d+)\\s*:\\s*(\\d+)\\s*"); + private static final Pattern ratePattern = Pattern.compile("\\s*(\\d+)\\s*:\\s*(\\d+)\\s*"); // Stuff used to interact with the server. final Logger log = Logger.getLogger("Minecraft"); @@ -40,10 +38,8 @@ public class TradeCraft extends JavaPlugin { TradeCraftConfigurationFile configuration; TradeCraftDataFile data; - private final TradeCraftBlockListener blockListener = new TradeCraftBlockListener( - this); - private final TradeCraftPlayerListener playerListener = new TradeCraftPlayerListener( - this); + private final TradeCraftBlockListener blockListener = new TradeCraftBlockListener(this); + private final TradeCraftPlayerListener playerListener = new TradeCraftPlayerListener(this); public TradeCraftPermissions permissions = new TradeCraftPermissions(this); public Permissions permissionsPlugin = null; public boolean permEnabled = false; @@ -56,27 +52,21 @@ public void onEnable() { configuration = new TradeCraftConfigurationFile(this); data = new TradeCraftDataFile(this); - currency = Material.getMaterial(properties.getCurrencyTypeId()); configuration.load(); data.load(); + currency = Material.getMaterial(properties.getCurrencyTypeId()); permissions.setupPermissions(); PluginManager pm = this.getServer().getPluginManager(); - pm.registerEvent(Type.PLAYER_INTERACT, playerListener, - Priority.Normal, this); + pm.registerEvent(Type.PLAYER_INTERACT, playerListener, Priority.Normal, this); - pm - .registerEvent(Type.SIGN_CHANGE, blockListener, - Priority.Normal, this); - pm - .registerEvent(Type.BLOCK_BREAK, blockListener, - Priority.Normal, this); + pm.registerEvent(Type.SIGN_CHANGE, blockListener,Priority.Normal, this); + pm.registerEvent(Type.BLOCK_BREAK, blockListener,Priority.Normal, this); PluginDescriptionFile pdfFile = this.getDescription(); System.out.println(pdfFile.getName() + " version " + pdfFile.getVersion() + " is enabled!"); - } @Override @@ -361,6 +351,11 @@ public void onLoad() { } + /** + * Get a CamelCased string based on the current currency. + * + * @return a string representing the currency. + */ public static String getCurrencyName() { String baseName = TradeCraft.currency.name(); String[] words = baseName.split("_"); From 831a1ba7ad4ab7b577ed5d0e1e3d11111d1b084c Mon Sep 17 00:00:00 2001 From: ArmEagle Date: Wed, 6 Apr 2011 16:35:46 +0200 Subject: [PATCH 010/100] Added properties setting "strict-playershop-owner-name" (default true). If true the player name on a shop sign has to match the actual player name. If set to false, the actual player's name only has to start with the name set on the sign. --- TradeCraft.properties | 1 + .../TradeCraft/TradeCraftPlayerOwnedShop.java | 13 ++++++++++++- com/mjmr89/TradeCraft/TradeCraftPropertiesFile.java | 4 ++++ 3 files changed, 17 insertions(+), 1 deletion(-) diff --git a/TradeCraft.properties b/TradeCraft.properties index 690c234..7fb6876 100644 --- a/TradeCraft.properties +++ b/TradeCraft.properties @@ -4,3 +4,4 @@ repair-shops-enabled: false repair-cost: 10 enable-debug-messages: false currency-id: 266 +strict-playershop-owner-name: false \ No newline at end of file diff --git a/com/mjmr89/TradeCraft/TradeCraftPlayerOwnedShop.java b/com/mjmr89/TradeCraft/TradeCraftPlayerOwnedShop.java index 4a20303..78d5664 100644 --- a/com/mjmr89/TradeCraft/TradeCraftPlayerOwnedShop.java +++ b/com/mjmr89/TradeCraft/TradeCraftPlayerOwnedShop.java @@ -34,7 +34,18 @@ public boolean shopCanBeWithdrawnFrom() { } public boolean isOwnedByPlayer(Player player) { - return ownerName != null && player.getName().equals(ownerName); + if ( ownerName == null ) { + return false; + } else { + // option for less strict player name matching. + if ( this.plugin.properties.getStrictPlayerShopOwnerNameRequired() ) { + // strict is enforced, so use a perfect name match + return player.getName().equals(ownerName); + } else { + // strict is not enforced, so allow the playername to start with the set owner name + return player.getName().indexOf(ownerName) == 0; + } + } } public int getItemType() { diff --git a/com/mjmr89/TradeCraft/TradeCraftPropertiesFile.java b/com/mjmr89/TradeCraft/TradeCraftPropertiesFile.java index fd7b035..35ea5d1 100644 --- a/com/mjmr89/TradeCraft/TradeCraftPropertiesFile.java +++ b/com/mjmr89/TradeCraft/TradeCraftPropertiesFile.java @@ -86,4 +86,8 @@ public int getRepairCost() { public boolean getEnableDebugMessages() { return properties.getBoolean("enable-debug-messages", false); } + + public boolean getStrictPlayerShopOwnerNameRequired() { + return properties.getBoolean("strict-playershop-owner-name", true); + } } From 270b3a60662ca2db40a622ab67a69f1ffd240ff5 Mon Sep 17 00:00:00 2001 From: ArmEagle Date: Wed, 6 Apr 2011 16:45:12 +0200 Subject: [PATCH 011/100] Prevents the plugin to register event listeners multiple times when it is disable and then enabled. Clears some memory use on disabling. Prevents event listeners from triggering when the plugin is disabled (really need to be able to unhook listeners). --- com/mjmr89/TradeCraft/TradeCraft.java | 23 +++++++++++++------ .../TradeCraft/TradeCraftBlockListener.java | 8 +++++++ .../TradeCraft/TradeCraftPlayerListener.java | 4 ++++ 3 files changed, 28 insertions(+), 7 deletions(-) diff --git a/com/mjmr89/TradeCraft/TradeCraft.java b/com/mjmr89/TradeCraft/TradeCraft.java index 9327979..163cfd5 100644 --- a/com/mjmr89/TradeCraft/TradeCraft.java +++ b/com/mjmr89/TradeCraft/TradeCraft.java @@ -43,30 +43,39 @@ public class TradeCraft extends JavaPlugin { public TradeCraftPermissions permissions = new TradeCraftPermissions(this); public Permissions permissionsPlugin = null; public boolean permEnabled = false; + + // prevent the script from registering the event listeners multiple times (by dis-/enable) + public static boolean hasRegisteredEventListeners = false; public void onDisable() { + properties = null; + configuration = null; + data.save(); + data = null; } public void onEnable() { properties = new TradeCraftPropertiesFile(this); configuration = new TradeCraftConfigurationFile(this); data = new TradeCraftDataFile(this); - + configuration.load(); data.load(); currency = Material.getMaterial(properties.getCurrencyTypeId()); permissions.setupPermissions(); - PluginManager pm = this.getServer().getPluginManager(); - - pm.registerEvent(Type.PLAYER_INTERACT, playerListener, Priority.Normal, this); - - pm.registerEvent(Type.SIGN_CHANGE, blockListener,Priority.Normal, this); - pm.registerEvent(Type.BLOCK_BREAK, blockListener,Priority.Normal, this); + if ( !TradeCraft.hasRegisteredEventListeners ) { + PluginManager pm = this.getServer().getPluginManager(); + pm.registerEvent(Type.PLAYER_INTERACT, playerListener, Priority.Normal, this); + pm.registerEvent(Type.SIGN_CHANGE, blockListener,Priority.Normal, this); + pm.registerEvent(Type.BLOCK_BREAK, blockListener,Priority.Normal, this); + TradeCraft.hasRegisteredEventListeners = true; + } PluginDescriptionFile pdfFile = this.getDescription(); System.out.println(pdfFile.getName() + " version " + pdfFile.getVersion() + " is enabled!"); + } @Override diff --git a/com/mjmr89/TradeCraft/TradeCraftBlockListener.java b/com/mjmr89/TradeCraft/TradeCraftBlockListener.java index 08e8b0f..b81eea2 100644 --- a/com/mjmr89/TradeCraft/TradeCraftBlockListener.java +++ b/com/mjmr89/TradeCraft/TradeCraftBlockListener.java @@ -25,6 +25,10 @@ public void debug(String str){ @Override public void onBlockBreak(BlockBreakEvent e) { + if ( !this.plugin.isEnabled() ) { + return; + } + Player player = e.getPlayer(); Block block = e.getBlock(); ArrayList shops = plugin.getShopsFromBlock(player, block); @@ -78,6 +82,10 @@ public void stopDestruction(Block b, BlockBreakEvent e){ } public void onSignChange(SignChangeEvent e) { + if ( !this.plugin.isEnabled() ) { + return; + } + Player player = e.getPlayer(); Sign sign = (Sign) e.getBlock().getState(); diff --git a/com/mjmr89/TradeCraft/TradeCraftPlayerListener.java b/com/mjmr89/TradeCraft/TradeCraftPlayerListener.java index 3314bf8..025ced8 100644 --- a/com/mjmr89/TradeCraft/TradeCraftPlayerListener.java +++ b/com/mjmr89/TradeCraft/TradeCraftPlayerListener.java @@ -16,6 +16,10 @@ public class TradeCraftPlayerListener extends PlayerListener{ } @Override public void onPlayerInteract(PlayerInteractEvent e) { + if ( !this.plugin.isEnabled() ) { + return; + } + if ( e.getAction() == Action.RIGHT_CLICK_BLOCK ) { Block blockClicked = e.getClickedBlock(); Player player = e.getPlayer(); From 817244592a9e492246aa48e51842d0a32777a535 Mon Sep 17 00:00:00 2001 From: ArmEagle Date: Wed, 6 Apr 2011 16:50:18 +0200 Subject: [PATCH 012/100] Forgot to add player owner shop name strictness check on sub method of the command that lists the shops owned by the player --- com/mjmr89/TradeCraft/TradeCraftDataFile.java | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/com/mjmr89/TradeCraft/TradeCraftDataFile.java b/com/mjmr89/TradeCraft/TradeCraftDataFile.java index 7a21492..be2fa11 100644 --- a/com/mjmr89/TradeCraft/TradeCraftDataFile.java +++ b/com/mjmr89/TradeCraft/TradeCraftDataFile.java @@ -156,8 +156,14 @@ public ArrayList shopsOwned(String name){ for (String key : data.keySet()) { TradeCraftDataInfo info = data.get(key); - if(info.ownerName.equalsIgnoreCase(name)){ - list.add(info); + if ( this.plugin.properties.getStrictPlayerShopOwnerNameRequired() ) { + if(info.ownerName.equalsIgnoreCase(name)){ + list.add(info); + } + } else { + if ( name.indexOf(info.ownerName) == 0 ) { + list.add(info); + } } } From 35e32e3eb626994799eabc2ddcc3e37ae2b6508f Mon Sep 17 00:00:00 2001 From: ArmEagle Date: Wed, 6 Apr 2011 16:53:46 +0200 Subject: [PATCH 013/100] set default strict player owner shop naming to true --- TradeCraft.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/TradeCraft.properties b/TradeCraft.properties index 7fb6876..c2a7f63 100644 --- a/TradeCraft.properties +++ b/TradeCraft.properties @@ -4,4 +4,4 @@ repair-shops-enabled: false repair-cost: 10 enable-debug-messages: false currency-id: 266 -strict-playershop-owner-name: false \ No newline at end of file +strict-playershop-owner-name: true \ No newline at end of file From 421677e4f0292a400e421ea28af2bd823e817532 Mon Sep 17 00:00:00 2001 From: ArmEagle Date: Wed, 6 Apr 2011 16:59:51 +0200 Subject: [PATCH 014/100] Added configurable player shop name setting use on placing of sign. --- com/mjmr89/TradeCraft/TradeCraftBlockListener.java | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/com/mjmr89/TradeCraft/TradeCraftBlockListener.java b/com/mjmr89/TradeCraft/TradeCraftBlockListener.java index b81eea2..24cb60d 100644 --- a/com/mjmr89/TradeCraft/TradeCraftBlockListener.java +++ b/com/mjmr89/TradeCraft/TradeCraftBlockListener.java @@ -115,10 +115,16 @@ public void onSignChange(SignChangeEvent e) { return; } - if (player.getName().startsWith(ownerName)) { - plugin.data.setOwnerOfSign(player.getName(), sign); - - return; + if ( this.plugin.properties.getStrictPlayerShopOwnerNameRequired() ) { + if (player.getName().startsWith(ownerName)) { + plugin.data.setOwnerOfSign(player.getName(), sign); + return; + } + } else { + if (player.getName().equalsIgnoreCase(ownerName)) { + plugin.data.setOwnerOfSign(player.getName(), sign); + return; + } } plugin.sendMessage(player, "You can't create signs with other players names on them!"); From 7a0f196abcad8d05aef943e5411784f3ca6dc199 Mon Sep 17 00:00:00 2001 From: ArmEagle Date: Wed, 6 Apr 2011 17:03:59 +0200 Subject: [PATCH 015/100] Changed version to 0.9AE --- plugin.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugin.yml b/plugin.yml index 0d9e992..52a9873 100644 --- a/plugin.yml +++ b/plugin.yml @@ -1,6 +1,6 @@ name: TradeCraft main: com.mjmr89.TradeCraft.TradeCraft -version: 0.81AE +version: 0.9AE commands: myshops: description: See a printout of any personal shops you have From b06cf375bf3539ce28e21185996be551c61a90c4 Mon Sep 17 00:00:00 2001 From: ArmEagle Date: Thu, 7 Apr 2011 14:49:18 +0200 Subject: [PATCH 016/100] Strict playername check was wrong way around. Fixed permissions check on placing of a sign (now using new style, lines from event). --- com/mjmr89/TradeCraft/TradeCraft.java | 32 ++++++++----------- .../TradeCraft/TradeCraftBlockListener.java | 16 ++++------ .../TradeCraft/TradeCraftInfiniteShop.java | 2 +- .../TradeCraft/TradeCraftPlayerOwnedShop.java | 2 +- 4 files changed, 22 insertions(+), 30 deletions(-) diff --git a/com/mjmr89/TradeCraft/TradeCraft.java b/com/mjmr89/TradeCraft/TradeCraft.java index 163cfd5..e5628ba 100644 --- a/com/mjmr89/TradeCraft/TradeCraft.java +++ b/com/mjmr89/TradeCraft/TradeCraft.java @@ -223,7 +223,7 @@ TradeCraftShop getShopFromSignBlock(Player player, Block block) { return null; } - String itemName = getItemName(sign); + String itemName = getItemName(sign.getLines()); if (itemName == null) { trace(player, "There is no item name on the sign."); @@ -265,7 +265,7 @@ TradeCraftShop getShopFromSignBlock(Player player, Block block) { return null; } - String ownerName = getOwnerName(sign); + String ownerName = getOwnerName(sign.getLine(3)); if (ownerName == null) { trace(player, "There is no owner name on the sign."); @@ -290,17 +290,17 @@ TradeCraftShop getShopFromSignBlock(Player player, Block block) { return new TradeCraftPlayerOwnedShop(this, sign, chest, ownerName); } - String getItemName(Sign sign) { - return getSpecialText(sign, "[", "]"); + String getItemName(String[] signLines) { + return getSpecialText(signLines, "[", "]"); } - String getOwnerName(Sign sign) { - return getSpecialTextOnLine(sign, "-", "-", 3); + String getOwnerName(String signLine) { + return getSpecialTextOnLine(signLine, "-", "-"); } - private String getSpecialText(Sign sign, String prefix, String suffix) { + private String getSpecialText(String[] signLines, String prefix, String suffix) { for (int i = 0; i < 4; i++) { - String text = getSpecialTextOnLine(sign, prefix, suffix, i); + String text = getSpecialTextOnLine(signLines[i], prefix, suffix); if (text != null) { return text; @@ -310,22 +310,18 @@ private String getSpecialText(Sign sign, String prefix, String suffix) { return null; } - private String getSpecialTextOnLine(Sign sign, String prefix, - String suffix, int lineNumber) { - String signText = sign.getLine(lineNumber); - - if (signText == null) { + private String getSpecialTextOnLine(String signLine, String prefix, String suffix) { + if (signLine == null) { return null; } - signText = signText.trim(); + signLine = signLine.trim(); - if (signText.startsWith(prefix) && signText.endsWith(suffix) - && signText.length() > 2) { + if (signLine.startsWith(prefix) && signLine.endsWith(suffix) + && signLine.length() > 2) { - String text = signText.substring(1, signText.length() - 1); + String text = signLine.substring(1, signLine.length() - 1); text = text.trim(); - if (text.equals("")) { return null; } diff --git a/com/mjmr89/TradeCraft/TradeCraftBlockListener.java b/com/mjmr89/TradeCraft/TradeCraftBlockListener.java index 24cb60d..d33a1bc 100644 --- a/com/mjmr89/TradeCraft/TradeCraftBlockListener.java +++ b/com/mjmr89/TradeCraft/TradeCraftBlockListener.java @@ -88,47 +88,43 @@ public void onSignChange(SignChangeEvent e) { Player player = e.getPlayer(); Sign sign = (Sign) e.getBlock().getState(); - - String ownerName = plugin.getOwnerName(sign); + String ownerName = plugin.getOwnerName(e.getLine(3)); if (ownerName == null) { - String itemName = plugin.getItemName(sign); + String itemName = plugin.getItemName(e.getLines()); if (itemName == null) { - return; } if (plugin.permissions.canMakeInfShops(player)){ - return; } plugin.sendMessage(player, "You can't create infinite shops!"); - + e.setCancelled(true); return; } if ( plugin.permissions.canMakePlayerShops(player)){ - return; } if ( this.plugin.properties.getStrictPlayerShopOwnerNameRequired() ) { - if (player.getName().startsWith(ownerName)) { + if (player.getName().equalsIgnoreCase(ownerName)) { plugin.data.setOwnerOfSign(player.getName(), sign); return; } } else { - if (player.getName().equalsIgnoreCase(ownerName)) { + if (player.getName().startsWith(ownerName)) { plugin.data.setOwnerOfSign(player.getName(), sign); return; } } plugin.sendMessage(player, "You can't create signs with other players names on them!"); - + e.setCancelled(true); return; } diff --git a/com/mjmr89/TradeCraft/TradeCraftInfiniteShop.java b/com/mjmr89/TradeCraft/TradeCraftInfiniteShop.java index 77d1224..c96344d 100644 --- a/com/mjmr89/TradeCraft/TradeCraftInfiniteShop.java +++ b/com/mjmr89/TradeCraft/TradeCraftInfiniteShop.java @@ -10,7 +10,7 @@ public class TradeCraftInfiniteShop extends TradeCraftItemShop { public TradeCraftInfiniteShop(TradeCraft plugin, Sign sign, Chest chest) { super(plugin, sign, chest); - String itemName = plugin.getItemName(sign); + String itemName = plugin.getItemName(sign.getLines()); configurationInfo = plugin.configuration.get(itemName); } diff --git a/com/mjmr89/TradeCraft/TradeCraftPlayerOwnedShop.java b/com/mjmr89/TradeCraft/TradeCraftPlayerOwnedShop.java index 78d5664..4529e93 100644 --- a/com/mjmr89/TradeCraft/TradeCraftPlayerOwnedShop.java +++ b/com/mjmr89/TradeCraft/TradeCraftPlayerOwnedShop.java @@ -16,7 +16,7 @@ public TradeCraftPlayerOwnedShop(TradeCraft plugin, Sign sign, Chest chest, Stri // ownerName = plugin.data.getOwnerOfSign(sign); this.ownerName = ownerName; - itemName = plugin.getItemName(sign); + itemName = plugin.getItemName(sign.getLines()); itemType = plugin.configuration.get(itemName).id; buyRate = plugin.getExchangeRate(sign, 1); sellRate = plugin.getExchangeRate(sign, 2); From 0e5791eb86a2f488d8fc1b498f4aba66bc7628d5 Mon Sep 17 00:00:00 2001 From: ArmEagle Date: Thu, 7 Apr 2011 14:51:50 +0200 Subject: [PATCH 017/100] Should not be placing blocks anymore when right-clicking on a sign from distance (server reverts, client will let you type text on the sign, but the sign won't be there). --- com/mjmr89/TradeCraft/TradeCraftPlayerListener.java | 1 + 1 file changed, 1 insertion(+) diff --git a/com/mjmr89/TradeCraft/TradeCraftPlayerListener.java b/com/mjmr89/TradeCraft/TradeCraftPlayerListener.java index 025ced8..12fa265 100644 --- a/com/mjmr89/TradeCraft/TradeCraftPlayerListener.java +++ b/com/mjmr89/TradeCraft/TradeCraftPlayerListener.java @@ -31,6 +31,7 @@ public void onPlayerInteract(PlayerInteractEvent e) { } shop.handleRightClick(player); + e.setCancelled(true); } } From b5040b114266aea6f701124f9d87084002134376 Mon Sep 17 00:00:00 2001 From: ArmEagle Date: Thu, 7 Apr 2011 14:52:15 +0200 Subject: [PATCH 018/100] changed version number to 0.91AE --- plugin.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugin.yml b/plugin.yml index 52a9873..5905854 100644 --- a/plugin.yml +++ b/plugin.yml @@ -1,6 +1,6 @@ name: TradeCraft main: com.mjmr89.TradeCraft.TradeCraft -version: 0.9AE +version: 0.91AE commands: myshops: description: See a printout of any personal shops you have From a25dc3453ceb5121fef7e3208e9db184a37d5bfd Mon Sep 17 00:00:00 2001 From: ArmEagle Date: Fri, 8 Apr 2011 17:29:41 +0200 Subject: [PATCH 019/100] Made CraftBukkit 670 compatible (removed one check for AIR with a null check). Version updated to 0.92AE --- com/mjmr89/TradeCraft/TradeCraftChest.java | 4 +--- plugin.yml | 2 +- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/com/mjmr89/TradeCraft/TradeCraftChest.java b/com/mjmr89/TradeCraft/TradeCraftChest.java index 365c4c2..bccc81d 100644 --- a/com/mjmr89/TradeCraft/TradeCraftChest.java +++ b/com/mjmr89/TradeCraft/TradeCraftChest.java @@ -2,8 +2,6 @@ import java.util.ArrayList; import java.util.List; - -import org.bukkit.Material; import org.bukkit.block.Chest; import org.bukkit.inventory.Inventory; import org.bukkit.inventory.ItemStack; @@ -21,7 +19,7 @@ public TradeCraftChest(Chest c) { for (ItemStack item : chest.getContents()) { - if(!item.getType().equals(Material.AIR)){ + if(item != null){ if(id != 0 && id != item.getTypeId()){ diffFlag = true; return; diff --git a/plugin.yml b/plugin.yml index 5905854..0dddbf5 100644 --- a/plugin.yml +++ b/plugin.yml @@ -1,6 +1,6 @@ name: TradeCraft main: com.mjmr89.TradeCraft.TradeCraft -version: 0.91AE +version: 0.92AE commands: myshops: description: See a printout of any personal shops you have From eb33f18754e5b210880caf95f669852630c7464b Mon Sep 17 00:00:00 2001 From: ArmEagle Date: Sun, 10 Apr 2011 13:06:25 +0200 Subject: [PATCH 020/100] adding .classpath --- .classpath | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.classpath b/.classpath index 7821aa2..6a08ce8 100644 --- a/.classpath +++ b/.classpath @@ -3,6 +3,6 @@ - + From b8cab395e1640aa1af1f2c989aa7c069b39bf03e Mon Sep 17 00:00:00 2001 From: ArmEagle Date: Tue, 19 Apr 2011 22:55:27 +0200 Subject: [PATCH 021/100] Support for colored wool, etc; data value of items. Initial version --- TODO.txt | 20 ++++++++++ com/mjmr89/TradeCraft/TradeCraft.java | 27 +++++++------ com/mjmr89/TradeCraft/TradeCraftChest.java | 29 ++++++++------ .../TradeCraftConfigurationFile.java | 26 ++++++++++++- .../TradeCraftConfigurationInfo.java | 2 +- com/mjmr89/TradeCraft/TradeCraftDataFile.java | 22 ++++++++--- com/mjmr89/TradeCraft/TradeCraftDataInfo.java | 2 +- .../TradeCraft/TradeCraftInfiniteShop.java | 4 +- com/mjmr89/TradeCraft/TradeCraftItem.java | 39 +++++++++++++++++++ com/mjmr89/TradeCraft/TradeCraftItemShop.java | 27 +++++++------ .../TradeCraft/TradeCraftPlayerListener.java | 2 +- .../TradeCraft/TradeCraftPlayerOwnedShop.java | 6 +-- .../TradeCraft/TradeCraftPropertiesFile.java | 11 ++++-- .../TradeCraft/TradeCraftRepairShop.java | 4 +- 14 files changed, 162 insertions(+), 59 deletions(-) create mode 100644 TODO.txt create mode 100644 com/mjmr89/TradeCraft/TradeCraftItem.java diff --git a/TODO.txt b/TODO.txt new file mode 100644 index 0000000..1a4bb7e --- /dev/null +++ b/TODO.txt @@ -0,0 +1,20 @@ +- repairshop has it's own handling code / enable repair shop + +gsand: +- Would it be possible to make it so players can buy 6 items for 2 gold, but not 3 for 1 gold. + +Digi said: + - Also, could you add a feature to place signs ON chests (like Lockette) ? + - The wool, dye, log, etc sub-items (just re-mentioning, I understand it's gonna be possible in #564+) + - Protection of chest contents while you're using it... or something, so other people don't just sit by and wait for you to buy and steal your stuff. + - The sign labels would be better understood if they're: "Buy 5 for 1" and "Sell 5 for 1" and you should suppot texts like "Buy 5 for 1g" or "Buy5for1" etc. + - The sign shouldn't require you to type your name since you can only make shops for yourself, it should overwrite the last line with your nickname. + - Additional information about current amount of items and currency available when right clicking as client. + + + +MOVE TradeCraft.getShopFromSignBlock(Player player, Block block) { + Which creates the actual shops, to the sign change code, that way we have a handle on the player who + placed the chest and can put down the full name. Which already seems to happen partially, but not for real?! + + - [launch player] triggers infinite shop check \ No newline at end of file diff --git a/com/mjmr89/TradeCraft/TradeCraft.java b/com/mjmr89/TradeCraft/TradeCraft.java index e5628ba..04941c4 100644 --- a/com/mjmr89/TradeCraft/TradeCraft.java +++ b/com/mjmr89/TradeCraft/TradeCraft.java @@ -16,6 +16,7 @@ import org.bukkit.entity.Player; import org.bukkit.event.Event.Priority; import org.bukkit.event.Event.Type; +import org.bukkit.inventory.ItemStack; import org.bukkit.plugin.PluginDescriptionFile; import org.bukkit.plugin.PluginManager; import org.bukkit.plugin.java.JavaPlugin; @@ -33,7 +34,7 @@ public class TradeCraft extends JavaPlugin { final Server server = this.getServer(); // Objects used by the plugin. - static Material currency; + static TradeCraftItem currency; TradeCraftPropertiesFile properties; TradeCraftConfigurationFile configuration; TradeCraftDataFile data; @@ -61,7 +62,7 @@ public void onEnable() { configuration.load(); data.load(); - currency = Material.getMaterial(properties.getCurrencyTypeId()); + currency = properties.getCurrencyType(); permissions.setupPermissions(); if ( !TradeCraft.hasRegisteredEventListeners ) { @@ -91,18 +92,20 @@ public boolean onCommand(CommandSender sender, Command command, if ( !this.permissions.canSetCurrency(p) ) { p.sendMessage("You do not have the permission to set the currency"); } else { - Material testCurrency; + TradeCraftItem testCurrency = null; try { int cid = Integer.parseInt(args[0]); - testCurrency = Material.getMaterial(cid); + testCurrency = new TradeCraftItem(cid); + + // TODO optionally parse data bit } catch (NumberFormatException nfe) { - testCurrency = Material.getMaterial(args[0]); + } if ( testCurrency == null ) { p.sendMessage(args[0] +" is not a valid value for a currency."); } else { currency = testCurrency; - this.properties.setCurrencyTypeId(currency.getId()); + this.properties.setCurrencyType(currency); p.sendMessage("Currency is set to " + TradeCraft.getCurrencyName()); } } @@ -132,7 +135,7 @@ void displayShops(Player p) { p.sendMessage("Your shops:"); for (TradeCraftDataInfo info : list) { - p.sendMessage("Item: " + Material.getMaterial(info.itemType) + p.sendMessage("Item: " + info.itemType + " Amount: " + info.itemAmount + " " + TradeCraft.getCurrencyName() +": " + info.currencyAmount); @@ -240,8 +243,7 @@ TradeCraftShop getShopFromSignBlock(Player player, Block block) { return null; } - Chest chest = (Chest) player.getWorld().getBlockAt(x, y - 1, z) - .getState(); + Chest chest = (Chest) player.getWorld().getBlockAt(x, y - 1, z).getState(); if (itemName.toLowerCase().equals("repair")) { if (!properties.getRepairShopsEnabled()) { @@ -260,8 +262,8 @@ TradeCraftShop getShopFromSignBlock(Player player, Block block) { if (!configuration.isConfigured(itemName)) { trace(player, - "The item name %s is not configured in the config file.", - itemName); + "The item name %s is not configured in the config file.", + itemName); return null; } @@ -362,7 +364,8 @@ public void onLoad() { * @return a string representing the currency. */ public static String getCurrencyName() { - String baseName = TradeCraft.currency.name(); + ItemStack stack = new ItemStack(TradeCraft.currency.id, 1, TradeCraft.currency.data); // weird that there's no Material.getMaterial(id, short/byte) + String baseName = stack.getType().name(); String[] words = baseName.split("_"); String name = ""; for ( int word_ind = 0; word_ind < words.length; word_ind++ ) { diff --git a/com/mjmr89/TradeCraft/TradeCraftChest.java b/com/mjmr89/TradeCraft/TradeCraftChest.java index bccc81d..0d69fae 100644 --- a/com/mjmr89/TradeCraft/TradeCraftChest.java +++ b/com/mjmr89/TradeCraft/TradeCraftChest.java @@ -5,10 +5,11 @@ import org.bukkit.block.Chest; import org.bukkit.inventory.Inventory; import org.bukkit.inventory.ItemStack; +import org.bukkit.material.MaterialData; class TradeCraftChest { private Inventory chest; - public int id = 0; + public TradeCraftItem type = new TradeCraftItem(0); public int total = 0; public boolean diffFlag = false; @@ -20,11 +21,17 @@ public TradeCraftChest(Chest c) { for (ItemStack item : chest.getContents()) { if(item != null){ - if(id != 0 && id != item.getTypeId()){ + if(type.id != 0 && (type.id != item.getTypeId() || type.data != item.getData().getData()) ){ diffFlag = true; return; } - id = item.getTypeId(); + type.id = item.getTypeId(); + MaterialData itemData = item.getData(); + if ( itemData != null ) { + type.data = itemData.getData(); + } else { + type.data = (short)0; + } total += item.getAmount(); } @@ -42,27 +49,27 @@ public void clear() { chest.clear(); } - public void add(int id, int amount) { - int maxStackSize = TradeCraft.getMaxStackSize(id); + public void add(TradeCraftItem type, int amount) { + int maxStackSize = TradeCraft.getMaxStackSize(type.id); int blocks = amount / maxStackSize; for (int i = 0; i < blocks; i++) { - chest.addItem(new ItemStack(id, maxStackSize)); + chest.addItem(new ItemStack(type.id, maxStackSize, type.data)); } int remainder = amount % maxStackSize; if (remainder > 0) { - chest.addItem(new ItemStack(id, remainder)); + chest.addItem(new ItemStack(type.id, remainder, type.data)); } } public void update() { } - public void populateChest(int id, int amount) { + public void populateChest(TradeCraftItem type, int amount) { clear(); - add(id, amount); + add(type, amount); update(); } @@ -70,7 +77,7 @@ public int getAmountOfCurrencyInChest() { int amount = 0; for (ItemStack item : ((Inventory)chest).getContents()) { if (item != null) { - if (item.getType() == TradeCraft.currency) { + if (item.getTypeId() == TradeCraft.currency.id && item.getData().getData() == TradeCraft.currency.data) { amount += item.getAmount(); } } @@ -82,7 +89,7 @@ public List getNonCurrencyItems() { List items = new ArrayList(); for (ItemStack item : chest.getContents()) { if (item != null) { - if (item.getType() != TradeCraft.currency) { + if (item.getTypeId() != TradeCraft.currency.id || item.getData().getData() != TradeCraft.currency.data) { items.add(item); } } diff --git a/com/mjmr89/TradeCraft/TradeCraftConfigurationFile.java b/com/mjmr89/TradeCraft/TradeCraftConfigurationFile.java index a096e06..50a49ba 100644 --- a/com/mjmr89/TradeCraft/TradeCraftConfigurationFile.java +++ b/com/mjmr89/TradeCraft/TradeCraftConfigurationFile.java @@ -19,9 +19,11 @@ class TradeCraftConfigurationFile { private static final Pattern commentPattern = Pattern.compile("^\\s*#.*$"); private static final Pattern infoPattern = Pattern.compile( "^\\s*([^,]+)\\s*," + // name - "\\s*(\\d+)\\s*" + // id + "\\s*(\\d+(?:;\\d+)?)\\s*" + // id[;data] (optional ;data) "(?:,\\s*(\\d+)\\s*:\\s*(\\d+))?\\s*" + // buyAmount:buyValue "(?:,\\s*(\\d+)\\s*:\\s*(\\d+))?\\s*$"); // sellAmount:sellValue + private static final Pattern infoPatternIdSplitData = Pattern.compile( + "^(\\d+)(?:;(\\d+))?$"); private final TradeCraft plugin; private final Map infos = new HashMap(); @@ -102,7 +104,27 @@ void load() { TradeCraftConfigurationInfo info = new TradeCraftConfigurationInfo(); info.name = infoMatcher.group(1); - info.id = Integer.parseInt(infoMatcher.group(2)); + + // try to split ID and Data, separated by a semicolon mark + Matcher IdSplitData = infoPatternIdSplitData.matcher(infoMatcher.group(2)); + plugin.log.info("TC configinfo: |"+ infoMatcher.group(2) +"|"); + + if (!IdSplitData.matches()) { + plugin.log.info( + "Failed to parse line number " + lineNumber + + " in " + filePath + File.separator + fileName + + ": " + line); + continue; + } + + int id = Integer.parseInt(IdSplitData.group(1)); + if ( IdSplitData.group(2) != null ) { + short data = Short.parseShort(IdSplitData.group(2)); + info.type = new TradeCraftItem(id, data); + plugin.log.info(" Conf - matching item data bit: "+ data); + } else { + info.type = new TradeCraftItem(id); + } if (infoMatcher.group(3) != null) { info.sellAmount = info.buyAmount = Integer.parseInt(infoMatcher.group(3)); diff --git a/com/mjmr89/TradeCraft/TradeCraftConfigurationInfo.java b/com/mjmr89/TradeCraft/TradeCraftConfigurationInfo.java index a2c8d58..23ed1f2 100644 --- a/com/mjmr89/TradeCraft/TradeCraftConfigurationInfo.java +++ b/com/mjmr89/TradeCraft/TradeCraftConfigurationInfo.java @@ -2,7 +2,7 @@ class TradeCraftConfigurationInfo { public String name; - public int id; + public TradeCraftItem type; public int buyAmount; public int buyValue; public int sellAmount; diff --git a/com/mjmr89/TradeCraft/TradeCraftDataFile.java b/com/mjmr89/TradeCraft/TradeCraftDataFile.java index be2fa11..f9eeac5 100644 --- a/com/mjmr89/TradeCraft/TradeCraftDataFile.java +++ b/com/mjmr89/TradeCraft/TradeCraftDataFile.java @@ -25,9 +25,11 @@ class TradeCraftDataFile { private static final Pattern infoPattern2 = Pattern.compile( "^\\s*([^,]+)\\s*," + // ownerName "\\s*(-?\\d+)\\s*,\\s*(-?\\d+)\\s*,\\s*(-?\\d+)\\s*," + // x,y,z - "\\s*(\\d+)\\s*," + // itemType + "\\s*(\\d+(?:!\\d+)?)\\s*," + // itemType[!data] "\\s*(\\d+)\\s*," + // itemAmount "\\s*(\\d+)\\s*$"); // currencyAmount + private static final Pattern infoPatternIdSplitData = Pattern.compile( + "^(\\d+)(?:l(\\d+))?$"); // optional data value, separated by a semicolon private final TradeCraft plugin; private final Map data = new HashMap(); @@ -69,19 +71,27 @@ public void load() { int x = Integer.parseInt(infoMatcher2.group(2)); int y = Integer.parseInt(infoMatcher2.group(3)); int z = Integer.parseInt(infoMatcher2.group(4)); - int itemType = Integer.parseInt(infoMatcher2.group(5)); +// int itemType = Integer.parseInt(IdSplitData.group(1)); int itemAmount = Integer.parseInt(infoMatcher2.group(6)); int currencyAmount = Integer.parseInt(infoMatcher2.group(7)); - + + TradeCraftDataInfo info = new TradeCraftDataInfo(); String key = getKey(x, y, z); - TradeCraftDataInfo info = new TradeCraftDataInfo(); info.ownerName = ownerName; - info.itemType = itemType; info.itemAmount = itemAmount; info.currencyAmount = currencyAmount; data.put(key, info); + + Matcher IdSplitData = infoPatternIdSplitData.matcher(infoMatcher2.group(5)); + int itemId = Integer.parseInt(infoMatcher2.group(5)); + if ( IdSplitData.group(2) != null ) { + info.itemType = new TradeCraftItem(itemId, Short.parseShort(IdSplitData.group(2))); + } else { + info.itemType = new TradeCraftItem(itemId); + } + } else { Matcher infoMatcher1 = infoPattern1.matcher(line); @@ -202,7 +212,7 @@ public int getCurrencyAmount(Sign sign) { return 0; } - public void depositItems(String ownerName, Sign sign, int itemType, int itemAmount) { + public void depositItems(String ownerName, Sign sign, TradeCraftItem itemType, int itemAmount) { String key = getKeyFromSign(sign); if (data.containsKey(key)) { TradeCraftDataInfo info = data.get(key); diff --git a/com/mjmr89/TradeCraft/TradeCraftDataInfo.java b/com/mjmr89/TradeCraft/TradeCraftDataInfo.java index 1d2aba8..3f05110 100644 --- a/com/mjmr89/TradeCraft/TradeCraftDataInfo.java +++ b/com/mjmr89/TradeCraft/TradeCraftDataInfo.java @@ -2,7 +2,7 @@ class TradeCraftDataInfo { public String ownerName; - public int itemType; + public TradeCraftItem itemType; public int itemAmount; public int currencyAmount; } \ No newline at end of file diff --git a/com/mjmr89/TradeCraft/TradeCraftInfiniteShop.java b/com/mjmr89/TradeCraft/TradeCraftInfiniteShop.java index c96344d..29aa9bc 100644 --- a/com/mjmr89/TradeCraft/TradeCraftInfiniteShop.java +++ b/com/mjmr89/TradeCraft/TradeCraftInfiniteShop.java @@ -26,8 +26,8 @@ public boolean isOwnedByPlayer(Player player) { return false; } - public int getItemType() { - return configurationInfo.id; + public TradeCraftItem getItemType() { + return configurationInfo.type; } public String getItemName() { diff --git a/com/mjmr89/TradeCraft/TradeCraftItem.java b/com/mjmr89/TradeCraft/TradeCraftItem.java new file mode 100644 index 0000000..c0a6559 --- /dev/null +++ b/com/mjmr89/TradeCraft/TradeCraftItem.java @@ -0,0 +1,39 @@ +package com.mjmr89.TradeCraft; + +/** + * + * @author ArmEagle + * Store item type(ID) and optionally the data bit + */ +public class TradeCraftItem implements Comparable { + public int id; + public short data; + + TradeCraftItem(int id) { + this(id, (short)0); + } + TradeCraftItem(int id, short data) { + this.id = id; + this.data = data; + } + + public String toString() { + return "TradeCraftItem("+ this.id +";"+ this.data +")"; + } + + public int compareTo(TradeCraftItem compare) { + if ( this.id < compare.id ) { + return -1; + } else if ( this.id > compare.id ) { + return 1; + } else { + if ( this.data < compare.data ) { + return -1; + } else if ( this.data > compare.data ) { + return 1; + } else { + return 0; + } + } + } +} diff --git a/com/mjmr89/TradeCraft/TradeCraftItemShop.java b/com/mjmr89/TradeCraft/TradeCraftItemShop.java index 1159870..832beff 100644 --- a/com/mjmr89/TradeCraft/TradeCraftItemShop.java +++ b/com/mjmr89/TradeCraft/TradeCraftItemShop.java @@ -27,7 +27,7 @@ private void handleOwnerClick(Player player) { if (getChestItemCount() == 0) { int currencyAmount = withdrawCurrency(); if (currencyAmount > 0) { - populateChest(TradeCraft.currency.getId(), currencyAmount); + populateChest(TradeCraft.currency, currencyAmount); plugin.sendMessage(player, "Withdrew %1$d "+ TradeCraft.getCurrencyName() +".", currencyAmount); } else { int itemAmount = withdrawItems(); @@ -38,10 +38,10 @@ private void handleOwnerClick(Player player) { plugin.sendMessage(player, "There is nothing to withdraw."); } } - } else if (getChestItemType() == TradeCraft.currency.getId()) { + } else if (getChestItemType() == TradeCraft.currency) { depositCurrency(getChestItemCount()); plugin.sendMessage(player, "Deposited %1$d "+ TradeCraft.getCurrencyName() +".", getChestItemCount()); - populateChest(0, 0); + populateChest(new TradeCraftItem(0), 0); int itemAmount = withdrawItems(); if (itemAmount > 0) { populateChest(getItemType(), itemAmount); @@ -49,7 +49,7 @@ private void handleOwnerClick(Player player) { } } else if (getChestItemType() == getItemType()) { depositItems(getChestItemCount()); - populateChest(0, 0); + populateChest(new TradeCraftItem(0), 0); plugin.sendMessage(player, "Deposited %1$d %2$s.", getChestItemCount(), getItemName()); } else { plugin.sendMessage(player, "You can't deposit that here!"); @@ -91,14 +91,13 @@ private void handlePatronClick(Player player) { } - - if (getChestItemType() == TradeCraft.currency.getId()) { + if ( getChestItemType().compareTo(TradeCraft.currency) == 0 ) { if (!playerCanBuy) { plugin.sendMessage(player, "You are not allowed to buy from shops!"); } else { playerWantsToBuy(player); } - } else if (getChestItemType() == getItemType()) { + } else if ( getChestItemType().compareTo(getItemType()) == 0 ) { if (!playerCanSell) { plugin.sendMessage(player, "You are not allowed to sell to shops!"); } else { @@ -139,7 +138,7 @@ private void playerWantsToBuy(Player player) { updateItemAndCurrencyAmounts(-amountPlayerWantsToBuy, requiredCurrencyForThatAmount); chest.clear(); - chest.add(TradeCraft.currency.getId(), currencyPlayerWantsToSpend - requiredCurrencyForThatAmount); + chest.add(TradeCraft.currency, currencyPlayerWantsToSpend - requiredCurrencyForThatAmount); chest.add(getItemType(), amountPlayerWantsToBuy); chest.update(); @@ -180,7 +179,7 @@ private void playerWantsToSell(Player player) { chest.clear(); chest.add(getItemType(), amountPlayerWantsToSell - amountThatCanBeSold); - chest.add(TradeCraft.currency.getId(), currencyPlayerShouldReceive); + chest.add(TradeCraft.currency, currencyPlayerShouldReceive); chest.update(); plugin.sendMessage(player, @@ -190,8 +189,8 @@ private void playerWantsToSell(Player player) { currencyPlayerShouldReceive); } - public int getChestItemType() { - return chest.id; + public TradeCraftItem getChestItemType() { + return chest.type; } public int getChestItemCount() { @@ -202,13 +201,13 @@ public boolean chestContentsAreOK() { return chest.containsOnlyOneItemType(); } - public void populateChest(int id, int amount) { - chest.populateChest(id, amount); + public void populateChest(TradeCraftItem type, int amount) { + chest.populateChest(type, amount); } public abstract boolean isOwnedByPlayer(Player player); - public abstract int getItemType(); + public abstract TradeCraftItem getItemType(); public abstract String getItemName(); diff --git a/com/mjmr89/TradeCraft/TradeCraftPlayerListener.java b/com/mjmr89/TradeCraft/TradeCraftPlayerListener.java index 12fa265..0c44e54 100644 --- a/com/mjmr89/TradeCraft/TradeCraftPlayerListener.java +++ b/com/mjmr89/TradeCraft/TradeCraftPlayerListener.java @@ -25,7 +25,7 @@ public void onPlayerInteract(PlayerInteractEvent e) { Player player = e.getPlayer(); TradeCraftShop shop = plugin.getShopFromSignBlock(player, blockClicked); - + if (shop == null) { return; } diff --git a/com/mjmr89/TradeCraft/TradeCraftPlayerOwnedShop.java b/com/mjmr89/TradeCraft/TradeCraftPlayerOwnedShop.java index 4529e93..e945e94 100644 --- a/com/mjmr89/TradeCraft/TradeCraftPlayerOwnedShop.java +++ b/com/mjmr89/TradeCraft/TradeCraftPlayerOwnedShop.java @@ -7,7 +7,7 @@ public class TradeCraftPlayerOwnedShop extends TradeCraftItemShop { private final String ownerName; private final String itemName; - private final int itemType; + private final TradeCraftItem itemType; private final TradeCraftExchangeRate buyRate; private final TradeCraftExchangeRate sellRate; @@ -17,7 +17,7 @@ public TradeCraftPlayerOwnedShop(TradeCraft plugin, Sign sign, Chest chest, Stri // ownerName = plugin.data.getOwnerOfSign(sign); this.ownerName = ownerName; itemName = plugin.getItemName(sign.getLines()); - itemType = plugin.configuration.get(itemName).id; + itemType = plugin.configuration.get(itemName).type; buyRate = plugin.getExchangeRate(sign, 1); sellRate = plugin.getExchangeRate(sign, 2); @@ -48,7 +48,7 @@ public boolean isOwnedByPlayer(Player player) { } } - public int getItemType() { + public TradeCraftItem getItemType() { return itemType; } diff --git a/com/mjmr89/TradeCraft/TradeCraftPropertiesFile.java b/com/mjmr89/TradeCraft/TradeCraftPropertiesFile.java index 35ea5d1..8759ddd 100644 --- a/com/mjmr89/TradeCraft/TradeCraftPropertiesFile.java +++ b/com/mjmr89/TradeCraft/TradeCraftPropertiesFile.java @@ -59,11 +59,14 @@ public TradeCraftPropertiesFile(TradeCraft plugin) { properties.load(); } - public int getCurrencyTypeId(){ - return properties.getInt("currency-id",266); + public TradeCraftItem getCurrencyType(){ + int id = properties.getInt("currency-id",266); + short data = (short)properties.getInt("currency-data",0); + return new TradeCraftItem(id, data); } - public void setCurrencyTypeId(int id) { - properties.setProperty("currency-id", id); + public void setCurrencyType(TradeCraftItem item) { + properties.setProperty("currency-id", item.id); + properties.setProperty("currency-data", item.data); properties.save(); } diff --git a/com/mjmr89/TradeCraft/TradeCraftRepairShop.java b/com/mjmr89/TradeCraft/TradeCraftRepairShop.java index 691c166..f16fd94 100644 --- a/com/mjmr89/TradeCraft/TradeCraftRepairShop.java +++ b/com/mjmr89/TradeCraft/TradeCraftRepairShop.java @@ -41,10 +41,10 @@ public void handleRightClick(Player player) { chest.clear(); for (ItemStack item : items) { - chest.add(item.getType().getId(), 1); + chest.add(new TradeCraftItem(item.getTypeId(), item.getData().getData()), 1); } - chest.add(TradeCraft.currency.getId(), (currencyAmount - actualCost)); + chest.add(TradeCraft.currency, (currencyAmount - actualCost)); chest.update(); From 13d4cf96a79e422e8bed0af6123081ee3b0cbf57 Mon Sep 17 00:00:00 2001 From: ArmEagle Date: Tue, 19 Apr 2011 22:58:54 +0200 Subject: [PATCH 022/100] changed namespace in preparation of own separate build --- {com/mjmr89 => nl/armeagle}/TradeCraft/TradeCraft.java | 2 +- .../armeagle}/TradeCraft/TradeCraftBlockListener.java | 2 +- {com/mjmr89 => nl/armeagle}/TradeCraft/TradeCraftChest.java | 2 +- .../armeagle}/TradeCraft/TradeCraftConfigurationFile.java | 2 +- .../armeagle}/TradeCraft/TradeCraftConfigurationInfo.java | 2 +- {com/mjmr89 => nl/armeagle}/TradeCraft/TradeCraftDataFile.java | 2 +- {com/mjmr89 => nl/armeagle}/TradeCraft/TradeCraftDataInfo.java | 2 +- .../armeagle}/TradeCraft/TradeCraftExchangeRate.java | 2 +- .../armeagle}/TradeCraft/TradeCraftInfiniteShop.java | 2 +- {com/mjmr89 => nl/armeagle}/TradeCraft/TradeCraftItem.java | 2 +- {com/mjmr89 => nl/armeagle}/TradeCraft/TradeCraftItemShop.java | 2 +- .../armeagle}/TradeCraft/TradeCraftPermissions.java | 2 +- .../armeagle}/TradeCraft/TradeCraftPlayerListener.java | 2 +- .../armeagle}/TradeCraft/TradeCraftPlayerOwnedShop.java | 2 +- .../armeagle}/TradeCraft/TradeCraftPropertiesFile.java | 2 +- .../mjmr89 => nl/armeagle}/TradeCraft/TradeCraftRepairShop.java | 2 +- {com/mjmr89 => nl/armeagle}/TradeCraft/TradeCraftShop.java | 2 +- 17 files changed, 17 insertions(+), 17 deletions(-) rename {com/mjmr89 => nl/armeagle}/TradeCraft/TradeCraft.java (99%) rename {com/mjmr89 => nl/armeagle}/TradeCraft/TradeCraftBlockListener.java (99%) rename {com/mjmr89 => nl/armeagle}/TradeCraft/TradeCraftChest.java (98%) rename {com/mjmr89 => nl/armeagle}/TradeCraft/TradeCraftConfigurationFile.java (99%) rename {com/mjmr89 => nl/armeagle}/TradeCraft/TradeCraftConfigurationInfo.java (86%) rename {com/mjmr89 => nl/armeagle}/TradeCraft/TradeCraftDataFile.java (99%) rename {com/mjmr89 => nl/armeagle}/TradeCraft/TradeCraftDataInfo.java (82%) rename {com/mjmr89 => nl/armeagle}/TradeCraft/TradeCraftExchangeRate.java (72%) rename {com/mjmr89 => nl/armeagle}/TradeCraft/TradeCraftInfiniteShop.java (98%) rename {com/mjmr89 => nl/armeagle}/TradeCraft/TradeCraftItem.java (91%) rename {com/mjmr89 => nl/armeagle}/TradeCraft/TradeCraftItemShop.java (99%) rename {com/mjmr89 => nl/armeagle}/TradeCraft/TradeCraftPermissions.java (98%) rename {com/mjmr89 => nl/armeagle}/TradeCraft/TradeCraftPlayerListener.java (98%) rename {com/mjmr89 => nl/armeagle}/TradeCraft/TradeCraftPlayerOwnedShop.java (98%) rename {com/mjmr89 => nl/armeagle}/TradeCraft/TradeCraftPropertiesFile.java (98%) rename {com/mjmr89 => nl/armeagle}/TradeCraft/TradeCraftRepairShop.java (98%) rename {com/mjmr89 => nl/armeagle}/TradeCraft/TradeCraftShop.java (95%) diff --git a/com/mjmr89/TradeCraft/TradeCraft.java b/nl/armeagle/TradeCraft/TradeCraft.java similarity index 99% rename from com/mjmr89/TradeCraft/TradeCraft.java rename to nl/armeagle/TradeCraft/TradeCraft.java index 04941c4..d763947 100644 --- a/com/mjmr89/TradeCraft/TradeCraft.java +++ b/nl/armeagle/TradeCraft/TradeCraft.java @@ -1,4 +1,4 @@ -package com.mjmr89.TradeCraft; +package nl.armeagle.TradeCraft; import java.util.ArrayList; import java.util.logging.Logger; diff --git a/com/mjmr89/TradeCraft/TradeCraftBlockListener.java b/nl/armeagle/TradeCraft/TradeCraftBlockListener.java similarity index 99% rename from com/mjmr89/TradeCraft/TradeCraftBlockListener.java rename to nl/armeagle/TradeCraft/TradeCraftBlockListener.java index d33a1bc..1ed877b 100644 --- a/com/mjmr89/TradeCraft/TradeCraftBlockListener.java +++ b/nl/armeagle/TradeCraft/TradeCraftBlockListener.java @@ -1,4 +1,4 @@ -package com.mjmr89.TradeCraft; +package nl.armeagle.TradeCraft; import java.util.ArrayList; diff --git a/com/mjmr89/TradeCraft/TradeCraftChest.java b/nl/armeagle/TradeCraft/TradeCraftChest.java similarity index 98% rename from com/mjmr89/TradeCraft/TradeCraftChest.java rename to nl/armeagle/TradeCraft/TradeCraftChest.java index 0d69fae..e92781b 100644 --- a/com/mjmr89/TradeCraft/TradeCraftChest.java +++ b/nl/armeagle/TradeCraft/TradeCraftChest.java @@ -1,4 +1,4 @@ -package com.mjmr89.TradeCraft; +package nl.armeagle.TradeCraft; import java.util.ArrayList; import java.util.List; diff --git a/com/mjmr89/TradeCraft/TradeCraftConfigurationFile.java b/nl/armeagle/TradeCraft/TradeCraftConfigurationFile.java similarity index 99% rename from com/mjmr89/TradeCraft/TradeCraftConfigurationFile.java rename to nl/armeagle/TradeCraft/TradeCraftConfigurationFile.java index 50a49ba..c9c926d 100644 --- a/com/mjmr89/TradeCraft/TradeCraftConfigurationFile.java +++ b/nl/armeagle/TradeCraft/TradeCraftConfigurationFile.java @@ -1,4 +1,4 @@ -package com.mjmr89.TradeCraft; +package nl.armeagle.TradeCraft; import java.io.BufferedReader; import java.io.File; diff --git a/com/mjmr89/TradeCraft/TradeCraftConfigurationInfo.java b/nl/armeagle/TradeCraft/TradeCraftConfigurationInfo.java similarity index 86% rename from com/mjmr89/TradeCraft/TradeCraftConfigurationInfo.java rename to nl/armeagle/TradeCraft/TradeCraftConfigurationInfo.java index 23ed1f2..9b345ad 100644 --- a/com/mjmr89/TradeCraft/TradeCraftConfigurationInfo.java +++ b/nl/armeagle/TradeCraft/TradeCraftConfigurationInfo.java @@ -1,4 +1,4 @@ -package com.mjmr89.TradeCraft; +package nl.armeagle.TradeCraft; class TradeCraftConfigurationInfo { public String name; diff --git a/com/mjmr89/TradeCraft/TradeCraftDataFile.java b/nl/armeagle/TradeCraft/TradeCraftDataFile.java similarity index 99% rename from com/mjmr89/TradeCraft/TradeCraftDataFile.java rename to nl/armeagle/TradeCraft/TradeCraftDataFile.java index f9eeac5..302fba7 100644 --- a/com/mjmr89/TradeCraft/TradeCraftDataFile.java +++ b/nl/armeagle/TradeCraft/TradeCraftDataFile.java @@ -1,4 +1,4 @@ -package com.mjmr89.TradeCraft; +package nl.armeagle.TradeCraft; import java.io.BufferedReader; import java.io.BufferedWriter; diff --git a/com/mjmr89/TradeCraft/TradeCraftDataInfo.java b/nl/armeagle/TradeCraft/TradeCraftDataInfo.java similarity index 82% rename from com/mjmr89/TradeCraft/TradeCraftDataInfo.java rename to nl/armeagle/TradeCraft/TradeCraftDataInfo.java index 3f05110..d710ca4 100644 --- a/com/mjmr89/TradeCraft/TradeCraftDataInfo.java +++ b/nl/armeagle/TradeCraft/TradeCraftDataInfo.java @@ -1,4 +1,4 @@ -package com.mjmr89.TradeCraft; +package nl.armeagle.TradeCraft; class TradeCraftDataInfo { public String ownerName; diff --git a/com/mjmr89/TradeCraft/TradeCraftExchangeRate.java b/nl/armeagle/TradeCraft/TradeCraftExchangeRate.java similarity index 72% rename from com/mjmr89/TradeCraft/TradeCraftExchangeRate.java rename to nl/armeagle/TradeCraft/TradeCraftExchangeRate.java index d019bef..5944342 100644 --- a/com/mjmr89/TradeCraft/TradeCraftExchangeRate.java +++ b/nl/armeagle/TradeCraft/TradeCraftExchangeRate.java @@ -1,4 +1,4 @@ -package com.mjmr89.TradeCraft; +package nl.armeagle.TradeCraft; public class TradeCraftExchangeRate { public int amount; diff --git a/com/mjmr89/TradeCraft/TradeCraftInfiniteShop.java b/nl/armeagle/TradeCraft/TradeCraftInfiniteShop.java similarity index 98% rename from com/mjmr89/TradeCraft/TradeCraftInfiniteShop.java rename to nl/armeagle/TradeCraft/TradeCraftInfiniteShop.java index 29aa9bc..b44e396 100644 --- a/com/mjmr89/TradeCraft/TradeCraftInfiniteShop.java +++ b/nl/armeagle/TradeCraft/TradeCraftInfiniteShop.java @@ -1,4 +1,4 @@ -package com.mjmr89.TradeCraft; +package nl.armeagle.TradeCraft; import org.bukkit.block.Chest; import org.bukkit.block.Sign; diff --git a/com/mjmr89/TradeCraft/TradeCraftItem.java b/nl/armeagle/TradeCraft/TradeCraftItem.java similarity index 91% rename from com/mjmr89/TradeCraft/TradeCraftItem.java rename to nl/armeagle/TradeCraft/TradeCraftItem.java index c0a6559..8717fb3 100644 --- a/com/mjmr89/TradeCraft/TradeCraftItem.java +++ b/nl/armeagle/TradeCraft/TradeCraftItem.java @@ -1,4 +1,4 @@ -package com.mjmr89.TradeCraft; +package nl.armeagle.TradeCraft; /** * diff --git a/com/mjmr89/TradeCraft/TradeCraftItemShop.java b/nl/armeagle/TradeCraft/TradeCraftItemShop.java similarity index 99% rename from com/mjmr89/TradeCraft/TradeCraftItemShop.java rename to nl/armeagle/TradeCraft/TradeCraftItemShop.java index 832beff..efd028f 100644 --- a/com/mjmr89/TradeCraft/TradeCraftItemShop.java +++ b/nl/armeagle/TradeCraft/TradeCraftItemShop.java @@ -1,4 +1,4 @@ -package com.mjmr89.TradeCraft; +package nl.armeagle.TradeCraft; import org.bukkit.block.Chest; import org.bukkit.block.Sign; diff --git a/com/mjmr89/TradeCraft/TradeCraftPermissions.java b/nl/armeagle/TradeCraft/TradeCraftPermissions.java similarity index 98% rename from com/mjmr89/TradeCraft/TradeCraftPermissions.java rename to nl/armeagle/TradeCraft/TradeCraftPermissions.java index 94a9e96..b5eb6bd 100644 --- a/com/mjmr89/TradeCraft/TradeCraftPermissions.java +++ b/nl/armeagle/TradeCraft/TradeCraftPermissions.java @@ -1,4 +1,4 @@ -package com.mjmr89.TradeCraft; +package nl.armeagle.TradeCraft; import org.bukkit.entity.Player; import org.bukkit.plugin.Plugin; diff --git a/com/mjmr89/TradeCraft/TradeCraftPlayerListener.java b/nl/armeagle/TradeCraft/TradeCraftPlayerListener.java similarity index 98% rename from com/mjmr89/TradeCraft/TradeCraftPlayerListener.java rename to nl/armeagle/TradeCraft/TradeCraftPlayerListener.java index 0c44e54..6ace4a5 100644 --- a/com/mjmr89/TradeCraft/TradeCraftPlayerListener.java +++ b/nl/armeagle/TradeCraft/TradeCraftPlayerListener.java @@ -1,4 +1,4 @@ -package com.mjmr89.TradeCraft; +package nl.armeagle.TradeCraft; import org.bukkit.block.Block; import org.bukkit.entity.Player; diff --git a/com/mjmr89/TradeCraft/TradeCraftPlayerOwnedShop.java b/nl/armeagle/TradeCraft/TradeCraftPlayerOwnedShop.java similarity index 98% rename from com/mjmr89/TradeCraft/TradeCraftPlayerOwnedShop.java rename to nl/armeagle/TradeCraft/TradeCraftPlayerOwnedShop.java index e945e94..6d0e016 100644 --- a/com/mjmr89/TradeCraft/TradeCraftPlayerOwnedShop.java +++ b/nl/armeagle/TradeCraft/TradeCraftPlayerOwnedShop.java @@ -1,4 +1,4 @@ -package com.mjmr89.TradeCraft; +package nl.armeagle.TradeCraft; import org.bukkit.block.Chest; import org.bukkit.block.Sign; diff --git a/com/mjmr89/TradeCraft/TradeCraftPropertiesFile.java b/nl/armeagle/TradeCraft/TradeCraftPropertiesFile.java similarity index 98% rename from com/mjmr89/TradeCraft/TradeCraftPropertiesFile.java rename to nl/armeagle/TradeCraft/TradeCraftPropertiesFile.java index 8759ddd..c9979ee 100644 --- a/com/mjmr89/TradeCraft/TradeCraftPropertiesFile.java +++ b/nl/armeagle/TradeCraft/TradeCraftPropertiesFile.java @@ -1,4 +1,4 @@ -package com.mjmr89.TradeCraft; +package nl.armeagle.TradeCraft; import java.io.File; import java.io.FileOutputStream; diff --git a/com/mjmr89/TradeCraft/TradeCraftRepairShop.java b/nl/armeagle/TradeCraft/TradeCraftRepairShop.java similarity index 98% rename from com/mjmr89/TradeCraft/TradeCraftRepairShop.java rename to nl/armeagle/TradeCraft/TradeCraftRepairShop.java index f16fd94..79b0f72 100644 --- a/com/mjmr89/TradeCraft/TradeCraftRepairShop.java +++ b/nl/armeagle/TradeCraft/TradeCraftRepairShop.java @@ -1,4 +1,4 @@ -package com.mjmr89.TradeCraft; +package nl.armeagle.TradeCraft; import java.util.List; diff --git a/com/mjmr89/TradeCraft/TradeCraftShop.java b/nl/armeagle/TradeCraft/TradeCraftShop.java similarity index 95% rename from com/mjmr89/TradeCraft/TradeCraftShop.java rename to nl/armeagle/TradeCraft/TradeCraftShop.java index f94b13e..b344b8a 100644 --- a/com/mjmr89/TradeCraft/TradeCraftShop.java +++ b/nl/armeagle/TradeCraft/TradeCraftShop.java @@ -1,4 +1,4 @@ -package com.mjmr89.TradeCraft; +package nl.armeagle.TradeCraft; import org.bukkit.block.Chest; import org.bukkit.block.Sign; From 4e97dbfcb666c27f10a1737e781f8b2c7f93c6b1 Mon Sep 17 00:00:00 2001 From: ArmEagle Date: Tue, 19 Apr 2011 23:03:13 +0200 Subject: [PATCH 023/100] Be as little restrictive on sign placement as possible. Only block signs with item name line "[name]" if "name" is found in the configuration file. --- nl/armeagle/TradeCraft/TradeCraftBlockListener.java | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/nl/armeagle/TradeCraft/TradeCraftBlockListener.java b/nl/armeagle/TradeCraft/TradeCraftBlockListener.java index 1ed877b..5c872d1 100644 --- a/nl/armeagle/TradeCraft/TradeCraftBlockListener.java +++ b/nl/armeagle/TradeCraft/TradeCraftBlockListener.java @@ -97,10 +97,16 @@ public void onSignChange(SignChangeEvent e) { return; } - if (plugin.permissions.canMakeInfShops(player)){ + // Check whether this is an existing item. Try to prevent as little normal sign placement as possible. + // Only treat signs with "[name]" as item line where "name" is found in the configuration (.txt) file. + if ( plugin.configuration.get(itemName) == null ) { return; } + if (plugin.permissions.canMakeInfShops(player)){ + return; + } + plugin.sendMessage(player, "You can't create infinite shops!"); e.setCancelled(true); From 35fe1800433a2369ad181b988888cd978ec3698a Mon Sep 17 00:00:00 2001 From: ArmEagle Date: Wed, 20 Apr 2011 01:07:44 +0200 Subject: [PATCH 024/100] try and remove the com folder --- com/mjmr89/TradeCraft/removefolder | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 com/mjmr89/TradeCraft/removefolder diff --git a/com/mjmr89/TradeCraft/removefolder b/com/mjmr89/TradeCraft/removefolder new file mode 100644 index 0000000..e69de29 From 837f0896a15dadfc201d415b4aec8696a2c8eb53 Mon Sep 17 00:00:00 2001 From: ArmEagle Date: Wed, 20 Apr 2011 01:09:05 +0200 Subject: [PATCH 025/100] try and remove com folder 2 --- com/mjmr89/TradeCraft/removefolder | 0 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 com/mjmr89/TradeCraft/removefolder diff --git a/com/mjmr89/TradeCraft/removefolder b/com/mjmr89/TradeCraft/removefolder deleted file mode 100644 index e69de29..0000000 From 6de40c23314e4495b6c8714dd896f5ddc328f6ea Mon Sep 17 00:00:00 2001 From: ArmEagle Date: Wed, 20 Apr 2011 16:41:44 +0200 Subject: [PATCH 026/100] Fixing issues with setting currency, after allowing you to use the data value. --- TODO.txt | 6 +- nl/armeagle/TradeCraft/TradeCraft.java | 80 ++++++++++++------- nl/armeagle/TradeCraft/TradeCraftChest.java | 21 +++-- .../TradeCraftConfigurationFile.java | 19 +++-- .../TradeCraft/TradeCraftDataFile.java | 4 +- .../TradeCraft/TradeCraftItemShop.java | 20 ++--- .../TradeCraft/TradeCraftPermissions.java | 2 +- .../TradeCraft/TradeCraftPlayerListener.java | 1 - .../TradeCraft/TradeCraftRepairShop.java | 15 ++-- plugin.yml | 2 +- 10 files changed, 101 insertions(+), 69 deletions(-) diff --git a/TODO.txt b/TODO.txt index 1a4bb7e..d8be24a 100644 --- a/TODO.txt +++ b/TODO.txt @@ -17,4 +17,8 @@ MOVE TradeCraft.getShopFromSignBlock(Player player, Block block) { Which creates the actual shops, to the sign change code, that way we have a handle on the player who placed the chest and can put down the full name. Which already seems to happen partially, but not for real?! - - [launch player] triggers infinite shop check \ No newline at end of file + - limit currency and configured items to valid data values + - chest.getContents --> item.getData() always seems to return null, workaround with using getDurability + + + [launch player] triggers infinite shop check + \ No newline at end of file diff --git a/nl/armeagle/TradeCraft/TradeCraft.java b/nl/armeagle/TradeCraft/TradeCraft.java index d763947..1a24e94 100644 --- a/nl/armeagle/TradeCraft/TradeCraft.java +++ b/nl/armeagle/TradeCraft/TradeCraft.java @@ -17,6 +17,7 @@ import org.bukkit.event.Event.Priority; import org.bukkit.event.Event.Type; import org.bukkit.inventory.ItemStack; +import org.bukkit.material.MaterialData; import org.bukkit.plugin.PluginDescriptionFile; import org.bukkit.plugin.PluginManager; import org.bukkit.plugin.java.JavaPlugin; @@ -28,6 +29,7 @@ public class TradeCraft extends JavaPlugin { static final String pluginName = "TradeCraft"; private static final Pattern ratePattern = Pattern.compile("\\s*(\\d+)\\s*:\\s*(\\d+)\\s*"); + public static final Pattern itemPatternIdSplitData = Pattern.compile("^(\\d+)(?:;(\\d+))?$"); // Stuff used to interact with the server. final Logger log = Logger.getLogger("Minecraft"); @@ -74,8 +76,7 @@ public void onEnable() { } PluginDescriptionFile pdfFile = this.getDescription(); - System.out.println(pdfFile.getName() + " version " - + pdfFile.getVersion() + " is enabled!"); + this.log.info(pdfFile.getName() + " version " + pdfFile.getVersion() + " is enabled!"); } @@ -93,25 +94,28 @@ public boolean onCommand(CommandSender sender, Command command, p.sendMessage("You do not have the permission to set the currency"); } else { TradeCraftItem testCurrency = null; - try { - int cid = Integer.parseInt(args[0]); - testCurrency = new TradeCraftItem(cid); - - // TODO optionally parse data bit - } catch (NumberFormatException nfe) { - - } - if ( testCurrency == null ) { - p.sendMessage(args[0] +" is not a valid value for a currency."); + // try to split ID and Data, separated by a semicolon mark + Matcher IdSplitData = TradeCraft.itemPatternIdSplitData.matcher(args[0]); + + if ( !IdSplitData.matches() ) { + p.sendMessage(args[0] +" is not a valid value for a currency, use 'id[;data]'"); + return false; + } + + int cid = Integer.parseInt(IdSplitData.group(1)); + if ( IdSplitData.group(2) != null ) { + testCurrency = new TradeCraftItem(cid, Short.parseShort(IdSplitData.group(2))); } else { - currency = testCurrency; - this.properties.setCurrencyType(currency); - p.sendMessage("Currency is set to " + TradeCraft.getCurrencyName()); + testCurrency = new TradeCraftItem(cid); } + + currency = testCurrency; + this.properties.setCurrencyType(currency); + p.sendMessage("Currency is set to " + this.getCurrencyName()); } } else if (name.equalsIgnoreCase("displaycurrency") && args.length == 0) { - p.sendMessage("Currency is: " + TradeCraft.getCurrencyName()); + p.sendMessage("Currency is: " + this.getCurrencyName()); } else if (name.equalsIgnoreCase("canplayer") && args.length == 1) { permissions.debug(args[0]); } else if (name.equalsIgnoreCase("myshops")) { @@ -137,7 +141,7 @@ void displayShops(Player p) { p.sendMessage("Item: " + info.itemType + " Amount: " + info.itemAmount + " " - + TradeCraft.getCurrencyName() +": " + info.currencyAmount); + + this.getCurrencyName() +": " + info.currencyAmount); } @@ -363,19 +367,35 @@ public void onLoad() { * * @return a string representing the currency. */ - public static String getCurrencyName() { - ItemStack stack = new ItemStack(TradeCraft.currency.id, 1, TradeCraft.currency.data); // weird that there's no Material.getMaterial(id, short/byte) - String baseName = stack.getType().name(); - String[] words = baseName.split("_"); - String name = ""; - for ( int word_ind = 0; word_ind < words.length; word_ind++ ) { - String word = words[word_ind]; - if ( word_ind > 0 ) { - name = name.concat(" "); - } - name = name.concat(word.substring(0, 1).toUpperCase()).concat(word.substring(1).toLowerCase()); - } - return name; + public String getCurrencyName() { + // Try to get the name from the configuration file first + TradeCraftConfigurationInfo configInfo = this.configuration.get(TradeCraft.currency); + if ( configInfo != null ) { + return configInfo.name; + } else { + + ItemStack currencyStack = new ItemStack(TradeCraft.currency.id, 1, TradeCraft.currency.data); // weird that there's no Material.getMaterial(id, short/byte) + MaterialData currencyData = currencyStack.getType().getNewData((byte)TradeCraft.currency.data); + String currencyString; + if ( currencyData == null ) { + currencyString = currencyStack.getType().name(); + } else { + currencyString = currencyData.toString(); + } + + //String baseName = stack.getType().name(); + String[] words = currencyString.replace("null ", "").split("\\(")[0].split("[ _]{1}"); + String name = ""; + for ( int word_ind = 0; word_ind < words.length; word_ind++ ) { + String word = words[word_ind]; + if ( word_ind > 0 ) { + name = name.concat(" "); + } + name = name.concat(word.substring(0, 1).toUpperCase()).concat(word.substring(1).toLowerCase()); + } + System.out.println("bukkit: "+name); + return name; + } } } \ No newline at end of file diff --git a/nl/armeagle/TradeCraft/TradeCraftChest.java b/nl/armeagle/TradeCraft/TradeCraftChest.java index e92781b..fd519b8 100644 --- a/nl/armeagle/TradeCraft/TradeCraftChest.java +++ b/nl/armeagle/TradeCraft/TradeCraftChest.java @@ -5,7 +5,6 @@ import org.bukkit.block.Chest; import org.bukkit.inventory.Inventory; import org.bukkit.inventory.ItemStack; -import org.bukkit.material.MaterialData; class TradeCraftChest { private Inventory chest; @@ -18,20 +17,16 @@ class TradeCraftChest { public TradeCraftChest(Chest c) { chest = c.getInventory(); - for (ItemStack item : chest.getContents()) { if(item != null){ - if(type.id != 0 && (type.id != item.getTypeId() || type.data != item.getData().getData()) ){ + // TODO | DEBUG item.getData() always seems to return null + short itemData = item.getDurability(); //(item.getData() == null ? (short)0 : item.getData().getData()); + if(type.id != 0 && (type.id != item.getTypeId() || type.data != itemData) ){ diffFlag = true; return; } type.id = item.getTypeId(); - MaterialData itemData = item.getData(); - if ( itemData != null ) { - type.data = itemData.getData(); - } else { - type.data = (short)0; - } + type.data = itemData; total += item.getAmount(); } @@ -77,7 +72,9 @@ public int getAmountOfCurrencyInChest() { int amount = 0; for (ItemStack item : ((Inventory)chest).getContents()) { if (item != null) { - if (item.getTypeId() == TradeCraft.currency.id && item.getData().getData() == TradeCraft.currency.data) { + // TODO | DEBUG item.getData() always seems to return null + short itemData = item.getDurability(); //(item.getData() == null ? (short)0 : item.getData().getData()); + if (item.getTypeId() == TradeCraft.currency.id && itemData == TradeCraft.currency.data) { amount += item.getAmount(); } } @@ -89,7 +86,9 @@ public List getNonCurrencyItems() { List items = new ArrayList(); for (ItemStack item : chest.getContents()) { if (item != null) { - if (item.getTypeId() != TradeCraft.currency.id || item.getData().getData() != TradeCraft.currency.data) { + // TODO | DEBUG item.getData() always seems to return null + short itemData = item.getDurability(); //(item.getData() == null ? (short)0 : item.getData().getData()); + if (item.getTypeId() != TradeCraft.currency.id || itemData != TradeCraft.currency.data) { items.add(item); } } diff --git a/nl/armeagle/TradeCraft/TradeCraftConfigurationFile.java b/nl/armeagle/TradeCraft/TradeCraftConfigurationFile.java index c9c926d..23347b4 100644 --- a/nl/armeagle/TradeCraft/TradeCraftConfigurationFile.java +++ b/nl/armeagle/TradeCraft/TradeCraftConfigurationFile.java @@ -22,11 +22,12 @@ class TradeCraftConfigurationFile { "\\s*(\\d+(?:;\\d+)?)\\s*" + // id[;data] (optional ;data) "(?:,\\s*(\\d+)\\s*:\\s*(\\d+))?\\s*" + // buyAmount:buyValue "(?:,\\s*(\\d+)\\s*:\\s*(\\d+))?\\s*$"); // sellAmount:sellValue - private static final Pattern infoPatternIdSplitData = Pattern.compile( - "^(\\d+)(?:;(\\d+))?$"); + private final TradeCraft plugin; private final Map infos = new HashMap(); + // create an index from an TradeCraftItem (id;data) to the TradeCraftConfigurationInfo entry. This for configured name lookup based on id;data + private final Map TCitemInfoIndex = new HashMap(); TradeCraftConfigurationFile(TradeCraft plugin) { this.plugin = plugin; @@ -106,8 +107,7 @@ void load() { info.name = infoMatcher.group(1); // try to split ID and Data, separated by a semicolon mark - Matcher IdSplitData = infoPatternIdSplitData.matcher(infoMatcher.group(2)); - plugin.log.info("TC configinfo: |"+ infoMatcher.group(2) +"|"); + Matcher IdSplitData = TradeCraft.itemPatternIdSplitData.matcher(infoMatcher.group(2)); if (!IdSplitData.matches()) { plugin.log.info( @@ -121,7 +121,6 @@ void load() { if ( IdSplitData.group(2) != null ) { short data = Short.parseShort(IdSplitData.group(2)); info.type = new TradeCraftItem(id, data); - plugin.log.info(" Conf - matching item data bit: "+ data); } else { info.type = new TradeCraftItem(id); } @@ -137,6 +136,7 @@ void load() { } infos.put(info.name.toUpperCase(), info); + TCitemInfoIndex.put(info.type, info); } plugin.log.info("Loaded " + infos.size() + " configs"); @@ -159,4 +159,13 @@ public boolean isConfigured(String name) { public TradeCraftConfigurationInfo get(String name) { return infos.get(name.toUpperCase()); } + public TradeCraftConfigurationInfo get(int id) { + return this.get(new TradeCraftItem(id)); + } + public TradeCraftConfigurationInfo get(int id, short data) { + return this.get(new TradeCraftItem(id, data)); + } + public TradeCraftConfigurationInfo get(TradeCraftItem item) { + return TCitemInfoIndex.get(item); + } } \ No newline at end of file diff --git a/nl/armeagle/TradeCraft/TradeCraftDataFile.java b/nl/armeagle/TradeCraft/TradeCraftDataFile.java index 302fba7..4a28396 100644 --- a/nl/armeagle/TradeCraft/TradeCraftDataFile.java +++ b/nl/armeagle/TradeCraft/TradeCraftDataFile.java @@ -28,8 +28,6 @@ class TradeCraftDataFile { "\\s*(\\d+(?:!\\d+)?)\\s*," + // itemType[!data] "\\s*(\\d+)\\s*," + // itemAmount "\\s*(\\d+)\\s*$"); // currencyAmount - private static final Pattern infoPatternIdSplitData = Pattern.compile( - "^(\\d+)(?:l(\\d+))?$"); // optional data value, separated by a semicolon private final TradeCraft plugin; private final Map data = new HashMap(); @@ -84,7 +82,7 @@ public void load() { data.put(key, info); - Matcher IdSplitData = infoPatternIdSplitData.matcher(infoMatcher2.group(5)); + Matcher IdSplitData = TradeCraft.itemPatternIdSplitData.matcher(infoMatcher2.group(5)); int itemId = Integer.parseInt(infoMatcher2.group(5)); if ( IdSplitData.group(2) != null ) { info.itemType = new TradeCraftItem(itemId, Short.parseShort(IdSplitData.group(2))); diff --git a/nl/armeagle/TradeCraft/TradeCraftItemShop.java b/nl/armeagle/TradeCraft/TradeCraftItemShop.java index efd028f..b3ca634 100644 --- a/nl/armeagle/TradeCraft/TradeCraftItemShop.java +++ b/nl/armeagle/TradeCraft/TradeCraftItemShop.java @@ -28,7 +28,7 @@ private void handleOwnerClick(Player player) { int currencyAmount = withdrawCurrency(); if (currencyAmount > 0) { populateChest(TradeCraft.currency, currencyAmount); - plugin.sendMessage(player, "Withdrew %1$d "+ TradeCraft.getCurrencyName() +".", currencyAmount); + plugin.sendMessage(player, "Withdrew %1$d "+ plugin.getCurrencyName() +".", currencyAmount); } else { int itemAmount = withdrawItems(); if (itemAmount > 0) { @@ -40,7 +40,7 @@ private void handleOwnerClick(Player player) { } } else if (getChestItemType() == TradeCraft.currency) { depositCurrency(getChestItemCount()); - plugin.sendMessage(player, "Deposited %1$d "+ TradeCraft.getCurrencyName() +".", getChestItemCount()); + plugin.sendMessage(player, "Deposited %1$d "+ plugin.getCurrencyName() +".", getChestItemCount()); populateChest(new TradeCraftItem(0), 0); int itemAmount = withdrawItems(); if (itemAmount > 0) { @@ -72,7 +72,7 @@ private void handlePatronClick(Player player) { if (getChestItemCount() == 0) { if (playerCanBuy && playerCanBuy()) { plugin.sendMessage(player, - "You can buy %1$d %2$s for %3$d "+ TradeCraft.getCurrencyName() +".", + "You can buy %1$d %2$s for %3$d "+ plugin.getCurrencyName() +".", getBuyAmount(), getItemName(), getBuyValue()); @@ -80,7 +80,7 @@ private void handlePatronClick(Player player) { if (playerCanSell && playerCanSell()) { plugin.sendMessage(player, - "You can sell %1$d %2$s for %3$d "+ TradeCraft.getCurrencyName() +".", + "You can sell %1$d %2$s for %3$d "+ plugin.getCurrencyName() +".", getSellAmount(), getItemName(), getSellValue()); @@ -90,7 +90,7 @@ private void handlePatronClick(Player player) { return; } - + plugin.log.info(getChestItemType() +" "+ TradeCraft.currency +" "+ getItemType()); if ( getChestItemType().compareTo(TradeCraft.currency) == 0 ) { if (!playerCanBuy) { plugin.sendMessage(player, "You are not allowed to buy from shops!"); @@ -119,7 +119,7 @@ private void playerWantsToBuy(Player player) { if (amountPlayerWantsToBuy == 0) { plugin.sendMessage(player, - "You need to spend at least %1$d "+ TradeCraft.getCurrencyName() +" to get any %2$s.", + "You need to spend at least %1$d "+ plugin.getCurrencyName() +" to get any %2$s.", getBuyValue(), getItemName()); return; @@ -143,7 +143,7 @@ private void playerWantsToBuy(Player player) { chest.update(); plugin.sendMessage(player, - "You bought %1$d %2$s for %3$d "+ TradeCraft.getCurrencyName() +".", + "You bought %1$d %2$s for %3$d "+ plugin.getCurrencyName() +".", amountPlayerWantsToBuy, getItemName(), requiredCurrencyForThatAmount); @@ -160,7 +160,7 @@ private void playerWantsToSell(Player player) { if (currencyPlayerShouldReceive == 0) { plugin.sendMessage(player, - "You need to sell at least %1$d %2$s to get any "+ TradeCraft.getCurrencyName() +".", + "You need to sell at least %1$d %2$s to get any "+ plugin.getCurrencyName() +".", getSellAmount(), getItemName()); return; @@ -168,7 +168,7 @@ private void playerWantsToSell(Player player) { if (currencyPlayerShouldReceive > getCurrencyInShop()) { plugin.sendMessage(player, - "Cannot sell. This shop only has %1$d "+ TradeCraft.getCurrencyName() +".", + "Cannot sell. This shop only has %1$d "+ plugin.getCurrencyName() +".", getCurrencyInShop()); return; } @@ -183,7 +183,7 @@ private void playerWantsToSell(Player player) { chest.update(); plugin.sendMessage(player, - "You sold %1$d %2$s for %3$d "+ TradeCraft.getCurrencyName() +".", + "You sold %1$d %2$s for %3$d "+ plugin.getCurrencyName() +".", amountThatCanBeSold, getItemName(), currencyPlayerShouldReceive); diff --git a/nl/armeagle/TradeCraft/TradeCraftPermissions.java b/nl/armeagle/TradeCraft/TradeCraftPermissions.java index b5eb6bd..9196ed3 100644 --- a/nl/armeagle/TradeCraft/TradeCraftPermissions.java +++ b/nl/armeagle/TradeCraft/TradeCraftPermissions.java @@ -22,7 +22,7 @@ public void setupPermissions() { if (test != null) { this.permHandler = ((Permissions)test).getHandler(); plugin.permEnabled = true; - System.out.println("[TradeCraft] has recognized Permissions"); + plugin.log.info("[TradeCraft] has recognized Permissions"); } } } diff --git a/nl/armeagle/TradeCraft/TradeCraftPlayerListener.java b/nl/armeagle/TradeCraft/TradeCraftPlayerListener.java index 6ace4a5..7a65396 100644 --- a/nl/armeagle/TradeCraft/TradeCraftPlayerListener.java +++ b/nl/armeagle/TradeCraft/TradeCraftPlayerListener.java @@ -19,7 +19,6 @@ public void onPlayerInteract(PlayerInteractEvent e) { if ( !this.plugin.isEnabled() ) { return; } - if ( e.getAction() == Action.RIGHT_CLICK_BLOCK ) { Block blockClicked = e.getClickedBlock(); Player player = e.getPlayer(); diff --git a/nl/armeagle/TradeCraft/TradeCraftRepairShop.java b/nl/armeagle/TradeCraft/TradeCraftRepairShop.java index 79b0f72..3491645 100644 --- a/nl/armeagle/TradeCraft/TradeCraftRepairShop.java +++ b/nl/armeagle/TradeCraft/TradeCraftRepairShop.java @@ -19,36 +19,39 @@ public void handleRightClick(Player player) { int repairCost = plugin.properties.getRepairCost(); if (currencyAmount == 0 && items.size() == 0) { - plugin.sendMessage(player, "It costs %d "+ TradeCraft.getCurrencyName() +" to repair an item.", repairCost); + plugin.sendMessage(player, "It costs %d "+ plugin.getCurrencyName() +" to repair an item.", repairCost); return; } int actualCost = items.size() * repairCost; if (items.size() == 0) { - plugin.sendMessage(player, "With this much "+ TradeCraft.getCurrencyName() +", you can repair %d items.", currencyAmount / repairCost); + plugin.sendMessage(player, "With this much "+ plugin.getCurrencyName() +", you can repair %d items.", currencyAmount / repairCost); return; } if (currencyAmount < actualCost) { if (currencyAmount > 0) { - plugin.sendMessage(player, "That's not enough "+ TradeCraft.getCurrencyName() +"."); + plugin.sendMessage(player, "That's not enough "+ plugin.getCurrencyName() +"."); } - plugin.sendMessage(player, "You need %d "+ TradeCraft.getCurrencyName() +" to repair all this.", actualCost); + plugin.sendMessage(player, "You need %d "+ plugin.getCurrencyName() +" to repair all this.", actualCost); return; } chest.clear(); for (ItemStack item : items) { - chest.add(new TradeCraftItem(item.getTypeId(), item.getData().getData()), 1); + // TODO | DEBUG item.getData() always seems to return null + short itemData = item.getDurability(); //(item.getData() == null ? (short)0 : item.getData().getData()); + + chest.add(new TradeCraftItem(item.getTypeId(), itemData), 1); } chest.add(TradeCraft.currency, (currencyAmount - actualCost)); chest.update(); - plugin.sendMessage(player, "You repaired %d items for %d "+ TradeCraft.getCurrencyName() +".", items.size(), actualCost); + plugin.sendMessage(player, "You repaired %d items for %d "+ plugin.getCurrencyName() +".", items.size(), actualCost); } public boolean playerCanDestroy(Player player) { diff --git a/plugin.yml b/plugin.yml index 0dddbf5..7a83452 100644 --- a/plugin.yml +++ b/plugin.yml @@ -1,5 +1,5 @@ name: TradeCraft -main: com.mjmr89.TradeCraft.TradeCraft +main: nl.armeagle.TradeCraft.TradeCraft version: 0.92AE commands: myshops: From 0906d4e644678089272c6429918bfe3b688656a2 Mon Sep 17 00:00:00 2001 From: ArmEagle Date: Wed, 20 Apr 2011 16:59:23 +0200 Subject: [PATCH 027/100] Fixed an issue wherem when setting an item for sale at, for example, 6:2, one could sell/buy as if it was 3:1. --- TradeCraft.jar | Bin 34234 -> 61291 bytes .../TradeCraft/TradeCraftItemShop.java | 4 ++-- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/TradeCraft.jar b/TradeCraft.jar index 7f9a454f6f5408f787704bac1ca8c0c9bfbf6355..ed2b50942f54863bc0bdb91615898ed09e664bdb 100644 GIT binary patch literal 61291 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 V1u{Jb0PaJ6W{`jW^#l6X{{bU{!v6pO delta 30235 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 H@1On;wk Date: Wed, 20 Apr 2011 21:02:45 +0200 Subject: [PATCH 028/100] default properties/txt files with extra options (currency-data and a data-value item in txt). Fixed some small things. Changed version to AE-1.0.0-alpha --- TradeCraft.properties | 1 + TradeCraft.txt | 1 + nl/armeagle/TradeCraft/TradeCraft.java | 1 - .../TradeCraft/TradeCraftDataFile.java | 25 ++++++++++------ nl/armeagle/TradeCraft/TradeCraftItem.java | 15 +++++++--- .../TradeCraft/TradeCraftItemShop.java | 30 +++++++++++++------ plugin.yml | 2 +- 7 files changed, 51 insertions(+), 24 deletions(-) diff --git a/TradeCraft.properties b/TradeCraft.properties index c2a7f63..c3d53ff 100644 --- a/TradeCraft.properties +++ b/TradeCraft.properties @@ -4,4 +4,5 @@ repair-shops-enabled: false repair-cost: 10 enable-debug-messages: false currency-id: 266 +currency-data: 0 strict-playershop-owner-name: true \ No newline at end of file diff --git a/TradeCraft.txt b/TradeCraft.txt index 2b13d93..b5cb4df 100644 --- a/TradeCraft.txt +++ b/TradeCraft.txt @@ -14,3 +14,4 @@ Obsidian, 49, 1:32 Diamond, 264, 1:64 Bread, 297, 4:1 Glass, 20 +LGWool, 35;8, 10:1 diff --git a/nl/armeagle/TradeCraft/TradeCraft.java b/nl/armeagle/TradeCraft/TradeCraft.java index 1a24e94..28eb26f 100644 --- a/nl/armeagle/TradeCraft/TradeCraft.java +++ b/nl/armeagle/TradeCraft/TradeCraft.java @@ -393,7 +393,6 @@ public String getCurrencyName() { } name = name.concat(word.substring(0, 1).toUpperCase()).concat(word.substring(1).toLowerCase()); } - System.out.println("bukkit: "+name); return name; } } diff --git a/nl/armeagle/TradeCraft/TradeCraftDataFile.java b/nl/armeagle/TradeCraft/TradeCraftDataFile.java index 4a28396..0a998bc 100644 --- a/nl/armeagle/TradeCraft/TradeCraftDataFile.java +++ b/nl/armeagle/TradeCraft/TradeCraftDataFile.java @@ -25,7 +25,7 @@ class TradeCraftDataFile { private static final Pattern infoPattern2 = Pattern.compile( "^\\s*([^,]+)\\s*," + // ownerName "\\s*(-?\\d+)\\s*,\\s*(-?\\d+)\\s*,\\s*(-?\\d+)\\s*," + // x,y,z - "\\s*(\\d+(?:!\\d+)?)\\s*," + // itemType[!data] + "\\s*(\\d+(?:;\\d+)?)\\s*," + // itemType[!data] "\\s*(\\d+)\\s*," + // itemAmount "\\s*(\\d+)\\s*$"); // currencyAmount @@ -69,7 +69,6 @@ public void load() { int x = Integer.parseInt(infoMatcher2.group(2)); int y = Integer.parseInt(infoMatcher2.group(3)); int z = Integer.parseInt(infoMatcher2.group(4)); -// int itemType = Integer.parseInt(IdSplitData.group(1)); int itemAmount = Integer.parseInt(infoMatcher2.group(6)); int currencyAmount = Integer.parseInt(infoMatcher2.group(7)); @@ -81,15 +80,23 @@ public void load() { info.currencyAmount = currencyAmount; data.put(key, info); - + Matcher IdSplitData = TradeCraft.itemPatternIdSplitData.matcher(infoMatcher2.group(5)); - int itemId = Integer.parseInt(infoMatcher2.group(5)); - if ( IdSplitData.group(2) != null ) { - info.itemType = new TradeCraftItem(itemId, Short.parseShort(IdSplitData.group(2))); + + if ( IdSplitData.matches() ) { + int itemId = Integer.parseInt(IdSplitData.group(1)); + if ( IdSplitData.group(2) != null ) { + info.itemType = new TradeCraftItem(itemId, Short.parseShort(IdSplitData.group(2))); + } else { + info.itemType = new TradeCraftItem(itemId); + } } else { - info.itemType = new TradeCraftItem(itemId); + plugin.log.warning( + "Failed to parse line number " + lineNumber + + " in " + fileName + + ": " + line); + continue; } - } else { Matcher infoMatcher1 = infoPattern1.matcher(line); @@ -133,7 +140,7 @@ public void save() { TradeCraftDataInfo info = data.get(key); writer.write(info.ownerName + "," + key + "," + - info.itemType + "," + + info.itemType.toShortString() + "," + info.itemAmount + "," + info.currencyAmount); writer.newLine(); diff --git a/nl/armeagle/TradeCraft/TradeCraftItem.java b/nl/armeagle/TradeCraft/TradeCraftItem.java index 8717fb3..c83a113 100644 --- a/nl/armeagle/TradeCraft/TradeCraftItem.java +++ b/nl/armeagle/TradeCraft/TradeCraftItem.java @@ -17,10 +17,6 @@ public class TradeCraftItem implements Comparable { this.data = data; } - public String toString() { - return "TradeCraftItem("+ this.id +";"+ this.data +")"; - } - public int compareTo(TradeCraftItem compare) { if ( this.id < compare.id ) { return -1; @@ -36,4 +32,15 @@ public int compareTo(TradeCraftItem compare) { } } } + + public String toString() { + return "TradeCraftItem("+ this.id +";"+ this.data +")"; + } + public String toShortString() { + if ( this.data == 0 ) { + return String.valueOf(this.id); + } else { + return this.id +";"+ this.data; + } + } } diff --git a/nl/armeagle/TradeCraft/TradeCraftItemShop.java b/nl/armeagle/TradeCraft/TradeCraftItemShop.java index 6311fe1..93437c8 100644 --- a/nl/armeagle/TradeCraft/TradeCraftItemShop.java +++ b/nl/armeagle/TradeCraft/TradeCraftItemShop.java @@ -38,7 +38,7 @@ private void handleOwnerClick(Player player) { plugin.sendMessage(player, "There is nothing to withdraw."); } } - } else if (getChestItemType() == TradeCraft.currency) { + } else if ( getChestItemType().compareTo(TradeCraft.currency) == 0 ) { depositCurrency(getChestItemCount()); plugin.sendMessage(player, "Deposited %1$d "+ plugin.getCurrencyName() +".", getChestItemCount()); populateChest(new TradeCraftItem(0), 0); @@ -47,7 +47,7 @@ private void handleOwnerClick(Player player) { populateChest(getItemType(), itemAmount); plugin.sendMessage(player, "Withdrew %1$d %2$s.", itemAmount, getItemName()); } - } else if (getChestItemType() == getItemType()) { + } else if ( getChestItemType().compareTo(getItemType()) == 0 ) { depositItems(getChestItemCount()); populateChest(new TradeCraftItem(0), 0); plugin.sendMessage(player, "Deposited %1$d %2$s.", getChestItemCount(), getItemName()); @@ -85,8 +85,13 @@ private void handlePatronClick(Player player) { getItemName(), getSellValue()); } - - plugin.sendMessage(player, "The chest is empty."); + + if ( this instanceof TradeCraftInfiniteShop ) { + plugin.sendMessage(player, "This is an infinite shop"); + } else { + plugin.sendMessage(player, "The shop contains "+ this.getItemsInShop() +" of "+ this.getItemName() +" and can buy for a total of "+ this.getCurrencyInShop() +" "+ this.plugin.getCurrencyName()); + } + plugin.sendMessage(player, "There are no items in the chest."); return; } @@ -126,11 +131,18 @@ private void playerWantsToBuy(Player player) { } if (amountPlayerWantsToBuy > getItemsInShop()) { - plugin.sendMessage(player, - "Cannot buy. This shop only has %1$d %2$s.", - getItemsInShop(), - getItemName()); - return; + if ( getItemsInShop() == 0 ) { + plugin.sendMessage(player, + "Cannot buy. This shop has no more %2$s left.", + getItemsInShop(), + getItemName()); + } else { + plugin.sendMessage(player, + "Cannot buy. This shop only has %1$d %2$s.", + getItemsInShop(), + getItemName()); + } + return; } int requiredCurrencyForThatAmount = amountPlayerWantsToBuy * getBuyValue() / getBuyAmount(); diff --git a/plugin.yml b/plugin.yml index 7a83452..f066bbb 100644 --- a/plugin.yml +++ b/plugin.yml @@ -1,6 +1,6 @@ name: TradeCraft main: nl.armeagle.TradeCraft.TradeCraft -version: 0.92AE +version: AE-1.0.0-alpha commands: myshops: description: See a printout of any personal shops you have From 243f4ba854460f1af9a76213ada12f5f45634d5a Mon Sep 17 00:00:00 2001 From: ArmEagle Date: Thu, 21 Apr 2011 08:55:46 +0200 Subject: [PATCH 029/100] README updated to match changes of the last versions. TODO also updated. --- README.txt | 32 +++++++++++++++++--------------- TODO.txt | 20 +++++++++++--------- 2 files changed, 28 insertions(+), 24 deletions(-) diff --git a/README.txt b/README.txt index b55d491..48784c0 100644 --- a/README.txt +++ b/README.txt @@ -10,14 +10,13 @@ Installation The plugin is installed like any other Bukkitplugin. Put the TradeCraft.jar file in your plugins folder. -The plugin is configured using a file called TradeCraft.txt that you need to -create in the same folder as your server.properties folder. Details on the -format of the file appear below. +The plugin is configured using a file called TradeCraft.txt that will automatically +be placed in the plugins/TradeCraft/ folder. Details on the format of the file +appear below. -There is also a file called TradeCraft.properties that you can put in the same -folder as your server.properties folder. You can set properties that configure -how the plugin works in this file. Look inside the example file that comes with -the plugin to see the properties you can set. +There is also a file called TradeCraft.properties in the same folder. You can set +properties that configure how the plugin works in this file. Look inside the +example file that comes with the plugin to see the properties you can set. Shops ===== @@ -67,8 +66,8 @@ No state is maintained in infinite shops. Players can buy an infinite amount of items (assuming they have enough gold) from infinite shops. They can also sell an infinite amount of items (earning an infinite amount of gold). -TODO: Allow administrators to disable infinite shops using -TradeCraft.properties. +TODO: Allow administrators to disable infinite shops using a command and +storing that setting in TradeCraft.properties. Player-owned shops ================== @@ -104,11 +103,12 @@ be part of their name. For example, if the player's name was "NumberOneMinecraftFan", they could use "-NumberOne-" as the name on the sign. TODO: Allow administrators to set aliases or nicknames for players for use on -signs. +signs. Or have support for an existing alias plugin. Players are not allowed to create signs that contain other players' names. Likewise, players are not allowed to destroy signs (or the chests underneath -them) containing other players' names. +them, or the block behind then) containing other players' names. Also all items +have to be removed from player shops before they can be destroyed. Using shops =========== @@ -159,11 +159,14 @@ to edit TradeCraft.txt. The file should look like this: # Comments look like this. Sand,12,32:1 Diamond,264,1:64 +LGWool,35;8,10:1 The first value is the name of the item as you want it to appear on your signs and in the messages the players see when they make their trades. -The second value is the block or item ID. You can see these values here: +The second value is the block or item ID, optionally with a data value added +(separated with a semicolon) for colored wool, logs, etc. You can see these +values here: http://www.minecraftwiki.net/wiki/Data_values @@ -195,6 +198,5 @@ configure it like this: Diamond,264,1:64,0:0 If you click on a sign above an empty chest, you'll see a message saying what -the exchange rates for both buying and selling are. This is only necessary for -infinite shops since player-owned shops have to display their exchange rates -on the sign. +the exchange rates for both buying and selling are. On player shops you will +also get information about the amount of items and 'money' still in the shop. diff --git a/TODO.txt b/TODO.txt index d8be24a..2f3f91e 100644 --- a/TODO.txt +++ b/TODO.txt @@ -1,24 +1,26 @@ - repairshop has it's own handling code / enable repair shop -gsand: -- Would it be possible to make it so players can buy 6 items for 2 gold, but not 3 for 1 gold. Digi said: - Also, could you add a feature to place signs ON chests (like Lockette) ? - - The wool, dye, log, etc sub-items (just re-mentioning, I understand it's gonna be possible in #564+) - Protection of chest contents while you're using it... or something, so other people don't just sit by and wait for you to buy and steal your stuff. - The sign labels would be better understood if they're: "Buy 5 for 1" and "Sell 5 for 1" and you should suppot texts like "Buy 5 for 1g" or "Buy5for1" etc. - The sign shouldn't require you to type your name since you can only make shops for yourself, it should overwrite the last line with your nickname. - - Additional information about current amount of items and currency available when right clicking as client. - - -MOVE TradeCraft.getShopFromSignBlock(Player player, Block block) { +?MOVE TradeCraft.getShopFromSignBlock(Player player, Block block) { Which creates the actual shops, to the sign change code, that way we have a handle on the player who placed the chest and can put down the full name. Which already seems to happen partially, but not for real?! - limit currency and configured items to valid data values - - chest.getContents --> item.getData() always seems to return null, workaround with using getDurability + - Allow administrators to disable infinite shops using a command and storing that setting in TradeCraft.properties. - + [launch player] triggers infinite shop check + Coding issue: + - chest.getContents --> item.getData().getData() always seems to return 0, workaround with using getDurability which accesses the same data. + + + Done: + - [launch player] triggers infinite shop check + - (gsand) Would it be possible to make it so players can buy 6 items for 2 gold, but not 3 for 1 gold. + - The wool, dye, log, etc sub-items (just re-mentioning, I understand it's gonna be possible in #564+) + - Additional information about current amount of items and currency available when right clicking as client. \ No newline at end of file From 626dd87de150e78ea7001181afc50c581b47fdeb Mon Sep 17 00:00:00 2001 From: ArmEagle Date: Thu, 21 Apr 2011 09:55:16 +0200 Subject: [PATCH 030/100] Added equals and hashCode functions to TradeCraftItem so it can be used as lookup in a hashmap (used for plugin myShops command). --- nl/armeagle/TradeCraft/TradeCraft.java | 3 +-- nl/armeagle/TradeCraft/TradeCraftDataFile.java | 1 - nl/armeagle/TradeCraft/TradeCraftItem.java | 18 ++++++++++++++++-- 3 files changed, 17 insertions(+), 5 deletions(-) diff --git a/nl/armeagle/TradeCraft/TradeCraft.java b/nl/armeagle/TradeCraft/TradeCraft.java index 28eb26f..0c0b1a0 100644 --- a/nl/armeagle/TradeCraft/TradeCraft.java +++ b/nl/armeagle/TradeCraft/TradeCraft.java @@ -138,8 +138,7 @@ void displayShops(Player p) { } p.sendMessage("Your shops:"); for (TradeCraftDataInfo info : list) { - - p.sendMessage("Item: " + info.itemType + p.sendMessage("Item: " + this.configuration.get(info.itemType).name +"("+ info.itemType.toShortString() +")" + " Amount: " + info.itemAmount + " " + this.getCurrencyName() +": " + info.currencyAmount); diff --git a/nl/armeagle/TradeCraft/TradeCraftDataFile.java b/nl/armeagle/TradeCraft/TradeCraftDataFile.java index 0a998bc..aefa4df 100644 --- a/nl/armeagle/TradeCraft/TradeCraftDataFile.java +++ b/nl/armeagle/TradeCraft/TradeCraftDataFile.java @@ -168,7 +168,6 @@ public void deleteShop(TradeCraftShop shop){ public ArrayList shopsOwned(String name){ ArrayList list = new ArrayList(); - for (String key : data.keySet()) { TradeCraftDataInfo info = data.get(key); if ( this.plugin.properties.getStrictPlayerShopOwnerNameRequired() ) { diff --git a/nl/armeagle/TradeCraft/TradeCraftItem.java b/nl/armeagle/TradeCraft/TradeCraftItem.java index c83a113..978b188 100644 --- a/nl/armeagle/TradeCraft/TradeCraftItem.java +++ b/nl/armeagle/TradeCraft/TradeCraftItem.java @@ -17,7 +17,15 @@ public class TradeCraftItem implements Comparable { this.data = data; } - public int compareTo(TradeCraftItem compare) { + /** + * @param compare + * @throws NullPointerException if compare is null + * @return default < 0 > compare values + */ + @Override public int compareTo(TradeCraftItem compare) { + if ( this == compare ) { + return 0; + } if ( this.id < compare.id ) { return -1; } else if ( this.id > compare.id ) { @@ -32,8 +40,14 @@ public int compareTo(TradeCraftItem compare) { } } } + @Override public boolean equals(Object compare) { + return (compare == null ? false : (compare instanceof TradeCraftItem? this.compareTo((TradeCraftItem)compare) == 0 : false)); + } + @Override public int hashCode() { + return this.id * 32768 + this.data; + } - public String toString() { + @Override public String toString() { return "TradeCraftItem("+ this.id +";"+ this.data +")"; } public String toShortString() { From 1d8b76f2cbabcf22f5b48c5dd28cb5a7de9c0a95 Mon Sep 17 00:00:00 2001 From: ArmEagle Date: Thu, 21 Apr 2011 10:14:59 +0200 Subject: [PATCH 031/100] Added url to forum thread in readme --- README.txt | 2 ++ TODO.txt | 2 ++ 2 files changed, 4 insertions(+) diff --git a/README.txt b/README.txt index 48784c0..b4e4125 100644 --- a/README.txt +++ b/README.txt @@ -4,6 +4,8 @@ TradeCraft This is a plugin for Bukkit that allows you to buy and sell items using gold ingots (bars) as a currency. +See also: http://forums.bukkit.org/threads/econ-tradecraft-ae-gold-based-economy.13797/ + Installation ============ diff --git a/TODO.txt b/TODO.txt index 2f3f91e..92a26cd 100644 --- a/TODO.txt +++ b/TODO.txt @@ -13,6 +13,8 @@ Digi said: - limit currency and configured items to valid data values - Allow administrators to disable infinite shops using a command and storing that setting in TradeCraft.properties. + - Allow setting of currency by name (basic names from Bukkit, or also as defined in config?) again. + Coding issue: - chest.getContents --> item.getData().getData() always seems to return 0, workaround with using getDurability which accesses the same data. From a53933756caa4ea663cad10b91cbf1ddaa49c96a Mon Sep 17 00:00:00 2001 From: ArmEagle Date: Sun, 24 Apr 2011 09:37:11 +0200 Subject: [PATCH 032/100] Removed debug line. Buy/sell rates should now simply be matching any line with two separate numbers. No need to specifically use a colon. This is in preparation for nicer text to be used in these lines. --- nl/armeagle/TradeCraft/TradeCraft.java | 2 +- nl/armeagle/TradeCraft/TradeCraftItemShop.java | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/nl/armeagle/TradeCraft/TradeCraft.java b/nl/armeagle/TradeCraft/TradeCraft.java index 0c0b1a0..69ea99d 100644 --- a/nl/armeagle/TradeCraft/TradeCraft.java +++ b/nl/armeagle/TradeCraft/TradeCraft.java @@ -28,7 +28,7 @@ public class TradeCraft extends JavaPlugin { // The plugin name. static final String pluginName = "TradeCraft"; - private static final Pattern ratePattern = Pattern.compile("\\s*(\\d+)\\s*:\\s*(\\d+)\\s*"); + private static final Pattern ratePattern = Pattern.compile("\\s*(\\d+)\\D+(\\d+)\\s*"); public static final Pattern itemPatternIdSplitData = Pattern.compile("^(\\d+)(?:;(\\d+))?$"); // Stuff used to interact with the server. diff --git a/nl/armeagle/TradeCraft/TradeCraftItemShop.java b/nl/armeagle/TradeCraft/TradeCraftItemShop.java index 93437c8..333e9c3 100644 --- a/nl/armeagle/TradeCraft/TradeCraftItemShop.java +++ b/nl/armeagle/TradeCraft/TradeCraftItemShop.java @@ -95,7 +95,6 @@ private void handlePatronClick(Player player) { return; } - plugin.log.info(getChestItemType() +" "+ TradeCraft.currency +" "+ getItemType()); if ( getChestItemType().compareTo(TradeCraft.currency) == 0 ) { if (!playerCanBuy) { plugin.sendMessage(player, "You are not allowed to buy from shops!"); From 1c7af163ee86a573aa453ced2b8d8e19a2765429 Mon Sep 17 00:00:00 2001 From: ArmEagle Date: Sun, 24 Apr 2011 10:07:13 +0200 Subject: [PATCH 033/100] I thought I had it changed before, but here we go again. If Permissions is not detected, then normal players can create player shops. --- nl/armeagle/TradeCraft/TradeCraftPermissions.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nl/armeagle/TradeCraft/TradeCraftPermissions.java b/nl/armeagle/TradeCraft/TradeCraftPermissions.java index 9196ed3..1bcbdd7 100644 --- a/nl/armeagle/TradeCraft/TradeCraftPermissions.java +++ b/nl/armeagle/TradeCraft/TradeCraftPermissions.java @@ -52,7 +52,7 @@ public boolean canMakePlayerShops(Player p){ if(plugin.permEnabled == true){ return permHandler.has(p, "TradeCraft.canMakePlayerShops"); }else - return p.isOp(); + return true; } public boolean canDestroyShops(Player p){ From 6aa101ae250cfe9b16cfa397a4881d24c7e9897d Mon Sep 17 00:00:00 2001 From: ArmEagle Date: Sun, 24 Apr 2011 15:55:21 +0200 Subject: [PATCH 034/100] Player name is automatically added (overwritten) on a sign of a player owned shop. If the name is longer than 13 characters it will be cut off. But it will stay stored correctly just fine. Also changed some information messages that show when a customer right-clicks on a sign. --- TODO.txt | 10 ++-- TradeCraft.properties | 3 +- nl/armeagle/TradeCraft/TradeCraft.java | 23 ++++----- .../TradeCraft/TradeCraftBlockListener.java | 49 +++++++++++-------- .../TradeCraft/TradeCraftDataFile.java | 19 +++++++ .../TradeCraft/TradeCraftExchangeRate.java | 21 ++++++++ .../TradeCraft/TradeCraftItemShop.java | 46 ++++++++++++----- .../TradeCraft/TradeCraftPlayerOwnedShop.java | 12 ++--- 8 files changed, 119 insertions(+), 64 deletions(-) diff --git a/TODO.txt b/TODO.txt index 92a26cd..e2c7a23 100644 --- a/TODO.txt +++ b/TODO.txt @@ -5,19 +5,15 @@ Digi said: - Also, could you add a feature to place signs ON chests (like Lockette) ? - Protection of chest contents while you're using it... or something, so other people don't just sit by and wait for you to buy and steal your stuff. - The sign labels would be better understood if they're: "Buy 5 for 1" and "Sell 5 for 1" and you should suppot texts like "Buy 5 for 1g" or "Buy5for1" etc. - - The sign shouldn't require you to type your name since you can only make shops for yourself, it should overwrite the last line with your nickname. - -?MOVE TradeCraft.getShopFromSignBlock(Player player, Block block) { - Which creates the actual shops, to the sign change code, that way we have a handle on the player who - placed the chest and can put down the full name. Which already seems to happen partially, but not for real?! - limit currency and configured items to valid data values - Allow administrators to disable infinite shops using a command and storing that setting in TradeCraft.properties. - Allow setting of currency by name (basic names from Bukkit, or also as defined in config?) again. - + - warn when config files use old format? Coding issue: - chest.getContents --> item.getData().getData() always seems to return 0, workaround with using getDurability which accesses the same data. + - items with 'damage' bits too high will act as the basic item, but cannot be traded or stacked as such. Be careful not to create these. Done: @@ -25,4 +21,4 @@ Digi said: - (gsand) Would it be possible to make it so players can buy 6 items for 2 gold, but not 3 for 1 gold. - The wool, dye, log, etc sub-items (just re-mentioning, I understand it's gonna be possible in #564+) - Additional information about current amount of items and currency available when right clicking as client. - \ No newline at end of file + - You don't have to write your name on the sign of a player shop anymore. Also, too long names will be stored just fine, but will show abbreviated on the sign. diff --git a/TradeCraft.properties b/TradeCraft.properties index c3d53ff..35b431e 100644 --- a/TradeCraft.properties +++ b/TradeCraft.properties @@ -4,5 +4,4 @@ repair-shops-enabled: false repair-cost: 10 enable-debug-messages: false currency-id: 266 -currency-data: 0 -strict-playershop-owner-name: true \ No newline at end of file +currency-data: 0 \ No newline at end of file diff --git a/nl/armeagle/TradeCraft/TradeCraft.java b/nl/armeagle/TradeCraft/TradeCraft.java index 69ea99d..0b76381 100644 --- a/nl/armeagle/TradeCraft/TradeCraft.java +++ b/nl/armeagle/TradeCraft/TradeCraft.java @@ -28,7 +28,6 @@ public class TradeCraft extends JavaPlugin { // The plugin name. static final String pluginName = "TradeCraft"; - private static final Pattern ratePattern = Pattern.compile("\\s*(\\d+)\\D+(\\d+)\\s*"); public static final Pattern itemPatternIdSplitData = Pattern.compile("^(\\d+)(?:;(\\d+))?$"); // Stuff used to interact with the server. @@ -270,7 +269,9 @@ TradeCraftShop getShopFromSignBlock(Player player, Block block) { return null; } - String ownerName = getOwnerName(sign.getLine(3)); + // TODO change to use chest getOwner + //String ownerName = getOwnerName(sign.getLine(3)); + String ownerName = data.getOwnerOfSign(sign); if (ownerName == null) { trace(player, "There is no owner name on the sign."); @@ -337,20 +338,14 @@ private String getSpecialTextOnLine(String signLine, String prefix, String suffi return null; } - TradeCraftExchangeRate getExchangeRate(Sign sign, int lineNumber) { - TradeCraftExchangeRate rate = new TradeCraftExchangeRate(); - - String signText = sign.getLine(lineNumber); - - Matcher matcher = ratePattern.matcher(signText); - - if (matcher.find()) { - rate.amount = Integer.parseInt(matcher.group(1)); - rate.value = Integer.parseInt(matcher.group(2)); + TradeCraftExchangeRate getExchangeRate(String[] signLines, int lineNumber) { + if ( lineNumber < 0 || lineNumber >= signLines.length ) { + return null; } - - return rate; + return getExchangeRate(signLines[lineNumber]); } + TradeCraftExchangeRate getExchangeRate(String signLine) { + return new TradeCraftExchangeRate(signLine); } static int getMaxStackSize(int itemType) { return Material.getMaterial(itemType).getMaxStackSize(); diff --git a/nl/armeagle/TradeCraft/TradeCraftBlockListener.java b/nl/armeagle/TradeCraft/TradeCraftBlockListener.java index 5c872d1..35cafe8 100644 --- a/nl/armeagle/TradeCraft/TradeCraftBlockListener.java +++ b/nl/armeagle/TradeCraft/TradeCraftBlockListener.java @@ -88,35 +88,47 @@ public void onSignChange(SignChangeEvent e) { Player player = e.getPlayer(); Sign sign = (Sign) e.getBlock().getState(); - String ownerName = plugin.getOwnerName(e.getLine(3)); + String ownerName = player.getName(); - if (ownerName == null) { - String itemName = plugin.getItemName(e.getLines()); + String itemName = plugin.getItemName(e.getLines()); - if (itemName == null) { - return; - } - - // Check whether this is an existing item. Try to prevent as little normal sign placement as possible. - // Only treat signs with "[name]" as item line where "name" is found in the configuration (.txt) file. - if ( plugin.configuration.get(itemName) == null ) { - return; - } + if (itemName == null) { + plugin.trace(player, "sign change, no item name, ignore"); + return; + } + // Check whether this is an existing item. Try to prevent as little normal sign placement as possible. + // Only treat signs with "[name]" as item line where "name" is found in the configuration (.txt) file. + TradeCraftConfigurationInfo itemInfo = plugin.configuration.get(itemName); + if ( itemInfo == null ) { + plugin.trace(player, "sign change, %s is not a valid item name, ignore this sign", itemName); + return; + } + TradeCraftExchangeRate buyRate = new TradeCraftExchangeRate(e.getLine(1)); + TradeCraftExchangeRate sellRate = new TradeCraftExchangeRate(e.getLine(2)); + // no buy rate means this is an infinite shop + if ( !buyRate.isValid() && !sellRate.isValid() ) { if (plugin.permissions.canMakeInfShops(player)){ + plugin.trace(player, "sign change, infinite chest of %s", itemName); return; } plugin.sendMessage(player, "You can't create infinite shops!"); e.setCancelled(true); - return; } - - if ( plugin.permissions.canMakePlayerShops(player)){ - return; + // there is a buy rate, so this is a player owned shop + if ( !plugin.permissions.canMakePlayerShops(player)){ + plugin.sendMessage(player, "You do not have the permission to create a player shop!"); + e.setCancelled(true); + return; } + plugin.trace(player, "Setting owner of sign to: %s", ownerName); + // set the player name on the last line + e.setLine(3, "-"+ ownerName.substring(0, Math.min(ownerName.length(), 15)) +"-"); + plugin.data.createNewSign(ownerName, itemInfo, sign); + /* if ( this.plugin.properties.getStrictPlayerShopOwnerNameRequired() ) { if (player.getName().equalsIgnoreCase(ownerName)) { plugin.data.setOwnerOfSign(player.getName(), sign); @@ -128,11 +140,8 @@ public void onSignChange(SignChangeEvent e) { return; } } - - plugin.sendMessage(player, "You can't create signs with other players names on them!"); - e.setCancelled(true); + */ return; } - } diff --git a/nl/armeagle/TradeCraft/TradeCraftDataFile.java b/nl/armeagle/TradeCraft/TradeCraftDataFile.java index aefa4df..2477e80 100644 --- a/nl/armeagle/TradeCraft/TradeCraftDataFile.java +++ b/nl/armeagle/TradeCraft/TradeCraftDataFile.java @@ -294,4 +294,23 @@ private String getKeyFromSign(Sign sign) { private String getKey(int x, int y, int z) { return x + "," + y + "," + z; } + + public void createNewSign(String ownerName, TradeCraftConfigurationInfo itemInfo, Sign sign) { + String key = getKeyFromSign(sign); + if (data.containsKey(key)) { + TradeCraftDataInfo info = data.get(key); + info.ownerName = ownerName; + info.currencyAmount = 0; + info.itemAmount = 0; + info.itemType = itemInfo.type; + } else { + TradeCraftDataInfo info = new TradeCraftDataInfo(); + info.ownerName = ownerName; + info.currencyAmount = 0; + info.itemAmount = 0; + info.itemType = itemInfo.type; + data.put(key, info); + } + save(); + } } \ No newline at end of file diff --git a/nl/armeagle/TradeCraft/TradeCraftExchangeRate.java b/nl/armeagle/TradeCraft/TradeCraftExchangeRate.java index 5944342..a63d6e7 100644 --- a/nl/armeagle/TradeCraft/TradeCraftExchangeRate.java +++ b/nl/armeagle/TradeCraft/TradeCraftExchangeRate.java @@ -1,6 +1,27 @@ package nl.armeagle.TradeCraft; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + public class TradeCraftExchangeRate { public int amount; public int value; + + private static final Pattern ratePattern = Pattern.compile("(\\d+)\\D+(\\d+)\\s*"); + + TradeCraftExchangeRate(String signLine) { + Matcher matcher = ratePattern.matcher(signLine); + + if (matcher.find()) { + this.amount = Integer.parseInt(matcher.group(1)); + this.value = Integer.parseInt(matcher.group(2)); + } else { + this.amount = 0; + this.value = 0; + } + } + + public boolean isValid() { + return this.amount != 0; + } } diff --git a/nl/armeagle/TradeCraft/TradeCraftItemShop.java b/nl/armeagle/TradeCraft/TradeCraftItemShop.java index 333e9c3..ce8d4d8 100644 --- a/nl/armeagle/TradeCraft/TradeCraftItemShop.java +++ b/nl/armeagle/TradeCraft/TradeCraftItemShop.java @@ -71,30 +71,50 @@ private void handlePatronClick(Player player) { if (getChestItemCount() == 0) { if (playerCanBuy && playerCanBuy()) { - plugin.sendMessage(player, - "You can buy %1$d %2$s for %3$d "+ plugin.getCurrencyName() +".", - getBuyAmount(), - getItemName(), - getBuyValue()); + if ( this instanceof TradeCraftInfiniteShop ) { + plugin.sendMessage(player, + "You can buy %1$d %2$s for %3$d %4$s.", + getBuyAmount(), + getItemName(), + getBuyValue(), + plugin.getCurrencyName()); + } else { + plugin.sendMessage(player, + "You can buy %1$d %2$s for %3$d %4$s, up to %5$d.", + getBuyAmount(), + getItemName(), + getBuyValue(), + plugin.getCurrencyName(), + this.getItemsInShop()); + } } if (playerCanSell && playerCanSell()) { - plugin.sendMessage(player, - "You can sell %1$d %2$s for %3$d "+ plugin.getCurrencyName() +".", - getSellAmount(), - getItemName(), - getSellValue()); + if ( this instanceof TradeCraftInfiniteShop ) { + plugin.sendMessage(player, + "You can sell %1$d %2$s for %3$d %4$s.", + getSellAmount(), + getItemName(), + getSellValue(), + plugin.getCurrencyName()); + } else { + plugin.sendMessage(player, + "You can sell %1$d %2$s for %3$d %4$s, up to %5$d %4$s.", + getSellAmount(), + getItemName(), + getSellValue(), + plugin.getCurrencyName(), + this.getCurrencyInShop()); + } } if ( this instanceof TradeCraftInfiniteShop ) { plugin.sendMessage(player, "This is an infinite shop"); - } else { - plugin.sendMessage(player, "The shop contains "+ this.getItemsInShop() +" of "+ this.getItemName() +" and can buy for a total of "+ this.getCurrencyInShop() +" "+ this.plugin.getCurrencyName()); } - plugin.sendMessage(player, "There are no items in the chest."); return; } + plugin.trace(player, "%s %s %s", getChestItemType(), TradeCraft.currency, getItemType()); if ( getChestItemType().compareTo(TradeCraft.currency) == 0 ) { if (!playerCanBuy) { plugin.sendMessage(player, "You are not allowed to buy from shops!"); diff --git a/nl/armeagle/TradeCraft/TradeCraftPlayerOwnedShop.java b/nl/armeagle/TradeCraft/TradeCraftPlayerOwnedShop.java index 6d0e016..97d247f 100644 --- a/nl/armeagle/TradeCraft/TradeCraftPlayerOwnedShop.java +++ b/nl/armeagle/TradeCraft/TradeCraftPlayerOwnedShop.java @@ -14,15 +14,11 @@ public class TradeCraftPlayerOwnedShop extends TradeCraftItemShop { public TradeCraftPlayerOwnedShop(TradeCraft plugin, Sign sign, Chest chest, String ownerName) { super(plugin, sign, chest); -// ownerName = plugin.data.getOwnerOfSign(sign); this.ownerName = ownerName; - itemName = plugin.getItemName(sign.getLines()); - itemType = plugin.configuration.get(itemName).type; - buyRate = plugin.getExchangeRate(sign, 1); - sellRate = plugin.getExchangeRate(sign, 2); - -// plugin.getServer().broadcastMessage("owner" + ownerName + " itemname " + itemName + -// " item type " + itemType + " buyrate " + buyRate + " sell rate" + sellRate); + this.itemName = plugin.getItemName(sign.getLines()); + this.itemType = plugin.configuration.get(this.itemName).type; + this.buyRate = new TradeCraftExchangeRate(sign.getLine(1)); + this.sellRate = new TradeCraftExchangeRate(sign.getLine(2)); } public boolean playerCanDestroy(Player player) { From 647635cc5786782b6769716b2c4bb40e55c5e0a0 Mon Sep 17 00:00:00 2001 From: ArmEagle Date: Sun, 24 Apr 2011 15:55:57 +0200 Subject: [PATCH 035/100] changed version to 1.0.0alpha3 --- plugin.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugin.yml b/plugin.yml index f066bbb..d27a93a 100644 --- a/plugin.yml +++ b/plugin.yml @@ -1,6 +1,6 @@ name: TradeCraft main: nl.armeagle.TradeCraft.TradeCraft -version: AE-1.0.0-alpha +version: AE-1.0.0-alpha3 commands: myshops: description: See a printout of any personal shops you have From 16783b21cb1bb5def2242c809f3aa5fe71cf72d8 Mon Sep 17 00:00:00 2001 From: ArmEagle Date: Sun, 24 Apr 2011 16:25:41 +0200 Subject: [PATCH 036/100] Forgot to remove some use of the strict player name needed setting. --- .../TradeCraft/TradeCraftConfigurationFile.java | 5 +++++ nl/armeagle/TradeCraft/TradeCraftDataFile.java | 13 +++---------- .../TradeCraft/TradeCraftPlayerOwnedShop.java | 9 +-------- .../TradeCraft/TradeCraftPropertiesFile.java | 8 ++++---- plugin.yml | 2 +- 5 files changed, 14 insertions(+), 23 deletions(-) diff --git a/nl/armeagle/TradeCraft/TradeCraftConfigurationFile.java b/nl/armeagle/TradeCraft/TradeCraftConfigurationFile.java index 23347b4..96dfd0e 100644 --- a/nl/armeagle/TradeCraft/TradeCraftConfigurationFile.java +++ b/nl/armeagle/TradeCraft/TradeCraftConfigurationFile.java @@ -12,6 +12,11 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; +/** + * The name of this class is a bit misleading. This class stores all the items and their default + * trade rates that can be used in the game. The actual configuration of the plugin itself + * is handled by the TradeCraftProperties class. + */ class TradeCraftConfigurationFile { private static final String fileName = TradeCraft.pluginName + ".txt"; private static final String filePath = "plugins" + File.separator + TradeCraft.pluginName; diff --git a/nl/armeagle/TradeCraft/TradeCraftDataFile.java b/nl/armeagle/TradeCraft/TradeCraftDataFile.java index 2477e80..cb55c4f 100644 --- a/nl/armeagle/TradeCraft/TradeCraftDataFile.java +++ b/nl/armeagle/TradeCraft/TradeCraftDataFile.java @@ -170,18 +170,11 @@ public ArrayList shopsOwned(String name){ ArrayList list = new ArrayList(); for (String key : data.keySet()) { TradeCraftDataInfo info = data.get(key); - if ( this.plugin.properties.getStrictPlayerShopOwnerNameRequired() ) { - if(info.ownerName.equalsIgnoreCase(name)){ - list.add(info); - } - } else { - if ( name.indexOf(info.ownerName) == 0 ) { - list.add(info); - } - } + if(info.ownerName.equalsIgnoreCase(name)){ + list.add(info); + } } - return list; } diff --git a/nl/armeagle/TradeCraft/TradeCraftPlayerOwnedShop.java b/nl/armeagle/TradeCraft/TradeCraftPlayerOwnedShop.java index 97d247f..e453e64 100644 --- a/nl/armeagle/TradeCraft/TradeCraftPlayerOwnedShop.java +++ b/nl/armeagle/TradeCraft/TradeCraftPlayerOwnedShop.java @@ -33,14 +33,7 @@ public boolean isOwnedByPlayer(Player player) { if ( ownerName == null ) { return false; } else { - // option for less strict player name matching. - if ( this.plugin.properties.getStrictPlayerShopOwnerNameRequired() ) { - // strict is enforced, so use a perfect name match - return player.getName().equals(ownerName); - } else { - // strict is not enforced, so allow the playername to start with the set owner name - return player.getName().indexOf(ownerName) == 0; - } + return player.getName().equals(ownerName); } } diff --git a/nl/armeagle/TradeCraft/TradeCraftPropertiesFile.java b/nl/armeagle/TradeCraft/TradeCraftPropertiesFile.java index c9979ee..95ed454 100644 --- a/nl/armeagle/TradeCraft/TradeCraftPropertiesFile.java +++ b/nl/armeagle/TradeCraft/TradeCraftPropertiesFile.java @@ -6,6 +6,10 @@ import java.io.InputStream; import org.bukkit.util.config.Configuration; +/** + * This class, handles the actual configuration of the plugin. The TradeCraftConfiguration class + * actually handles all the items that can be used by the shops in the game. + */ public class TradeCraftPropertiesFile { private static final String fileName = TradeCraft.pluginName + ".properties"; private static final String filePath = "plugins" + File.separator + TradeCraft.pluginName; @@ -89,8 +93,4 @@ public int getRepairCost() { public boolean getEnableDebugMessages() { return properties.getBoolean("enable-debug-messages", false); } - - public boolean getStrictPlayerShopOwnerNameRequired() { - return properties.getBoolean("strict-playershop-owner-name", true); - } } diff --git a/plugin.yml b/plugin.yml index d27a93a..fa913dd 100644 --- a/plugin.yml +++ b/plugin.yml @@ -1,6 +1,6 @@ name: TradeCraft main: nl.armeagle.TradeCraft.TradeCraft -version: AE-1.0.0-alpha3 +version: AE-1.0.0-alpha4 commands: myshops: description: See a printout of any personal shops you have From 062b2ba8520317e7eef36f0aadb8ed1c9424b780 Mon Sep 17 00:00:00 2001 From: ArmEagle Date: Sun, 24 Apr 2011 17:55:04 +0200 Subject: [PATCH 037/100] Initial support for localization. Better input error handling for setting of currency. --- TradeCraft.en.lang | 9 ++ TradeCraft.properties | 3 +- nl/armeagle/TradeCraft/TradeCraft.java | 48 ++++++----- .../TradeCraft/TradeCraftLocalization.java | 85 +++++++++++++++++++ .../TradeCraft/TradeCraftPropertiesFile.java | 5 ++ 5 files changed, 130 insertions(+), 20 deletions(-) create mode 100644 TradeCraft.en.lang create mode 100644 nl/armeagle/TradeCraft/TradeCraftLocalization.java diff --git a/TradeCraft.en.lang b/TradeCraft.en.lang new file mode 100644 index 0000000..caf5718 --- /dev/null +++ b/TradeCraft.en.lang @@ -0,0 +1,9 @@ +NO_PERMISSION_SET_CURRENCY: "You do not have the permission to set the currency" +IS_NO_VALID_CURRENCY_USE_INSTEAD: "is not a valid value for a currency, use 'id[;data]'" +INVALID_CURRENCY: "Invalid currency:" +CURRENCY_IS_SET_TO: "Currency is set to" +CURRENCY_IS: "Currency is:" +YOU_DONT_OWN_ANY_SHOPS: "You don't own any shops!" +YOUR_SHOPS: "Your shops:" +ITEM: "Item" +AMOUNT: "Amount" diff --git a/TradeCraft.properties b/TradeCraft.properties index 35b431e..e8ae278 100644 --- a/TradeCraft.properties +++ b/TradeCraft.properties @@ -4,4 +4,5 @@ repair-shops-enabled: false repair-cost: 10 enable-debug-messages: false currency-id: 266 -currency-data: 0 \ No newline at end of file +currency-data: 0 +language: "en" \ No newline at end of file diff --git a/nl/armeagle/TradeCraft/TradeCraft.java b/nl/armeagle/TradeCraft/TradeCraft.java index 0b76381..5fc0f54 100644 --- a/nl/armeagle/TradeCraft/TradeCraft.java +++ b/nl/armeagle/TradeCraft/TradeCraft.java @@ -38,6 +38,7 @@ public class TradeCraft extends JavaPlugin { static TradeCraftItem currency; TradeCraftPropertiesFile properties; TradeCraftConfigurationFile configuration; + public TradeCraftLocalization localization; TradeCraftDataFile data; private final TradeCraftBlockListener blockListener = new TradeCraftBlockListener(this); @@ -52,6 +53,7 @@ public class TradeCraft extends JavaPlugin { public void onDisable() { properties = null; configuration = null; + this.localization = null; data.save(); data = null; } @@ -60,6 +62,7 @@ public void onEnable() { properties = new TradeCraftPropertiesFile(this); configuration = new TradeCraftConfigurationFile(this); data = new TradeCraftDataFile(this); + this.localization = new TradeCraftLocalization(this); configuration.load(); data.load(); @@ -80,8 +83,7 @@ public void onEnable() { } @Override - public boolean onCommand(CommandSender sender, Command command, - String label, String[] args) { + public boolean onCommand(CommandSender sender, Command command, String label, String[] args) { String name = command.getName(); Player p; @@ -90,31 +92,39 @@ public boolean onCommand(CommandSender sender, Command command, if (name.equalsIgnoreCase("setcurrency") && args.length == 1) { if ( !this.permissions.canSetCurrency(p) ) { - p.sendMessage("You do not have the permission to set the currency"); + p.sendMessage(TradeCraftLocalization.get("NO_PERMISSION_SET_CURRENCY")); } else { TradeCraftItem testCurrency = null; // try to split ID and Data, separated by a semicolon mark Matcher IdSplitData = TradeCraft.itemPatternIdSplitData.matcher(args[0]); if ( !IdSplitData.matches() ) { - p.sendMessage(args[0] +" is not a valid value for a currency, use 'id[;data]'"); + p.sendMessage(args[0] +" "+ TradeCraftLocalization.get("IS_NO_VALID_CURRENCY_USE_INSTEAD")); return false; } - int cid = Integer.parseInt(IdSplitData.group(1)); - if ( IdSplitData.group(2) != null ) { - testCurrency = new TradeCraftItem(cid, Short.parseShort(IdSplitData.group(2))); - } else { - testCurrency = new TradeCraftItem(cid); - } + try { + int cid = Integer.parseInt(IdSplitData.group(1)); + if ( IdSplitData.group(2) != null ) { + testCurrency = new TradeCraftItem(cid, Short.parseShort(IdSplitData.group(2))); + } else { + testCurrency = new TradeCraftItem(cid); + } + if ( this.configuration.get(testCurrency) != null ) { + currency = testCurrency; + this.properties.setCurrencyType(currency); + p.sendMessage(TradeCraftLocalization.get("CURRENCY_IS_SET_TO")+" "+ this.getCurrencyName()); + } else { + p.sendMessage(TradeCraftLocalization.get("INVALID_CURRENCY")+" "+ args[0]); + } + } catch ( NumberFormatException e ) { + p.sendMessage(TradeCraftLocalization.get("INVALID_CURRENCY")+" "+ args[0]); + } - currency = testCurrency; - this.properties.setCurrencyType(currency); - p.sendMessage("Currency is set to " + this.getCurrencyName()); } } else if (name.equalsIgnoreCase("displaycurrency") && args.length == 0) { - p.sendMessage("Currency is: " + this.getCurrencyName()); + p.sendMessage(TradeCraftLocalization.get("CURRENCY_IS") +" "+ this.getCurrencyName()); } else if (name.equalsIgnoreCase("canplayer") && args.length == 1) { permissions.debug(args[0]); } else if (name.equalsIgnoreCase("myshops")) { @@ -132,14 +142,14 @@ void displayShops(Player p) { String name = p.getName(); ArrayList list = data.shopsOwned(name); if (list.size() == 0) { - p.sendMessage("You don't own any shops!"); + p.sendMessage(TradeCraftLocalization.get("YOU_DONT_OWN_ANY_SHOPS")); return; } - p.sendMessage("Your shops:"); + p.sendMessage(TradeCraftLocalization.get("YOUR_SHOPS")); for (TradeCraftDataInfo info : list) { - p.sendMessage("Item: " + this.configuration.get(info.itemType).name +"("+ info.itemType.toShortString() +")" - + " Amount: " + info.itemAmount + " " - + this.getCurrencyName() +": " + info.currencyAmount); + p.sendMessage(TradeCraftLocalization.get("ITEM") +": "+ this.configuration.get(info.itemType).name +"("+ info.itemType.toShortString() +")" + +" "+ TradeCraftLocalization.get("AMOUNT") +": "+ info.itemAmount +" " + + this.getCurrencyName() +": "+ info.currencyAmount); } diff --git a/nl/armeagle/TradeCraft/TradeCraftLocalization.java b/nl/armeagle/TradeCraft/TradeCraftLocalization.java new file mode 100644 index 0000000..51a737e --- /dev/null +++ b/nl/armeagle/TradeCraft/TradeCraftLocalization.java @@ -0,0 +1,85 @@ +package nl.armeagle.TradeCraft; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; + +import org.bukkit.util.config.Configuration; + +public class TradeCraftLocalization { + private static final String replaceString = "##lang##"; + private static final String filePreName = TradeCraft.pluginName + "."+ TradeCraftLocalization.replaceString +".lang"; + private static final String filePath = "plugins" + File.separator + TradeCraft.pluginName; + + private TradeCraft plugin; + private String language; + private static Configuration localization; + + public TradeCraftLocalization(TradeCraft plugin) { + this.plugin = plugin; + this.language = plugin.properties.getLanguage(); + + // make folder in the plugins dir if it doesn't exist yet + File path = new File(filePath); + if ( !path.exists() ) { + path.mkdirs(); + } + path = null; + + // if file does not exist in this directory, copy it from the jar + String fileName = TradeCraftLocalization.filePreName.replace(TradeCraftLocalization.replaceString, this.language); + File file = new File(filePath + File.separator + fileName); + if ( !file.exists() ) { + InputStream input = this.getClass().getResourceAsStream("/" + fileName); + + // If this file does not exist (not a default supported language), then revert back to the default language file. + // That file will exist in the jar for sure + if ( input == null ) { + this.plugin.log.info(fileName +" was not found in "+ TradeCraft.pluginName +".jar, using default language \""+ TradeCraftPropertiesFile.defaultLanguage +"\" instead"); + this.language = TradeCraftPropertiesFile.defaultLanguage; + fileName = TradeCraftLocalization.filePreName.replace(TradeCraftLocalization.replaceString, this.language); + file = new File(filePath + File.separator + fileName); + if ( !file.exists() ) { + input = this.getClass().getResourceAsStream("/" + fileName); + } + } + // if input is not null, then we need to copy the given file from the jar + if ( input != null ) { + this.plugin.log.info(filePath + File.separator + fileName +" does not exist, creating..."); + + FileOutputStream output = null; + + try { + output = new FileOutputStream(file); + byte[] buf = new byte[8192]; + int length = 0; + while ((length = input.read(buf)) > 0) { + output.write(buf, 0, length); + } + } catch (IOException e) { + e.printStackTrace(); + } finally { + try { + if (input != null) { + input.close(); + } + } catch (IOException e) {} + + try { + if (output != null) { + output.close(); + } + } catch (IOException e) {} + } + } + } + + TradeCraftLocalization.localization = new Configuration(file); + TradeCraftLocalization.localization.load(); + } + + public static String get(String key) { + return TradeCraftLocalization.localization.getString(key, "key error \""+ key +"\""); + } +} diff --git a/nl/armeagle/TradeCraft/TradeCraftPropertiesFile.java b/nl/armeagle/TradeCraft/TradeCraftPropertiesFile.java index 95ed454..80bebb2 100644 --- a/nl/armeagle/TradeCraft/TradeCraftPropertiesFile.java +++ b/nl/armeagle/TradeCraft/TradeCraftPropertiesFile.java @@ -13,6 +13,7 @@ public class TradeCraftPropertiesFile { private static final String fileName = TradeCraft.pluginName + ".properties"; private static final String filePath = "plugins" + File.separator + TradeCraft.pluginName; + public static final String defaultLanguage = "en"; private TradeCraft plugin; private final Configuration properties; @@ -93,4 +94,8 @@ public int getRepairCost() { public boolean getEnableDebugMessages() { return properties.getBoolean("enable-debug-messages", false); } + + public String getLanguage() { + return properties.getString("language", TradeCraftPropertiesFile.defaultLanguage); + } } From d9d14e05260e45b8f51d12c2e2f240b19910fe4a Mon Sep 17 00:00:00 2001 From: ArmEagle Date: Sun, 24 Apr 2011 21:02:56 +0200 Subject: [PATCH 038/100] Converted all source to use Localization for all messages to the player (and cleaned up some ugly string concatenation I added before). --- TradeCraft.en.lang | 38 +++++++++++ .../TradeCraft/TradeCraftBlockListener.java | 12 ++-- .../TradeCraft/TradeCraftItemShop.java | 66 ++++++++++--------- .../TradeCraft/TradeCraftRepairShop.java | 20 ++++-- 4 files changed, 94 insertions(+), 42 deletions(-) diff --git a/TradeCraft.en.lang b/TradeCraft.en.lang index caf5718..421408e 100644 --- a/TradeCraft.en.lang +++ b/TradeCraft.en.lang @@ -1,3 +1,4 @@ +# TradeCraft.java NO_PERMISSION_SET_CURRENCY: "You do not have the permission to set the currency" IS_NO_VALID_CURRENCY_USE_INSTEAD: "is not a valid value for a currency, use 'id[;data]'" INVALID_CURRENCY: "Invalid currency:" @@ -7,3 +8,40 @@ YOU_DONT_OWN_ANY_SHOPS: "You don't own any shops!" YOUR_SHOPS: "Your shops:" ITEM: "Item" AMOUNT: "Amount" + +# TradeCraftBlockListener.java +ALL_ITEMS_MUST_BE_WITHDRAWN: "All items and currency must be withdrawn before you can destroy this sign or chest!" +YOU_CANT_DESTROY_THIS_SIGN: "You can't destroy this sign!" +YOU_CANT_DESTROY_THIS_CHEST: "You can't destroy this chest!" +YOU_CANT_DESTROY_THIS_BLOCK_ATTACHED: "You can't destroy this block because there are signs attached to it!" +YOU_CANT_CREATE_INF_SHOPS: "You can't create infinite shops!" +YOU_DONT_HAVE_PERM_CREATE_PLAYER_SHOP: "You do not have the permission to create a player shop!" + +# TradeCraftItemShop.java +THE_CHEST_HAS_MORE_THAN_ONE_TYPE: "The chest has more than one type of item in it!" +WITHDREW: "Withdrew" +THERE_IS_NOTHING_TO_WITHDRAW: "There is nothing to withdraw." +DEPOSITED: "Deposited" +YOU_CANT_DEPOSIT_THAT_HERE: "You can't deposit that here!" +YOU_CAN_BUY_Y_A_FOR_X_B: "You can buy %1$d %2$s for %3$d %4$s." +YOU_CAN_BUY_X_A_FOR_Y_B_UP_TO_Z: "You can buy %1$d %2$s for %3$d %4$s, up to %5$d." +YOU_CAN_SELL_X_A_FOR_Y_B: "You can sell %1$d %2$s for %3$d %4$s." +YOU_CAN_SELL_X_A_FOR_Y_B_UP_TO_Z: "You can sell %1$d %2$s for %3$d %4$s, up to %5$d %4$s." +THIS_IS_AN_INFINITE_SHOP: "This is an infinite shop" +YOU_ARE_NOT_ALLOWED_TO_BUY: "You are not allowed to buy from shops!" +YOU_ARE_NO_ALLOWED_TO_SELL: "You are not allowed to sell to shops!" +YOU_CANT_SELL_THAT: "You can't sell that here!" +YOU_CANT_BUY_HERE: "You can't buy here!" +YOU_NEED_TO_SPEND_AT_LEAST_X_A_TO_GET_ANY_B: "You need to spend at least %1$d %2$s to get any %3$s." +CANT_BUY_SHOP_HAS_NO_A_LEFT: "Cannot buy. This shop has no more %2$s left." +CANNOT_BUY_SHOP_ONLY_HAS_X_A: "Cannot buy. This shop only has %1$d %2$s." +YOU_NEED_TO_SELL_AT_LEAST_X_A_TO_GET_ANY_B: "You need to sell at least %1$d %2$s to get any %3$s." +CANNOT_SELL_SHOP_ONLY_HAS_X_A: "Cannot sell. This shop only has %1$d %2$s." +YOU_SOLD_X_A_FOR_Y_B: "You sold %1$d %2$s for %3$d %4$s." + +# TradeCraftRepairShop.java +IT_COSTS_X_A_TO_REPAIR_AN_ITEM: "It costs %1$d %2$s to repair an item." +WITH_THIS_MUCH_A_YOU_CAN_REPAIR_Y_ITEMS: "With this much %1$s you can repair %2$d items." +THAT_IS_NOT_ENOUGH_A: "That's not enough %1$s." +YOU_NEED_X_A_TO_REPAIR_ALL_THIS: "You need %1$d %2$s to repair all this." +YOU_REPAIRED_X_ITEMS_FOR_Y_B: "You repaired %1$d items for %2$d %3$s." \ No newline at end of file diff --git a/nl/armeagle/TradeCraft/TradeCraftBlockListener.java b/nl/armeagle/TradeCraft/TradeCraftBlockListener.java index 35cafe8..564744c 100644 --- a/nl/armeagle/TradeCraft/TradeCraftBlockListener.java +++ b/nl/armeagle/TradeCraft/TradeCraftBlockListener.java @@ -44,14 +44,14 @@ public void onBlockBreak(BlockBreakEvent e) { shop.shopCanBeWithdrawnFrom() ) { // cannot destroy this shop, so cancel destruction, use distinct error messages if ( shop.shopCanBeWithdrawnFrom() ) { - plugin.sendMessage(player, "All items and currency must be withdrawn before you can destroy this sign or chest!"); + plugin.sendMessage(player, TradeCraftLocalization.get("ALL_ITEMS_MUST_BE_WITHDRAWN")); } else { if ( block.getType() == Material.WALL_SIGN ) { - plugin.sendMessage(player, "You can't destroy this sign!"); + plugin.sendMessage(player, TradeCraftLocalization.get("YOU_CANT_DESTROY_THIS_SIGN")); } else if ( block.getType() == Material.CHEST ) { - plugin.sendMessage(player, "You can't destroy this chest!"); + plugin.sendMessage(player, TradeCraftLocalization.get("YOU_CANT_DESTROY_THIS_CHEST")); } else { - plugin.sendMessage(player, "You can't destroy this block because there are signs attached to it!"); + plugin.sendMessage(player, TradeCraftLocalization.get("YOU_CANT_DESTROY_THIS_BLOCK_ATTACHED")); } } stopDestruction(block,e); @@ -113,13 +113,13 @@ public void onSignChange(SignChangeEvent e) { return; } - plugin.sendMessage(player, "You can't create infinite shops!"); + plugin.sendMessage(player, TradeCraftLocalization.get("YOU_CANT_CREATE_INF_SHOPS")); e.setCancelled(true); return; } // there is a buy rate, so this is a player owned shop if ( !plugin.permissions.canMakePlayerShops(player)){ - plugin.sendMessage(player, "You do not have the permission to create a player shop!"); + plugin.sendMessage(player, TradeCraftLocalization.get("YOU_DONT_HAVE_PERM_CREATE_PLAYER_SHOP")); e.setCancelled(true); return; } diff --git a/nl/armeagle/TradeCraft/TradeCraftItemShop.java b/nl/armeagle/TradeCraft/TradeCraftItemShop.java index ce8d4d8..8368efd 100644 --- a/nl/armeagle/TradeCraft/TradeCraftItemShop.java +++ b/nl/armeagle/TradeCraft/TradeCraftItemShop.java @@ -20,7 +20,7 @@ public void handleRightClick(Player player) { private void handleOwnerClick(Player player) { if (!chestContentsAreOK()) { - plugin.sendMessage(player, "The chest has more than one type of item in it!"); + plugin.sendMessage(player, TradeCraftLocalization.get("THE_CHEST_HAS_MORE_THAN_ONE_TYPE")); return; } @@ -28,31 +28,31 @@ private void handleOwnerClick(Player player) { int currencyAmount = withdrawCurrency(); if (currencyAmount > 0) { populateChest(TradeCraft.currency, currencyAmount); - plugin.sendMessage(player, "Withdrew %1$d "+ plugin.getCurrencyName() +".", currencyAmount); + plugin.sendMessage(player, "%1$s %2$d %3$s.", TradeCraftLocalization.get("WITHDREW"), currencyAmount, plugin.getCurrencyName()); } else { int itemAmount = withdrawItems(); if (itemAmount > 0) { populateChest(getItemType(), itemAmount); - plugin.sendMessage(player, "Withdrew %1$d %2$s.", itemAmount, getItemName()); + plugin.sendMessage(player, "%1$s %2$d %3$s.", TradeCraftLocalization.get("WITHDREW"), itemAmount, getItemName()); } else { - plugin.sendMessage(player, "There is nothing to withdraw."); + plugin.sendMessage(player, TradeCraftLocalization.get("THERE_IS_NOTHING_TO_WITHDRAW")); } } } else if ( getChestItemType().compareTo(TradeCraft.currency) == 0 ) { depositCurrency(getChestItemCount()); - plugin.sendMessage(player, "Deposited %1$d "+ plugin.getCurrencyName() +".", getChestItemCount()); + plugin.sendMessage(player, "%1$s %2$d %3$s.", TradeCraftLocalization.get("DEPOSITED"), getChestItemCount(), plugin.getCurrencyName()); populateChest(new TradeCraftItem(0), 0); int itemAmount = withdrawItems(); if (itemAmount > 0) { populateChest(getItemType(), itemAmount); - plugin.sendMessage(player, "Withdrew %1$d %2$s.", itemAmount, getItemName()); + plugin.sendMessage(player, "%1$s %2$d %3$s.", TradeCraftLocalization.get("WITHDREW"), itemAmount, getItemName()); } } else if ( getChestItemType().compareTo(getItemType()) == 0 ) { depositItems(getChestItemCount()); populateChest(new TradeCraftItem(0), 0); - plugin.sendMessage(player, "Deposited %1$d %2$s.", getChestItemCount(), getItemName()); + plugin.sendMessage(player, "%1$s %2$d %3$s.", TradeCraftLocalization.get("DEPOSITED"), getChestItemCount(), getItemName()); } else { - plugin.sendMessage(player, "You can't deposit that here!"); + plugin.sendMessage(player, TradeCraftLocalization.get("YOU_CANT_DEPOSIT_THAT_HERE")); } } @@ -65,7 +65,7 @@ private void handlePatronClick(Player player) { getChestItemCount(); if (!chestContentsAreOK()) { - plugin.sendMessage(player, "The chest has more than one type of item in it!"); + plugin.sendMessage(player, TradeCraftLocalization.get("THE_CHEST_HAS_MORE_THAN_ONE_TYPE")); return; } @@ -73,14 +73,14 @@ private void handlePatronClick(Player player) { if (playerCanBuy && playerCanBuy()) { if ( this instanceof TradeCraftInfiniteShop ) { plugin.sendMessage(player, - "You can buy %1$d %2$s for %3$d %4$s.", + TradeCraftLocalization.get("YOU_CAN_BUY_Y_A_FOR_X_B"), getBuyAmount(), getItemName(), getBuyValue(), plugin.getCurrencyName()); } else { plugin.sendMessage(player, - "You can buy %1$d %2$s for %3$d %4$s, up to %5$d.", + TradeCraftLocalization.get("YOU_CAN_BUY_X_A_FOR_Y_B_UP_TO_Z"), getBuyAmount(), getItemName(), getBuyValue(), @@ -92,14 +92,14 @@ private void handlePatronClick(Player player) { if (playerCanSell && playerCanSell()) { if ( this instanceof TradeCraftInfiniteShop ) { plugin.sendMessage(player, - "You can sell %1$d %2$s for %3$d %4$s.", + TradeCraftLocalization.get("YOU_CAN_SELL_X_A_FOR_Y_B"), getSellAmount(), getItemName(), getSellValue(), plugin.getCurrencyName()); } else { plugin.sendMessage(player, - "You can sell %1$d %2$s for %3$d %4$s, up to %5$d %4$s.", + TradeCraftLocalization.get("YOU_CAN_SELL_X_A_FOR_Y_B_UP_TO_Z"), getSellAmount(), getItemName(), getSellValue(), @@ -109,7 +109,7 @@ private void handlePatronClick(Player player) { } if ( this instanceof TradeCraftInfiniteShop ) { - plugin.sendMessage(player, "This is an infinite shop"); + plugin.sendMessage(player, TradeCraftLocalization.get("THIS_IS_AN_INFINITE_SHOP")); } return; } @@ -117,24 +117,24 @@ private void handlePatronClick(Player player) { plugin.trace(player, "%s %s %s", getChestItemType(), TradeCraft.currency, getItemType()); if ( getChestItemType().compareTo(TradeCraft.currency) == 0 ) { if (!playerCanBuy) { - plugin.sendMessage(player, "You are not allowed to buy from shops!"); + plugin.sendMessage(player, TradeCraftLocalization.get("YOU_ARE_NOT_ALLOWED_TO_BUY")); } else { playerWantsToBuy(player); } } else if ( getChestItemType().compareTo(getItemType()) == 0 ) { if (!playerCanSell) { - plugin.sendMessage(player, "You are not allowed to sell to shops!"); + plugin.sendMessage(player, TradeCraftLocalization.get("YOU_ARE_NO_ALLOWED_TO_SELL")); } else { playerWantsToSell(player); } } else { - plugin.sendMessage(player, "You can't sell that here!"); + plugin.sendMessage(player, TradeCraftLocalization.get("YOU_CANT_SELL_THAT")); } } private void playerWantsToBuy(Player player) { if (!playerCanBuy()) { - plugin.sendMessage(player, "You can't buy that here!"); + plugin.sendMessage(player, TradeCraftLocalization.get("YOU_CANT_BUY_HERE")); return; } @@ -143,7 +143,8 @@ private void playerWantsToBuy(Player player) { if (amountPlayerWantsToBuy == 0) { plugin.sendMessage(player, - "You need to spend at least %1$d "+ plugin.getCurrencyName() +" to get any %2$s.", + TradeCraftLocalization.get("YOU_NEED_TO_SPEND_AT_LEAST_X_A_TO_GET_ANY_B"), + plugin.getCurrencyName(), getBuyValue(), getItemName()); return; @@ -152,12 +153,11 @@ private void playerWantsToBuy(Player player) { if (amountPlayerWantsToBuy > getItemsInShop()) { if ( getItemsInShop() == 0 ) { plugin.sendMessage(player, - "Cannot buy. This shop has no more %2$s left.", - getItemsInShop(), + TradeCraftLocalization.get("CANT_BUY_SHOP_HAS_NO_A_LEFT"), getItemName()); } else { plugin.sendMessage(player, - "Cannot buy. This shop only has %1$d %2$s.", + TradeCraftLocalization.get("CANNOT_BUY_SHOP_ONLY_HAS_X_A"), getItemsInShop(), getItemName()); } @@ -174,15 +174,16 @@ private void playerWantsToBuy(Player player) { chest.update(); plugin.sendMessage(player, - "You bought %1$d %2$s for %3$d "+ plugin.getCurrencyName() +".", + "You bought %1$d %2$s for %3$d %4$s.", amountPlayerWantsToBuy, getItemName(), - requiredCurrencyForThatAmount); + requiredCurrencyForThatAmount, + plugin.getCurrencyName()); } private void playerWantsToSell(Player player) { if (!playerCanSell()) { - plugin.sendMessage(player, "You can't sell that here!"); + plugin.sendMessage(player, TradeCraftLocalization.get("YOU_CANT_SELL_THAT")); return; } @@ -191,16 +192,18 @@ private void playerWantsToSell(Player player) { if (currencyPlayerShouldReceive == 0) { plugin.sendMessage(player, - "You need to sell at least %1$d %2$s to get any "+ plugin.getCurrencyName() +".", + TradeCraftLocalization.get("YOU_NEED_TO_SELL_AT_LEAST_X_A_TO_GET_ANY_B"), getSellAmount(), - getItemName()); + getItemName(), + plugin.getCurrencyName()); return; } if (currencyPlayerShouldReceive > getCurrencyInShop()) { plugin.sendMessage(player, - "Cannot sell. This shop only has %1$d "+ plugin.getCurrencyName() +".", - getCurrencyInShop()); + TradeCraftLocalization.get("CANNOT_SELL_SHOP_ONLY_HAS_X_A"), + getCurrencyInShop(), + plugin.getCurrencyName()); return; } @@ -214,10 +217,11 @@ private void playerWantsToSell(Player player) { chest.update(); plugin.sendMessage(player, - "You sold %1$d %2$s for %3$d "+ plugin.getCurrencyName() +".", + TradeCraftLocalization.get("YOU_SOLD_X_A_FOR_Y_B"), amountThatCanBeSold, getItemName(), - currencyPlayerShouldReceive); + currencyPlayerShouldReceive, + plugin.getCurrencyName()); } public TradeCraftItem getChestItemType() { diff --git a/nl/armeagle/TradeCraft/TradeCraftRepairShop.java b/nl/armeagle/TradeCraft/TradeCraftRepairShop.java index 3491645..091755f 100644 --- a/nl/armeagle/TradeCraft/TradeCraftRepairShop.java +++ b/nl/armeagle/TradeCraft/TradeCraftRepairShop.java @@ -19,22 +19,29 @@ public void handleRightClick(Player player) { int repairCost = plugin.properties.getRepairCost(); if (currencyAmount == 0 && items.size() == 0) { - plugin.sendMessage(player, "It costs %d "+ plugin.getCurrencyName() +" to repair an item.", repairCost); + plugin.sendMessage(player, TradeCraftLocalization.get("IT_COSTS_X_A_TO_REPAIR_AN_ITEM"), + repairCost, + plugin.getCurrencyName()); return; } int actualCost = items.size() * repairCost; if (items.size() == 0) { - plugin.sendMessage(player, "With this much "+ plugin.getCurrencyName() +", you can repair %d items.", currencyAmount / repairCost); + plugin.sendMessage(player, TradeCraftLocalization.get("WITH_THIS_MUCH_A_YOU_CAN_REPAIR_Y_ITEMS"), + plugin.getCurrencyName(), + currencyAmount / repairCost); return; } if (currencyAmount < actualCost) { if (currencyAmount > 0) { - plugin.sendMessage(player, "That's not enough "+ plugin.getCurrencyName() +"."); + plugin.sendMessage(player, TradeCraftLocalization.get("THAT_IS_NOT_ENOUGH_A"), + plugin.getCurrencyName()); } - plugin.sendMessage(player, "You need %d "+ plugin.getCurrencyName() +" to repair all this.", actualCost); + plugin.sendMessage(player, TradeCraftLocalization.get("YOU_NEED_X_A_TO_REPAIR_ALL_THIS"), + actualCost, + plugin.getCurrencyName()); return; } @@ -51,7 +58,10 @@ public void handleRightClick(Player player) { chest.update(); - plugin.sendMessage(player, "You repaired %d items for %d "+ plugin.getCurrencyName() +".", items.size(), actualCost); + plugin.sendMessage(player, TradeCraftLocalization.get("YOU_REPAIRED_X_ITEMS_FOR_Y_B"), + items.size(), + actualCost, + plugin.getCurrencyName()); } public boolean playerCanDestroy(Player player) { From 8b46fe3e3271383f603d3a14fbd7f89ca5d2bfd0 Mon Sep 17 00:00:00 2001 From: ArmEagle Date: Sun, 24 Apr 2011 22:08:30 +0200 Subject: [PATCH 039/100] Cleaned up and add a little bit of comment to the Localization class. --- .../TradeCraft/TradeCraftLocalization.java | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/nl/armeagle/TradeCraft/TradeCraftLocalization.java b/nl/armeagle/TradeCraft/TradeCraftLocalization.java index 51a737e..3875c51 100644 --- a/nl/armeagle/TradeCraft/TradeCraftLocalization.java +++ b/nl/armeagle/TradeCraft/TradeCraftLocalization.java @@ -7,9 +7,17 @@ import org.bukkit.util.config.Configuration; +/** + * + * @author ArmEagle + * Localization support. + * + * You can put different language files in the plugins/TradeCraft/ folder, similar to the default TradeCraft.en.lang. + * There is a "language" option in the TradeCraft.properties file (default: "en") with which you can then select + * such other language file to be used. + */ public class TradeCraftLocalization { - private static final String replaceString = "##lang##"; - private static final String filePreName = TradeCraft.pluginName + "."+ TradeCraftLocalization.replaceString +".lang"; + private static final String filePreName = TradeCraft.pluginName + ".%1$s.lang"; private static final String filePath = "plugins" + File.separator + TradeCraft.pluginName; private TradeCraft plugin; @@ -28,7 +36,7 @@ public TradeCraftLocalization(TradeCraft plugin) { path = null; // if file does not exist in this directory, copy it from the jar - String fileName = TradeCraftLocalization.filePreName.replace(TradeCraftLocalization.replaceString, this.language); + String fileName = String.format(TradeCraftLocalization.filePreName, this.language); File file = new File(filePath + File.separator + fileName); if ( !file.exists() ) { InputStream input = this.getClass().getResourceAsStream("/" + fileName); @@ -38,7 +46,7 @@ public TradeCraftLocalization(TradeCraft plugin) { if ( input == null ) { this.plugin.log.info(fileName +" was not found in "+ TradeCraft.pluginName +".jar, using default language \""+ TradeCraftPropertiesFile.defaultLanguage +"\" instead"); this.language = TradeCraftPropertiesFile.defaultLanguage; - fileName = TradeCraftLocalization.filePreName.replace(TradeCraftLocalization.replaceString, this.language); + fileName = String.format(TradeCraftLocalization.filePreName, this.language); file = new File(filePath + File.separator + fileName); if ( !file.exists() ) { input = this.getClass().getResourceAsStream("/" + fileName); From d0c6a5031a08c01856452b256dd9be084c155d11 Mon Sep 17 00:00:00 2001 From: ArmEagle Date: Sun, 24 Apr 2011 22:09:27 +0200 Subject: [PATCH 040/100] Changed version to 1.0.0-beta. --- plugin.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugin.yml b/plugin.yml index fa913dd..28ce492 100644 --- a/plugin.yml +++ b/plugin.yml @@ -1,6 +1,6 @@ name: TradeCraft main: nl.armeagle.TradeCraft.TradeCraft -version: AE-1.0.0-alpha4 +version: AE-1.0.0-beta1 commands: myshops: description: See a printout of any personal shops you have From 14cffaf1db8900eb7e170502e956fac04438c62a Mon Sep 17 00:00:00 2001 From: ArmEagle Date: Tue, 26 Apr 2011 10:19:27 +0200 Subject: [PATCH 041/100] A shop would let a customer buy more than the chest belonging to that should could contain. Now giving a warning to the customer and preventing that (gives a different warning if the shop would even return too many items at the lowest amount bought possible). --- TradeCraft.en.lang | 2 ++ nl/armeagle/TradeCraft/TradeCraftChest.java | 4 ++++ nl/armeagle/TradeCraft/TradeCraftItemShop.java | 12 ++++++++++-- 3 files changed, 16 insertions(+), 2 deletions(-) diff --git a/TradeCraft.en.lang b/TradeCraft.en.lang index 421408e..537a2a4 100644 --- a/TradeCraft.en.lang +++ b/TradeCraft.en.lang @@ -38,6 +38,8 @@ CANNOT_BUY_SHOP_ONLY_HAS_X_A: "Cannot buy. This shop only has %1$d %2$s." YOU_NEED_TO_SELL_AT_LEAST_X_A_TO_GET_ANY_B: "You need to sell at least %1$d %2$s to get any %3$s." CANNOT_SELL_SHOP_ONLY_HAS_X_A: "Cannot sell. This shop only has %1$d %2$s." YOU_SOLD_X_A_FOR_Y_B: "You sold %1$d %2$s for %3$d %4$s." +THIS_SHOP_ALWAYS_RETURNS_TOO_MUCH: "This shop always returns more goods than the chest could contain, contact the shop owner." +THIS_SHOP_WOULD_RETURN_TOO_MANY_BUY_LESS: "This shop would return more goods than the chest could contain, try to buy less." # TradeCraftRepairShop.java IT_COSTS_X_A_TO_REPAIR_AN_ITEM: "It costs %1$d %2$s to repair an item." diff --git a/nl/armeagle/TradeCraft/TradeCraftChest.java b/nl/armeagle/TradeCraft/TradeCraftChest.java index fd519b8..d5e529b 100644 --- a/nl/armeagle/TradeCraft/TradeCraftChest.java +++ b/nl/armeagle/TradeCraft/TradeCraftChest.java @@ -95,4 +95,8 @@ public List getNonCurrencyItems() { } return items; } + + public int getSize() { + return (this.chest == null ? 0 : this.chest.getSize()); + } } diff --git a/nl/armeagle/TradeCraft/TradeCraftItemShop.java b/nl/armeagle/TradeCraft/TradeCraftItemShop.java index 8368efd..cfcd3ea 100644 --- a/nl/armeagle/TradeCraft/TradeCraftItemShop.java +++ b/nl/armeagle/TradeCraft/TradeCraftItemShop.java @@ -139,8 +139,16 @@ private void playerWantsToBuy(Player player) { } int currencyPlayerWantsToSpend = getChestItemCount(); - int amountPlayerWantsToBuy = ((currencyPlayerWantsToSpend - (currencyPlayerWantsToSpend % getBuyValue()) ) / getBuyValue()) * getBuyAmount(); - + int amountPlayerWantsToBuy = ((currencyPlayerWantsToSpend - (currencyPlayerWantsToSpend % getBuyValue()) ) / getBuyValue()) * getBuyAmount(); + if ( amountPlayerWantsToBuy > this.chest.getSize()*64 ) { + if ( getBuyValue() > this.chest.getSize()*64 ) { + plugin.sendMessage(player, TradeCraftLocalization.get("THIS_SHOP_ALWAYS_RETURNS_TOO_MUCH")); + } else { + plugin.sendMessage(player, TradeCraftLocalization.get("THIS_SHOP_WOULD_RETURN_TOO_MANY_BUY_LESS")); + } + return; + } + if (amountPlayerWantsToBuy == 0) { plugin.sendMessage(player, TradeCraftLocalization.get("YOU_NEED_TO_SPEND_AT_LEAST_X_A_TO_GET_ANY_B"), From 7e8dce20eba086572bab6c0fc382d7ce012d3ca0 Mon Sep 17 00:00:00 2001 From: ArmEagle Date: Tue, 26 Apr 2011 10:25:20 +0200 Subject: [PATCH 042/100] A shop would let you sell so many items that it could return more currency than the chest could hold. Giving a warning now and aborting. --- TradeCraft.en.lang | 4 +++- nl/armeagle/TradeCraft/TradeCraftItemShop.java | 10 +++++++++- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/TradeCraft.en.lang b/TradeCraft.en.lang index 537a2a4..d06a5d8 100644 --- a/TradeCraft.en.lang +++ b/TradeCraft.en.lang @@ -38,8 +38,10 @@ CANNOT_BUY_SHOP_ONLY_HAS_X_A: "Cannot buy. This shop only has %1$d %2$s." YOU_NEED_TO_SELL_AT_LEAST_X_A_TO_GET_ANY_B: "You need to sell at least %1$d %2$s to get any %3$s." CANNOT_SELL_SHOP_ONLY_HAS_X_A: "Cannot sell. This shop only has %1$d %2$s." YOU_SOLD_X_A_FOR_Y_B: "You sold %1$d %2$s for %3$d %4$s." -THIS_SHOP_ALWAYS_RETURNS_TOO_MUCH: "This shop always returns more goods than the chest could contain, contact the shop owner." +THIS_SHOP_ALWAYS_RETURNS_TOO_MUCH: "This shop would always return more goods than the chest could contain, contact the shop owner." THIS_SHOP_WOULD_RETURN_TOO_MANY_BUY_LESS: "This shop would return more goods than the chest could contain, try to buy less." +THIS_SHOP_ALWAYS_RETURNS_TOO_MUCH_CURRENCY: "This shop would always return more %1$s than the chest could contain, contact the shop owner." +THIS_SHOP_WOULD_RETURN_TOO_MUCH_CURRENCY_SELL_LESS: "This shop would return more %1$s than the chest could contain, try to sell less items." # TradeCraftRepairShop.java IT_COSTS_X_A_TO_REPAIR_AN_ITEM: "It costs %1$d %2$s to repair an item." diff --git a/nl/armeagle/TradeCraft/TradeCraftItemShop.java b/nl/armeagle/TradeCraft/TradeCraftItemShop.java index cfcd3ea..8fee9f1 100644 --- a/nl/armeagle/TradeCraft/TradeCraftItemShop.java +++ b/nl/armeagle/TradeCraft/TradeCraftItemShop.java @@ -197,7 +197,15 @@ private void playerWantsToSell(Player player) { int amountPlayerWantsToSell = getChestItemCount(); int currencyPlayerShouldReceive = ((amountPlayerWantsToSell - (amountPlayerWantsToSell % getSellAmount())) / getSellAmount()) * getSellValue(); - + if ( currencyPlayerShouldReceive > this.chest.getSize()*64 ) { + if ( getSellValue() > this.chest.getSize()*64 ) { + plugin.sendMessage(player, TradeCraftLocalization.get("THIS_SHOP_ALWAYS_RETURNS_TOO_MUCH_CURRENCY"), this.plugin.getCurrencyName()); + } else { + plugin.sendMessage(player, TradeCraftLocalization.get("THIS_SHOP_WOULD_RETURN_TOO_MUCH_CURRENCY_SELL_LESS"), this.plugin.getCurrencyName()); + } + return; + } + if (currencyPlayerShouldReceive == 0) { plugin.sendMessage(player, TradeCraftLocalization.get("YOU_NEED_TO_SELL_AT_LEAST_X_A_TO_GET_ANY_B"), From e4a00cf8237113ed39fae970e9b12e01721057bc Mon Sep 17 00:00:00 2001 From: ArmEagle Date: Tue, 26 Apr 2011 14:13:59 +0200 Subject: [PATCH 043/100] More preventing of too many items ending up in a shop chest. Changed customer side to take both currency+items in chest issue. Added shop owner handling, taking out only max items and leaving the rest stored in the data file. Localization change: Checks for updates of language files and updates them. --- .classpath | 2 +- TradeCraft.en.lang | 2 + TradeCraft.properties | 3 +- nl/armeagle/TradeCraft/TradeCraft.java | 35 ++++++++++ .../TradeCraft/TradeCraftDataFile.java | 9 +++ .../TradeCraft/TradeCraftItemShop.java | 69 +++++++++++++------ .../TradeCraft/TradeCraftLocalization.java | 32 +++++++-- .../TradeCraft/TradeCraftPropertiesFile.java | 4 ++ 8 files changed, 127 insertions(+), 29 deletions(-) diff --git a/.classpath b/.classpath index 6a08ce8..161c485 100644 --- a/.classpath +++ b/.classpath @@ -3,6 +3,6 @@ - + diff --git a/TradeCraft.en.lang b/TradeCraft.en.lang index d06a5d8..cfde10b 100644 --- a/TradeCraft.en.lang +++ b/TradeCraft.en.lang @@ -20,6 +20,8 @@ YOU_DONT_HAVE_PERM_CREATE_PLAYER_SHOP: "You do not have the permission to create # TradeCraftItemShop.java THE_CHEST_HAS_MORE_THAN_ONE_TYPE: "The chest has more than one type of item in it!" WITHDREW: "Withdrew" +WITHDREW_X_CURRENCY_SHOP_STILL_HOLDS_Y_CURRENCY: "Withdrew the maximum %1$d %2$s, there is still %3$d %2$s in the shop." +WITHDREW_X_A_SHOP_STILL_HOLDS_Y_A: "Withdrew the maximum of %1$d %2$s, there are still %3$d %2$s in the shop." THERE_IS_NOTHING_TO_WITHDRAW: "There is nothing to withdraw." DEPOSITED: "Deposited" YOU_CANT_DEPOSIT_THAT_HERE: "You can't deposit that here!" diff --git a/TradeCraft.properties b/TradeCraft.properties index e8ae278..043a864 100644 --- a/TradeCraft.properties +++ b/TradeCraft.properties @@ -5,4 +5,5 @@ repair-cost: 10 enable-debug-messages: false currency-id: 266 currency-data: 0 -language: "en" \ No newline at end of file +language: "en" +auto-update-language-files: true \ No newline at end of file diff --git a/nl/armeagle/TradeCraft/TradeCraft.java b/nl/armeagle/TradeCraft/TradeCraft.java index 5fc0f54..1c9e7ac 100644 --- a/nl/armeagle/TradeCraft/TradeCraft.java +++ b/nl/armeagle/TradeCraft/TradeCraft.java @@ -1,6 +1,10 @@ package nl.armeagle.TradeCraft; +import java.io.IOException; +import java.net.URL; +import java.net.URLConnection; import java.util.ArrayList; +import java.util.logging.Level; import java.util.logging.Logger; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -400,5 +404,36 @@ public String getCurrencyName() { return name; } } + + /** + * Return the last modified time stamp of a resource with the given file path. + * @param filePath + * @return -1 in case of errors, or else the seconds since epoch at which point this resource was last modified. + */ + public static long resourceLastModified(String filePath) { + URL resource = TradeCraft.class.getResource(filePath); + if ( resource == null) { + return -1; + } + URLConnection resConn; + try { + resConn = resource.openConnection(); + } catch ( IOException ioe ) { + return -1; + } + if ( resConn == null ) { + return -1; + } + long lastModified = resConn.getLastModified(); + // calling getLastModified apparently opens an InputStream that should be closed + try { + resConn.getInputStream().close(); + } catch ( Exception e ) {} + // finally return the last modified time code + return lastModified; + } + public void log(Level level, String format, Object... args) { + this.log.log(level, TradeCraft.pluginName +": "+ String.format(format, args)); + } } \ No newline at end of file diff --git a/nl/armeagle/TradeCraft/TradeCraftDataFile.java b/nl/armeagle/TradeCraft/TradeCraftDataFile.java index cb55c4f..8e29e84 100644 --- a/nl/armeagle/TradeCraft/TradeCraftDataFile.java +++ b/nl/armeagle/TradeCraft/TradeCraftDataFile.java @@ -32,6 +32,7 @@ class TradeCraftDataFile { private final TradeCraft plugin; private final Map data = new HashMap(); private final File dFile = new File(fileName); + public boolean wasLoaded = false; TradeCraftDataFile(TradeCraft plugin) { @@ -130,9 +131,17 @@ public void load() { } catch (IOException e) { plugin.log.warning("Error reading " + fileName); } + this.wasLoaded = true; } public void save() { + if ( ! this.wasLoaded ) { + this.plugin.log.severe("TradeCraft: failed to load data file when plugin was enabled, will not save to prevent loss of items."); + // The failure should have been such that no interaction with shops would have been possible, so no items should have been lost since the plugin was + // loaded till this save point. TODO, make sure that no items are lost, when save is actually called after motations, even though that situation + // should never possibly occur. + return; + } try { BufferedWriter writer = new BufferedWriter(new FileWriter(fileName)); diff --git a/nl/armeagle/TradeCraft/TradeCraftItemShop.java b/nl/armeagle/TradeCraft/TradeCraftItemShop.java index 8fee9f1..0caf904 100644 --- a/nl/armeagle/TradeCraft/TradeCraftItemShop.java +++ b/nl/armeagle/TradeCraft/TradeCraftItemShop.java @@ -27,13 +27,34 @@ private void handleOwnerClick(Player player) { if (getChestItemCount() == 0) { int currencyAmount = withdrawCurrency(); if (currencyAmount > 0) { - populateChest(TradeCraft.currency, currencyAmount); - plugin.sendMessage(player, "%1$s %2$d %3$s.", TradeCraftLocalization.get("WITHDREW"), currencyAmount, plugin.getCurrencyName()); + // limit amount of currency dropped into the chest to the max amount it can hold + int maxCurrencyChestCanHold = chest.getSize() * TradeCraft.getMaxStackSize(TradeCraft.currency.id); + if ( currencyAmount > maxCurrencyChestCanHold ) { + populateChest(TradeCraft.currency, maxCurrencyChestCanHold); // fill to the max + depositCurrency(currencyAmount - maxCurrencyChestCanHold); // return remaining money back to shop data + plugin.sendMessage(player, TradeCraftLocalization.get("WITHDREW_X_CURRENCY_SHOP_STILL_HOLDS_Y_CURRENCY"), + maxCurrencyChestCanHold, + plugin.getCurrencyName(), + currencyAmount - maxCurrencyChestCanHold); + } else { + populateChest(TradeCraft.currency, currencyAmount); + plugin.sendMessage(player, "%1$s %2$d %3$s.", TradeCraftLocalization.get("WITHDREW"), currencyAmount, plugin.getCurrencyName()); + } } else { int itemAmount = withdrawItems(); if (itemAmount > 0) { - populateChest(getItemType(), itemAmount); - plugin.sendMessage(player, "%1$s %2$d %3$s.", TradeCraftLocalization.get("WITHDREW"), itemAmount, getItemName()); + int maxItemsChestCanHold = chest.getSize() * TradeCraft.getMaxStackSize(getChestItemType().id); + if ( itemAmount > maxItemsChestCanHold ) { + populateChest(getItemType(), maxItemsChestCanHold); + depositItems(itemAmount - maxItemsChestCanHold); + plugin.sendMessage(player, TradeCraftLocalization.get("WITHDREW_X_A_SHOP_STILL_HOLDS_Y_A"), + itemAmount, + getItemName(), + itemAmount - maxItemsChestCanHold); + } else { + populateChest(getItemType(), itemAmount); + plugin.sendMessage(player, "%1$s %2$d %3$s.", TradeCraftLocalization.get("WITHDREW"), itemAmount, getItemName()); + } } else { plugin.sendMessage(player, TradeCraftLocalization.get("THERE_IS_NOTHING_TO_WITHDRAW")); } @@ -140,14 +161,10 @@ private void playerWantsToBuy(Player player) { int currencyPlayerWantsToSpend = getChestItemCount(); int amountPlayerWantsToBuy = ((currencyPlayerWantsToSpend - (currencyPlayerWantsToSpend % getBuyValue()) ) / getBuyValue()) * getBuyAmount(); - if ( amountPlayerWantsToBuy > this.chest.getSize()*64 ) { - if ( getBuyValue() > this.chest.getSize()*64 ) { - plugin.sendMessage(player, TradeCraftLocalization.get("THIS_SHOP_ALWAYS_RETURNS_TOO_MUCH")); - } else { - plugin.sendMessage(player, TradeCraftLocalization.get("THIS_SHOP_WOULD_RETURN_TOO_MANY_BUY_LESS")); - } - return; - } + if ( getBuyAmount() > this.chest.getSize() * TradeCraft.getMaxStackSize(getItemType().id) ) { + plugin.sendMessage(player, TradeCraftLocalization.get("THIS_SHOP_ALWAYS_RETURNS_TOO_MUCH")); + return; + } if (amountPlayerWantsToBuy == 0) { plugin.sendMessage(player, @@ -174,6 +191,13 @@ private void playerWantsToBuy(Player player) { int requiredCurrencyForThatAmount = amountPlayerWantsToBuy * getBuyValue() / getBuyAmount(); + if ( Math.ceil( (currencyPlayerWantsToSpend - requiredCurrencyForThatAmount) / TradeCraft.getMaxStackSize(TradeCraft.currency.id)) + + Math.ceil( amountPlayerWantsToBuy / TradeCraft.getMaxStackSize(getItemType().id)) + > this.chest.getSize() ) { + plugin.sendMessage(player, TradeCraftLocalization.get("THIS_SHOP_WOULD_RETURN_TOO_MANY_BUY_LESS")); + return; + } + updateItemAndCurrencyAmounts(-amountPlayerWantsToBuy, requiredCurrencyForThatAmount); chest.clear(); @@ -197,14 +221,11 @@ private void playerWantsToSell(Player player) { int amountPlayerWantsToSell = getChestItemCount(); int currencyPlayerShouldReceive = ((amountPlayerWantsToSell - (amountPlayerWantsToSell % getSellAmount())) / getSellAmount()) * getSellValue(); - if ( currencyPlayerShouldReceive > this.chest.getSize()*64 ) { - if ( getSellValue() > this.chest.getSize()*64 ) { - plugin.sendMessage(player, TradeCraftLocalization.get("THIS_SHOP_ALWAYS_RETURNS_TOO_MUCH_CURRENCY"), this.plugin.getCurrencyName()); - } else { - plugin.sendMessage(player, TradeCraftLocalization.get("THIS_SHOP_WOULD_RETURN_TOO_MUCH_CURRENCY_SELL_LESS"), this.plugin.getCurrencyName()); - } - return; - } + // prevent too much currency (more than can fit in a chest) to be given to the customer + if ( getSellValue() > this.chest.getSize() * TradeCraft.getMaxStackSize(TradeCraft.currency.id) ) { + plugin.sendMessage(player, TradeCraftLocalization.get("THIS_SHOP_ALWAYS_RETURNS_TOO_MUCH_CURRENCY"), this.plugin.getCurrencyName()); + return; + } if (currencyPlayerShouldReceive == 0) { plugin.sendMessage(player, @@ -225,6 +246,14 @@ private void playerWantsToSell(Player player) { int amountThatCanBeSold = currencyPlayerShouldReceive * getSellAmount() / getSellValue(); + // prevent too much items+currency stacks to end up in the chest + if ( Math.ceil( (amountPlayerWantsToSell - amountThatCanBeSold)/ TradeCraft.getMaxStackSize(getItemType().id) ) + + Math.ceil( currencyPlayerShouldReceive / TradeCraft.getMaxStackSize(TradeCraft.currency.id) ) + > chest.getSize() ) { + plugin.sendMessage(player, TradeCraftLocalization.get("THIS_SHOP_WOULD_RETURN_TOO_MUCH_CURRENCY_SELL_LESS"), this.plugin.getCurrencyName()); + return; + } + updateItemAndCurrencyAmounts(amountThatCanBeSold, -currencyPlayerShouldReceive); chest.clear(); diff --git a/nl/armeagle/TradeCraft/TradeCraftLocalization.java b/nl/armeagle/TradeCraft/TradeCraftLocalization.java index 3875c51..91c1b78 100644 --- a/nl/armeagle/TradeCraft/TradeCraftLocalization.java +++ b/nl/armeagle/TradeCraft/TradeCraftLocalization.java @@ -4,6 +4,7 @@ import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; +import java.util.logging.Level; import org.bukkit.util.config.Configuration; @@ -35,26 +36,44 @@ public TradeCraftLocalization(TradeCraft plugin) { } path = null; - // if file does not exist in this directory, copy it from the jar + // If file does not exist in this directory, copy it from the jar. + // Or if the file in the jar was updated and the setting allows for automatic updating, then do so. String fileName = String.format(TradeCraftLocalization.filePreName, this.language); + File file = new File(filePath + File.separator + fileName); - if ( !file.exists() ) { - InputStream input = this.getClass().getResourceAsStream("/" + fileName); + + if ( !file.exists() + || this.plugin.properties.autoUpdateLanguageFiles() + && TradeCraft.resourceLastModified("/"+ fileName) > file.lastModified() ) { + InputStream input = this.getClass().getResourceAsStream("/" + fileName); // If this file does not exist (not a default supported language), then revert back to the default language file. // That file will exist in the jar for sure if ( input == null ) { - this.plugin.log.info(fileName +" was not found in "+ TradeCraft.pluginName +".jar, using default language \""+ TradeCraftPropertiesFile.defaultLanguage +"\" instead"); + this.plugin.log(Level.INFO, "%1$s was not found in %2$s.jar, using default language \"%3$s\" instead", + fileName, + TradeCraft.pluginName, + TradeCraftPropertiesFile.defaultLanguage); this.language = TradeCraftPropertiesFile.defaultLanguage; fileName = String.format(TradeCraftLocalization.filePreName, this.language); file = new File(filePath + File.separator + fileName); - if ( !file.exists() ) { + // check whether this file already exists, or else copy it over + if ( !file.exists() || this.plugin.properties.autoUpdateLanguageFiles() + && TradeCraft.resourceLastModified("/"+ fileName) > file.lastModified()) { input = this.getClass().getResourceAsStream("/" + fileName); } } // if input is not null, then we need to copy the given file from the jar if ( input != null ) { - this.plugin.log.info(filePath + File.separator + fileName +" does not exist, creating..."); + if ( !file.exists() ) { + this.plugin.log(Level.INFO, "%1$s%2$s%3$s does not exist, creating...", + filePath, + File.separator, + fileName); + } else { + this.plugin.log(Level.INFO, "%1$s has a new version, updating...", + fileName); + } FileOutputStream output = null; @@ -82,7 +101,6 @@ public TradeCraftLocalization(TradeCraft plugin) { } } } - TradeCraftLocalization.localization = new Configuration(file); TradeCraftLocalization.localization.load(); } diff --git a/nl/armeagle/TradeCraft/TradeCraftPropertiesFile.java b/nl/armeagle/TradeCraft/TradeCraftPropertiesFile.java index 80bebb2..5b90b32 100644 --- a/nl/armeagle/TradeCraft/TradeCraftPropertiesFile.java +++ b/nl/armeagle/TradeCraft/TradeCraftPropertiesFile.java @@ -98,4 +98,8 @@ public boolean getEnableDebugMessages() { public String getLanguage() { return properties.getString("language", TradeCraftPropertiesFile.defaultLanguage); } + + public boolean autoUpdateLanguageFiles() { + return properties.getBoolean("auto-update-language-files", true); + } } From b2301edbabedeb99b3e7c6ae305c4a18a9569dae Mon Sep 17 00:00:00 2001 From: ArmEagle Date: Tue, 26 Apr 2011 14:14:24 +0200 Subject: [PATCH 044/100] untrack TradeCraft.jar --- TradeCraft.jar | Bin 61291 -> 47700 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/TradeCraft.jar b/TradeCraft.jar index ed2b50942f54863bc0bdb91615898ed09e664bdb..bff783bdb28f0de9ead542ba7060c11ab75757af 100644 GIT binary patch delta 42055 zcmV)YK&-#(-UHP10v%9G0|XQR2mlBG>tLEb000000000000000CjbPIp(ua;$^Zbh zdI?}u)!Fv@p3LM|V1boUH7M zba#ZKm8(N*LzN5gXR&@~^4bdxp-ziwSiN44grXgl!DKuf?Wkrtp*9vxBty~UvQVVk z@=y^D^&nyz5l&j2i$lqz6^{nmf?bhta&9OYV)EJ0x|88ZW!&np)>nTzy8yHx66;_Z zowWlKprfO*9#1QdErkgyzSfE}71w9+5!91d_Oy1#<5skF1JmUCe&rDWjn%?!Jl18! zlVK~tbXxz%7H7843r8#wMr$nE9`5LlhmzqK#CLlCFlw_;IAF|3tThw~Z*-tk_79~# z>i___+O&u(`adYTNE3g*5@^@dhZ9LFDuSKf|LHl|Cjeq_S0uE-iub@6h!As*0LUQ7 zv@@JYfEB1=+5jN*?8osFvyP|Dcv^jHtg|v2UL9T&Thkp)TT9l#%3L6thPr5IWyES@ z;)|G0S`|txu{wmzxYbs-)`}+6G=oadIjxa!G@P6b+bu0y1`U7I#@gTwW$>CyOD}G# zC@Vd$s#*`q&O6mh6*SeLDNG~#@Bx#iQ6-ZBvb&%irm3aJI$v3z9#ljSy-8jgB%_%o zok|m!hSZ0nRzr8^N-N$ZoQX$^-7E{m!}9J-7M`Oy@lpYuWzsB~6Oox@UC!m6U80h8TLiZe#Ogh1f(Oi@2Xr2u)==gm}X;~i6 zmvi$?8cahBS^%t{&V;=bpnBnX5!0m7{w=S%Y?&ZzG-))QWYA~etDn-|y|kF>1xAxe zV`!{Fu$?KN#^SxS)CSOO(l{C~qZKCk>0}vQV$vz1c~^gORTx$}VE`OK$g$|$a6)WB zK&&)r7!}K?&7|Q}BBOSbM$kwZtukp8`Mk86A_lEtDjRUXF{zU#fwDwsEo{yci`w31 zP}hKjm=k%u6r*~Bz~PKfA8)-Br+VQ45Z1xbK2@ys(potjr;R3Eq4|l#LTv(VqY!nq zNuQ-_;CX+r+gc|fHErku!)37Efu$u8^Cpvq(utz#%_dzZc!nmdWOrAV9Souy=q7`1 zL;yU-D@^)4-HbD!!j6cGLeY@+$_cqnkqm-3EW%#WcQ;ZHwdKSUjBEVA4JGB}Au& zSiCb7LHW`b-1LF!L9~VLGiYlr?0h_$bU*zUGGN>Z@EQ=)+S23Fe(Yiot=O%0^EzWgw!e*DMNkIawc3zc$v{84}LhbL_s=p00G3gAh(H3-7g~`dGQG$Ro;&y!5(Q)DM49 z>GuuHpf^!yWV=%|2?wgQ)2N4j1YbZbOqcs&O+PW|E&3_!$-2Bd6iEa+qOrJD8%kJx zSi?mUSbuKPFX)$Wpw>_{*b}W0zjA{>Hg+8K(62#$Lu1S0x+RMO!C;`Vp(R+?)Ka^2 z$&$K;+Ga@*zcJ~z^gC3BFpFL`+^2t8A$Yw(zc=Y!u?R32ZChj|641ERj(;%ekMt*K zzB80;T_tgRW;W2;RiStgyn}u#Pm#FDJ_L*X3;oTYzvihzoh>H)o&J$QlhCy-IQ}Op zV!wy}&EyXRTfqCWn)<-p49hJ`gLR1i!KS*Jxq{K_Gvx&mN&q z4Eh-Pt7k0`O-)nryH~ zdO<;^XZk=F^fI#-VTK2Rxza$eY?;iQATxvE#$a<9N&_AW_4A1+#s*MO>Wn{t${?qT z2_RlTeladKc{rDVr9|&?qkMnBxR-f&BpQQ^GNECy=cYzv8TOexTH;oAJtLvr!(-rX zfreZ|@bEZnZ3`!)D@;46hy5_fhleMN4Q@!R zighJ~iZYYSxdLI;0UeZuejIg~!BbHO^k~uUgXT#wk*r(aYB}k$Frk0L8`CNMhd{eO zvqR>X z-E08=EXghXNTb?dG;#j^ z-|2jDq|lC}y~2!hLPDusNgygEX)VC*0f#XRbYenBMR_a52L;$3MV7Mb1N_Kz8L16s<72E-+Rd|9*5hm!Y zL3|J2YcSf)5&5y0yoI;gt&N>vN9ML!h`AOFE&|)XpC2$7&1G?(Ws@J|hoFbr`E|i2 zG5EefeA(njWM|Rxn)><{=?O(8kD0uU(e$0WFlqqUJW7GxU^}$%3%%riCel_o{|+We9kAEFNjIOMomA^=!4+z;8|dovvB1_d=QYy~*#UCz@sA4<`RHJ#o<>{xkp8 z;J=`l?hVf5zwzJgs|78jnxs^`fk2>73-$1ODDs+P-Tu}{xOI)y<`4N3keNS}^iOP? z;{2Ot@V|1dUTmL5JI3#uw447UB~ngucbb1PZEPQ!{CVCad>k?PWB$aJN-zl>q1x`# zSoMZ#L!qfFIcA6G)uYhKjcHnC*%XHp{%Fh}iA90epF}ZhO&i2VmCICaRcPasZyO2y zRUl7o4N{fIR7J`Q#9) zt)E{A&$}*XCnm6KDAR;&XwqLN{VT1g6-ut^0cWUT`ErmB@2DHpa8s2?4L2wmtBk<$`M2ry1=Ca`A&1J9qQQJ(3wtq>E`0 z&UECATm?@|*jh~bTQmFoVO#6=jK4h`v8JIzQZr07Q=M*K)JlI8Z)~@NTt$DLJfw-t zYmKvd*-uPB9czxF?6XXDw&-c37(R%Jg792*BZ`g-MD;=+`u3=Ek*+e@gB?47(h59p zAYV?hv3u2QRqj!95TZnw`RRAuMYx!2syg9fwEl=8#f$DPa~Zh+Qn44O3c$8Lxe4g# zlD5UJ-bcXDLZ;~h{Fav?#H)W6s6}Fy=t?{OKbU2{n`|^|>+x>`{%zE`RBUNvAAp@! z(hhxt^}-x05?g0mpj!u_yrqGSUv7y-Px{J7p#$a>9)&)aFOdLysi|7jWwvDpyR25^ z=qBCHEzB2V2QrhlyD1VDooH{Soyew0E;ptMDeb1}P^+98sE3qBeU5)=L7wL7e@%}k zO^-BQajQMNUT%@P+x0-YZC4VgB@o9<)g{5&g`=G^5jRyr0*p7248@a)<>BNineH~# zTAB7((YDOwdQ)wXI-xLPMLX=ND@=8zx=JL{y)vQ6(YZR`?W#w8mWk|(ySnwQtFUA( z(Mh^4pK<~NsQrWj)eQPAQvH>sOuWm8bt?D+r97tE*F#Xa%{yt8Qe9=@lX%)nx^|4T!q3%T7%-S4| z$~V4Pe1lvRb!wzV%<67a-6K~YD%>Wo_nI_HZGmY7z3L8ipM-yl`vDVrlM7^Z2j45W z9yHZM>S4RyYp~YY{lmo4Ic0fE;WU}DO_TbFT=qQURgckYhT4{UiI?}3M?H=J5bcgc z{9;;PG1ZgmDfBK#(-GY!&F%9GLqA_0a?S9oki*5Lf?|iMcB);{G|1(pRAPC|CXa1; z)NatV!ryYq6{mk)C4f?<+AE(|3Xy7CL)z2!o9a2IELz&tWtT-V|224we2VbeSvjCT zw0+tiX*u^wC!}w(w4x#6`s15|T->IQ*y=o`e_du>m<1gVcdv`Z+vF!l@@xsq<+DqM zh<0%+5$le(TJ@nsa#5@;+#a@icjRfoF7T>vsqYx-+c|$9Kx!kQM8Z_xRflcISmFQ# zt$p9&My+J!(k1oP?xW6sUiG?q!%#l}!`T~5^``nE7>adSQ3y1OE<<0rk1ovx)U#IY zRXZj^wwx8!RjaGYsoc@KWeyM(CC#68Nt6Oe4tx%_ZVWVM&W0hG8 zK>4=%t)YK@lfHt^T{hM4j3%^8(s+=pzyV>o7IZb@?~&_5!~5eP3}swvI)gDY#T6&dJVrAMJzGEe#aV(v$a+;i~Npca3U{e^o-59(=h1jGa&k+?QoD`BXk zx$RQcZ!aM5^sg`AF>-51CnF;k5YZ4CMQ$p@n}Ih)9`ea==P({Bzd+N^!PrGu8A>OT z{5On>^>a9tkYv^oG*Z8h!dY1zO(*H+7#geB$I*EG^aHCrPoW8T7RfL3@Eifi6KN9W zC*yzZ3_Rt(6NZ)Uqw;5Idcm_aW2YwVbo?ExiJpKXXM()(*e)vqbXEq$ZRFBG=9cfJ zGs}may^pH*P)&JxifZ@J1?2}Q(2SLZd#C~9#WG&9hk_U{mGSaDbYVH~p^M53_Rz&W zm?wc?0!9}`Cjd_gO$R%t(Nvm2XVXlYja`4W4n?MopDv{qP1|J|+Aboc*Tx8nko;+t zKUVo}>PS&o#vMI4M?*10RZvhh$4-~EIeZ}c%o@M+<-G8m)F2nOxU_y z&>f(-jFW=(0IimBHxOdHR>tdXLRwR_!KUX*JVGhD>RGxLqYG2?xm`44D+CKs?x%n2 znd&Qc(JfdC?xH)&_tU*@!o&lZXvm&;IA_8Ig#y5%^_Z^g_nZPf5}DtK1bY(;Hj(nTnU_jjBSQd)Nme=|x%moH z^s}ur%vU(&Vd-rq+b%_{PF+!!9D(Fd5DOPtqzT(Fmms*LS4*q+|YW|bl8^%*H1 za*(QhhOO7oF1j|w!)(08JOY@qFqfxzR2JgxbSS;?q}+`*BUu|Cs45!h)`ZVp%wt7x zGy{U$N>kD&$M-|c*>dt^eR6-47CD`$MJ^b4!glh>`Xs$R7@|#a>2`7#Jmq={%Djrl zvY)3owsp0RQY+wcm%_nY5aU9yua&T`R=Ar5tLuP8g=rbBrYmR-T}3gv5f*kkT~3cv zf}W*rdXd(`zShBV*3T~bsrA#mJiYixy zZ6cRsOr)4w#74x%UAs91GvCc^m?`E}+UoS&YCX{@PW7H1#%zDrvJcCq%Tt`pwzi&? zby!K;o=~!pY3tFL#3z@wp;~-F22S|1NPdrSQ9-? zq=Jv>RIQ=;N0EQ03k|Icdx8wuB7Hm3ton zL60I6J(flE&ckr&kyGzOiooo2!E#xh) zm;!y@))V$^%#S1LJ^@31Qe!T+Ph~I<%U~Xs!92{td{}=&V&o3~BHs?rxI>uYJGD(d z4?qa#wC@1VZ{EvyHB8yhcQftc`yQhq>G8wcXf()5@uO8PpX(sqGS%lw@#D+4(6>g(ZbdbDLeMN$HvztrY2zL)3eSGSUjP=>s zF$MqLhwQeWO6fV2CkK$(UPRt|39;>E#Isi@f~>cJ4$&>hbN9gh9)y4%MS^=0ds6g6 zE#59&1MbU+cV9-l`}lkO-?~!V!Qa=`SV}|qb^d<=s8~uNeuLk{=t8=be~1X<2F3vY zh<8FI5ju~5jI#zXkLRE8TY&K(nfh!|#*%HgKXueLo{S^pht(dTVuOE%u<|iYF!&e9 z%8lv+DO%+_uwzS~2V$|5nHd#sqX`H#zhuJ9Z}i`HPyt_tC)$+#{3jP;kf`-LJ2J*L zLYsf|Pe^`AIf{OYlIks*LchqW8D?grGBYEUnT}K(Q=0<14yWlVxE6Fmocw;qoE{@L z@33i_Z4))iWfL{aU5>!_kHY8qpUnkh_wm1BRwXX|beFj0nc@#@p2n8ItTYq*`6G#m zM^S(j9zq7JC{|nm2-wOykoNYg0;ZHQ#PENd^+7%Bcss$R@%fA#=&m*gz8GYWSLpLY zGUt!oZj+- zRkp*aV$_taG(t92W>e3y+<{cDPOI|zyocy~*>Z*h$0p%S2Uk@Nu5&WDW~XtTuNQx6 z9QfW~212a^VO|b|`56ccYzRK@URB>?TL;lF5WNlC`VE5GJ7^+)j~?k=#OptxcKs84 z<*%qR{)TGeAE+k&MVnAX+(7?9Hu(^lKXZ~?u^h4gp!&`~bJ|GZqn zgLotlMpZL}r|?iz55-)?!}$Wt26=xJGN+F(<>IPvNyZfj99)zJ(|8 zy*!z>B4=;o3Vs$4UPa|`m`~%kcn1HB&)|1?R!@ZB_cOK1`)Oam$o7R2ep@vn1{Ba( zeoHOJ$VC(Rb@drLF7VxIi3%bvY~pU!gr@=C&Q(is)&s5z)iSjlap7{hUR{5PRj>A~ zSu|+yQEDf{$!L5JqxRsVL`P^m^4k%VFMQ7tsxj1wBSwuoLNhTrsubbANO_$waX#pf z^k9n0nv2znz3P%3nKW0Zd8pDnjS!d-qtSSuzza0$**4G&>WOJaFk0NMc$=N=54tQPt&J=*)_#DV5wpm6%-Tev~FxxMY9iYD=k&l18yr z9>wZv9UgGn=h~6Q)HH2j4XAP&fp-ZF;wCzYmx8I~G@UP`Gr1Yn?L{<~FQE(gvaEt6 zkYP07$XG^cMgtC`(`lq;&!yQjsi5!(YF^s;q3lxEsB0Y-+q6)KhL5?AipEsD@UW0t zUNL4yN^R<)Ru?JUMqYomkkdM{^2P8BHN!JvAFi&0i0ym<`|+~IxY@yYF3t(1MF-UN z%@upqjlB(Ob(TRDWxy9X%zD&K>hlh$8N?c)XY@XGb4q>Tsa}vevu+YR+zO&Fscz5Q zSX9Wn{M*^`L7H_}e6|2Q-uS0=k9TI&{bNPWdY@>GAICf{*D-O-G>-Rdrv zetc=K+PVl<^I!Jc?J4y@!_-5lzc7_jU#@~L6v3Dtwe4lQHkl`i)zc~UOu>v|^(>xM zZp>B{7R*A+dzQ~#V$7)Wlo$_?iMIQ_l-eV?(dQ|E)oo0veVC9AS{*=s(u?nIR)gpX^}Kok(ki2y)r;ySNcSwdQoXFct}FZk^@_I5 z25NUkVOpqOWwG!m&C@+WA$rgA)oW0ifu6KNAwa-Q59@#O7{)x@VC!QyA z*!Cwh(eBzt2=gHb^HD^p$7mF9 zqcJcRKRu-ipWwC#t=ijeu1IhIYKT&{n$7PhjmFqq+Z9X3)w`y%XBS*@_U(% z$gO*fadJ^*_nPXT=rVPKTj^>P-Kjn)p!0onE1rD5@BaW$O9u$us+&IW9RL96KmY(xO9KQH00;mG z0Q#Poli@2xf1Owhd|Oo+|DQJLP18Htb!)rqnF$-|+O9Ark9KypX}3n&bT(OMH=x`! zx9z2AQj(jE4irH|QSpJ#)z<)JD!!(WQE&*pP{0THL{Y&fsEE8lW%YmVO>FYa{8wLBp)o_v&%8HX>wOELuj~?V_!CjxjeHcTP@oFr`zpH%A#jQcCkh0L+l;s#ObgM6VX> z(vpV!f1TZQm;**i;EegNonaw$c1HC`x?iBo*LI1vSF4R_@&4M7nKa`4)XoV zHVTjHmze@fCnQ)g?}%KY_nP$zS_Rh43svbJH@4#qD$Yk6;Wli!!mq`fb;2nzBCzTN z9;@74NPh=abYO=-(XgHzGEylc5oZc+f1E!BcTCaKot+3P2nkf544;afQt#s4NsXtm;=$Iwl6LL;lgf#6^(6eJ%Mmbd#I;Vm zY}^%e;{sfy;6j0#c{Q?1k+EMhsgsHxT+EmgeO3XSovgFB6h!F!h z>G9}f4QM(MjAu#TGaDb3T}2qcB?=5WC^zm^3?e3=(2nrPFfXl`nRe|O;b>^F`a;Ap ztRTTm$Z@RVQV{2byQHf_f$c$7u$*FCpud)PckJx(HwME!%`KsDXGeEWxHS;6_~bQv zRqT_}oc>lGyFh%d$jy-@xSTC6e>Xu>TZjMrp2l#vk)+LTwwgDqcnhv1m{Mk9*g{jf zR|ZtT@0+VOEWD7)jJ`_6)e@4*fR+j}d1dxoqvGwjmf0NDOpUd9{I?!l&&PM^=+MteLaLSp+N?H_{ceC_+Ymya)$zvx?8)v$jMzIHjx+WY6r< zAUo_K6`#ZBIVGfYlXbFJe~(!m-r?IVQF{=#s`w(lMAW8iVX|ojat%q6_$w;Dio=Y1 zdN^uXBE_)Miip(>`z#DzQQRy`_xDuXe~0h0bd#Q~d&uTF z-Iv3-9AaF!pTnl~n77wbrrH}%csV!>dE*pR?d3@#smqHVRPjUEBb;VZ>(ymZ`LT+h z;2|QhHxchM`qN3xE@YeMN3nl8N4CuT6hBw+Gl5Mf&n>&*F9ce1_Q8`A7l`*I=$2mz z)Xi|qx|G*Q(K)79fAj8T8;;KQl2<+pypl`7uNTtLTQ0MucM`)gppUBfEq-Um-p+wC zUm$1jAHiet3A0qL{-ENIR=qb;T~aYQXsQc;6Yxy7G=FDHW4NU!5Zu-iYVFt&lDZy}*F2@-f1mQ2((E;MwaMP}FV2Qi zL~{p4v^I9N^z3NqY@ZOeqph*KrPGqCm~(7u75|mPazRXw_nQN59HYlD&bQwbA*X}^ zRS3anZvK#be~3DLfq)#UUl9&fI0avbg{gETWy!M&RPN|JCtRv16mH(84G(jqA{Z7l zqGc_zM5wA*BnyGdOxTCg>$kESixO2V7Ns)p*aJ;HsP79|CGX-Kcl(~zjzx$0D-Xr| zJjLQHg#$}SyA&n-Rc=#bG52Auq*Q{RG)uEGw`XM*Z@3MMGAOGW!-_oYniA}`jw^V< zvS~t#e)2YGN^3lM{Q7UR@Y>3R-MD+$lx4# zDi^P-D>#IkJOyKD6u1Z5>zp2Ebq0Y^ES4;{p`E|pcmPEnX9isdurK>vVF@hlz5{#m zk3!3!_YlH0V_?dC4MVp~oTsLrZ=N_$Po7^kfBk%^y|;gAZ$k~0zv8e|@2vu(roAxsIVhxX@^Ysnrg^M3}g`9`9v% ze@a;K-iP-yh-G*lAHWU7jTcYigZL0N+{*6{<0Hh$vrx0EkCI&_r%m-SQXSw^PO4tVDnwZ>4wIxYN#62R}P4KR4JK zO{PPTn7V=b%9ZqZ9wlwU*Kr%~Sj|}7jts3>4-byu4tiJmvIP0ZP^sXcf-zD2e=<`| zFvfRflPOAH@LTw2Cet@$GJSK>`06p-BaN3tJoruq-|D}Tz-T5A8NRr)!N4l%%p5Gk7@9Pxnmoxjzy)AmDs8F9))Haa{7H5pMn73%#A2C$Q9*u|iCGX%Stkb9Vb7cyKu zI0qLK1R6o0BTTS&qaQK8iiR+V%elIa@BRZw;1Grxs!JIx6AvQIl-$dde;mPMyx|#A zzKARE65sD`TqVxJ)uM?hCt-7e9n#74^y7N5M5ZUH?DSkE)@Na}POKJ7nM4k3Vk4Ku z!bzQ;7UiOX;JcjD{4(JoNUp=3VmVhX3sf~IC>m#hD6lf?sFhg+*em=8jyCHRvEo&< zD|m$e%2~L@Dp~Ty>i+>y97_iX7k`N|=?VY@6aWYS2mmNinvB!ZZM#?&CzAcS`DFmu2o!wenHm~^$WKhU>Ut)x$W z=|icpHEE8UNtk0fIlNo1v*j%dp?q$vSbPzPHpKi>6nYW;CBj+krNO}fx4_UH+ME?%=7i4 zKw!wqTh6dRYoylkY_5=5NT`4Zqf>M_QplP?c+sq*0WAXFNOVfW4uS6f6n{hz+p$w# z-9=lGWOS+?0(*35sFPBwj(P;8)UG20J%s(})X*WY^T|Q!IDjqzecp5`$Os&WM7Kgu z`smi-M<9g5h-!!k?ECjSI%0?mgy?IekaxJbEYKB+UV4fzrki$*%)($0y+~*{@>CCH zO&!nRS%C(67%keytd+Bz6@P_sx}Lxt)o~0#0gsiHCi`_fkK+RMRG%ahl3Asil&U9m z3}T2FXAQ?-h12rFu#QvZ3#S(g#XM=&n6mQbx#HriX-^unInt>mRmd2*DZ{qpySwRg zu2{@AvNhF8T%pU1!0x9}i3Tku{dAUBup?2W=BP@eLG6tjODZZ2uYU-1Zi&9Yt_;g% zSo!6Nd~RhTZ_2Yt)jEXhqHMXGY1onn=XIP@tTwU)GyWgaRh>m12QdjY!UaaO`Xs%1APqj!wzY-qi6H-WJdpT2f)xAK5bZXi5#3 zqGRO}W5)6oLPtCThJRapeep5(0aYTFSy&{AZ7veCtZDP0xI`XNqhYDARLoISWel>1 z&dbHp@hV=E5-Ds-0QXiXHZeC+v~4q=SxM&At$HGr*HJ(nYnyS;1h(_WxkA38%p4d= zOQbf1l%=>+pSI@nhEuc|(XmYmLtB9|JgA{aht+GRZQEFpcYlL$&>X^byr+RNZF}6Z z%<_GKa3r;HY!50LS*4hpHb=bk@tz|0+`j7j|G|?2gi2OS(&AGcH)Kx-(}kj)G0$4k z>W&TVctkou2kYbIhsp;bmV zc<&*+hTSpm8h`f6r7ae}k9`7bI2gNwgZFVrVCIDXVZYBid>fbd_}lx&dw#`a&t3HN z2dJ|94*I@Cmn(7mxm7fl-Uh;fTd0ruX!QrVBM;ZmC&h4J4Fj>6JJ`$3lb7lIrdPCw zec!IH{=oRWc!BGSXyV$;m_w9wgqg`+wBR6i;3VHpV}BQ3!fq^J50-fH4%+Ym+VMH| z;S1_~g>LGAv)(6jro9nBNuxMJL{8!Y&eDR1|F2*SLqsyk^B3S>$8iKS2#sGY!S!We zoj(h-(w2r^wJ9JqJ2q;1Xgh`R66P4@lZps2@3t@D_kQR5u99Lv9Ys`_dP&U1c@!lG9LD!s+ZSAr^azt`wA3xJZR|E(7;4lfI8PJea=e7h%6F)ZcCGOa1<^{~L5~z7+O9eydv4 zLv#*)9P98b%XyR^xMTb~N|>H!paU$!30!3!W;%#?vh0@iknHEL77rBnm`zYJ+j566#x>m`nv65G#o0mXN63=PR;yw5Au4Jm4>L2m%)u^ndI>mJwop_xA zUu69+F`+j&0;c&ryIhm%l$&ZDf%pI)GV)HIlv0heiltOPa!FFs`j}Euk;k{rc~lw= z*Q-P1kUK=K*9?($=DtBu!gQlTftTIB4@+2ngXX;ry@b#w0vo4EwuZLhnrY&PCnhu| z?R=(yzxfYPO9u$0T}lvs1^@u<3;+O7O9KQH00;mG0H}hPlf*?^e?(dC96-1Xm~cn} zmS6xS2qXyhBl(gn+3dzW1hrOc6{*;2y)U%b+E}#~5H=iM)rwbJTW!T^ZL2NS*4k>T zwOW<_-`m|}lPsW=Uw-?&H#6@)Z|2RLna8uA?R^Bm6sa=c5KIXMO0964Z8Zn%(q&<* z(XI+xP0_SrRVdixe{YV3t*Ac~Jk1}l4Y&lyt+URvN&{A~xpc|eb#_Bk;BF#wvDIb^ z3TnF9wb8IY*jypVBT^)41*6NYK+JX{N6@cZ&BW-26vi1=w1vEeP+OZFjB*yW!$E;J z0UC??1EpcR+1^l^WDz^pA8ZPBNoKbPV$FUIM%5hEJTA^se~Tl6{%HypS?%QH)cTu) zRx}o-O)GkGXZAJm=*oKdvYJ{eW>ZW5WmW#D-KOT%!A5(7AkbUaQIV?&aLgNU&uj?z zgZ}7jfwQn^xxiHwYUDW&6%6TZWo67CXtcv_3=UN6rzZbR(rc0Y!pn$1lfm_++l?q zB?3>k?Fw>&iHRts#nDhgSTL}#FGNKS1d~m8knP4)imy34Qaoy6@n{cD#&iQ^DaZ5; zMiVn|3SGnKZKq*J+;oOeR*EZ3&D*F9FdvUC7tsP!wtqtf~9BQxv%dN0q&6C8jg`eY2 z6)pF`iFqm{h8Q?akl$_Z0xQzOnD$^k<|+NvCXPeC8l7%pU}Ch$L_hRr97bFGyqMFz zU|_3=e_~j>0wJr>!+<+U{nVORhO-z=_6C0>8quL%uNW&#tkjIQ)<%Dr!Ca5C4Xo;s z;}Yj)Vl~$AY|m2(Y)O-LM(;eBAs{Q1GrnxX!diw>vmLDpSP^buK#$1NL!0~q7?=Lg5%RK ztvJ_27_=yBeVCby9x7Z_SzWDoF|0Rm9(R~Qt%(gd zpU1>8jn3klqG}H=#6!i_5Ulz~wAGJ<_R(E0U776*lkBdFpx4Ch>0E&F0iJNe<3dM6{Rv^+VSEk~q zUL~7bP27gt>CrYT+R$Q$1yj>yt6HpZe=Sk$V1u0@t1L<^VgTNO?-;l8~Iv@W)FzhsoZfx{LLq2Bu zi0xzHv3)_Ec6=&B-=~uP{S+*$K2mCbfKK)KgFZL52^{5Ix&u26JeUgO3>z@9e@hi8 zW4#p)l4_vy3%5V2VtNqAJmt{6CidYW#!+)P6l+(9m|@3E=y5z^;!#!JTtRk6%C$c> z@wm#lgH_#6rvB>2+V+4yI?sw)%9x*+cv5ZG<<~l&!ZQY*P9;*UpgI_}wOP;NIp!7Z zPSOrjd3m#bhUX3ZyhjqLZ3%^We}KQhFIg%zf~Fh>qt?KSf-#?~8S&r+{90X)m((d*p(UB&po-FB9+URz#&gReRm|H>0zLHH-H{Df^F!kMJ=q zj`%OIb-k)~;}fp%HRzy>D40v!IK*aoemEQo`xF#UN`*~(qKWRAtx0dvO$l3GZNZ?7 z<$#$fr1O8*ea=u7DCJxme`67Jtg7UKfE{dB8pZB`+MT;8pWo2CO7ptbWX`=5_XN5+ z5>?DO>3$U~N~OcTuYy$4OTG%aB)@1Eq1$7Eq+LuJ>u0HQHR`d{va;wZ#G<0xBSYMV zoRF$r`ox#DghT5St&=MK>V2dWe$lGF7mSB4!%gwY2s*UYKEKwEf2xX`p4UmL$dyqt z#*ooH>KYlQjFoW=p=fAsIBc=978b6`5RMsWhOo^ZkYPt2DUk_=j88Q~T9rnRm@-jR zX^ZH5F*u_tJ|(h(35!jCB;{SZ@i6(NUO8ZzDJQGqn9E>^T6|rcZqpQJv+~7)j2+D} z9IH}viYaAMe)J|Te@PNdR|)?U(J>bh-rCv4|}MdS5?4tnF9f`&Nk#S`}97Abe_Lu(xc z+R9z|K0M?u&vKS!cOcK}@@6>)WyjGz!J8FFw5}|B%b|BVu&_K!%`f0;b3z;!cOa|W z?RD+PBCjisOFJ;X+}(jw%Z)w)c}W2^H6A?@yLcxSf9%26h3(TOnsw!oO}nZS-(2i0 zb9Km_m$^i3?wW$@;<(`!WOv9_J5a7jx0GjlY0MT)T)zXe6GX#nka#QRQf19f+@)jZ z9x55c&D>k?L+YSj{oAFq;79yX%a!^#ws*+5eTZ{syJ-kbTquv|^z8m|?Jl-6Iy&oeIgWdRPS+3W;4=>aW@(jxD#IJVYKnLfT=Cy7d%57#4wNiT7ynG3 ze=kQRj$u3V!7@JaF5?4nGp@sp{N9Yaa5ulpNV%6+>VE7*C+Bq zf8@4e7T=pQxsy5ANTkh_z8dqmqtm#fYHYzm?y816n#&zkVH$|9xp?r{?h?U|)os7aNnS`@tD$bEvuw*{g$^zK36wS1w zMJ)IwgmrQ`TIFhvuO;mUKI(7gn|&+Vf8~C@&i5iFkMk}54A#p5Y>;>O`2LXNk4gI! z7cfs<m8xOu?m&Q*pVY8XFy}afRbtTUsf^nyx#k1?k+sU=fiuCst06aO&N@wzxTa?l62ic?%Xe~)Fj zPO>DMDb&VrVH?Gi+Ja${gV$L@HX=(rlFQ$r@`{*}2dA7U2c#dlF1AazOMgmbIiA4e z#7Jhz#d4hF^U<|b)=2?JhHRA+@T3fYTlP}_K+6jXZ=}7zTtx{TDOPe`<)AsXXTplX_M!mYQAIuaZBKqkk(68G1++wcdi< z+E5@(@(^9q2M$KJ#7B_NFvGwmiqsc;f`JC^_ykM%`ziRYjqB-T$(uCh5KCP;)Fy8C z5T?V~MbrBn$CDYxU`tfMmDI1Q)-Zi@J3XC{a_x$Bq@SA|H(2XsiiWhrJvHmI`{S-nd;z$N#SPeD!DZcuU}Uc z?Wf+;8m~;IC-LSd@$$RzW^luZZ|enlcN%1V7i4}KWJMBkF5^*2T-KX(<3CV7YlXz;R!qlOWXJ_!`!ji)o z{y=75%Bgro?^Jg~Qu#knO9u!noc&d+3;+Nk8j~Qk9J8TWg#iPZW}1`CbvBbfTn>L! z)tUc)lbOt9a>FB##DN3{A|X!#2#5*q01^$L27w?Tm*gfHn9RhP2?#wX4<_?Ejp5XEKuvf{MS)oO{l>-}%1( z_x~PeeEiI}9s)2=ZuP_#mXYPwL@X5PY7`iuNN4nFGm)@jk-36^y{9h` z3fIT1E^DCPSsS%=o*=h3+}9O~2+CW|t0mudcTiKo__PDonY~=dYYlZp%tU`*j4H0r zTwOd?5-a;c;SMY2!xVwnMj`G;2}%u=VH%NH>_{rA9BV+;raS~O!;9%e zG+ikJ<;bOCajVyinTcpjiJX6FpaQcf((LWEBGk1mo5(R!j)sjsR0;ee*!WOGOlB)r zDCuS zOZ-@X%e+{agm*0C3@pOs1h_rg)2n^>7N%$6tJ1mAfEPX=mIykv&ewnMsNH^EowGb~ zT)C{FQB5ryZAyk&>eOWh^59cc4@Io?eLcIZ*hX_#SVv&A-3)IsVSo~ z@*@YU6v2wTSRMQ(^P3GADDabIrRuT8zywTGqxA*~F-eWCHsD7- zNuKBq#RWAN7FAB@xWj*p8zufa1Dmi}P|zN;%!IYx>bHZ90kkzAZMfbGMpi*aNE_IO zB66@jY?(1Hb_j}-L9nu~v(t)M9oJeslxsT)SbBii^M2fbQa`q%U4g-fm{ggHZF`3X zYXLe9bfKFB>M;}T-BwI+X*z+Ly3JUtbz`3uX}7Y_Xr291qaS~}5%ywF7M?m=4D=`v zoMYnZg{XmE+(;|CV$r@{l?R%$M3om$+~RbWuSR2n6hRL_V3==U2ziLYJcJ#1#&=Qmbnf ze7->L?)P$oKb|q{_Xt$DQ7O4{ZWgx|H*jGrG^sC1mreh#L({ZH%gU41yUfi%7 z1CVC_FieN_j^{#Flm>nk2iRXgkH9V!JXRq59maDzfu|DUHQC?tNGBf?{=C^fUPht6Q7TuOw&wsFLoQg z2z0GvL5gnN9*?@D7AVqgFsI2SsHFHn0?MfTm-sk2jrCgPs56`}MM1ud6()aRW@kE& z3O>A57|zNbvY@GzSlvym`t{_?4Hc!)aVX33Sq)q740~5aC!_SuSkflU2JqP5kbCza ztC4K619K?3Dx{E9cEl7~3I=}csF9m-N`HUW<)bMsa$5y`7dUWWMvw(c<)0c(12(Ni zkx>!jAc}GXO@(|~olXHb9%}5GjvahkuzaaWP=Kqt&ndS7&fjrajs= zGRpkbA38DWNLgnzSnt7iNoJ9(0lyhL*`1jR7N4pG?V)@2N|LL`6IGhJ&&QY(jl;Eh zV9R^V+UGXsJ%7@2L{3rYd=DJahWxq-~Qjx7}QITCh&{9~RbNU%iyJ{Fx0}2uuVC-MO zp4F_%E+8dk%I(8C4^8_C^3lf9J zaFT6Uq8sNWZ}f~N6^9H<*77d&8$^xU{dK6?+m1KWFy-N!X(VPErmZ$&Nd%oVHU{KF z(HgF~)FY7^)7xvvkyS9)r7$~P@3uRNJ&W1ZEEz^$jcPAea}rU6fGZPT6oxKUW?cK* zND*VTB&@dC1!J3&ETV z=bY>sdyEasa7u0)fFQ|hrbhD^x=Me+K@Bq!L?4N6A`Ws#^^f?Kq*kO%9Q$3d#D z!BF`xigN_gPHrQ!KIiG+<0r_{65pQc?Nm0h#e@nootMBf;B_&LJ!mesw~>Evd}9@Z zD~5z@4dCPKlL5bwD+eGoM+wT{SMU5&Xt(HEX-*i3SP#TR*`Rm+f=V( zT69nOoSMdc;w&mqe-!2A8BT0zd7j9#d+raNN+0OCJqXw~Z>%xK=#wWfnG?+=J4ny% zkO}HTN4mTsfELYyCnT|Tu%Dq6e`G|sy|_M+z;bBN$`7{SRdAXz+a36cKt%Y!=)33n zm+!n&O{9_yQzd0*s`&KLv=8Wfzp(nb?xjAK8duocV9gm-RoH@xFLc#bsN;xZ^$Eke z8>(}t0L6rf#vAXo^q9CWQb}a75;wGU6+^xSi zWI@+3nu9Z<{kreV$xYhum#c2Yh>I_BeVd zZEUR%Nyr#~MUG+vW}EeBr1eLTP&n-wM7ib~ZeKIQ&cd?t@|!OJIz>od((*mhEx-nG zAoclXZ%vVj+*GtB3)I)YI*|IDY77BWDYA&(h>@S(xSiGrh%LTAngL~MeB`9u5O#VD zKy|AzGsn8_PhF2@ZCHTCekg@200VD`cY8dk+m5aOI|*W9=cpY%&VYGtJ+REf@SSwe z3vFw?eeX9)gL3IWGSzjWCocW|Hc_)QF_$S?X9*ag<%r!z1aBjRp`^q1T*vo$0?2vF zRz?(mX!>?c`*u(;?vS1*Cdi}jbyA)IppPde>lh0+9;04HZ;-ZD3AebVt&?uRw)TYM zSStqMeBC<;X|CMKs4hF;UHB{fTxGK8ozZxfl-{V8cTk2iC~yB;#HKg9Kj~dv?3t>& z(67`ebITv&-d<;>B&Baup7Rm8pzNMG)@S&_@nr~YY3~EUjhJ=Vhqu5Uj(OwE1WeEyZ~6PBocmoJEeWy2gXM)VbHxL8ek zmYnJ24@(ec@*)3yUh`w0g)_qAZ2n@@jhb<04;(M|jHg6Q|z zPsuaZ{o0+g_G69-uxQZy+ZUMRp;+&S$m>e}z&_kS@#{4^U)I51av20=pFX# z_Vg^R{fy5iBe6FS;z703T;a*B7cP5f3))9R>X<0E6PdfEPgkcy>~m}8ZF2F87bSPX zd;)AsbWX8IJzx!qYwAxT;87W?YsO#3QEIOT_3-p9G1HjorC6p6!X&kR7Ac48XXa=R z*sSek5=O)-AxaIE3n5B(U5A%sN`-e?vMTKDvHNqKYCDe0C&%X-{AmiwR=dSlqY|Gk z0j;p|-eWqIOe`qMKE;ySxmmP2#onkv-7G15*}J9s0DVVs98K#lfd5ewuj3)P+7>63 zdd-xnVt_ZC^Ot*~bS#=AU4vGLSUL*ImHTu}wV4$a@#U69lbw%l)4=WZ`BX@Xh%aw|E1U`z(CbzVwdW|b>V@njuuk1(z0ALP?UufSF~eu zMu~;q5wMND(tEtxfCF>fiZns=Cg;W!R4)C>QS^%1@WNpc1$DfMCue4)G2RG4bz)kj zFwDi66w(Y1=rjEFL%Wt|+Vy}tb$z$4#uI1z&MQ4jpkb{ykEm01hqizUx`66i1n;}D zeYTN(e4ap+Y?r%S^Xu`xx&k|2Czojli?aN2-wR|4N)@f4Q?HfNk4Ko1swACF&CWJ; zYrrb?DLGY$1s=c9r9ApK9=3=ma`Cc}X5(LH>h!Tf0JHX9`j1G?Um2Glp%{G_8n^Ncp%Iw^qf!M%*Q$J-K2_s`(&GuY(}HcvS&ER6 zlVKz9hI1*$KisHA?Hdf4OVxM(UTjQz$Su*EF2ki0DxQT=f_Lx<8jnap)>N+{@(T?z z#v~~OK(*U<4jq9mMMsCMx7lPfo~f2_$F?W%f1Rf}#dELI&XWg(%x5f={5 z3%A=FH|v=~CTCb(x@~Rx&za>;?5`|_Jm1TyLp+9 z!*l_9jEHqp_(s*5R2@)v}@; zz~d^>VYePxaX}*G=^!>-I+d#%@ zqVcANX-4TqIcRyIujyjXOV8&tU+i?T?`eEhdSUoVD~e{g#R;qRezR4WPCShzUmpGG zPYVg{)3y9TJ%5Vl>(tC6U(Ar-4baIanFrOAjI+<4P;A^JkF3dnPM}*9upa!+|71u1 zP}#V5Bp{$xvj54BHeBs~fy_CjCn2#hCCPiB0F+NJ zem{SHK>C?-D=-blIa>#IO7~s0F|+9EB3#q%JkFLeKFxx-T^~}tg+#**J<{$Rhsd%X z0AAke>G;CI?ApD;(A12OvG_v_Vye*$GP=P3&IL!Xl^a&Av{~WDN+-;d zXmc&uUW~q!If4u_R^dkRhUGS#hbep80Q6h3y;O~&lhlbDDtB7XJ1pWV-dB`wCRJkv z2VG%gM@c`iA=zo|28X ztqx<(mi)Rq6B#LCY%aKp+t#mE6CEl2V_aeMC@LpdV)Nd_BP6~9>~?WxYv(p%fRxj@ zZx!7HADuNMF;UAWwu(r2>{>k+tk_52tK6-`SS7a;t`j)M;>%Xuk{Tlk6+Zi({k4)NbW)(xa+VN3@(>bLDeNTI#BMTnihB_x z;p}Vd9Ve0#c8*Mrfq8Qo+yzr0fU?V|#3ZxSfjUUlS0d;#94`q)MrA%==$Tx`B-YWG z*gLudWcqw#p7A#K_Y<157n@Jz89z}0^`e;g4}}4-@{BSvq6H)rSJ*iKt2769fXLD_ z`iPMX1iEP_*uy{UR|YVCg8bsd!Y8nz4&fChxSw))VPFMCWiUMF1G&aI3!^?U{L|(P8Aljc78uka{4RT#7FFny1XRdUw?kxHk;LP2 zl<&GIHV5B4_mh3Up>m%T;dcsXfJ#c4)VHFl`78PnM?qxGR@P3=M{kDx>Cea8P99M0 zJ_HIv|O!#W6rR zPi~ECi+eFmn^yhz;R=|D=5MXE0FovuME%E2y?P<@kH#L!x)!+|S;617hLBhR8ofFv zQ7$9aU2mOR+O>U1gQnCunKrgkdq+Kd(crRo5&beJ8m!4m3=E?(Wnj_WJsBjY^fc)b z^{j@35y=9LfYM3_b}nGKpY(Y)G^Lk zB~F7>kCh!1OagjwB~*zwv#uRS2337zRVQ-r+GKTe0mXLsPOCg6ptZ_R(zqT$AQ)h$ zv4KAXKuQAZu=O^C2wBf1lUaEDka)@(Bep1Q@URvhriO6t<;?P?RadqAhn2AsEzcRF z3i8S;a%*)R?!0zK6G2Wh&Y&~WE7r}X^CzQw{LxCBmroj8ry4cM4gp-Y^C-f>YMH8k z+`#0i06iP{fuf&=jdqFp!Mml*hS$c&tQknz`K%?eVMt?;jV3W6b{rAP{*9Pw6!p>I z-U56q<`UeWn66~I`_arF;754^P2rEMHCU6U!GUJ?6%5r7xwirTW(IV?WIIbe1-<3W zHYmFVrq^A6`ZIue`v}o5Z2iP(CbJwjqXFHD03282Yh?d|#LO~ie@A5NK#Msgp)-n@ zr{_E(LV6?gv~lt~sM(GJ=UKF$P|NP&jzNfU zb4+JwfZ+APp>z5|n#8q;zKPvuzhoo~-tRg;zQRsNGtCPXN@?5tixvUlM9LiZQqzhAWrF4`*)1PBNk;{QTe5|011 zESQq+&q0&yLtz0h1}Y~hKR+gkSc70h*a9=Z(E~jp1&a-EJV}xUU~^1_%!DAV!lX|V zqh-tvc|1Cdv~4Tu+*+))Euz#Et$bC2!Kqrd{;aRoZmNE2*s8B>rfha*d2&Q&AdagSRn}@vwU^oCwwQ1`I69#&+KFA z>)g7}8qfM?$hYDbyAHJKQ6fx*(8bAv2~U?$K@%4TowP7R%5BYF@>U~h?!Sk`i%kW> zn0Jz~%;%qInbrccoWQBHNGwx|Q4%;(3e|MLupt)%Dsj0QWo1W? zw(^~gz7K$Cp`CZNleeAz9as{le}ap_Z77>iE~aKZ!&9u*5`{(LPla^XQaq~Uw~Gml z!SnF9S5w~zk2!QOdso7agz3>H?#nHdKk&q|X~_ZDtkZ)X&!Rjio2e{DvfR}tjvnlg zcOTePiU$Nf)sCR2ii5rHr=0e1!@+x=n`25tXbmBP}WxR!^Kt{wf*sD}9%Fld-jUQ!h7ha4}qTq&#aF`~+ARu7V=s-U%V?Uz5nxz{xa z=1YJF6MNyAb4qp)$P?kE0@*c8-VG+20VQ_6a)*O;*jW z*yE(n)dx+5?RLO=Of|3?WzHp`RSvAGBhdjWWVtV#TWG3RJmFN!?qEc;Gk8C>=YIg@ zbxlP>d#6{iq1r+>3l1cRuwvRm1E&tnTbk5CvH0w457S``NCpxw*p_gN4g|OVC}s0t zHtOm8%zcVRa7W>~P`+VW_Tw9?rOydJxfe{s{a+~k&6U!prl%^jxfof>t{2_YLI{66 z!$080W0kSNkDJSm8^~_Q$Dw1U{P_Sh^deR!V?b!D3J1eqRDrEl>tums$_jd+(|7ar z%Gos=vuv#1_wKL@z!>bYCtG3%dnE_pjuLvc9792I)GADPj38ix3#|&pr1Nf~EXa{s zL^vH6YTM-pWIjrUO7fKe0ePH+WT6|80wt7t19s1>@0$XL--q_-5$w_xU7LU)?F{QK z;T%PMV{H;EWeF-1qhd*D<)>fE>-HmG(|*3*KP2t5fP3(qrrU^r&|Ej7#}l+?Y5Z{v=AliQl}ybM|Oj}FX*;97D{LB1_WJP z%Y%MfB8z^qZqqBQhC@`=>yX1hu=vQLlWD_Tk*xE)cm6Z_EUs=Xb{5Gl%(?|cYx)qZ z-iI^Dwprf}tEWYz^$BD3S&9a5PShV`Ha>@k z7Jbl;N7`PZ&@`AMYgk;hvGQD?V&JHxcHn;z#~3jLlhRO-&9`#Ri#=y9oD96BqgSR= z9@mj%m9g>=v(}Sjt$JGw@Ncx+ndmT<7sd=2luzx^WL|JDe1pKvWu&1_Vd-+&!zQ5a z+zjv|7}Fi6%s@WDVCw-`=oyF7Y3+Yt?g-^1GP=?{j2zeZ2dGUPSC0`%CwbnXl*2pJ znO#}HyRf^47T^KJO+~aaEd>_VWpyJ#c?|-P?pb@Sr8|48xTe+M-!a{j;Ypm{A$eQ2 zMPNA-)v+G;&SV70;bFLm3flEs#oF|iS9^f_P@FmxIbor7scC{;fT9NmIhgQgxBYm`z|6_n?&p~I2C^Y?7AOTpU$(X0yd zo9WRXp{BTGVTqUinzxw`v7vkWZB7@mm|mOR{^?JDD-QaKv(c{_(tY~M4&H>+t)O1_ zOcDT0Fag5VIUa;!KPoK!-aQ?{v+w+9?wme;ww$~rciljy41@+zFs zD85W>4R_!ZeE2BwePpkZ@}7(GkIH1a*<@8d$pZJ#q1*FpKkm34U%xv>S$t_mY zQ~DPVE)Tp>Km;4RnNjLjF3=f@XNt{WPdUKQd#}GvRm2D;0dr7Cgb^XDgUkP;TWfCQ zrF@iJ@&m4QBaHHj0{zglb(Y>3$uB_qo|?)3Y<0wRML-~8;Is#alaq0lePpDTn_^a+VZE0m4dOp63Pk`6F6 z2D?|CUZM2T4rh(U2seJ{){C~(TDQ-9y7iR(NwcT3+%%j`%~WK=SDNjO%_~LA6HvIc zUcNly!x`YjC;wnKcR&(&Qrz1gF$B(sHFsI;@y_s*+wd1*Ds1TEPkj`^iFwzyu;7ZIjOcndB-b=PLz85sI^V}-tTxo5blsQ^^kQI)qoo-7?vxWTcu{Z+>9xI7tmT2>3WIU zKKM#tT!mY0h-1=bKfVi$_XGgcPBG8$>Ya4E^mg>-5|OVe_5l6`_g00zzQY^Ey~6!o zG!Ss7;Hg6XL^#Gyear%W&euJ>pI)+SWc`Tj`}nH~R-QMeB+|uNJ$E=Wh1v4LN?kFf zN&@(z!{;{|?=U$`<|eJ<{_x}!v5~2#Y019(Z?d^ygD!cZ>)ryZ#3n%1#7}oP0Y>=# z23?~W;1U%D!i$caDSO&;o;sm{fR^cc#3t%Zf8uJ=cy6{{b#>8NhiZbJR|V*`yB^}t z@b<8hI%!p^auiBe+#$v;;nmC?zPEB$Up;+anm&5xP*w`j2X)lt8j}Po8}(_|qbbIq zJ8j^eCi2fdeo>%WyBHwk4zWDX2p__^ENhG##=VJeJ8GjnF2k39wf>?98DXDl(1o~H z4w7GlfnSD!KQQZ_!tQk2wi>qW$dTVwIot?^)_%gX=!7d;g*GGeoEjskn8$WSpt1 zeeh@&7PQh04_C0#843?^^_P=%Osw4b#G9a-FOrK-|D>W@totY1!#BsnH^{>m<=0nY z#VhUMvu)|xk;LKJdVsqF@zOc%JMHc9wyp<8UKmN!TlgbjPkEOhe3BnI;qJha=mhI4 zLxXp~dq#3LcORCUEnDrZiMC2bYDRgaQm$h;Of}-MN(oH`zeP<}NxgyzO*v@=y`-W! zv(U}y$BVTED60$C=GLixLOMKVMr}72G>iTKN%h*mnMx{rCJr$RMozM;`1q~VO}r6v z@=|-aA}j@P#aMQhPz!lUqugfp#+GVF`d65Nt?%;y=?l&HQT+j*75r~L70v|UHvQ{L zlY#cx!@PR9M3Dtsj+aVOi!2np*}GSgV0OJ^C=1;6Zs=`$nmxzg#5+E0@c4ll_O z+^~Q2r$s|_+{V%OvwyUH)0q9{k)pTHFn(V1SPl*I4U?W}9LVdJl($b-d0~T9rbgZ5 z-~Wl4@N=$$yy1a>to})1{=X0=(J=GB*c2dRZ)|8|>1F6*X>UhoY-8x`T%%^|prVfD zH@g$p)wK;I`L6Z*1{rxfI37(32^|}nvY;R zvK~Z(C_gp+RJeSY3C@s){Esb4Cc3zNjD)T`BDG^!BvqiRyDasp&>ZVFR3AWq!T#7~ zu+gT%DJ9gZx>Slcjk%HX)RruzjYjtOHnu>Gz`0m;@noE3cwsC)x0+-+RK#wjku@T) zHES?QIT#ePbu$L|Cv2DW-Bf!=s^jgdt@126`kKgDd~N3~BF}q!v}#9lgWwR4g`mwa z8Jasxb!QDUTwcG~pOmJWY(oHdX#$1@zZ$ysMl@Rf;l&>rlH4gWTkB;)GdFhux zm$CF`-B1z&AaAi@A(%BDRQr6!gUe5~{!Y`zHL z#xQ%X{`Q*$(kDtdu0I;>(ca9Wn2VEH&}+IRo2Y+O>v1t;wie^TCrT2}-ed)~63Bst z2~f+kdK%XAYc_NSC?f!kjIqCqj0_U7bhIWkEEfg1QW-*{qeBJbeY$IIXz_p#hG~p! z=Cb?gBm)wPy!aAZvL*Ud$4iOK*p#{ghCM{uaEL;9 zb{U^lfLi^HOiY2-e}lm?Iy95j6LVF4cc?161C4kSOBF zN+UZ^oq+R}?n(IQ_qnaI;FoH|yxm({Cwt27NzwF8O3fwmLg|kzPeb>rX$j_*TS6U> z^_3r1D#*Fq@T39QoSxhk@BifUvYcY!QqRXg5rmc!%ySkVYPTPWSk(3s3UlQidiiE_ zZE}!h_rWBw_Lk{!U`|}s2hq!CGaAS_Yn(QXlg4dltrcmkSTkA9bqgQtVYY{nw7RQ$ zACkwVuy9>Yqe|p&8g_D^spMu;s3knUV2crpT}cp3Vr&4~m?*WM=q#4K@CqNy_5zzF z=i8HlLo~EHS+f3`zS^Ny*JhUnkM+i-bF0;^I9*mLCwvC{D9l#djf6`eI%KI9SO>)+ zQ=J1^>5!zhy;H2^*$G6)1!($0xio35ZU5SIPRf`^7B$8#{N6zyjoG|B<`ibIU$dSI zoqOG|I}L#8oG=qhhi3&d!b#7DH#i2f;RULtEPj$(l zDxrhF$e9|!uD4X>WYMFEOn0n->0>85FKI|s`rc4iS8IC1x}r86!KIAV*H~cgx3<<4 zMq^l@9M@dGH>rSk_4}8OD*D1pXQ|u!bfu&?l6wJxuUGM_DTs=K+c>XEBMjIcWr)1~eEvU0&a* z@NZ~{^z|Po9{%PT7(HDxG&~X;^v3XaUCtWJkZc!`wu1`vH_HuQk?bSU zN$|u5arD~sISABuy8A+C!R>)Z-4S*t)>Q-6)xy*foiH-Li!Ub4S>f;l&gwDF-GI6y zyY3L#chAPbJV&5>FhB-#?c2V_;70viS8)92MFBawXk9D z`4RmOBToWKuN`8({oOu?_*?>|?)jA8f6jfSDuN4W1nU|Dy{kIZr+U0@ZTEM@aMf-@ zDw^*X2(#MqZl={nVBTX%fczwT5!(UE5r?AP!>4wIyLXA)Jhd-(jx$lthn7AB{Qhz8 z`uzwGOaw1yhy$A4z$tzzhM@dLyR``^e$jdHh_i|xqAD@W-S}uiBVs8T{ea`8%9Omn zsXpJ!jT$Fm8nuVPk$I8(92rtQbUKMeh;1jE6CvCmRg?PV793J5(E4u6E&h<>G14XFLv?Wa)W*b1DDPJrHD{*Sh%a zce_~Mk5jw64*2z7NUi`6H9`mp1SF2|e-9a>3h7A>o=iz#87Kg2Wo30#U5I(mc3?3Y zJ_Vvy%t7K*s}`e%K&|C_3qh6T&8%Z&x?t}mZ~Nl)Hs8KIgwN)^@5AWB`B{!{74ttO zfc&2%xw5NcJxSn*epqsH^Sh?C&8ur)xt~9GZQMYe_f-g{z&B!4g<8y+nKG6Miqf(c zzNuUX$FgCp)?$D*+Z`4g0xHnU(q(BYk`nBRaRnQxC~n{t%RkRmVnijHzy}uF*%6q3 zlKE`K7CUwlbV%mfv<+;zQY0@!`)AK1Wr{xqcjj~O15)RyzO=_css2m{tA#jKD=S1` z2RtfVX^^Ie1+r>ToBsmidk3>B>MVdWGc(F$tsGUtrd0$m*Orc|NT==fj6jg`GWiiSUFjaCEW4TIP_B1YMwbGi9pkd~%;YM=r z=)#s=KM+_%2eFX#})aHQIibV)m(zZL|IWKvgOL_6*|wp&Cf?zoAdUXK<5tAgrP z&$*p;UP9L#WAT<6AiR2+`Yp$+#v>vgEw;)J`0)%>N@Yj}(AvpiiOY+&3mi^JBke3XZN=%_$kadxR}T4UE(I$;=LV#QZuC4 zmUY!YE5iO^_>HJ_gG7Nj$N*NV1)KokkFIEtJ(jGO=yyQ&iy$M$q9U1~Z2dkY`h@Ea zY!)1_Y+k<_?aW~Wav!e( z-GfUxH&WsrJZ6*OyjwcVli%la$Opl^keU zeCysw(hw;}r?QQpw(tYVxE*3JYW6U9Icow?fWy9wMrR=pv|MB1wgh+Own&$oXHn|T z;Uch#*p!^ESw6LZF9cq0zma zIj;ydQ#C}tI=)W~OnZRN5|k}t4RUfvUC9^0l)A?jP5A~0hd@2nKclah ztI6~THJ8$$M~g44rLHp8Gg>XnZ&&Tnbdq1IZdZe~l{J?TppDMW+pr!NbjZM{cgC&s zSdGl8+udZ+5H3;+=}pVFWAI?LX7>P;re9~wa35phnZ~BC`hjZBY$6C3$~e7nq%?Mp zi*7Ji_9kt1`Z(J}6gb4VJ&Q zGIobxEGUJOty|m|7GKFThin1)zW6!FIk{+U^Q8RthY$S$p18>-lT9YAD4>As6%;q> zfGkM#QL-QGt!Vt-drqS;9-|ivVfsH3=a78~`+esR2NZoxusoS(Sh}~C4uV~^g<^@; zFt~YR6IW=l#v^t0C0k8xochQWC_Xh*}UWF^Jo0(JgI| zBwymO(};3XC2V0hsOwQ{gB$=kuzlPJwT?)&j-1;gDJb5(7DnFT&1ng5HEa#*lMy4* zQvvFn(?_mua=DE6r?%aAW@$a#ac_V$ZT`pVJk%Dy zQ_VgKtMADsKjq~=Ip*Jf3z=7EbQHMq3T;W<7KPm{;OlC_H1CGEMRHJ7t|8yfa7FDw z%I^^7jhUgk3FxdnVFV8-q}2m^nyyor2V{PyTAsCcS-w7_pnem2wrfBC(=!#>2b0|g z0|FX{``>Z^@!y(D9Qn*7`WEIUib7K`kdc2H3*|~lfVMk|D(VmXOo^=gT?jg~#4l1w z$^b1D3WY5t5^M?mQ$kN8J4@G6MO4v<R*sF z1Se0-RO6_=L?vucOA(t|EJs1H>El3LKE|(5 zK)_28=b!qtg4k>$jtlokQ{Q4kS2F3{+aW)Cp({4Yd`|fM04J%kM zeRa)n63@&dEZoA)MkYZLq4fhlAI#x+iCto$LCUER$}tWf3)CiaP7Z7=EzpY#Au>8^ zWhEZl=1?0UsWB2+Bx~!Xk~ym6V33x7$>l;xc{6W^3t@?Z+qIUkY(gc^3BRy<%B7J5 zxdAtSuuo@z0YmG&Vf=vOujFTyU>P;fU zMzDn2%ODFYgbrYCD=LmBQ zYGGCH!~?EQ8JdhVM`%rbE8&R5f`w7=nQY9N<6_9Q`)Fj#+A}-t0QaARU`Yij(^R?b zy$UrzBJJwk|48^3wLHewRB`syDH`+KII$HrW57r75jdY|y3;w*%5GB&K-XM@rSTHVrtl~%*ld(a(}0_~*XP_)?tHA`YP_3=wIjnG!E#wpF#vuG0-En(D@$+}((-Yp7} z06nQR-uGw60-YbY#VVa1K+U+gY!I_T9*3BH6kdp>AfuPI`A?979X5H8ulFZGK5P3% zOEA0^&#+i1vRCRn&b9V%?LLv0^m5W7!AjR0das z>`;um0bTb!QjV)hjyA09?c`PtdTgfZ6gG03B|6PgS4BkbA7mY01jh(L8tL%5dp4Zi z7o}pi#hn|vrjDsrK*v<$#v)1Q2o0*V@;KA3>@rB7%J6Rmv%0NA<7aYmPJeIef$!LT zPHr1w^!AK?aSTQPhTYnC=VUw{g|Vd{i-{5Ql?1y^%I!X2>BM2T5U2k9lJ<&f=|}-f zvFBzd#mb`w8L*6`?=XzOeNNywtX~tVUjo=hB4~ODlhDcrv>^H!B8g(}QXJGsWjbKV zM#(bk2AzIOWh%$Au-_+$I)^ezkhs1<%ZBA6OKvF-j#1YPnUCj`6z9=UhuU>vc8(C+ z51*FK;1`VkF6N?MkN9YkI(1%-G%}seVRJ5<@u>>^)K}{S3@m(av0C)j7I$1rI794d zu_!(&H%GM!FlFY}kZ#wwC<##?f^Wd17;tZd62Ov=T;tCK>+)lt=ly{Fcf**I`dP#J z=R#6J{NIxDUq#VX{jZ{k1Jo^DP}b0Xa!I{O7)O8C2TdcRAlJh+LK6zuG#VIdfdxs* z$iOwb(8P@NYZOI`(d?|TG|RGg3Q9|>w^7Ol_{%WLX3{mwYGu-0403)fE%Eb~m7NuT zEd5yQ-0@COAN6ytPItTCecrmKx$ix}{O$Up@n>zXEfD&h(1H@m4Y0t*+LF0;iD}+A zzy{eZp2Mq6V%D8wdeWxZT1G@ockbsfA+aIOl5}|^U{KB#u<#$e8O^6eyvam-^QcXk zX@*j9=h)~rH|fzEqDv(ZjQ_H6^ z$6!L|Pj%f{2FZ+*0kG9~PdPG2{0t5RrKVAMq~=I)Zv3K7I#;L*wDw3%I;or=O|~_q zz4HqYFjlS;s2`GK26Y8)lYMLGG1#zNf-?;8S0hS`%SV6VLN`K!vZo`r33)*EU$Zs+Of zen4khUd9mwvNowcmBg`jofeY~7OY5%d*Nc+*YmQ$Z+O(xD=}_EZN6A92MvN)N}clG zi-Tb0VJH!i1}wYU2yrVrORVa@`5&_OMEeGc#jzVlg~|n#u)7M6;XIHfHsOud^#+$* zMxj)P$rSz@LI?;0MQ1I>W8RB*?FK(tsSHiFZhUOaNKH+J?5x)vVhaFcE;v6=bwz7# z1c`;+9pb_lL{Ls=?1A4JXO?;@Emn|Sd@TZYh1%=E01VJQTY4}%bK7Rb?yL(781WnZ zQ#58Vv7cZOE@7ftWOjUMj853p?x(11v2>eG7dZvEV{|(@f_gW|r}l4wp0c^gswsmt zVtjKc84Rf7yV1tAl(61f%9EAY0DZDDTt4|sU)DSLCmgovOtH#NK-!qF!pi9_?0Axx zo*wV60S*lCi{gvuK^c`Gu9=H>dXqRF8Y-F$d8pxg2jkV_vaVGhm>NY- z0NGp^%6n)m{ZRyDZ-~7$<{^H@eNb*jCArO@dvVmn1Wq4~P13=8Z7lOU{*VaM2{ac~ z{UA9R-;QF#dv9#rz68m#8{Ej5gS8MN%yT#J@ zGg9AW$pNtcjXCeEhJs+Q&axR4U0zz!r=Ergcui&-kf(R} z%P>uxiK=hu!TT$K9o8x)=L<)#5BndWKYok}Hs{GQR>(ePGf5^C3NV^3X3$eue8-Gv zzm_Y;x=<{eL|>^wpIbiFDwf@iz$DZ&qE?*KM-#^uM}u^nu7c__PG_~$JBwR`@)I}R zI`Kw#ElL-U`M~APpZ5Q;Kr5^m<}eWvS~8?+|M&y5m=cI?lDKidJ(+AnMn24YN_klTmgDf09jy zN||*Scj`91eL{Tc2!$ z>p-=rfvs8!m&dA(Rsy$ZA&JpoR#6RJYnSC3q|9I70VVuk$aHx$;6a@@Wpb}A^b-M$5eft zU0|;!u6T7%dtXpnkXC_e){O2^dZ#q5QGBwq#p_m;3uKiKTOkbqup4!#PmO!R6>1A&TEU@VYuS**JSj8g6M?;7857VA|9 z#>WKsqyd#UB~F8Y+@;wt$SQII`B*385MP&xLrH_gM+)8t5K#}I3;t*AeU$hASJqcR zRq;jrUb?%bLAtv;rMnT3M!I_d0cpfbcXvoD-AE%!cS^T(yzBq_)>Gfxcg8 z*UY_pe&_76@JTVa)H?odS*MYolg>mXL$X>5pM!CFaT7qz|E<~i8+(Vhr+@Rfjv}vH zjVI@$q1Fdt1@>(U-Fji~$|?Dn`(!y{U1a~HZc;8{HNUnp3(svh|3~Z&{XR_^V1z_0 z0Lgk}lZy2?-3Cvc6aM3&0LHgnof+*EDm7hbk`c~M)^&v8#gWg=_E%)-cy*c#T+~Yv zV!~YLMn%%g$VzNwN!_rDKgc%*A*p;Fm||H_8`eAG^;35vkh;SV z{9>=~e-Z~-A@zggul#Yt!j+_gp?ai&MK>Hv+~)wAqvAbiIENyGV`y7Nq+A`EC5444 zn{-n!KC;b2I`h!f5v?Qdbck0Q)5@=41nbLwmjMFO;-KcUZn{CDfQ!!4F+PNcfL2=u zIQt{1>()%Sj#Yu5UgJYGx}En(U5@_cc`CUhO;l59+!o!OR77$+v8mLmUoMh?URnKg zl@>k5w}=o$wIm{uxes|9zAv+}W2-QiV=E_SN?NoDa+tLwxKt!&L==vN1VnG@eJlO^ zElxdHyfTqbQZHPV^p_2$UK+atwSE=}UuFzRGiva3q=t$?!4$`IhbN(Pw3SS*pNrP4 zNc>bzoi(@c4lc@E_f%u;UbyC9zV))l+z6d`fcw; zKiVSC^4?c0MHOQ7cT{h|HAxPePPSv7Ttxi1x8!H}R##THn2w^hWgK285NSSffVF+k9kOtcGT=`d{yal%_6yBDYgq2$Ns%P-?|I z`g|)7qu6wN=VJHJkl-f89|3WX!s!@yMlDvWvy+f=Tq4Czwz(o;gI!W zf_EnwpioTuVD;rgBDiu`sJ9tZphfoWj2;_W8LxL7gD5GAS?ysG0Dg3jd8)j`7wmh3 zM8)vw$VQkebqsaj`+Q!)97Tax@{6GX!W#qRfi~2_cl?quY27PI$@xQCw2L`Pu_VQk zn7z;kNji0%ola)ZI7kzp8N*`Yuwu%4no;+>X*kR2U6lq2<4|CgXPpZTUA16;&dRiT zk~#Fe63$z9!5<%M76j_NKNi--S}%pISKd~dWg2A=@O{sIv#T)PP{*0(Y3WD>8`*{q zQ{pdptxYQ6Xn{XJ zfj2TEJ!j5OH3P{D4L0db#!$NMsPZ>aB+SDKQx`dE@fGmrF_2EXnP$#zJWU+iiVan! zA{CngN%i4&0&K?k2Pn`-jI)7(?f!lbU(pM3b?{ov*0E}>Jb>f0bHF(K9@N=AUVXHjg82}BAQ*Zh*2nfK zQ-f);PrHN-AUuR5j`xSyfz{#2rvqZxU+Ycb1`@5_-$w^zp3{k_;Mdtzd z!LcKPg==>osllXc{FrHu!5M1ths^9g0_$e?9eJ&jy}sfaCwk!^^u9m*;~r68=UJE` zbbt1pemlB}lZ}d#P9XE-3ihW&w4(VHjb}Lc1+iX16H%GlF0}K+^BS^N^t>l4#13*> zQ9#RCG60A?a#+C2#JnMLRh2UAMRgUCpt%%5Hn)4*1k2WqJepew`|20U?33E{>V|E| z@Wm3J&kXSB)3LJFe(bkPT}oM&6W=#dK0I$dH}G81Cd|!+b2KW6U~tBMEXg)QG428F zK)5l+QE47+Ffm>aWjz${X4Dz3>`iLuoISP)vjE(YnECgg%#J=`?Dnud#GVq*b`}r= zrgTqRY!~X-rMrEiweRcQK?u^=2>tZe=F+*|+)C~iz0C2SeX-aQ zm?Su2QX=!w$2PFqLUe};JZp1O$G^3j=f`~=Pxu0yAk!J`eXVO#XqzxJ@#lN+iL`Wf$-9P_sL-_ylZgf82mrZxqwLiX;sM7IU7vg zND3|wrU9#t!_||HK0?8~tW5<6O^^buj=k)tGF|}Opl3uvN^s+3E?bVKPVTOh@5!N(WqGx#$vxvJVM^j2#70VX?W;RAdnIyOYhJ7 zdR{#V+&M`KEILvTVg=B30lV}VQ_#4LEz+L2H{C0ZI8ax^U5xA@6o~1Wv;re(wpPAY z3{L^$W&7DEGL!_pC?7Jj_ggZ{1+{&v9d<;2cup(Pt$I8w(|sShpv8cHHmqk_@OYh; zR5ONglN9-`WTwDZR7 z;B;Is%DBnnsC?UOd{I;gA-G#CRr&iNS#`Cup<(!~Bx~lqZ@{#@oprtQc5+9Hed+Z_ zUlRkbAt6=*-p_|0RRerdO;!Uh2A!(u?`o0J+#8jTEAkSClu}R&+kIy&i@!EcmI@NC z+8c2i0T|{dd+g_&(lcwDocV=cGh-a%1xgD#9AMbxV29z@k-Y zpmkAzu-+NbX{yB&8V+{XfyS$G^-If;v3wM)A<%NX(+NeVXb43Sw6In=H~po@{vpsb zbpGC#cv}K$MVL>_uhX*deDrMBedD$L7bdOemo=fg0W0?5J)rMvnNL$p&bxS;^o|d# z(z&PiHDMy31v4f29R*Q@1B>I%t&n1PL2>I7Txh7Z9WHqs1Tn7bR>kMBL;kJwOG26g z@IXr7a&J*ratCKtsK0R${}~ol-H?k|XLK8pmgEc82VH47k7pDr53;SVvr9)rdfNS! zb|!XeYfT8*0l^7uinkUWIHhdJolEIEpETJdG4aAc-wJ08T~yb69<^VmsU)9A8xR5M zDuIii6@ltHU@OJiNVHu|E@@ zw2K^m=L1{G*r(8`c;ksZuGm=vGP(7s)_&vdYDD*@XtyBxl9Ws?(_Er7;wDu&RNl2X z8)sMCQ+)fvAxR>*zdICY)B@BOw`X`hrlZOx=w{K^&V2H<;n}DcpX|Blkd!@OLId)o z`A-~*yl;@yqvtuOzpk)%9rj1JMz~w`A|65ISbay*P7tDEq{&u9Qti%6OdpcQ^M7Rj zz@GK+$R+x0F1Rk!xw%>53s2BL+t~n8`Skwj$o+M+)yW7Nq~TO7j#ya=EIR^8k2cen zdMrQFL7(0Nca=HHnl=AbUJZe@v<@Jg1i{QvG&du6M_v5&vH$X9kyW80#bGgcv>W_F z;ZAg>qhOyUDSF0V63vbfCb1qn*!;A?J)R1?5R*ivK$hL`Q9n0R8JCgZG~2OxVh4N5716o_QDq(uQ6~Q_%jBR0=zYl zBWYj37Mc21md+R-G2sRs24CPiytc@j_{HOvHqc@To#vD1ceHjniPDda`X4+Gf0>PZCqK>I;f?`8qSaNlz zw#rSYnDH{VAWYuFot6{vW3QRdr<#LGhW;F8T&N?Wv^e=VMOg*kWORkLcAw9*e6Y;7 zzm5$9+ea(d92d93Iu8@SzD3fev8D0-q$8~>b7I4h@4;KgU?)SjPRar>NfsZL!l279 z5PFR6qg+pO@PD-as;-RIq7E*DIhN1DDT~l>HYG8)DrA1sCvU^m$TD+x`a)@k>P=6H z`}@{>$#VzvvA)uos@anT0Bv`@|A!}E0Nz*?DWr??YQU0~hx zypmxwul>HS=R9ef@TStNU&Bv?XBi=JZMp7|Z_Y4MdclYq3YeI5Dm@cziCiSxRLH80 z1|$ol#0@0D#LFm!inJOKhvedi;rc%0#v)ln$B7tecRny;y~Z%!FMa|T{Z>GBd*NsI zm>{02{8*RsTOi^M!1g8qcOyCw!E_q0dKoc&I4yD_>EX)0**7H-D=}vZVSL*@nHJ}i zxgV0L-ineSH6$dM*WAfa zJYEj5p>L@2)%cT9Oc=H^BE8J3cGE4M>$Zx$g;X|;JEG5r(gS_9+$^*|JY06pabbBm(JC;%J&ye`m z#oJBI!c}cg1$b{_Z#gSHaS(ZoyDn7w>(y2)2}7Yo+5*j+K8nfH$|gStZBlIwMD`*o zcbdGHV2Y~dM7UtYxc0J)UiCy5NA{rMhD@99?E86Mky`xPnhJ?it#b-G*&cG~tKmYK z3Ifxcc56}%^bXmNA3}5baPOyg!rV1;X^BLL+flF+Mt~VQN#Yms+@m-$wj35P2Qo5V z)74--k8O}L+c8dD;-$9B?rlU*qiV~C{7nle4*HE@EfSAC;`WdZzB9oj;w*>c^e zZ#(N)DZuDap%nqSqPja*4x!c4tm{h%Gxarb8ig9O0@Ff0X9QF487jMmf+>IA}See~Jpd<#&Q zG$)`(R~`{c?Rb2B|JbI~=aS=e;j^926!`fLG3=?VhGx+nmMD!kT@)8F0sGot1~-YN zWertEJel$%$3}wVSDM(A1pcd@VgdqPeA+|yf*RKlKAYn{81}B|M-Q7;7u=MYAL@G$ zYxJdXa&_hF0;)x3!qnN&9&eK3nw~&Ts{?>Ggb?0!FEq{Y_=<*f0=GK^X@Kg&NV$j) z2P@jw1Se7jfzH^oDqIKkC#VQZq^wz%)5LA>YCW8>r=+MplAvVhgg03z51BhQ#IYEM z=g>{q1K9sz+|coJ5)n!sG`?DTyZ5==OK7)sd8qa56_&pkg<(9&s(POkS%!g0lLmlK z;$RW3>9%0BQD4WhM`XZtI5YIO3X*=06sON@l9hZ;m%5jg&L_9+qZeX* z?PXw|6cL{b@7w`+lRpy3-JSXt^8p19_@X|;dTAYkZssrg4P98{qcbh9lQ0KICOv&DF-!zteq-|p;6k$43IzmW zCHotkHwC7Og8m}NY8yyPRa%wR-P>JTOWEmw7qboFE1*}1Fj!g@xI?;#&rl5 z&83eb)7emXqnrROwsE)Wm7I8wy@-%Lbv!;kewdJ7X>Xq1tH4E9=aWL8De~mK2~_6T zAj(E`P+?EBgIR*OH?Cwm**xnK=hEYw`P>W1jgb+g@vL{Tk!v~nxRHNihq>lXpHU

uUd(Yh7f} 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 delta 54345 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%0D*LGb17*qZ5=bP$hrC|rp9d!bKbO= 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 ZF=zcZjWL7w5CD(^KOP|X?{~D{{sF>WGok Date: Tue, 26 Apr 2011 14:29:03 +0200 Subject: [PATCH 045/100] Wasn't limiting depositing of items for the shop owner in the case the shop was holding both items and currency. First click the shop would drop currency into the chest. Next click currency would be deposited again and items retrieved. Those items retrieved would not be limited in chest depositing amount. --- nl/armeagle/TradeCraft/TradeCraftItemShop.java | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/nl/armeagle/TradeCraft/TradeCraftItemShop.java b/nl/armeagle/TradeCraft/TradeCraftItemShop.java index 0caf904..401c172 100644 --- a/nl/armeagle/TradeCraft/TradeCraftItemShop.java +++ b/nl/armeagle/TradeCraft/TradeCraftItemShop.java @@ -65,8 +65,18 @@ private void handleOwnerClick(Player player) { populateChest(new TradeCraftItem(0), 0); int itemAmount = withdrawItems(); if (itemAmount > 0) { - populateChest(getItemType(), itemAmount); - plugin.sendMessage(player, "%1$s %2$d %3$s.", TradeCraftLocalization.get("WITHDREW"), itemAmount, getItemName()); + int maxItemsChestCanHold = chest.getSize() * TradeCraft.getMaxStackSize(getChestItemType().id); + if ( itemAmount > maxItemsChestCanHold ) { + populateChest(getItemType(), maxItemsChestCanHold); + depositItems(itemAmount - maxItemsChestCanHold); + plugin.sendMessage(player, TradeCraftLocalization.get("WITHDREW_X_A_SHOP_STILL_HOLDS_Y_A"), + itemAmount, + getItemName(), + itemAmount - maxItemsChestCanHold); + } else { + populateChest(getItemType(), itemAmount); + plugin.sendMessage(player, "%1$s %2$d %3$s.", TradeCraftLocalization.get("WITHDREW"), itemAmount, getItemName()); + } } } else if ( getChestItemType().compareTo(getItemType()) == 0 ) { depositItems(getChestItemCount()); From 7b3a6e78b06f0f2791b081f78a6b9637d8f51ae5 Mon Sep 17 00:00:00 2001 From: ArmEagle Date: Tue, 26 Apr 2011 14:31:59 +0200 Subject: [PATCH 046/100] Would display the wrong amount of items retrieved from the shop. Would show all items still instead of the max possible. --- nl/armeagle/TradeCraft/TradeCraftItemShop.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/nl/armeagle/TradeCraft/TradeCraftItemShop.java b/nl/armeagle/TradeCraft/TradeCraftItemShop.java index 401c172..7f34e3f 100644 --- a/nl/armeagle/TradeCraft/TradeCraftItemShop.java +++ b/nl/armeagle/TradeCraft/TradeCraftItemShop.java @@ -48,7 +48,7 @@ private void handleOwnerClick(Player player) { populateChest(getItemType(), maxItemsChestCanHold); depositItems(itemAmount - maxItemsChestCanHold); plugin.sendMessage(player, TradeCraftLocalization.get("WITHDREW_X_A_SHOP_STILL_HOLDS_Y_A"), - itemAmount, + itemAmount - maxItemsChestCanHold, getItemName(), itemAmount - maxItemsChestCanHold); } else { @@ -70,7 +70,7 @@ private void handleOwnerClick(Player player) { populateChest(getItemType(), maxItemsChestCanHold); depositItems(itemAmount - maxItemsChestCanHold); plugin.sendMessage(player, TradeCraftLocalization.get("WITHDREW_X_A_SHOP_STILL_HOLDS_Y_A"), - itemAmount, + itemAmount - maxItemsChestCanHold, getItemName(), itemAmount - maxItemsChestCanHold); } else { From 6444ea60b2faffec7102ac857f2fd4a430fbe812 Mon Sep 17 00:00:00 2001 From: ArmEagle Date: Tue, 26 Apr 2011 14:36:18 +0200 Subject: [PATCH 047/100] Would display the wrong amount of items retrieved from the shop. Would show all items still instead of the max possible. Attempt 2. --- nl/armeagle/TradeCraft/TradeCraftItemShop.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/nl/armeagle/TradeCraft/TradeCraftItemShop.java b/nl/armeagle/TradeCraft/TradeCraftItemShop.java index 7f34e3f..34c4885 100644 --- a/nl/armeagle/TradeCraft/TradeCraftItemShop.java +++ b/nl/armeagle/TradeCraft/TradeCraftItemShop.java @@ -48,7 +48,7 @@ private void handleOwnerClick(Player player) { populateChest(getItemType(), maxItemsChestCanHold); depositItems(itemAmount - maxItemsChestCanHold); plugin.sendMessage(player, TradeCraftLocalization.get("WITHDREW_X_A_SHOP_STILL_HOLDS_Y_A"), - itemAmount - maxItemsChestCanHold, + maxItemsChestCanHold, getItemName(), itemAmount - maxItemsChestCanHold); } else { @@ -70,7 +70,7 @@ private void handleOwnerClick(Player player) { populateChest(getItemType(), maxItemsChestCanHold); depositItems(itemAmount - maxItemsChestCanHold); plugin.sendMessage(player, TradeCraftLocalization.get("WITHDREW_X_A_SHOP_STILL_HOLDS_Y_A"), - itemAmount - maxItemsChestCanHold, + maxItemsChestCanHold, getItemName(), itemAmount - maxItemsChestCanHold); } else { From 9cd1a98c8714ac80ba4e5ccef3520fc4f8511444 Mon Sep 17 00:00:00 2001 From: ArmEagle Date: Tue, 26 Apr 2011 17:00:00 +0200 Subject: [PATCH 048/100] Converted all commands to use /tc as main command. Merged displayCurrency and setCurrency into /tc currency (using an optional parameter). Changed /myShops into /tc shops. /canPlayer is a server console only command now. Added /tc reload (with a new Permissions entry; TradeCraft.canReload). Using the item getData() function over getDurability() now, since the related bug was fixed. --- TradeCraft.en.lang | 16 ++- TradeCraft.properties | 16 ++- nl/armeagle/TradeCraft/TradeCraft.java | 124 ++++++++++++------ nl/armeagle/TradeCraft/TradeCraftChest.java | 14 +- .../TradeCraft/TradeCraftPermissions.java | 10 +- .../TradeCraft/TradeCraftRepairShop.java | 3 +- plugin.yml | 24 ++-- 7 files changed, 135 insertions(+), 72 deletions(-) diff --git a/TradeCraft.en.lang b/TradeCraft.en.lang index cfde10b..27f884f 100644 --- a/TradeCraft.en.lang +++ b/TradeCraft.en.lang @@ -1,13 +1,21 @@ # TradeCraft.java NO_PERMISSION_SET_CURRENCY: "You do not have the permission to set the currency" -IS_NO_VALID_CURRENCY_USE_INSTEAD: "is not a valid value for a currency, use 'id[;data]'" -INVALID_CURRENCY: "Invalid currency:" -CURRENCY_IS_SET_TO: "Currency is set to" -CURRENCY_IS: "Currency is:" +IS_NO_VALID_CURRENCY_USE_INSTEAD: "%1$s is not a valid value for a currency, use 'id[;data]'" +INVALID_CURRENCY: "Invalid currency: %1$s" +CURRENCY_IS_SET_TO: "Currency is set to: %1$s" +CURRENCY_IS: "Currency is: %1$s" YOU_DONT_OWN_ANY_SHOPS: "You don't own any shops!" YOUR_SHOPS: "Your shops:" ITEM: "Item" AMOUNT: "Amount" +POSSIBLE_COMMANDS_FOR_THE_PLUGIN: "Possible commands for the %1$s plugin:" +TC_HELP_THIS_TEXT: "this help text" +TC_SHOPS: "lists the shops the player owns" +TC_CURRENCY_OPT_PARAM_GETSET_CURRENCY: "get and set the currency" +TC_CURRENCY_GET_CURRENCY: "get the current currency" +TC_RELOAD: "reload the script and with that all the configuration files" +RESTARTING_PLUGIN: "Restarting %1$s" +RESTARTING_PLUGIN_DONE: "Restarting %1$s completed" # TradeCraftBlockListener.java ALL_ITEMS_MUST_BE_WITHDRAWN: "All items and currency must be withdrawn before you can destroy this sign or chest!" diff --git a/TradeCraft.properties b/TradeCraft.properties index 043a864..e6c02a7 100644 --- a/TradeCraft.properties +++ b/TradeCraft.properties @@ -1,9 +1,17 @@ +#whether to allow creation and use of infinite shops at all infinite-shops-enabled: true +#whether to allow creation and use of player owned shops at all player-owned-shops-enabled: true -repair-shops-enabled: false -repair-cost: 10 -enable-debug-messages: false +#the object type ID to set the currency currency-id: 266 +#the data value so set a sub type for currency currency-data: 0 +#the language to use for language files language: "en" -auto-update-language-files: true \ No newline at end of file +#whether to automatically update language files from the jar when there have been changes +auto-update-language-files: true +#repair shops are not tested/supported yet +repair-shops-enabled: false +repair-cost: 10 +#to print debug messages or not +enable-debug-messages: false \ No newline at end of file diff --git a/nl/armeagle/TradeCraft/TradeCraft.java b/nl/armeagle/TradeCraft/TradeCraft.java index 1c9e7ac..8982db8 100644 --- a/nl/armeagle/TradeCraft/TradeCraft.java +++ b/nl/armeagle/TradeCraft/TradeCraft.java @@ -9,6 +9,7 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; +import org.bukkit.ChatColor; import org.bukkit.Material; import org.bukkit.Server; import org.bukkit.block.Block; @@ -17,6 +18,7 @@ import org.bukkit.block.Sign; import org.bukkit.command.Command; import org.bukkit.command.CommandSender; +import org.bukkit.command.ConsoleCommandSender; import org.bukkit.entity.Player; import org.bukkit.event.Event.Priority; import org.bukkit.event.Event.Type; @@ -34,6 +36,8 @@ public class TradeCraft extends JavaPlugin { public static final Pattern itemPatternIdSplitData = Pattern.compile("^(\\d+)(?:;(\\d+))?$"); + private static final String CommandString = "tc"; + // Stuff used to interact with the server. final Logger log = Logger.getLogger("Minecraft"); final Server server = this.getServer(); @@ -55,6 +59,9 @@ public class TradeCraft extends JavaPlugin { public static boolean hasRegisteredEventListeners = false; public void onDisable() { + this.disable(); + } + private void disable() { properties = null; configuration = null; this.localization = null; @@ -63,6 +70,9 @@ public void onDisable() { } public void onEnable() { + this.enable(); + } + private void enable() { properties = new TradeCraftPropertiesFile(this); configuration = new TradeCraftConfigurationFile(this); data = new TradeCraftDataFile(this); @@ -94,47 +104,68 @@ public boolean onCommand(CommandSender sender, Command command, String label, St if (sender instanceof Player) { p = (Player) sender; - if (name.equalsIgnoreCase("setcurrency") && args.length == 1) { - if ( !this.permissions.canSetCurrency(p) ) { - p.sendMessage(TradeCraftLocalization.get("NO_PERMISSION_SET_CURRENCY")); + if ( name.equalsIgnoreCase(TradeCraft.CommandString) ) { + if ( args.length == 0 || args[0].compareToIgnoreCase("help") == 0 ) { + displayCommandHelpText(p); + } else if ( args[0].compareToIgnoreCase("currency") == 0 ) { + if ( args.length == 2 && this.permissions.canSetCurrency(p) ) { + TradeCraftItem testCurrency = null; + // try to split ID and Data, separated by a semicolon mark + Matcher IdSplitData = TradeCraft.itemPatternIdSplitData.matcher(args[1]); + + if ( !IdSplitData.matches() ) { + this.sendMessage(p, TradeCraftLocalization.get("IS_NO_VALID_CURRENCY_USE_INSTEAD"), + args[1]); + return false; + } + + try { + int cid = Integer.parseInt(IdSplitData.group(1)); + if ( IdSplitData.group(2) != null ) { + testCurrency = new TradeCraftItem(cid, Short.parseShort(IdSplitData.group(2))); + } else { + testCurrency = new TradeCraftItem(cid); + } + if ( this.configuration.get(testCurrency) != null ) { + currency = testCurrency; + this.properties.setCurrencyType(currency); + this.sendMessage(p, TradeCraftLocalization.get("CURRENCY_IS_SET_TO"), + this.getCurrencyName()); + } else { + this.sendMessage(p, TradeCraftLocalization.get("INVALID_CURRENCY"), + args[1]); + return false; + } + } catch ( NumberFormatException e ) { + this.sendMessage(p, TradeCraftLocalization.get("INVALID_CURRENCY"), + args[1]); + return false; + } + } else { + this.sendMessage(p, TradeCraftLocalization.get("CURRENCY_IS"), + this.getCurrencyName()); + } + } else if ( args[0].equalsIgnoreCase("shops") ) { + displayShops(p); + } else if ( args[0].equalsIgnoreCase("reload") && this.permissions.canReload(p) ) { + this.sendMessage(p, TradeCraftLocalization.get("RESTARTING_PLUGIN"), + TradeCraft.pluginName); + this.disable(); + this.enable(); + this.sendMessage(p, TradeCraftLocalization.get("RESTARTING_PLUGIN_DONE"), + TradeCraft.pluginName); } else { - TradeCraftItem testCurrency = null; - // try to split ID and Data, separated by a semicolon mark - Matcher IdSplitData = TradeCraft.itemPatternIdSplitData.matcher(args[0]); - - if ( !IdSplitData.matches() ) { - p.sendMessage(args[0] +" "+ TradeCraftLocalization.get("IS_NO_VALID_CURRENCY_USE_INSTEAD")); - return false; - } - - try { - int cid = Integer.parseInt(IdSplitData.group(1)); - if ( IdSplitData.group(2) != null ) { - testCurrency = new TradeCraftItem(cid, Short.parseShort(IdSplitData.group(2))); - } else { - testCurrency = new TradeCraftItem(cid); - } - if ( this.configuration.get(testCurrency) != null ) { - currency = testCurrency; - this.properties.setCurrencyType(currency); - p.sendMessage(TradeCraftLocalization.get("CURRENCY_IS_SET_TO")+" "+ this.getCurrencyName()); - } else { - p.sendMessage(TradeCraftLocalization.get("INVALID_CURRENCY")+" "+ args[0]); - } - } catch ( NumberFormatException e ) { - p.sendMessage(TradeCraftLocalization.get("INVALID_CURRENCY")+" "+ args[0]); - } - + return false; } - } else if (name.equalsIgnoreCase("displaycurrency") - && args.length == 0) { - p.sendMessage(TradeCraftLocalization.get("CURRENCY_IS") +" "+ this.getCurrencyName()); - } else if (name.equalsIgnoreCase("canplayer") && args.length == 1) { - permissions.debug(args[0]); - } else if (name.equalsIgnoreCase("myshops")) { - displayShops(p); } - + } else if ( sender instanceof ConsoleCommandSender ) { + if ( name.equalsIgnoreCase(TradeCraft.CommandString) ) { + if ( args[0].equalsIgnoreCase("canplayer") && args.length == 2) { + permissions.debug(args[1]); + return true; + } + } + return false; } else { return false; } @@ -404,6 +435,22 @@ public String getCurrencyName() { return name; } } + + private void displayCommandHelpText(Player player) { + this.sendMessage(player, TradeCraftLocalization.get("POSSIBLE_COMMANDS_FOR_THE_PLUGIN"), + TradeCraft.pluginName); + this.sendMessage(player, "/tc [help]"+ ChatColor.GRAY +" "+ TradeCraftLocalization.get("TC_HELP_THIS_TEXT")); + this.sendMessage(player, "/tc shops"+ ChatColor.GRAY +" "+ TradeCraftLocalization.get("TC_SHOPS")); + if ( this.permissions.canSetCurrency(player) ) { + this.sendMessage(player, "/tc currency [id[;data]]"+ ChatColor.GRAY +" "+ TradeCraftLocalization.get("TC_CURRENCY_OPT_PARAM_GETSET_CURRENCY")); + } else { + this.sendMessage(player, "/tc currency"+ ChatColor.GRAY +" "+ TradeCraftLocalization.get("TC_CURRENCY_GET_CURRENCY")); + } + if ( this.permissions.canReload(player) ) { + this.sendMessage(player, "/tc reload"+ ChatColor.GRAY +" "+ TradeCraftLocalization.get("TC_RELOAD")); + } + } + /** * Return the last modified time stamp of a resource with the given file path. @@ -432,6 +479,7 @@ public static long resourceLastModified(String filePath) { // finally return the last modified time code return lastModified; } + public void log(Level level, String format, Object... args) { this.log.log(level, TradeCraft.pluginName +": "+ String.format(format, args)); } diff --git a/nl/armeagle/TradeCraft/TradeCraftChest.java b/nl/armeagle/TradeCraft/TradeCraftChest.java index d5e529b..c43530c 100644 --- a/nl/armeagle/TradeCraft/TradeCraftChest.java +++ b/nl/armeagle/TradeCraft/TradeCraftChest.java @@ -12,15 +12,13 @@ class TradeCraftChest { public int total = 0; public boolean diffFlag = false; - public TradeCraftChest(Chest c) { chest = c.getInventory(); for (ItemStack item : chest.getContents()) { if(item != null){ - // TODO | DEBUG item.getData() always seems to return null - short itemData = item.getDurability(); //(item.getData() == null ? (short)0 : item.getData().getData()); + short itemData = (item.getData() == null ? (short)0 : item.getData().getData()); if(type.id != 0 && (type.id != item.getTypeId() || type.data != itemData) ){ diffFlag = true; return; @@ -29,10 +27,6 @@ public TradeCraftChest(Chest c) { type.data = itemData; total += item.getAmount(); } - -// if (item != null) { -// addItem(item); -// } } } @@ -72,8 +66,7 @@ public int getAmountOfCurrencyInChest() { int amount = 0; for (ItemStack item : ((Inventory)chest).getContents()) { if (item != null) { - // TODO | DEBUG item.getData() always seems to return null - short itemData = item.getDurability(); //(item.getData() == null ? (short)0 : item.getData().getData()); + short itemData = (item.getData() == null ? (short)0 : item.getData().getData()); if (item.getTypeId() == TradeCraft.currency.id && itemData == TradeCraft.currency.data) { amount += item.getAmount(); } @@ -86,8 +79,7 @@ public List getNonCurrencyItems() { List items = new ArrayList(); for (ItemStack item : chest.getContents()) { if (item != null) { - // TODO | DEBUG item.getData() always seems to return null - short itemData = item.getDurability(); //(item.getData() == null ? (short)0 : item.getData().getData()); + short itemData = (item.getData() == null ? (short)0 : item.getData().getData()); if (item.getTypeId() != TradeCraft.currency.id || itemData != TradeCraft.currency.data) { items.add(item); } diff --git a/nl/armeagle/TradeCraft/TradeCraftPermissions.java b/nl/armeagle/TradeCraft/TradeCraftPermissions.java index 1bcbdd7..4f6e92e 100644 --- a/nl/armeagle/TradeCraft/TradeCraftPermissions.java +++ b/nl/armeagle/TradeCraft/TradeCraftPermissions.java @@ -70,10 +70,18 @@ public boolean canSetCurrency(Player p) { } } + public boolean canReload(Player p) { + if ( plugin.permEnabled ) { + return permHandler.has(p, "TradeCraft.canReload"); + } else { + return p.isOp(); + } + } + public void debug(String n){ Player p = plugin.getServer().getPlayer(n); if(p == null){ - plugin.getServer().broadcastMessage("/canPlayer used with a name of player who is not online."); + plugin.getServer().broadcastMessage("/tc canPlayer used with a name of player who is not online."); return; } String name = p.getName(); diff --git a/nl/armeagle/TradeCraft/TradeCraftRepairShop.java b/nl/armeagle/TradeCraft/TradeCraftRepairShop.java index 091755f..2b8dfea 100644 --- a/nl/armeagle/TradeCraft/TradeCraftRepairShop.java +++ b/nl/armeagle/TradeCraft/TradeCraftRepairShop.java @@ -48,8 +48,7 @@ public void handleRightClick(Player player) { chest.clear(); for (ItemStack item : items) { - // TODO | DEBUG item.getData() always seems to return null - short itemData = item.getDurability(); //(item.getData() == null ? (short)0 : item.getData().getData()); + short itemData = (item.getData() == null ? (short)0 : item.getData().getData()); chest.add(new TradeCraftItem(item.getTypeId(), itemData), 1); } diff --git a/plugin.yml b/plugin.yml index 28ce492..1303b06 100644 --- a/plugin.yml +++ b/plugin.yml @@ -2,15 +2,15 @@ name: TradeCraft main: nl.armeagle.TradeCraft.TradeCraft version: AE-1.0.0-beta1 commands: - myshops: - description: See a printout of any personal shops you have - usage: - setCurrency: - description: Set the currency used. - usage: Enter an id or a material name (using _ as spaces) - displayCurrency: - description: Check what currency is being used. - usage: Displays the current material name of the currency used. - canPlayer: - description: See what permissions a player has regarding TradeCraft - usage: enter a user name \ No newline at end of file + tc: + help: + description: Show TradeCradt command help + shops: + description: See a printout of any personal shops you have + usage: + currency: + description: Get/set the currency used. + usage: Enter an id with an optional data value separated by a semicolon + reload: + description: reload the script and with that all the configuration files + usage: From 067ab29e53afacdc22a0fcfb977bf57e63542205 Mon Sep 17 00:00:00 2001 From: ArmEagle Date: Tue, 26 Apr 2011 17:13:08 +0200 Subject: [PATCH 049/100] Added 'tc reload' and help options as console commands. --- TradeCraft.en.lang | 1 + nl/armeagle/TradeCraft/TradeCraft.java | 42 +++++++++++++++++++------- 2 files changed, 32 insertions(+), 11 deletions(-) diff --git a/TradeCraft.en.lang b/TradeCraft.en.lang index 27f884f..76f19e0 100644 --- a/TradeCraft.en.lang +++ b/TradeCraft.en.lang @@ -13,6 +13,7 @@ TC_HELP_THIS_TEXT: "this help text" TC_SHOPS: "lists the shops the player owns" TC_CURRENCY_OPT_PARAM_GETSET_CURRENCY: "get and set the currency" TC_CURRENCY_GET_CURRENCY: "get the current currency" +TC_CAN_PLAYER: "show the permissions of the given player" TC_RELOAD: "reload the script and with that all the configuration files" RESTARTING_PLUGIN: "Restarting %1$s" RESTARTING_PLUGIN_DONE: "Restarting %1$s completed" diff --git a/nl/armeagle/TradeCraft/TradeCraft.java b/nl/armeagle/TradeCraft/TradeCraft.java index 8982db8..b8ad884 100644 --- a/nl/armeagle/TradeCraft/TradeCraft.java +++ b/nl/armeagle/TradeCraft/TradeCraft.java @@ -159,11 +159,22 @@ public boolean onCommand(CommandSender sender, Command command, String label, St } } } else if ( sender instanceof ConsoleCommandSender ) { - if ( name.equalsIgnoreCase(TradeCraft.CommandString) ) { + if ( args.length == 0 || args[0].compareToIgnoreCase("help") == 0 ) { + displayCommandHelpText(null); + return true; + } else if ( name.equalsIgnoreCase(TradeCraft.CommandString) ) { if ( args[0].equalsIgnoreCase("canplayer") && args.length == 2) { permissions.debug(args[1]); return true; + } else if ( args[0].equalsIgnoreCase("reload") ) { + this.log(Level.INFO, TradeCraftLocalization.get("RESTARTING_PLUGIN"), + TradeCraft.pluginName); + this.disable(); + this.enable(); + this.log(Level.INFO, TradeCraftLocalization.get("RESTARTING_PLUGIN_DONE"), + TradeCraft.pluginName); } + return true; } return false; } else { @@ -437,17 +448,26 @@ public String getCurrencyName() { } private void displayCommandHelpText(Player player) { - this.sendMessage(player, TradeCraftLocalization.get("POSSIBLE_COMMANDS_FOR_THE_PLUGIN"), - TradeCraft.pluginName); - this.sendMessage(player, "/tc [help]"+ ChatColor.GRAY +" "+ TradeCraftLocalization.get("TC_HELP_THIS_TEXT")); - this.sendMessage(player, "/tc shops"+ ChatColor.GRAY +" "+ TradeCraftLocalization.get("TC_SHOPS")); - if ( this.permissions.canSetCurrency(player) ) { - this.sendMessage(player, "/tc currency [id[;data]]"+ ChatColor.GRAY +" "+ TradeCraftLocalization.get("TC_CURRENCY_OPT_PARAM_GETSET_CURRENCY")); + if ( player != null ) { + this.sendMessage(player, TradeCraftLocalization.get("POSSIBLE_COMMANDS_FOR_THE_PLUGIN"), + TradeCraft.pluginName); + this.sendMessage(player, "/tc [help]"+ ChatColor.GRAY +" "+ TradeCraftLocalization.get("TC_HELP_THIS_TEXT")); + this.sendMessage(player, "/tc shops"+ ChatColor.GRAY +" "+ TradeCraftLocalization.get("TC_SHOPS")); + if ( this.permissions.canSetCurrency(player) ) { + this.sendMessage(player, "/tc currency [id[;data]]"+ ChatColor.GRAY +" "+ TradeCraftLocalization.get("TC_CURRENCY_OPT_PARAM_GETSET_CURRENCY")); + } else { + this.sendMessage(player, "/tc currency"+ ChatColor.GRAY +" "+ TradeCraftLocalization.get("TC_CURRENCY_GET_CURRENCY")); + } + if ( this.permissions.canReload(player) ) { + this.sendMessage(player, "/tc reload"+ ChatColor.GRAY +" "+ TradeCraftLocalization.get("TC_RELOAD")); + } } else { - this.sendMessage(player, "/tc currency"+ ChatColor.GRAY +" "+ TradeCraftLocalization.get("TC_CURRENCY_GET_CURRENCY")); - } - if ( this.permissions.canReload(player) ) { - this.sendMessage(player, "/tc reload"+ ChatColor.GRAY +" "+ TradeCraftLocalization.get("TC_RELOAD")); + // console command help + this.log(Level.INFO, TradeCraftLocalization.get("POSSIBLE_COMMANDS_FOR_THE_PLUGIN"), + TradeCraft.pluginName); + this.log(Level.INFO, "tc [help]: "+ TradeCraftLocalization.get("TC_HELP_THIS_TEXT")); + this.log(Level.INFO, "tc canPlayer playername: "+ TradeCraftLocalization.get("TC_CAN_PLAYER")); + this.log(Level.INFO, "tc reload: "+ TradeCraftLocalization.get("TC_RELOAD")); } } From 2ee5dff3b95a2ae68614dc7352c8ae71c4dfe1aa Mon Sep 17 00:00:00 2001 From: ArmEagle Date: Thu, 28 Apr 2011 18:48:41 +0200 Subject: [PATCH 050/100] Forgot to set version in plugin.yml Few more items added to TODO list --- TODO.txt | 12 ++++++++++-- plugin.yml | 2 +- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/TODO.txt b/TODO.txt index e2c7a23..8b1d7b2 100644 --- a/TODO.txt +++ b/TODO.txt @@ -4,16 +4,22 @@ Digi said: - Also, could you add a feature to place signs ON chests (like Lockette) ? - Protection of chest contents while you're using it... or something, so other people don't just sit by and wait for you to buy and steal your stuff. - - The sign labels would be better understood if they're: "Buy 5 for 1" and "Sell 5 for 1" and you should suppot texts like "Buy 5 for 1g" or "Buy5for1" etc. + -- Can be done by implementing features like VirtualChest has. This used CraftBukkit code to open a virtual inventory. - limit currency and configured items to valid data values - Allow administrators to disable infinite shops using a command and storing that setting in TradeCraft.properties. - Allow setting of currency by name (basic names from Bukkit, or also as defined in config?) again. - warn when config files use old format? + - Messages after right-clicking use the item name as on the sign, not as in TradeCraft.txt (capitalization issue only). + - Customer right-clicking telling about rates and availability only display the amount of items. Could perhaps display "up to 12 Logs" instead of "up to 12". + Coding issue: - - chest.getContents --> item.getData().getData() always seems to return 0, workaround with using getDurability which accesses the same data. - items with 'damage' bits too high will act as the basic item, but cannot be traded or stacked as such. Be careful not to create these. + -- The server will automatically stack items that do not have actually existing multiple version through the damage bit. + + - What happens when a sign/chest is removed while the plugin is disabled .. / with the entry in the .data file later.. when a shop is placed in the exact same spot again? + - No message is shown when a customer places too few currency in the chest and right-clicks. Done: @@ -22,3 +28,5 @@ Digi said: - The wool, dye, log, etc sub-items (just re-mentioning, I understand it's gonna be possible in #564+) - Additional information about current amount of items and currency available when right clicking as client. - You don't have to write your name on the sign of a player shop anymore. Also, too long names will be stored just fine, but will show abbreviated on the sign. +- The sign labels would be better understood if they're: "Buy 5 for 1" and "Sell 5 for 1" and you should suppot texts like "Buy 5 for 1g" or "Buy5for1" etc. + \ No newline at end of file diff --git a/plugin.yml b/plugin.yml index 1303b06..bd4e5a9 100644 --- a/plugin.yml +++ b/plugin.yml @@ -1,6 +1,6 @@ name: TradeCraft main: nl.armeagle.TradeCraft.TradeCraft -version: AE-1.0.0-beta1 +version: AE-1.0.0 commands: tc: help: From ac434ff354a7514212d7c22b6ebed9fade3a3cda Mon Sep 17 00:00:00 2001 From: ArmEagle Date: Thu, 28 Apr 2011 18:56:09 +0200 Subject: [PATCH 051/100] Fixed: No message is shown when a customer places too few currency/items in the chest and right-clicks. Had wrong parameter order and it throwed an exception. --- TODO.txt | 6 +++--- nl/armeagle/TradeCraft/TradeCraftItemShop.java | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/TODO.txt b/TODO.txt index 8b1d7b2..999e8b7 100644 --- a/TODO.txt +++ b/TODO.txt @@ -19,7 +19,6 @@ Digi said: -- The server will automatically stack items that do not have actually existing multiple version through the damage bit. - What happens when a sign/chest is removed while the plugin is disabled .. / with the entry in the .data file later.. when a shop is placed in the exact same spot again? - - No message is shown when a customer places too few currency in the chest and right-clicks. Done: @@ -28,5 +27,6 @@ Digi said: - The wool, dye, log, etc sub-items (just re-mentioning, I understand it's gonna be possible in #564+) - Additional information about current amount of items and currency available when right clicking as client. - You don't have to write your name on the sign of a player shop anymore. Also, too long names will be stored just fine, but will show abbreviated on the sign. -- The sign labels would be better understood if they're: "Buy 5 for 1" and "Sell 5 for 1" and you should suppot texts like "Buy 5 for 1g" or "Buy5for1" etc. - \ No newline at end of file + - The sign labels would be better understood if they're: "Buy 5 for 1" and "Sell 5 for 1" and you should suppot texts like "Buy 5 for 1g" or "Buy5for1" etc. + - No message is shown when a customer places too few currency/items in the chest and right-clicks. + \ No newline at end of file diff --git a/nl/armeagle/TradeCraft/TradeCraftItemShop.java b/nl/armeagle/TradeCraft/TradeCraftItemShop.java index 34c4885..7c39f82 100644 --- a/nl/armeagle/TradeCraft/TradeCraftItemShop.java +++ b/nl/armeagle/TradeCraft/TradeCraftItemShop.java @@ -179,8 +179,8 @@ private void playerWantsToBuy(Player player) { if (amountPlayerWantsToBuy == 0) { plugin.sendMessage(player, TradeCraftLocalization.get("YOU_NEED_TO_SPEND_AT_LEAST_X_A_TO_GET_ANY_B"), - plugin.getCurrencyName(), getBuyValue(), + plugin.getCurrencyName(), getItemName()); return; } From 2974ba5929ce8e2c527f2824cf1d45645d0a66b4 Mon Sep 17 00:00:00 2001 From: ArmEagle Date: Thu, 28 Apr 2011 19:37:27 +0200 Subject: [PATCH 052/100] Allow setting of currency by name, as defined in config. --- TODO.txt | 14 +++--- TradeCraft.en.lang | 6 +-- nl/armeagle/TradeCraft/TradeCraft.java | 63 +++++++++++++++----------- plugin.yml | 2 +- 4 files changed, 48 insertions(+), 37 deletions(-) diff --git a/TODO.txt b/TODO.txt index 999e8b7..4e47c80 100644 --- a/TODO.txt +++ b/TODO.txt @@ -8,19 +8,15 @@ Digi said: - limit currency and configured items to valid data values - Allow administrators to disable infinite shops using a command and storing that setting in TradeCraft.properties. - - Allow setting of currency by name (basic names from Bukkit, or also as defined in config?) again. - warn when config files use old format? - - Messages after right-clicking use the item name as on the sign, not as in TradeCraft.txt (capitalization issue only). + - Customer right-clicking telling about rates and availability only display the amount of items. Could perhaps display "up to 12 Logs" instead of "up to 12". Coding issue: - items with 'damage' bits too high will act as the basic item, but cannot be traded or stacked as such. Be careful not to create these. -- The server will automatically stack items that do not have actually existing multiple version through the damage bit. - - What happens when a sign/chest is removed while the plugin is disabled .. / with the entry in the .data file later.. when a shop is placed in the exact same spot again? - - Done: - [launch player] triggers infinite shop check - (gsand) Would it be possible to make it so players can buy 6 items for 2 gold, but not 3 for 1 gold. @@ -29,4 +25,10 @@ Digi said: - You don't have to write your name on the sign of a player shop anymore. Also, too long names will be stored just fine, but will show abbreviated on the sign. - The sign labels would be better understood if they're: "Buy 5 for 1" and "Sell 5 for 1" and you should suppot texts like "Buy 5 for 1g" or "Buy5for1" etc. - No message is shown when a customer places too few currency/items in the chest and right-clicks. - \ No newline at end of file + - Allow setting of currency by name, as defined in config. + +Food for thought, answered: + - What happens when a sign/chest is removed while the plugin is disabled .. / with the entry in the .data file later.. when a shop is placed in the exact same spot again? + -- A new shop (sign on placement) overwrites the old one in the collection, since that uses only coordinates as key. + - Messages after right-clicking use the item name as on the sign, not as in TradeCraft.txt (capitalization issue only). + -- Just part of how the plugin works. Could perhaps change it, but it's a minor issue. \ No newline at end of file diff --git a/TradeCraft.en.lang b/TradeCraft.en.lang index 76f19e0..80c2ca6 100644 --- a/TradeCraft.en.lang +++ b/TradeCraft.en.lang @@ -1,9 +1,9 @@ # TradeCraft.java NO_PERMISSION_SET_CURRENCY: "You do not have the permission to set the currency" -IS_NO_VALID_CURRENCY_USE_INSTEAD: "%1$s is not a valid value for a currency, use 'id[;data]'" +IS_NO_VALID_CURRENCY_USE_INSTEAD: "%1$s is not a valid value for a currency, use 'id[;data]' or 'itemname'." INVALID_CURRENCY: "Invalid currency: %1$s" -CURRENCY_IS_SET_TO: "Currency is set to: %1$s" -CURRENCY_IS: "Currency is: %1$s" +CURRENCY_IS_SET_TO_A_IDDATA: "Currency is set to: %1$s (%2$s)" +CURRENCY_IS_A_IDDATA: "Currency is: %1$s (%2$s)" YOU_DONT_OWN_ANY_SHOPS: "You don't own any shops!" YOUR_SHOPS: "Your shops:" ITEM: "Item" diff --git a/nl/armeagle/TradeCraft/TradeCraft.java b/nl/armeagle/TradeCraft/TradeCraft.java index b8ad884..befc7a6 100644 --- a/nl/armeagle/TradeCraft/TradeCraft.java +++ b/nl/armeagle/TradeCraft/TradeCraft.java @@ -114,36 +114,45 @@ public boolean onCommand(CommandSender sender, Command command, String label, St Matcher IdSplitData = TradeCraft.itemPatternIdSplitData.matcher(args[1]); if ( !IdSplitData.matches() ) { - this.sendMessage(p, TradeCraftLocalization.get("IS_NO_VALID_CURRENCY_USE_INSTEAD"), - args[1]); - return false; - } - - try { - int cid = Integer.parseInt(IdSplitData.group(1)); - if ( IdSplitData.group(2) != null ) { - testCurrency = new TradeCraftItem(cid, Short.parseShort(IdSplitData.group(2))); - } else { - testCurrency = new TradeCraftItem(cid); - } - if ( this.configuration.get(testCurrency) != null ) { - currency = testCurrency; - this.properties.setCurrencyType(currency); - this.sendMessage(p, TradeCraftLocalization.get("CURRENCY_IS_SET_TO"), - this.getCurrencyName()); - } else { - this.sendMessage(p, TradeCraftLocalization.get("INVALID_CURRENCY"), - args[1]); + // try to match the parameter to item names from the configuration + TradeCraftConfigurationInfo setCurr = this.configuration.get(args[1]); + if ( setCurr == null ) { + this.sendMessage(p, TradeCraftLocalization.get("IS_NO_VALID_CURRENCY_USE_INSTEAD"), + args[1]); return false; - } - } catch ( NumberFormatException e ) { - this.sendMessage(p, TradeCraftLocalization.get("INVALID_CURRENCY"), - args[1]); - return false; + } else { + currency = setCurr.type; + } + } else { + try { + int cid = Integer.parseInt(IdSplitData.group(1)); + if ( IdSplitData.group(2) != null ) { + testCurrency = new TradeCraftItem(cid, Short.parseShort(IdSplitData.group(2))); + } else { + testCurrency = new TradeCraftItem(cid); + } + } catch ( NumberFormatException e ) { + this.sendMessage(p, TradeCraftLocalization.get("INVALID_CURRENCY"), + args[1]); + return false; + } + if ( this.configuration.get(testCurrency) != null ) { + currency = testCurrency; + } else { + this.sendMessage(p, TradeCraftLocalization.get("INVALID_CURRENCY"), + args[1]); + return false; + } } + + this.properties.setCurrencyType(currency); + this.sendMessage(p, TradeCraftLocalization.get("CURRENCY_IS_SET_TO_A_IDDATA"), + this.getCurrencyName(), + currency.toShortString()); } else { - this.sendMessage(p, TradeCraftLocalization.get("CURRENCY_IS"), - this.getCurrencyName()); + this.sendMessage(p, TradeCraftLocalization.get("CURRENCY_IS_A_IDDATA"), + this.getCurrencyName(), + currency.toShortString()); } } else if ( args[0].equalsIgnoreCase("shops") ) { displayShops(p); diff --git a/plugin.yml b/plugin.yml index bd4e5a9..474d909 100644 --- a/plugin.yml +++ b/plugin.yml @@ -1,6 +1,6 @@ name: TradeCraft main: nl.armeagle.TradeCraft.TradeCraft -version: AE-1.0.0 +version: AE-1.0.1 commands: tc: help: From 236b35534012591be84f25b1725e3b6f0b4823a7 Mon Sep 17 00:00:00 2001 From: ArmEagle Date: Thu, 5 May 2011 10:57:01 +0200 Subject: [PATCH 053/100] Fixed issue with shop withdrawing too much items into the chest when owner interacted with the shop through the sign. Version 1.0.2 --- nl/armeagle/TradeCraft/TradeCraftItemShop.java | 7 ++++++- plugin.yml | 2 +- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/nl/armeagle/TradeCraft/TradeCraftItemShop.java b/nl/armeagle/TradeCraft/TradeCraftItemShop.java index 7c39f82..ad2bade 100644 --- a/nl/armeagle/TradeCraft/TradeCraftItemShop.java +++ b/nl/armeagle/TradeCraft/TradeCraftItemShop.java @@ -41,9 +41,10 @@ private void handleOwnerClick(Player player) { plugin.sendMessage(player, "%1$s %2$d %3$s.", TradeCraftLocalization.get("WITHDREW"), currencyAmount, plugin.getCurrencyName()); } } else { + // limit amount of items dropped into the chest int itemAmount = withdrawItems(); if (itemAmount > 0) { - int maxItemsChestCanHold = chest.getSize() * TradeCraft.getMaxStackSize(getChestItemType().id); + int maxItemsChestCanHold = chest.getSize() * TradeCraft.getMaxStackSize(getItemType().id); if ( itemAmount > maxItemsChestCanHold ) { populateChest(getItemType(), maxItemsChestCanHold); depositItems(itemAmount - maxItemsChestCanHold); @@ -279,6 +280,10 @@ private void playerWantsToSell(Player player) { plugin.getCurrencyName()); } + /** + * Get the item type of the items that are currently in the chest. + * @return + */ public TradeCraftItem getChestItemType() { return chest.type; } diff --git a/plugin.yml b/plugin.yml index 474d909..4c220d5 100644 --- a/plugin.yml +++ b/plugin.yml @@ -1,6 +1,6 @@ name: TradeCraft main: nl.armeagle.TradeCraft.TradeCraft -version: AE-1.0.1 +version: AE-1.0.2 commands: tc: help: From da0ad2cc2d55c962a3566a9a969bf541f5056c5c Mon Sep 17 00:00:00 2001 From: ArmEagle Date: Mon, 30 May 2011 23:34:04 +0200 Subject: [PATCH 054/100] Updated to version 1.0.3 Using SimpleSignEdit, which places a temporary sign, would throw a ClassCastException because the time TradeCraft received the event, the sign was already gone again. Now checking whether the sign still exists. --- nl/armeagle/TradeCraft/TradeCraftBlockListener.java | 7 +++++++ plugin.yml | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/nl/armeagle/TradeCraft/TradeCraftBlockListener.java b/nl/armeagle/TradeCraft/TradeCraftBlockListener.java index 564744c..c7ac384 100644 --- a/nl/armeagle/TradeCraft/TradeCraftBlockListener.java +++ b/nl/armeagle/TradeCraft/TradeCraftBlockListener.java @@ -86,8 +86,15 @@ public void onSignChange(SignChangeEvent e) { return; } + // Check whether this block is (still) a sign. Can be revoked already in case the sign was temporary, + // for example when the plugin SimpleSignEdit is being used. + if ( e.getBlock().getType() != Material.SIGN && e.getBlock().getType() != Material.SIGN_POST ) { + return; + } Player player = e.getPlayer(); + Sign sign = (Sign) e.getBlock().getState(); + String ownerName = player.getName(); String itemName = plugin.getItemName(e.getLines()); diff --git a/plugin.yml b/plugin.yml index 4c220d5..22ea333 100644 --- a/plugin.yml +++ b/plugin.yml @@ -1,6 +1,6 @@ name: TradeCraft main: nl.armeagle.TradeCraft.TradeCraft -version: AE-1.0.2 +version: AE-1.0.3 commands: tc: help: From 84e357ffb3e7798f909be82d689f19ea4d4c5367 Mon Sep 17 00:00:00 2001 From: ArmEagle Date: Thu, 2 Jun 2011 01:07:20 +0200 Subject: [PATCH 055/100] version 1.0.5a Fixed exception caused by 'missing sign' when using SimpleSignEdit for real now without breaking the plugin. Initial addition of color support, needs to be made configurable. For now just to give the shop owner clearer difference between withdraw and deposit lines. --- nl/armeagle/TradeCraft/TradeCraft.java | 7 +++ .../TradeCraft/TradeCraftBlockListener.java | 12 ++-- .../TradeCraft/TradeCraftItemShop.java | 57 +++++++++++++------ .../TradeCraft/TradeCraftPropertiesFile.java | 15 ++++- plugin.yml | 2 +- 5 files changed, 69 insertions(+), 24 deletions(-) diff --git a/nl/armeagle/TradeCraft/TradeCraft.java b/nl/armeagle/TradeCraft/TradeCraft.java index befc7a6..b83aa11 100644 --- a/nl/armeagle/TradeCraft/TradeCraft.java +++ b/nl/armeagle/TradeCraft/TradeCraft.java @@ -31,6 +31,8 @@ import com.nijikokun.bukkit.Permissions.Permissions; public class TradeCraft extends JavaPlugin { + public static enum MessageTypes {WITHDRAW, DEPOSIT}; + // The plugin name. static final String pluginName = "TradeCraft"; @@ -210,6 +212,11 @@ void displayShops(Player p) { } + void sendMessage(Player player, TradeCraft.MessageTypes messageType, String format, Object... args) { + String message = String.format(format, args); + player.sendMessage(this.properties.getMessageTypeColor(messageType) + message); + } + void sendMessage(Player player, String format, Object... args) { String message = String.format(format, args); player.sendMessage(message); diff --git a/nl/armeagle/TradeCraft/TradeCraftBlockListener.java b/nl/armeagle/TradeCraft/TradeCraftBlockListener.java index c7ac384..e01804b 100644 --- a/nl/armeagle/TradeCraft/TradeCraftBlockListener.java +++ b/nl/armeagle/TradeCraft/TradeCraftBlockListener.java @@ -1,6 +1,7 @@ package nl.armeagle.TradeCraft; import java.util.ArrayList; +import java.util.logging.Level; import org.bukkit.Material; import org.bukkit.block.Block; @@ -85,17 +86,18 @@ public void onSignChange(SignChangeEvent e) { if ( !this.plugin.isEnabled() ) { return; } - + // Check whether this block is (still) a sign. Can be revoked already in case the sign was temporary, // for example when the plugin SimpleSignEdit is being used. - if ( e.getBlock().getType() != Material.SIGN && e.getBlock().getType() != Material.SIGN_POST ) { + if ( e.getBlock().getType() != Material.SIGN_POST && + e.getBlock().getType() != Material.WALL_SIGN ) { return; } - Player player = e.getPlayer(); Sign sign = (Sign) e.getBlock().getState(); - - String ownerName = player.getName(); + + Player player = e.getPlayer(); + String ownerName = player.getName(); String itemName = plugin.getItemName(e.getLines()); diff --git a/nl/armeagle/TradeCraft/TradeCraftItemShop.java b/nl/armeagle/TradeCraft/TradeCraftItemShop.java index ad2bade..4a052b8 100644 --- a/nl/armeagle/TradeCraft/TradeCraftItemShop.java +++ b/nl/armeagle/TradeCraft/TradeCraftItemShop.java @@ -32,13 +32,18 @@ private void handleOwnerClick(Player player) { if ( currencyAmount > maxCurrencyChestCanHold ) { populateChest(TradeCraft.currency, maxCurrencyChestCanHold); // fill to the max depositCurrency(currencyAmount - maxCurrencyChestCanHold); // return remaining money back to shop data - plugin.sendMessage(player, TradeCraftLocalization.get("WITHDREW_X_CURRENCY_SHOP_STILL_HOLDS_Y_CURRENCY"), - maxCurrencyChestCanHold, - plugin.getCurrencyName(), - currencyAmount - maxCurrencyChestCanHold); + plugin.sendMessage(player, TradeCraft.MessageTypes.WITHDRAW, + TradeCraftLocalization.get("WITHDREW_X_CURRENCY_SHOP_STILL_HOLDS_Y_CURRENCY"), + maxCurrencyChestCanHold, + plugin.getCurrencyName(), + currencyAmount - maxCurrencyChestCanHold); } else { populateChest(TradeCraft.currency, currencyAmount); - plugin.sendMessage(player, "%1$s %2$d %3$s.", TradeCraftLocalization.get("WITHDREW"), currencyAmount, plugin.getCurrencyName()); + plugin.sendMessage(player, TradeCraft.MessageTypes.WITHDRAW, + "%1$s %2$d %3$s.", + TradeCraftLocalization.get("WITHDREW"), + currencyAmount, + plugin.getCurrencyName()); } } else { // limit amount of items dropped into the chest @@ -48,13 +53,18 @@ private void handleOwnerClick(Player player) { if ( itemAmount > maxItemsChestCanHold ) { populateChest(getItemType(), maxItemsChestCanHold); depositItems(itemAmount - maxItemsChestCanHold); - plugin.sendMessage(player, TradeCraftLocalization.get("WITHDREW_X_A_SHOP_STILL_HOLDS_Y_A"), - maxItemsChestCanHold, - getItemName(), - itemAmount - maxItemsChestCanHold); + plugin.sendMessage(player, TradeCraft.MessageTypes.WITHDRAW, + TradeCraftLocalization.get("WITHDREW_X_A_SHOP_STILL_HOLDS_Y_A"), + maxItemsChestCanHold, + getItemName(), + itemAmount - maxItemsChestCanHold); } else { populateChest(getItemType(), itemAmount); - plugin.sendMessage(player, "%1$s %2$d %3$s.", TradeCraftLocalization.get("WITHDREW"), itemAmount, getItemName()); + plugin.sendMessage(player, TradeCraft.MessageTypes.WITHDRAW, + "%1$s %2$d %3$s.", + TradeCraftLocalization.get("WITHDREW"), + itemAmount, + getItemName()); } } else { plugin.sendMessage(player, TradeCraftLocalization.get("THERE_IS_NOTHING_TO_WITHDRAW")); @@ -62,7 +72,11 @@ private void handleOwnerClick(Player player) { } } else if ( getChestItemType().compareTo(TradeCraft.currency) == 0 ) { depositCurrency(getChestItemCount()); - plugin.sendMessage(player, "%1$s %2$d %3$s.", TradeCraftLocalization.get("DEPOSITED"), getChestItemCount(), plugin.getCurrencyName()); + plugin.sendMessage(player, TradeCraft.MessageTypes.DEPOSIT, + "%1$s %2$d %3$s.", + TradeCraftLocalization.get("DEPOSITED"), + getChestItemCount(), + plugin.getCurrencyName()); populateChest(new TradeCraftItem(0), 0); int itemAmount = withdrawItems(); if (itemAmount > 0) { @@ -70,19 +84,28 @@ private void handleOwnerClick(Player player) { if ( itemAmount > maxItemsChestCanHold ) { populateChest(getItemType(), maxItemsChestCanHold); depositItems(itemAmount - maxItemsChestCanHold); - plugin.sendMessage(player, TradeCraftLocalization.get("WITHDREW_X_A_SHOP_STILL_HOLDS_Y_A"), - maxItemsChestCanHold, - getItemName(), - itemAmount - maxItemsChestCanHold); + plugin.sendMessage(player, TradeCraft.MessageTypes.WITHDRAW, + TradeCraftLocalization.get("WITHDREW_X_A_SHOP_STILL_HOLDS_Y_A"), + maxItemsChestCanHold, + getItemName(), + itemAmount - maxItemsChestCanHold); } else { populateChest(getItemType(), itemAmount); - plugin.sendMessage(player, "%1$s %2$d %3$s.", TradeCraftLocalization.get("WITHDREW"), itemAmount, getItemName()); + plugin.sendMessage(player, TradeCraft.MessageTypes.WITHDRAW, + "%1$s %2$d %3$s.", + TradeCraftLocalization.get("WITHDREW"), + itemAmount, + getItemName()); } } } else if ( getChestItemType().compareTo(getItemType()) == 0 ) { depositItems(getChestItemCount()); populateChest(new TradeCraftItem(0), 0); - plugin.sendMessage(player, "%1$s %2$d %3$s.", TradeCraftLocalization.get("DEPOSITED"), getChestItemCount(), getItemName()); + plugin.sendMessage(player, TradeCraft.MessageTypes.DEPOSIT, + "%1$s %2$d %3$s.", + TradeCraftLocalization.get("DEPOSITED"), + getChestItemCount(), + getItemName()); } else { plugin.sendMessage(player, TradeCraftLocalization.get("YOU_CANT_DEPOSIT_THAT_HERE")); } diff --git a/nl/armeagle/TradeCraft/TradeCraftPropertiesFile.java b/nl/armeagle/TradeCraft/TradeCraftPropertiesFile.java index 5b90b32..7734c80 100644 --- a/nl/armeagle/TradeCraft/TradeCraftPropertiesFile.java +++ b/nl/armeagle/TradeCraft/TradeCraftPropertiesFile.java @@ -4,6 +4,8 @@ import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; + +import org.bukkit.ChatColor; import org.bukkit.util.config.Configuration; /** @@ -17,7 +19,7 @@ public class TradeCraftPropertiesFile { private TradeCraft plugin; private final Configuration properties; - + public TradeCraftPropertiesFile(TradeCraft plugin) { this.plugin = plugin; // make folder in the plugins dir if it doesn't exist yet @@ -102,4 +104,15 @@ public String getLanguage() { public boolean autoUpdateLanguageFiles() { return properties.getBoolean("auto-update-language-files", true); } + + public ChatColor getMessageTypeColor(TradeCraft.MessageTypes mtype) { + switch (mtype) { + case WITHDRAW: + return ChatColor.YELLOW; + case DEPOSIT: + return ChatColor.GRAY; + default: + return ChatColor.WHITE; + } + } } diff --git a/plugin.yml b/plugin.yml index 22ea333..dd2d755 100644 --- a/plugin.yml +++ b/plugin.yml @@ -1,6 +1,6 @@ name: TradeCraft main: nl.armeagle.TradeCraft.TradeCraft -version: AE-1.0.3 +version: AE-1.0.5 commands: tc: help: From 0fa4b256bb614af75fbd36b26465ba0b19779c3d Mon Sep 17 00:00:00 2001 From: ArmEagle Date: Thu, 2 Jun 2011 01:08:29 +0200 Subject: [PATCH 056/100] cleaned up warning, removed unused import --- nl/armeagle/TradeCraft/TradeCraftBlockListener.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/nl/armeagle/TradeCraft/TradeCraftBlockListener.java b/nl/armeagle/TradeCraft/TradeCraftBlockListener.java index e01804b..19d04d4 100644 --- a/nl/armeagle/TradeCraft/TradeCraftBlockListener.java +++ b/nl/armeagle/TradeCraft/TradeCraftBlockListener.java @@ -1,8 +1,6 @@ package nl.armeagle.TradeCraft; import java.util.ArrayList; -import java.util.logging.Level; - import org.bukkit.Material; import org.bukkit.block.Block; import org.bukkit.block.Sign; From 9466c53a306d32962900ce2dcd79d589a7b9c0c3 Mon Sep 17 00:00:00 2001 From: ArmEagle Date: Thu, 9 Jun 2011 21:55:44 +0200 Subject: [PATCH 057/100] Multi world support. Old shops are updated to new format on first interact. --- nl/armeagle/TradeCraft/TradeCraft.java | 2 +- .../TradeCraft/TradeCraftDataFile.java | 162 ++++++++++-------- 2 files changed, 91 insertions(+), 73 deletions(-) diff --git a/nl/armeagle/TradeCraft/TradeCraft.java b/nl/armeagle/TradeCraft/TradeCraft.java index b83aa11..da02017 100644 --- a/nl/armeagle/TradeCraft/TradeCraft.java +++ b/nl/armeagle/TradeCraft/TradeCraft.java @@ -289,7 +289,7 @@ TradeCraftShop getShopFromSignBlock(Player player, Block block) { int y = block.getY(); int z = block.getZ(); - trace(player, "You clicked a sign at %d, %d, %d.", x, y, z); + trace(player, "You clicked a sign at %d, %d, %d in world: %s.", x, y, z, player.getWorld().getName()); Sign sign = (Sign) player.getWorld().getBlockAt(x, y, z).getState(); diff --git a/nl/armeagle/TradeCraft/TradeCraftDataFile.java b/nl/armeagle/TradeCraft/TradeCraftDataFile.java index 8e29e84..d145c07 100644 --- a/nl/armeagle/TradeCraft/TradeCraftDataFile.java +++ b/nl/armeagle/TradeCraft/TradeCraftDataFile.java @@ -13,21 +13,30 @@ import java.util.regex.Pattern; import org.bukkit.Location; +import org.bukkit.World; import org.bukkit.block.Sign; class TradeCraftDataFile { + /* + * As of version 1.0.5 there is support for multiple worlds. + * Newly created shops will add the world name to the information stored. + * Old shops will be converted when first interacted with. + */ private static final String fileName = "plugins" + File.separator + TradeCraft.pluginName+ File.separator + TradeCraft.pluginName + ".data"; - private static final Pattern infoPattern1 = Pattern.compile( - "^\\s*(-?\\d+)\\s*,\\s*(-?\\d+)\\s*,\\s*(-?\\d+)" + // x,y,z - "\\s*=\\s*" + - "(\\d+)\\s*,\\s*(\\d+)\\s*$"); // itemAmount,currencyAmount - private static final Pattern infoPattern2 = Pattern.compile( + private static final Pattern infoPatternNoWorld = Pattern.compile( "^\\s*([^,]+)\\s*," + // ownerName "\\s*(-?\\d+)\\s*,\\s*(-?\\d+)\\s*,\\s*(-?\\d+)\\s*," + // x,y,z "\\s*(\\d+(?:;\\d+)?)\\s*," + // itemType[!data] "\\s*(\\d+)\\s*," + // itemAmount "\\s*(\\d+)\\s*$"); // currencyAmount + private static final Pattern infoPatternWorld = Pattern.compile( + "^\\s*([^,]+)\\s*," + // ownerName + "\\s*([^,]+)\\s*," + // world name + "\\s*(-?\\d+)\\s*,\\s*(-?\\d+)\\s*,\\s*(-?\\d+)\\s*," + // x,y,z + "\\s*(\\d+(?:;\\d+)?)\\s*," + // itemType[!data] + "\\s*(\\d+)\\s*," + // itemAmount + "\\s*(\\d+)\\s*$"); // currencyAmount private final TradeCraft plugin; private final Map data = new HashMap(); @@ -40,17 +49,6 @@ class TradeCraftDataFile { } public void load() { -// if (!dFile.exists()) { -// plugin.log.info("No " + fileName + " file to read. Creating one now."); -// try { -// dFile.createNewFile(); -// } catch (IOException e) { -// // TODO Auto-generated catch block -// e.printStackTrace(); -// } -// return; -// } - try { dFile.createNewFile(); data.clear(); @@ -63,66 +61,70 @@ public void load() { while ((line = reader.readLine()) != null) { lineNumber += 1; - Matcher infoMatcher2 = infoPattern2.matcher(line); + String ownerName; + String worldName; + int x; + int y; + int z; + int itemAmount; + int currencyAmount; + String itemIdData; + + Matcher infoMatcher2 = infoPatternNoWorld.matcher(line); if (infoMatcher2.matches()) { - String ownerName = infoMatcher2.group(1); - int x = Integer.parseInt(infoMatcher2.group(2)); - int y = Integer.parseInt(infoMatcher2.group(3)); - int z = Integer.parseInt(infoMatcher2.group(4)); - int itemAmount = Integer.parseInt(infoMatcher2.group(6)); - int currencyAmount = Integer.parseInt(infoMatcher2.group(7)); - - TradeCraftDataInfo info = new TradeCraftDataInfo(); - String key = getKey(x, y, z); - - info.ownerName = ownerName; - info.itemAmount = itemAmount; - info.currencyAmount = currencyAmount; - - data.put(key, info); - - Matcher IdSplitData = TradeCraft.itemPatternIdSplitData.matcher(infoMatcher2.group(5)); - - if ( IdSplitData.matches() ) { - int itemId = Integer.parseInt(IdSplitData.group(1)); - if ( IdSplitData.group(2) != null ) { - info.itemType = new TradeCraftItem(itemId, Short.parseShort(IdSplitData.group(2))); - } else { - info.itemType = new TradeCraftItem(itemId); - } - } else { - plugin.log.warning( - "Failed to parse line number " + lineNumber + - " in " + fileName + - ": " + line); - continue; - } + ownerName = infoMatcher2.group(1); + worldName = null; + x = Integer.parseInt(infoMatcher2.group(2)); + y = Integer.parseInt(infoMatcher2.group(3)); + z = Integer.parseInt(infoMatcher2.group(4)); + itemIdData = infoMatcher2.group(5); + itemAmount = Integer.parseInt(infoMatcher2.group(6)); + currencyAmount = Integer.parseInt(infoMatcher2.group(7)); } else { - Matcher infoMatcher1 = infoPattern1.matcher(line); - - if (!infoMatcher1.matches()) { - plugin.log.warning( + // support for multiple worlds + Matcher infoMatcher3 = infoPatternWorld.matcher(line); + if ( !infoMatcher3.matches()) { + plugin.log.warning( "Failed to parse line number " + lineNumber + " in " + fileName + ": " + line); continue; + } + + ownerName = infoMatcher3.group(1); + worldName = infoMatcher3.group(2); + x = Integer.parseInt(infoMatcher3.group(3)); + y = Integer.parseInt(infoMatcher3.group(4)); + z = Integer.parseInt(infoMatcher3.group(5)); + itemIdData = infoMatcher3.group(6); + itemAmount = Integer.parseInt(infoMatcher3.group(7)); + currencyAmount = Integer.parseInt(infoMatcher3.group(8)); + } + + TradeCraftDataInfo info = new TradeCraftDataInfo(); + info.ownerName = ownerName; + info.itemAmount = itemAmount; + info.currencyAmount = currencyAmount; + + String key = getKey(worldName, x, y, z); + data.put(key, info); + + Matcher IdSplitData = TradeCraft.itemPatternIdSplitData.matcher(itemIdData); + + if ( IdSplitData.matches() ) { + int itemId = Integer.parseInt(IdSplitData.group(1)); + if ( IdSplitData.group(2) != null ) { + info.itemType = new TradeCraftItem(itemId, Short.parseShort(IdSplitData.group(2))); + } else { + info.itemType = new TradeCraftItem(itemId); } - - int x = Integer.parseInt(infoMatcher1.group(1)); - int y = Integer.parseInt(infoMatcher1.group(2)); - int z = Integer.parseInt(infoMatcher1.group(3)); - int itemAmount = Integer.parseInt(infoMatcher1.group(4)); - int currencyAmount = Integer.parseInt(infoMatcher1.group(5)); - - String key = getKey(x, y, z); - - TradeCraftDataInfo info = new TradeCraftDataInfo(); - info.ownerName = "unknown"; - info.itemAmount = itemAmount; - info.currencyAmount = currencyAmount; - - data.put(key, info); + } else { + plugin.log.warning( + "Failed to parse line number " + lineNumber + + " in " + fileName + + ": " + line); + continue; } } @@ -168,7 +170,7 @@ public void save() { public void deleteShop(TradeCraftShop shop){ Location l = shop.sign.getBlock().getLocation(); - String key = getKey(l.getBlockX(),l.getBlockY(),l.getBlockZ()); + String key = getKey(shop.sign.getWorld().getName(), l.getBlockX(),l.getBlockY(),l.getBlockZ()); if(data.containsKey(key)){ data.remove(key); save(); @@ -290,11 +292,27 @@ public void updateItemAndCurrencyAmounts(Sign sign, int itemAdjustment, int curr } private String getKeyFromSign(Sign sign) { - return getKey(sign.getX(), sign.getY(), sign.getZ()); + String keyWithWorld = getKey(sign.getWorld().getName(), sign.getX(), sign.getY(), sign.getZ()); + // convert old style keys (without world name) to new style and return the new key + if ( !data.containsKey(keyWithWorld) ) { + // try the old style key, without the world part + String keyWithoutWorld = getKey(null, sign.getX(), sign.getY(), sign.getZ()); + if ( data.containsKey(keyWithoutWorld) ) { + TradeCraftDataInfo shopInfo = data.get(keyWithoutWorld); + data.remove(keyWithoutWorld); + data.put(keyWithWorld, shopInfo); + } + } + return keyWithWorld; } - private String getKey(int x, int y, int z) { - return x + "," + y + "," + z; + private String getKey(String world, int x, int y, int z) { + // support for multiple words now, optionally accepting a world passed on. + if ( world == null ) { + return x + "," + y + "," + z; + } else { + return world + "," + x + "," + y + "," + z; + } } public void createNewSign(String ownerName, TradeCraftConfigurationInfo itemInfo, Sign sign) { From 279fd326c4a875f0ad517d22477cfaf533db6bfb Mon Sep 17 00:00:00 2001 From: ArmEagle Date: Thu, 9 Jun 2011 22:00:33 +0200 Subject: [PATCH 058/100] Removed unneeded import. Version 1.0.5 With multi-world support and now fixed exception on sign edit/sign placing cancelled by other plugin. --- nl/armeagle/TradeCraft/TradeCraftDataFile.java | 1 - 1 file changed, 1 deletion(-) diff --git a/nl/armeagle/TradeCraft/TradeCraftDataFile.java b/nl/armeagle/TradeCraft/TradeCraftDataFile.java index d145c07..7456149 100644 --- a/nl/armeagle/TradeCraft/TradeCraftDataFile.java +++ b/nl/armeagle/TradeCraft/TradeCraftDataFile.java @@ -13,7 +13,6 @@ import java.util.regex.Pattern; import org.bukkit.Location; -import org.bukkit.World; import org.bukkit.block.Sign; class TradeCraftDataFile { From e7f2ac06d159c92544afa9c1f7ed1c1b0aed704f Mon Sep 17 00:00:00 2001 From: ArmEagle Date: Tue, 28 Jun 2011 09:15:52 +0200 Subject: [PATCH 059/100] Fixed error in localization file for value CANT_BUY_SHOP_HAS_NO_A_LEFT. --- TradeCraft.en.lang | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/TradeCraft.en.lang b/TradeCraft.en.lang index 80c2ca6..3f12a93 100644 --- a/TradeCraft.en.lang +++ b/TradeCraft.en.lang @@ -44,7 +44,7 @@ YOU_ARE_NO_ALLOWED_TO_SELL: "You are not allowed to sell to shops!" YOU_CANT_SELL_THAT: "You can't sell that here!" YOU_CANT_BUY_HERE: "You can't buy here!" YOU_NEED_TO_SPEND_AT_LEAST_X_A_TO_GET_ANY_B: "You need to spend at least %1$d %2$s to get any %3$s." -CANT_BUY_SHOP_HAS_NO_A_LEFT: "Cannot buy. This shop has no more %2$s left." +CANT_BUY_SHOP_HAS_NO_A_LEFT: "Cannot buy. This shop has no more %1$s left." CANNOT_BUY_SHOP_ONLY_HAS_X_A: "Cannot buy. This shop only has %1$d %2$s." YOU_NEED_TO_SELL_AT_LEAST_X_A_TO_GET_ANY_B: "You need to sell at least %1$d %2$s to get any %3$s." CANNOT_SELL_SHOP_ONLY_HAS_X_A: "Cannot sell. This shop only has %1$d %2$s." From 6e3655832e4f5c4e037dff13c2f1b3a163bc8dfd Mon Sep 17 00:00:00 2001 From: ArmEagle Date: Tue, 28 Jun 2011 09:17:12 +0200 Subject: [PATCH 060/100] Updated to version AE-1.0.6 as increment with fix. --- plugin.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugin.yml b/plugin.yml index dd2d755..ebc3e05 100644 --- a/plugin.yml +++ b/plugin.yml @@ -1,6 +1,6 @@ name: TradeCraft main: nl.armeagle.TradeCraft.TradeCraft -version: AE-1.0.5 +version: AE-1.0.6 commands: tc: help: From aa7f869b6be50919f4dd3ffa401078d373ea099d Mon Sep 17 00:00:00 2001 From: ArmEagle Date: Fri, 1 Jul 2011 16:28:37 +0200 Subject: [PATCH 061/100] Version 1.0.8 - Admin can use /tc shops username to lookup the shops owned by another player. - Added a localization entry for "You bought # xxx for # yyy.". - Converted WITHDREW and DEPOSITED localization entries into full sentence entries to be in line with all other entries. --- TradeCraft.en.lang | 9 ++- TradeCraft.jar | Bin 47700 -> 51895 bytes nl/armeagle/TradeCraft/TradeCraft.java | 52 ++++++++++++++---- .../TradeCraft/TradeCraftItemShop.java | 17 ++---- .../TradeCraft/TradeCraftPermissions.java | 8 +++ plugin.yml | 2 +- 6 files changed, 63 insertions(+), 25 deletions(-) diff --git a/TradeCraft.en.lang b/TradeCraft.en.lang index 3f12a93..78246b9 100644 --- a/TradeCraft.en.lang +++ b/TradeCraft.en.lang @@ -1,11 +1,15 @@ # TradeCraft.java +ERROR_IN_FORMAT_STRING: "There was an error in the format string:" NO_PERMISSION_SET_CURRENCY: "You do not have the permission to set the currency" IS_NO_VALID_CURRENCY_USE_INSTEAD: "%1$s is not a valid value for a currency, use 'id[;data]' or 'itemname'." INVALID_CURRENCY: "Invalid currency: %1$s" CURRENCY_IS_SET_TO_A_IDDATA: "Currency is set to: %1$s (%2$s)" CURRENCY_IS_A_IDDATA: "Currency is: %1$s (%2$s)" +NO_SUCH_PLAYER: "There is no such player." YOU_DONT_OWN_ANY_SHOPS: "You don't own any shops!" +A_DOES_NOT_OWN_ANY_SHOPS: "%1$s doesn't own any shops." YOUR_SHOPS: "Your shops:" +SHOPS_OF_A: "Shops of %1$s:" ITEM: "Item" AMOUNT: "Amount" POSSIBLE_COMMANDS_FOR_THE_PLUGIN: "Possible commands for the %1$s plugin:" @@ -28,11 +32,11 @@ YOU_DONT_HAVE_PERM_CREATE_PLAYER_SHOP: "You do not have the permission to create # TradeCraftItemShop.java THE_CHEST_HAS_MORE_THAN_ONE_TYPE: "The chest has more than one type of item in it!" -WITHDREW: "Withdrew" +WITHDREW_X_A: "Withdrew %1$d %2$s." WITHDREW_X_CURRENCY_SHOP_STILL_HOLDS_Y_CURRENCY: "Withdrew the maximum %1$d %2$s, there is still %3$d %2$s in the shop." WITHDREW_X_A_SHOP_STILL_HOLDS_Y_A: "Withdrew the maximum of %1$d %2$s, there are still %3$d %2$s in the shop." THERE_IS_NOTHING_TO_WITHDRAW: "There is nothing to withdraw." -DEPOSITED: "Deposited" +DEPOSITED_X_A: "Deposited %1$d %2$s." YOU_CANT_DEPOSIT_THAT_HERE: "You can't deposit that here!" YOU_CAN_BUY_Y_A_FOR_X_B: "You can buy %1$d %2$s for %3$d %4$s." YOU_CAN_BUY_X_A_FOR_Y_B_UP_TO_Z: "You can buy %1$d %2$s for %3$d %4$s, up to %5$d." @@ -49,6 +53,7 @@ CANNOT_BUY_SHOP_ONLY_HAS_X_A: "Cannot buy. This shop only has %1$d %2$s." YOU_NEED_TO_SELL_AT_LEAST_X_A_TO_GET_ANY_B: "You need to sell at least %1$d %2$s to get any %3$s." CANNOT_SELL_SHOP_ONLY_HAS_X_A: "Cannot sell. This shop only has %1$d %2$s." YOU_SOLD_X_A_FOR_Y_B: "You sold %1$d %2$s for %3$d %4$s." +YOU_BOUGHT_X_A_FOR_Y_B: "You bought %1$d %2$s for %3$d %4$s." THIS_SHOP_ALWAYS_RETURNS_TOO_MUCH: "This shop would always return more goods than the chest could contain, contact the shop owner." THIS_SHOP_WOULD_RETURN_TOO_MANY_BUY_LESS: "This shop would return more goods than the chest could contain, try to buy less." THIS_SHOP_ALWAYS_RETURNS_TOO_MUCH_CURRENCY: "This shop would always return more %1$s than the chest could contain, contact the shop owner." diff --git a/TradeCraft.jar b/TradeCraft.jar index bff783bdb28f0de9ead542ba7060c11ab75757af..883e0ef424361d2c41b72e7817ee0c2c1949dc41 100644 GIT binary patch delta 34053 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%0AvSVX8m7V8w|gS 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 z�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 YSCxs;qXbAeAUGiSpWjVh|M%+u2PCvr%m4rY diff --git a/nl/armeagle/TradeCraft/TradeCraft.java b/nl/armeagle/TradeCraft/TradeCraft.java index da02017..266823c 100644 --- a/nl/armeagle/TradeCraft/TradeCraft.java +++ b/nl/armeagle/TradeCraft/TradeCraft.java @@ -4,6 +4,7 @@ import java.net.URL; import java.net.URLConnection; import java.util.ArrayList; +import java.util.IllegalFormatException; import java.util.logging.Level; import java.util.logging.Logger; import java.util.regex.Matcher; @@ -157,7 +158,15 @@ public boolean onCommand(CommandSender sender, Command command, String label, St currency.toShortString()); } } else if ( args[0].equalsIgnoreCase("shops") ) { - displayShops(p); + // Check whether another parameter is passed, if so check whether + // the player can get information about other player's shops. + if ( args.length > 1 && this.permissions.canQueryOtherShops(p) ) { + // lookup other player's shops + displayShops(args[1], p, true); + } else { + // lookup own shows + displayShops(p.getName(), p, false); + } } else if ( args[0].equalsIgnoreCase("reload") && this.permissions.canReload(p) ) { this.sendMessage(p, TradeCraftLocalization.get("RESTARTING_PLUGIN"), TradeCraft.pluginName); @@ -195,16 +204,29 @@ public boolean onCommand(CommandSender sender, Command command, String label, St return true; } - void displayShops(Player p) { - String name = p.getName(); - ArrayList list = data.shopsOwned(name); + void displayShops(String infoPlayerName, Player displayTo, boolean otherQuery) { + ArrayList list = data.shopsOwned(infoPlayerName); if (list.size() == 0) { - p.sendMessage(TradeCraftLocalization.get("YOU_DONT_OWN_ANY_SHOPS")); + if ( otherQuery ) { + // elevated player looking for other player's shops + this.sendMessage(displayTo, + TradeCraftLocalization.get("A_DOES_NOT_OWN_ANY_SHOPS"), + infoPlayerName); + } else { + displayTo.sendMessage(TradeCraftLocalization.get("YOU_DONT_OWN_ANY_SHOPS")); + } return; } - p.sendMessage(TradeCraftLocalization.get("YOUR_SHOPS")); + + if ( otherQuery ) { + this.sendMessage(displayTo, + TradeCraftLocalization.get("SHOPS_OF_A"), + infoPlayerName); + } else { + displayTo.sendMessage(TradeCraftLocalization.get("YOUR_SHOPS")); + } for (TradeCraftDataInfo info : list) { - p.sendMessage(TradeCraftLocalization.get("ITEM") +": "+ this.configuration.get(info.itemType).name +"("+ info.itemType.toShortString() +")" + displayTo.sendMessage(TradeCraftLocalization.get("ITEM") +": "+ this.configuration.get(info.itemType).name +"("+ info.itemType.toShortString() +")" +" "+ TradeCraftLocalization.get("AMOUNT") +": "+ info.itemAmount +" " + this.getCurrencyName() +": "+ info.currencyAmount); @@ -213,13 +235,21 @@ void displayShops(Player p) { } void sendMessage(Player player, TradeCraft.MessageTypes messageType, String format, Object... args) { - String message = String.format(format, args); - player.sendMessage(this.properties.getMessageTypeColor(messageType) + message); + try { + String message = String.format(format, args); + player.sendMessage(this.properties.getMessageTypeColor(messageType) + message); + } catch ( IllegalFormatException e ) { + player.sendMessage(TradeCraftLocalization.get("ERROR_IN_FORMAT_STRING") +" "+ format); + } } void sendMessage(Player player, String format, Object... args) { - String message = String.format(format, args); - player.sendMessage(message); + try { + String message = String.format(format, args); + player.sendMessage(message); + } catch ( IllegalFormatException e ) { + player.sendMessage(TradeCraftLocalization.get("ERROR_IN_FORMAT_STRING") +" "+ format); + } } void trace(Player player, String format, Object... args) { diff --git a/nl/armeagle/TradeCraft/TradeCraftItemShop.java b/nl/armeagle/TradeCraft/TradeCraftItemShop.java index 4a052b8..db4ddb2 100644 --- a/nl/armeagle/TradeCraft/TradeCraftItemShop.java +++ b/nl/armeagle/TradeCraft/TradeCraftItemShop.java @@ -40,8 +40,7 @@ private void handleOwnerClick(Player player) { } else { populateChest(TradeCraft.currency, currencyAmount); plugin.sendMessage(player, TradeCraft.MessageTypes.WITHDRAW, - "%1$s %2$d %3$s.", - TradeCraftLocalization.get("WITHDREW"), + TradeCraftLocalization.get("WITHDREW_X_A"), currencyAmount, plugin.getCurrencyName()); } @@ -61,8 +60,7 @@ private void handleOwnerClick(Player player) { } else { populateChest(getItemType(), itemAmount); plugin.sendMessage(player, TradeCraft.MessageTypes.WITHDRAW, - "%1$s %2$d %3$s.", - TradeCraftLocalization.get("WITHDREW"), + TradeCraftLocalization.get("WITHDREW_X_A"), itemAmount, getItemName()); } @@ -73,8 +71,7 @@ private void handleOwnerClick(Player player) { } else if ( getChestItemType().compareTo(TradeCraft.currency) == 0 ) { depositCurrency(getChestItemCount()); plugin.sendMessage(player, TradeCraft.MessageTypes.DEPOSIT, - "%1$s %2$d %3$s.", - TradeCraftLocalization.get("DEPOSITED"), + TradeCraftLocalization.get("DEPOSITED_X_A"), getChestItemCount(), plugin.getCurrencyName()); populateChest(new TradeCraftItem(0), 0); @@ -92,8 +89,7 @@ private void handleOwnerClick(Player player) { } else { populateChest(getItemType(), itemAmount); plugin.sendMessage(player, TradeCraft.MessageTypes.WITHDRAW, - "%1$s %2$d %3$s.", - TradeCraftLocalization.get("WITHDREW"), + TradeCraftLocalization.get("WITHDREW_X_A"), itemAmount, getItemName()); } @@ -102,8 +98,7 @@ private void handleOwnerClick(Player player) { depositItems(getChestItemCount()); populateChest(new TradeCraftItem(0), 0); plugin.sendMessage(player, TradeCraft.MessageTypes.DEPOSIT, - "%1$s %2$d %3$s.", - TradeCraftLocalization.get("DEPOSITED"), + TradeCraftLocalization.get("DEPOSITED_X_A"), getChestItemCount(), getItemName()); } else { @@ -240,7 +235,7 @@ private void playerWantsToBuy(Player player) { chest.update(); plugin.sendMessage(player, - "You bought %1$d %2$s for %3$d %4$s.", + TradeCraftLocalization.get("YOU_BOUGHT_X_A_FOR_Y_B"), amountPlayerWantsToBuy, getItemName(), requiredCurrencyForThatAmount, diff --git a/nl/armeagle/TradeCraft/TradeCraftPermissions.java b/nl/armeagle/TradeCraft/TradeCraftPermissions.java index 4f6e92e..52b6cca 100644 --- a/nl/armeagle/TradeCraft/TradeCraftPermissions.java +++ b/nl/armeagle/TradeCraft/TradeCraftPermissions.java @@ -78,6 +78,14 @@ public boolean canReload(Player p) { } } + public boolean canQueryOtherShops(Player p) { + if ( plugin.permEnabled ) { + return permHandler.has(p, "TradeCraft.canQueryOtherShops"); + } else { + return p.isOp(); + } + } + public void debug(String n){ Player p = plugin.getServer().getPlayer(n); if(p == null){ diff --git a/plugin.yml b/plugin.yml index ebc3e05..bdae541 100644 --- a/plugin.yml +++ b/plugin.yml @@ -1,6 +1,6 @@ name: TradeCraft main: nl.armeagle.TradeCraft.TradeCraft -version: AE-1.0.6 +version: AE-1.0.8 commands: tc: help: From 2fc58075e251605f21d627502a64ed05cb65694b Mon Sep 17 00:00:00 2001 From: ArmEagle Date: Wed, 7 Sep 2011 21:36:36 +0200 Subject: [PATCH 062/100] Updated to match 1060-804 --- nl/armeagle/TradeCraft/TradeCraft.java | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/nl/armeagle/TradeCraft/TradeCraft.java b/nl/armeagle/TradeCraft/TradeCraft.java index 266823c..47d7006 100644 --- a/nl/armeagle/TradeCraft/TradeCraft.java +++ b/nl/armeagle/TradeCraft/TradeCraft.java @@ -280,7 +280,7 @@ ArrayList getShopsFromBlock(Player player, Block block) { for ( int index_sides = 0; index_sides < sides.length; index_sides++ ) { BlockFace side = sides[index_sides]; // get the block on that side - Block sideBlock = block.getFace(side); + Block sideBlock = block.getRelative(side); // check for it being a wall sign if ( sideBlock.getType() == Material.WALL_SIGN ) { // get the sign (extending MaterialData) for clean attached face checking @@ -452,11 +452,6 @@ TradeCraftExchangeRate getExchangeRate(String signLine) { static int getMaxStackSize(int itemType) { return Material.getMaterial(itemType).getMaxStackSize(); } - - public void onLoad() { - // TODO Auto-generated method stub - - } /** * Get a CamelCased string based on the current currency. From fd4f8904ef876dfae659c77afe1a8521cb1e6bad Mon Sep 17 00:00:00 2001 From: ArmEagle Date: Wed, 7 Sep 2011 21:39:25 +0200 Subject: [PATCH 063/100] and updated to 1.0.9 since we changed something --- .classpath | 4 ++-- TradeCraft.jar | Bin 51895 -> 51806 bytes plugin.yml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.classpath b/.classpath index 161c485..6c76279 100644 --- a/.classpath +++ b/.classpath @@ -2,7 +2,7 @@ - - + + diff --git a/TradeCraft.jar b/TradeCraft.jar index 883e0ef424361d2c41b72e7817ee0c2c1949dc41..c4adb1d23d3cec04dfed5ff1956b1e0a57425123 100644 GIT binary patch delta 15397 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 delta 15537 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 iwEzCB}M(OSP+n8AnO Date: Sun, 25 Sep 2011 16:48:40 +0200 Subject: [PATCH 064/100] version 1.1b - supporting built-in bukkit permissions. - reworked plugin.yml, changing commands removing the whitespace after "tc". - added option to always use 64 stack size. - added option to log shop interaction. --- .classpath | 4 +- TradeCraft.en.lang | 3 + TradeCraft.jar | Bin 51806 -> 55436 bytes TradeCraft.properties | 4 + nl/armeagle/TradeCraft/TradeCraft.java | 241 ++++++++++++------ .../TradeCraft/TradeCraftItemShop.java | 49 +++- .../TradeCraft/TradeCraftLocalization.java | 6 +- .../TradeCraft/TradeCraftPermissions.java | 93 ++++--- .../TradeCraft/TradeCraftPlayerListener.java | 6 - .../TradeCraft/TradeCraftPropertiesFile.java | 9 +- .../TradeCraft/TradeCraftRepairShop.java | 2 +- nl/armeagle/TradeCraft/TradeCraftShop.java | 8 + plugin.yml | 69 ++++- 13 files changed, 354 insertions(+), 140 deletions(-) diff --git a/.classpath b/.classpath index 6c76279..4244016 100644 --- a/.classpath +++ b/.classpath @@ -2,7 +2,7 @@ - - + + diff --git a/TradeCraft.en.lang b/TradeCraft.en.lang index 78246b9..02eb5aa 100644 --- a/TradeCraft.en.lang +++ b/TradeCraft.en.lang @@ -15,6 +15,7 @@ AMOUNT: "Amount" POSSIBLE_COMMANDS_FOR_THE_PLUGIN: "Possible commands for the %1$s plugin:" TC_HELP_THIS_TEXT: "this help text" TC_SHOPS: "lists the shops the player owns" +TC_PSHOPS: "lists the shops the given player owns" TC_CURRENCY_OPT_PARAM_GETSET_CURRENCY: "get and set the currency" TC_CURRENCY_GET_CURRENCY: "get the current currency" TC_CAN_PLAYER: "show the permissions of the given player" @@ -53,7 +54,9 @@ CANNOT_BUY_SHOP_ONLY_HAS_X_A: "Cannot buy. This shop only has %1$d %2$s." YOU_NEED_TO_SELL_AT_LEAST_X_A_TO_GET_ANY_B: "You need to sell at least %1$d %2$s to get any %3$s." CANNOT_SELL_SHOP_ONLY_HAS_X_A: "Cannot sell. This shop only has %1$d %2$s." YOU_SOLD_X_A_FOR_Y_B: "You sold %1$d %2$s for %3$d %4$s." +SOLD_X_A_FOR_Y_B: "Sold %1$d %2$s for %3$d %4$s." YOU_BOUGHT_X_A_FOR_Y_B: "You bought %1$d %2$s for %3$d %4$s." +BOUGHT_X_A_FOR_Y_B: "Bought %1$d %2$s for %3$d %4$s." THIS_SHOP_ALWAYS_RETURNS_TOO_MUCH: "This shop would always return more goods than the chest could contain, contact the shop owner." THIS_SHOP_WOULD_RETURN_TOO_MANY_BUY_LESS: "This shop would return more goods than the chest could contain, try to buy less." THIS_SHOP_ALWAYS_RETURNS_TOO_MUCH_CURRENCY: "This shop would always return more %1$s than the chest could contain, contact the shop owner." diff --git a/TradeCraft.jar b/TradeCraft.jar index c4adb1d23d3cec04dfed5ff1956b1e0a57425123..92c0172ed38e3ce084bead32380f3ee0fb557a00 100644 GIT binary patch delta 30759 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 C)Pc?b delta 27232 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 diff --git a/TradeCraft.properties b/TradeCraft.properties index e6c02a7..11dde7d 100644 --- a/TradeCraft.properties +++ b/TradeCraft.properties @@ -6,6 +6,10 @@ player-owned-shops-enabled: true currency-id: 266 #the data value so set a sub type for currency currency-data: 0 +#whether to use the basic game's default stack size for items. Will use limit of 64 when set to false +normal-stack-size: true +#whether to log interaction with shops +log-shop-use: false #the language to use for language files language: "en" #whether to automatically update language files from the jar when there have been changes diff --git a/nl/armeagle/TradeCraft/TradeCraft.java b/nl/armeagle/TradeCraft/TradeCraft.java index 47d7006..cd34128 100644 --- a/nl/armeagle/TradeCraft/TradeCraft.java +++ b/nl/armeagle/TradeCraft/TradeCraft.java @@ -1,9 +1,11 @@ package nl.armeagle.TradeCraft; -import java.io.IOException; +import java.io.*; import java.net.URL; import java.net.URLConnection; +import java.text.SimpleDateFormat; import java.util.ArrayList; +import java.util.Date; import java.util.IllegalFormatException; import java.util.logging.Level; import java.util.logging.Logger; @@ -39,15 +41,25 @@ public static enum MessageTypes {WITHDRAW, DEPOSIT}; public static final Pattern itemPatternIdSplitData = Pattern.compile("^(\\d+)(?:;(\\d+))?$"); - private static final String CommandString = "tc"; + private static enum Commands { + tcsetcurrency, + tcgetcurrency, + tcshops, + tcpshops, + tcreload, + tcplayerperms, + tchelp, + tc + }; // Stuff used to interact with the server. final Logger log = Logger.getLogger("Minecraft"); final Server server = this.getServer(); + protected BufferedWriter usageLog = null; // Objects used by the plugin. static TradeCraftItem currency; - TradeCraftPropertiesFile properties; + static TradeCraftPropertiesFile properties; TradeCraftConfigurationFile configuration; public TradeCraftLocalization localization; TradeCraftDataFile data; @@ -65,6 +77,13 @@ public void onDisable() { this.disable(); } private void disable() { + if ( this.usageLog != null ) { + try { + this.usageLog.close(); + } catch (IOException e) { + this.log(Level.WARNING, "Failed to close shop usage log file"); + } + } properties = null; configuration = null; this.localization = null; @@ -81,6 +100,23 @@ private void enable() { data = new TradeCraftDataFile(this); this.localization = new TradeCraftLocalization(this); + if ( TradeCraft.properties.logShopUse() ) { + File usageLogFile = new File(this.getDataFolder(), "shopUsage.log"); + try { + if ( !usageLogFile.exists() ) { + usageLogFile.createNewFile(); + } + if ( usageLogFile.canWrite() ) { + this.usageLog = new BufferedWriter(new FileWriter(usageLogFile, true)); + this.log(Level.INFO, "Writing shop usage to log file: "+ usageLogFile.toString()); + } else { + this.log(Level.WARNING, "Error opening shop usage log file: "+ usageLogFile.toString()); + } + } catch (IOException e) { + this.log(Level.WARNING, "Failed to open shop usage log file: "+ usageLogFile.toString()); + } + } + configuration.load(); data.load(); currency = properties.getCurrencyType(); @@ -100,28 +136,24 @@ private void enable() { } @Override - public boolean onCommand(CommandSender sender, Command command, String label, String[] args) { - String name = command.getName(); - Player p; - - if (sender instanceof Player) { - p = (Player) sender; - - if ( name.equalsIgnoreCase(TradeCraft.CommandString) ) { - if ( args.length == 0 || args[0].compareToIgnoreCase("help") == 0 ) { - displayCommandHelpText(p); - } else if ( args[0].compareToIgnoreCase("currency") == 0 ) { - if ( args.length == 2 && this.permissions.canSetCurrency(p) ) { + public boolean onCommand(CommandSender sender, Command cmd, String label, String[] args) { + try { + TradeCraft.Commands command = TradeCraft.Commands.valueOf(cmd.getName()); + if (sender instanceof Player) { + Player p = (Player) sender; + switch (command) { + case tcsetcurrency: + if ( args.length == 1 && this.permissions.canSetCurrency(p) ) { TradeCraftItem testCurrency = null; // try to split ID and Data, separated by a semicolon mark - Matcher IdSplitData = TradeCraft.itemPatternIdSplitData.matcher(args[1]); + Matcher IdSplitData = TradeCraft.itemPatternIdSplitData.matcher(args[0]); if ( !IdSplitData.matches() ) { // try to match the parameter to item names from the configuration - TradeCraftConfigurationInfo setCurr = this.configuration.get(args[1]); + TradeCraftConfigurationInfo setCurr = this.configuration.get(args[0]); if ( setCurr == null ) { this.sendMessage(p, TradeCraftLocalization.get("IS_NO_VALID_CURRENCY_USE_INSTEAD"), - args[1]); + args[0]); return false; } else { currency = setCurr.type; @@ -136,82 +168,112 @@ public boolean onCommand(CommandSender sender, Command command, String label, St } } catch ( NumberFormatException e ) { this.sendMessage(p, TradeCraftLocalization.get("INVALID_CURRENCY"), - args[1]); + args[0]); return false; } if ( this.configuration.get(testCurrency) != null ) { currency = testCurrency; } else { this.sendMessage(p, TradeCraftLocalization.get("INVALID_CURRENCY"), - args[1]); + args[0]); return false; } } - this.properties.setCurrencyType(currency); + TradeCraft.properties.setCurrencyType(currency); this.sendMessage(p, TradeCraftLocalization.get("CURRENCY_IS_SET_TO_A_IDDATA"), this.getCurrencyName(), currency.toShortString()); + return true; + } + return true; + case tcgetcurrency: + this.sendMessage(p, TradeCraftLocalization.get("CURRENCY_IS_A_IDDATA"), + this.getCurrencyName(), + currency.toShortString()); + return true; + case tcshops: + // lookup own shows + displayShops(p.getName(), p, false); + return true; + case tcpshops: + // Check whether another parameter is passed, if so check whether + // the player can get information about other player's shops. + if ( this.permissions.canQueryOtherShops(p) ) { + if ( args.length == 1 ) { + // lookup other player's shops + displayShops(args[0], p, true); + } else { + this.sendMessage(p, cmd.getUsage()); + } + } + return true; + case tcreload: + if ( this.permissions.canReload(p)) { + this.sendMessage(p, TradeCraftLocalization.get("RESTARTING_PLUGIN"), TradeCraft.pluginName); + this.disable(); + this.enable(); + this.sendMessage(p, TradeCraftLocalization.get("RESTARTING_PLUGIN_DONE"), TradeCraft.pluginName); + } + return true; + case tcplayerperms: + if ( this.permissions.canQueryPlayer(p)) { + if ( args.length == 1 ) { + permissions.debug(sender, args[0]); + } else { + sender.sendMessage(cmd.getUsage()); + } + } + return true; + default: + displayCommandHelpText(p); + return true; + } + } else if ( sender instanceof ConsoleCommandSender ) { + switch (command) { + case tcplayerperms: + if ( args.length == 1 ) { + permissions.debug(sender, args[0]); } else { - this.sendMessage(p, TradeCraftLocalization.get("CURRENCY_IS_A_IDDATA"), - this.getCurrencyName(), - currency.toShortString()); + sender.sendMessage(cmd.getUsage()); } - } else if ( args[0].equalsIgnoreCase("shops") ) { + return true; + case tcpshops: // Check whether another parameter is passed, if so check whether // the player can get information about other player's shops. - if ( args.length > 1 && this.permissions.canQueryOtherShops(p) ) { + if ( args.length == 1 ) { // lookup other player's shops - displayShops(args[1], p, true); + displayShops(args[0], sender, true); } else { - // lookup own shows - displayShops(p.getName(), p, false); + sender.sendMessage(cmd.getUsage()); } - } else if ( args[0].equalsIgnoreCase("reload") && this.permissions.canReload(p) ) { - this.sendMessage(p, TradeCraftLocalization.get("RESTARTING_PLUGIN"), - TradeCraft.pluginName); - this.disable(); - this.enable(); - this.sendMessage(p, TradeCraftLocalization.get("RESTARTING_PLUGIN_DONE"), - TradeCraft.pluginName); - } else { - return false; - } - } - } else if ( sender instanceof ConsoleCommandSender ) { - if ( args.length == 0 || args[0].compareToIgnoreCase("help") == 0 ) { - displayCommandHelpText(null); - return true; - } else if ( name.equalsIgnoreCase(TradeCraft.CommandString) ) { - if ( args[0].equalsIgnoreCase("canplayer") && args.length == 2) { - permissions.debug(args[1]); return true; - } else if ( args[0].equalsIgnoreCase("reload") ) { - this.log(Level.INFO, TradeCraftLocalization.get("RESTARTING_PLUGIN"), - TradeCraft.pluginName); + case tcreload: + sender.sendMessage(String.format(TradeCraftLocalization.get("RESTARTING_PLUGIN"), + TradeCraft.pluginName)); this.disable(); this.enable(); - this.log(Level.INFO, TradeCraftLocalization.get("RESTARTING_PLUGIN_DONE"), - TradeCraft.pluginName); + sender.sendMessage(String.format(TradeCraftLocalization.get("RESTARTING_PLUGIN_DONE"), + TradeCraft.pluginName)); + return true; + default: + displayCommandHelpText(null); + return true; } - return true; } - return false; - } else { + } catch (IllegalArgumentException e) { return false; } - - return true; + return false; } - void displayShops(String infoPlayerName, Player displayTo, boolean otherQuery) { + void displayShops(String infoPlayerName, CommandSender displayTo, boolean otherQuery) { ArrayList list = data.shopsOwned(infoPlayerName); if (list.size() == 0) { if ( otherQuery ) { // elevated player looking for other player's shops - this.sendMessage(displayTo, - TradeCraftLocalization.get("A_DOES_NOT_OWN_ANY_SHOPS"), - infoPlayerName); + displayTo.sendMessage(String.format(TradeCraftLocalization.get("A_DOES_NOT_OWN_ANY_SHOPS"), + infoPlayerName)); } else { displayTo.sendMessage(TradeCraftLocalization.get("YOU_DONT_OWN_ANY_SHOPS")); } @@ -219,9 +281,8 @@ void displayShops(String infoPlayerName, Player displayTo, boolean otherQuery) { } if ( otherQuery ) { - this.sendMessage(displayTo, - TradeCraftLocalization.get("SHOPS_OF_A"), - infoPlayerName); + displayTo.sendMessage(String.format(TradeCraftLocalization.get("SHOPS_OF_A"), + infoPlayerName)); } else { displayTo.sendMessage(TradeCraftLocalization.get("YOUR_SHOPS")); } @@ -237,7 +298,7 @@ void displayShops(String infoPlayerName, Player displayTo, boolean otherQuery) { void sendMessage(Player player, TradeCraft.MessageTypes messageType, String format, Object... args) { try { String message = String.format(format, args); - player.sendMessage(this.properties.getMessageTypeColor(messageType) + message); + player.sendMessage(TradeCraft.properties.getMessageTypeColor(messageType) + message); } catch ( IllegalFormatException e ) { player.sendMessage(TradeCraftLocalization.get("ERROR_IN_FORMAT_STRING") +" "+ format); } @@ -450,7 +511,11 @@ TradeCraftExchangeRate getExchangeRate(String signLine) { return new TradeCraftExchangeRate(signLine); } static int getMaxStackSize(int itemType) { - return Material.getMaterial(itemType).getMaxStackSize(); + if ( TradeCraft.properties.getNormalStackSizeUsed() ) { + return Material.getMaterial(itemType).getMaxStackSize(); + } else { + return 64; + } } /** @@ -492,23 +557,29 @@ private void displayCommandHelpText(Player player) { if ( player != null ) { this.sendMessage(player, TradeCraftLocalization.get("POSSIBLE_COMMANDS_FOR_THE_PLUGIN"), TradeCraft.pluginName); - this.sendMessage(player, "/tc [help]"+ ChatColor.GRAY +" "+ TradeCraftLocalization.get("TC_HELP_THIS_TEXT")); - this.sendMessage(player, "/tc shops"+ ChatColor.GRAY +" "+ TradeCraftLocalization.get("TC_SHOPS")); + this.sendMessage(player, "/tc[help]"+ ChatColor.GRAY +" "+ TradeCraftLocalization.get("TC_HELP_THIS_TEXT")); + this.sendMessage(player, "/tcshops"+ ChatColor.GRAY +" "+ TradeCraftLocalization.get("TC_SHOPS")); + if ( this.permissions.canQueryOtherShops(player) ) { + this.sendMessage(player, "/tcpshops [player]"+ ChatColor.GRAY +" "+ TradeCraftLocalization.get("TC_PSHOPS")); + } + this.sendMessage(player, "/tcgetcurrency"+ ChatColor.GRAY +" "+ TradeCraftLocalization.get("TC_CURRENCY_GET_CURRENCY")); if ( this.permissions.canSetCurrency(player) ) { - this.sendMessage(player, "/tc currency [id[;data]]"+ ChatColor.GRAY +" "+ TradeCraftLocalization.get("TC_CURRENCY_OPT_PARAM_GETSET_CURRENCY")); - } else { - this.sendMessage(player, "/tc currency"+ ChatColor.GRAY +" "+ TradeCraftLocalization.get("TC_CURRENCY_GET_CURRENCY")); + this.sendMessage(player, "/tcsetcurrency [id[;data]]"+ ChatColor.GRAY +" "+ TradeCraftLocalization.get("TC_CURRENCY_OPT_PARAM_GETSET_CURRENCY")); } if ( this.permissions.canReload(player) ) { - this.sendMessage(player, "/tc reload"+ ChatColor.GRAY +" "+ TradeCraftLocalization.get("TC_RELOAD")); + this.sendMessage(player, "/tcreload"+ ChatColor.GRAY +" "+ TradeCraftLocalization.get("TC_RELOAD")); + } + if ( this.permissions.canQueryPlayer(player) ) { + this.sendMessage(player, "/tcplayerperms"+ ChatColor.GRAY +" "+ TradeCraftLocalization.get("TC_CAN_PLAYER")); } } else { // console command help this.log(Level.INFO, TradeCraftLocalization.get("POSSIBLE_COMMANDS_FOR_THE_PLUGIN"), TradeCraft.pluginName); - this.log(Level.INFO, "tc [help]: "+ TradeCraftLocalization.get("TC_HELP_THIS_TEXT")); - this.log(Level.INFO, "tc canPlayer playername: "+ TradeCraftLocalization.get("TC_CAN_PLAYER")); - this.log(Level.INFO, "tc reload: "+ TradeCraftLocalization.get("TC_RELOAD")); + this.log(Level.INFO, "tc[help]: "+ TradeCraftLocalization.get("TC_HELP_THIS_TEXT")); + this.log(Level.INFO, "tcplayerperms playername: "+ TradeCraftLocalization.get("TC_CAN_PLAYER")); + this.log(Level.INFO, "tcpshops [player]: "+ TradeCraftLocalization.get("TC_PSHOPS")); + this.log(Level.INFO, "tcreload: "+ TradeCraftLocalization.get("TC_RELOAD")); } } @@ -544,5 +615,25 @@ public static long resourceLastModified(String filePath) { public void log(Level level, String format, Object... args) { this.log.log(level, TradeCraft.pluginName +": "+ String.format(format, args)); } + + public void useLog(Player player, TradeCraftShop shop, String format, Object... args) { + if ( TradeCraft.properties.logShopUse() ) { + if ( this.usageLog != null ) { + try { + SimpleDateFormat formatter = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss"); + this.usageLog.write(String.format("%s %s \t%s %s\n", + formatter.format(new Date()), + shop.toString(), + player.getDisplayName(), + String.format(format, args))); + this.usageLog.flush(); + } catch (IOException e) { + this.log(Level.WARNING, "Failed to write to shop use log file"); + } + } else { + this.log(Level.INFO, "not written to log file"); + } + } + } } \ No newline at end of file diff --git a/nl/armeagle/TradeCraft/TradeCraftItemShop.java b/nl/armeagle/TradeCraft/TradeCraftItemShop.java index db4ddb2..09b5fde 100644 --- a/nl/armeagle/TradeCraft/TradeCraftItemShop.java +++ b/nl/armeagle/TradeCraft/TradeCraftItemShop.java @@ -37,12 +37,21 @@ private void handleOwnerClick(Player player) { maxCurrencyChestCanHold, plugin.getCurrencyName(), currencyAmount - maxCurrencyChestCanHold); + plugin.useLog(player, this, + TradeCraftLocalization.get("WITHDREW_X_CURRENCY_SHOP_STILL_HOLDS_Y_CURRENCY"), + maxCurrencyChestCanHold, + plugin.getCurrencyName(), + currencyAmount - maxCurrencyChestCanHold); } else { populateChest(TradeCraft.currency, currencyAmount); plugin.sendMessage(player, TradeCraft.MessageTypes.WITHDRAW, TradeCraftLocalization.get("WITHDREW_X_A"), currencyAmount, plugin.getCurrencyName()); + plugin.useLog(player, this, + TradeCraftLocalization.get("WITHDREW_X_A"), + currencyAmount, + plugin.getCurrencyName()); } } else { // limit amount of items dropped into the chest @@ -57,12 +66,21 @@ private void handleOwnerClick(Player player) { maxItemsChestCanHold, getItemName(), itemAmount - maxItemsChestCanHold); + plugin.useLog(player, this, + TradeCraftLocalization.get("WITHDREW_X_A_SHOP_STILL_HOLDS_Y_A"), + maxItemsChestCanHold, + getItemName(), + itemAmount - maxItemsChestCanHold); } else { populateChest(getItemType(), itemAmount); plugin.sendMessage(player, TradeCraft.MessageTypes.WITHDRAW, TradeCraftLocalization.get("WITHDREW_X_A"), itemAmount, getItemName()); + plugin.useLog(player, this, + TradeCraftLocalization.get("WITHDREW_X_A"), + itemAmount, + getItemName()); } } else { plugin.sendMessage(player, TradeCraftLocalization.get("THERE_IS_NOTHING_TO_WITHDRAW")); @@ -74,6 +92,10 @@ private void handleOwnerClick(Player player) { TradeCraftLocalization.get("DEPOSITED_X_A"), getChestItemCount(), plugin.getCurrencyName()); + plugin.useLog(player, this, + TradeCraftLocalization.get("DEPOSITED_X_A"), + getChestItemCount(), + plugin.getCurrencyName()); populateChest(new TradeCraftItem(0), 0); int itemAmount = withdrawItems(); if (itemAmount > 0) { @@ -86,12 +108,21 @@ private void handleOwnerClick(Player player) { maxItemsChestCanHold, getItemName(), itemAmount - maxItemsChestCanHold); + plugin.useLog(player, this, + TradeCraftLocalization.get("WITHDREW_X_A_SHOP_STILL_HOLDS_Y_A"), + maxItemsChestCanHold, + getItemName(), + itemAmount - maxItemsChestCanHold); } else { populateChest(getItemType(), itemAmount); plugin.sendMessage(player, TradeCraft.MessageTypes.WITHDRAW, TradeCraftLocalization.get("WITHDREW_X_A"), itemAmount, getItemName()); + plugin.useLog(player, this, + TradeCraftLocalization.get("WITHDREW_X_A"), + itemAmount, + getItemName()); } } } else if ( getChestItemType().compareTo(getItemType()) == 0 ) { @@ -101,6 +132,10 @@ private void handleOwnerClick(Player player) { TradeCraftLocalization.get("DEPOSITED_X_A"), getChestItemCount(), getItemName()); + plugin.useLog(player, this, + TradeCraftLocalization.get("DEPOSITED_X_A"), + getChestItemCount(), + getItemName()); } else { plugin.sendMessage(player, TradeCraftLocalization.get("YOU_CANT_DEPOSIT_THAT_HERE")); } @@ -127,7 +162,7 @@ private void handlePatronClick(Player player) { getBuyAmount(), getItemName(), getBuyValue(), - plugin.getCurrencyName()); + plugin.getCurrencyName()); } else { plugin.sendMessage(player, TradeCraftLocalization.get("YOU_CAN_BUY_X_A_FOR_Y_B_UP_TO_Z"), @@ -240,6 +275,12 @@ private void playerWantsToBuy(Player player) { getItemName(), requiredCurrencyForThatAmount, plugin.getCurrencyName()); + plugin.useLog(player, this, + TradeCraftLocalization.get("BOUGHT_X_A_FOR_Y_B"), + amountPlayerWantsToBuy, + getItemName(), + requiredCurrencyForThatAmount, + plugin.getCurrencyName()); } private void playerWantsToSell(Player player) { @@ -296,6 +337,12 @@ private void playerWantsToSell(Player player) { getItemName(), currencyPlayerShouldReceive, plugin.getCurrencyName()); + plugin.useLog(player, this, + TradeCraftLocalization.get("SOLD_X_A_FOR_Y_B"), + amountThatCanBeSold, + getItemName(), + currencyPlayerShouldReceive, + plugin.getCurrencyName()); } /** diff --git a/nl/armeagle/TradeCraft/TradeCraftLocalization.java b/nl/armeagle/TradeCraft/TradeCraftLocalization.java index 91c1b78..067e896 100644 --- a/nl/armeagle/TradeCraft/TradeCraftLocalization.java +++ b/nl/armeagle/TradeCraft/TradeCraftLocalization.java @@ -27,7 +27,7 @@ public class TradeCraftLocalization { public TradeCraftLocalization(TradeCraft plugin) { this.plugin = plugin; - this.language = plugin.properties.getLanguage(); + this.language = TradeCraft.properties.getLanguage(); // make folder in the plugins dir if it doesn't exist yet File path = new File(filePath); @@ -43,7 +43,7 @@ public TradeCraftLocalization(TradeCraft plugin) { File file = new File(filePath + File.separator + fileName); if ( !file.exists() - || this.plugin.properties.autoUpdateLanguageFiles() + || TradeCraft.properties.autoUpdateLanguageFiles() && TradeCraft.resourceLastModified("/"+ fileName) > file.lastModified() ) { InputStream input = this.getClass().getResourceAsStream("/" + fileName); @@ -58,7 +58,7 @@ public TradeCraftLocalization(TradeCraft plugin) { fileName = String.format(TradeCraftLocalization.filePreName, this.language); file = new File(filePath + File.separator + fileName); // check whether this file already exists, or else copy it over - if ( !file.exists() || this.plugin.properties.autoUpdateLanguageFiles() + if ( !file.exists() || TradeCraft.properties.autoUpdateLanguageFiles() && TradeCraft.resourceLastModified("/"+ fileName) > file.lastModified()) { input = this.getClass().getResourceAsStream("/" + fileName); } diff --git a/nl/armeagle/TradeCraft/TradeCraftPermissions.java b/nl/armeagle/TradeCraft/TradeCraftPermissions.java index 52b6cca..ddf29f8 100644 --- a/nl/armeagle/TradeCraft/TradeCraftPermissions.java +++ b/nl/armeagle/TradeCraft/TradeCraftPermissions.java @@ -1,5 +1,6 @@ package nl.armeagle.TradeCraft; +import org.bukkit.command.CommandSender; import org.bukkit.entity.Player; import org.bukkit.plugin.Plugin; @@ -18,55 +19,60 @@ public class TradeCraftPermissions { public void setupPermissions() { Plugin test = plugin.getServer().getPluginManager().getPlugin("Permissions"); - if (permHandler == null) { - if (test != null) { - this.permHandler = ((Permissions)test).getHandler(); - plugin.permEnabled = true; - plugin.log.info("[TradeCraft] has recognized Permissions"); - } - } + if (permHandler == null) { + if (test != null) { + this.permHandler = ((Permissions)test).getHandler(); + plugin.permEnabled = true; + plugin.log.info("[TradeCraft] has recognized Permissions"); + } + } } - public boolean canBuy(Player p){ - if(plugin.permEnabled == true){ + public boolean canBuy(Player p) { + if ( plugin.permEnabled ) { return permHandler.has(p, "TradeCraft.canBuy"); - }else - return true; + } else { + return p.hasPermission("TradeCraft.canBuy"); + } } - public boolean canSell(Player p){ - if(plugin.permEnabled == true){ + public boolean canSell(Player p) { + if ( plugin.permEnabled ) { return permHandler.has(p, "TradeCraft.canSell"); - }else - return true; + } else { + return p.hasPermission("TradeCraft.canSell"); + } } - public boolean canMakeInfShops(Player p){ - if(plugin.permEnabled == true){ + public boolean canMakeInfShops(Player p) { + if ( plugin.permEnabled ) { return permHandler.has(p, "TradeCraft.canMakeInfShops"); - }else - return p.isOp(); + } else { + return p.hasPermission("TradeCraft.canMakeInfShops"); + } } - public boolean canMakePlayerShops(Player p){ - if(plugin.permEnabled == true){ + public boolean canMakePlayerShops(Player p) { + if ( plugin.permEnabled ) { return permHandler.has(p, "TradeCraft.canMakePlayerShops"); - }else - return true; + } else { + return p.hasPermission("TradeCraft.canMakePlayerShops"); + } } - public boolean canDestroyShops(Player p){ - if(plugin.permEnabled == true){ + public boolean canDestroyShops(Player p) { + if ( plugin.permEnabled ) { return permHandler.has(p, "TradeCraft.canDestroyShops"); - }else - return p.isOp(); + } else { + return p.hasPermission("TradeCraft.canDestroyShops"); + } } public boolean canSetCurrency(Player p) { if ( plugin.permEnabled ) { return permHandler.has(p, "TradeCraft.canSetCurrency"); } else { - return p.isOp(); + return p.hasPermission("TradeCraft.canSetCurrency"); } } @@ -74,7 +80,7 @@ public boolean canReload(Player p) { if ( plugin.permEnabled ) { return permHandler.has(p, "TradeCraft.canReload"); } else { - return p.isOp(); + return p.hasPermission("TradeCraft.canReload"); } } @@ -82,25 +88,34 @@ public boolean canQueryOtherShops(Player p) { if ( plugin.permEnabled ) { return permHandler.has(p, "TradeCraft.canQueryOtherShops"); } else { - return p.isOp(); + return p.hasPermission("TradeCraft.canQueryOtherShops"); } } - public void debug(String n){ + public boolean canQueryPlayer(Player p) { + if ( plugin.permEnabled ) { + return permHandler.has(p, "TradeCraft.canQueryPlayer"); + } else { + return p.hasPermission("TradeCraft.canQueryPlayer"); + } + } + + public void debug(CommandSender sender, String n){ Player p = plugin.getServer().getPlayer(n); if(p == null){ plugin.getServer().broadcastMessage("/tc canPlayer used with a name of player who is not online."); return; } String name = p.getName(); - plugin.log.info("" + name + " has:"); - plugin.log.info("canbuy " + canBuy(p)); - plugin.log.info("cansell " + canSell(p)); - plugin.log.info("canmakeinf " + canMakeInfShops(p)); - plugin.log.info("canmakepersonal " + canMakePlayerShops(p)); - plugin.log.info("candestroy " + canDestroyShops(p)); - - + sender.sendMessage("" + name + " has:"); + sender.sendMessage("canBuy " + canBuy(p)); + sender.sendMessage("canSell " + canSell(p)); + sender.sendMessage("canMakeInf " + canMakeInfShops(p)); + sender.sendMessage("canMakePersonal " + canMakePlayerShops(p)); + sender.sendMessage("canDestroy " + canDestroyShops(p)); + sender.sendMessage("canSetCurrency " + canSetCurrency(p)); + sender.sendMessage("canReload " + canReload(p)); + sender.sendMessage("canQueryOtherShops " + canQueryOtherShops(p)); } } diff --git a/nl/armeagle/TradeCraft/TradeCraftPlayerListener.java b/nl/armeagle/TradeCraft/TradeCraftPlayerListener.java index 7a65396..c76710e 100644 --- a/nl/armeagle/TradeCraft/TradeCraftPlayerListener.java +++ b/nl/armeagle/TradeCraft/TradeCraftPlayerListener.java @@ -52,10 +52,4 @@ private void displayItems(Player player) { plugin.sendMessage(player, sb.toString()); } } - - @SuppressWarnings("unused") - private void displaySecurity(Player player) { - plugin.permissions.debug(player.getName()); - } - } diff --git a/nl/armeagle/TradeCraft/TradeCraftPropertiesFile.java b/nl/armeagle/TradeCraft/TradeCraftPropertiesFile.java index 7734c80..8f69f9f 100644 --- a/nl/armeagle/TradeCraft/TradeCraftPropertiesFile.java +++ b/nl/armeagle/TradeCraft/TradeCraftPropertiesFile.java @@ -76,7 +76,10 @@ public void setCurrencyType(TradeCraftItem item) { properties.setProperty("currency-data", item.data); properties.save(); } - + public boolean getNormalStackSizeUsed(){ + return properties.getBoolean("normal-stack-size", true); + } + public boolean getInfiniteShopsEnabled() { return properties.getBoolean("infinite-shops-enabled", true); } @@ -105,6 +108,10 @@ public boolean autoUpdateLanguageFiles() { return properties.getBoolean("auto-update-language-files", true); } + public boolean logShopUse() { + return properties.getBoolean("log-shop-use", false); + } + public ChatColor getMessageTypeColor(TradeCraft.MessageTypes mtype) { switch (mtype) { case WITHDRAW: diff --git a/nl/armeagle/TradeCraft/TradeCraftRepairShop.java b/nl/armeagle/TradeCraft/TradeCraftRepairShop.java index 2b8dfea..72a7cc0 100644 --- a/nl/armeagle/TradeCraft/TradeCraftRepairShop.java +++ b/nl/armeagle/TradeCraft/TradeCraftRepairShop.java @@ -16,7 +16,7 @@ public TradeCraftRepairShop(TradeCraft plugin, Sign sign, Chest chest) { public void handleRightClick(Player player) { int currencyAmount = chest.getAmountOfCurrencyInChest(); List items = chest.getNonCurrencyItems(); - int repairCost = plugin.properties.getRepairCost(); + int repairCost = TradeCraft.properties.getRepairCost(); if (currencyAmount == 0 && items.size() == 0) { plugin.sendMessage(player, TradeCraftLocalization.get("IT_COSTS_X_A_TO_REPAIR_AN_ITEM"), diff --git a/nl/armeagle/TradeCraft/TradeCraftShop.java b/nl/armeagle/TradeCraft/TradeCraftShop.java index b344b8a..b66a611 100644 --- a/nl/armeagle/TradeCraft/TradeCraftShop.java +++ b/nl/armeagle/TradeCraft/TradeCraftShop.java @@ -20,4 +20,12 @@ public TradeCraftShop(TradeCraft plugin, Sign sign, Chest chest) { public abstract boolean playerCanDestroy(Player player); public abstract boolean shopCanBeWithdrawnFrom(); + + public String toString() { + return String.format("Shop(%s:%d,%d,%d)", + this.sign.getWorld().getName(), + this.sign.getX(), + this.sign.getY(), + this.sign.getZ()); + } } diff --git a/plugin.yml b/plugin.yml index 12b2ac4..6586dea 100644 --- a/plugin.yml +++ b/plugin.yml @@ -1,16 +1,61 @@ name: TradeCraft +version: AE-1.1b +description: Setup shops with a combination of chest and sign. +author: ArmEagle main: nl.armeagle.TradeCraft.TradeCraft -version: AE-1.0.9 commands: tc: - help: - description: Show TradeCradt command help - shops: - description: See a printout of any personal shops you have - usage: - currency: - description: Get/set the currency used. - usage: Enter an id with an optional data value separated by a semicolon - reload: - description: reload the script and with that all the configuration files - usage: + description: Show TradeCraft command help + usage: "Usage: /tc" + tchelp: + description: Show TradeCraft command help + usage: "Usage: /tchelp" + tcshops: + description: See a printout of any personal shops you have + usage: "Usage: /tcshops" + tcpshops: + description: See a printout of shops of a specific player + usage: "Usage: /tcpshops [player]" + tcgetcurrency: + description: Get the currency used + usage: "Usage: /tcgetcurrency" + tcsetcurrency: + description: Set the currency used + permission: TradeCraft.canSetCurrency + usage: "Usage: /tcsetcurrency [item id;data]" + tcreload: + description: reload the script and with that all the configuration files + permission: TradeCraft.canReload + usage: "Usage: /tcreload" + tcplayerperms: + description: Show the permissions of the given player + permission: TradeCraft.canQueryPlayer + usage: "Usage: /tccanplayer [player]" +permissions: + TradeCraft.canBuy: + description: Permission to buy from shops. + default: true + TradeCraft.canSell: + description: Permission to sell to shops. + default: true + TradeCraft.canMakePlayerShops: + description: Permission to make normal shops + default: true + TradeCraft.canMakeInfShops: + description: Permission to make infinite shops + default: op + TradeCraft.canDestroyShops: + description: Permission to destroy (any) shop + default: op + TradeCraft.canSetCurrency: + description: Permission to set the currency item type + default: op + TradeCraft.canReload: + description: Permission to reload the configuration + default: op + TradeCraft.canQueryOtherShops: + description: Permission to list the shops of other players + default: op + TradeCraft.canQueryPlayer: + description: Permission to show the permissions of a given player + default: op \ No newline at end of file From 0e9d6e94d76ec5bfbae38ce9e8d78748f4adf860 Mon Sep 17 00:00:00 2001 From: ArmEagle Date: Wed, 12 Oct 2011 22:05:40 +0200 Subject: [PATCH 065/100] made 1.1 release from 1.1 beta --- TradeCraft.jar | Bin 55436 -> 55435 bytes plugin.yml | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/TradeCraft.jar b/TradeCraft.jar index 92c0172ed38e3ce084bead32380f3ee0fb557a00..918d28dcfa1b15c65435701533aaa4e1427b28a2 100644 GIT binary patch delta 615 zcmV-t0+{`bu>*^-1F*ZY0|c;4lfJSzeYl6vlnq$c$xVgc^~wY_BW|(dR0(83Nb(IDL2+brgcm=npeurm-Bu7DSLb(pe@-*% z)k~2+jA76d*HeVI*o9qQ{mt*suD%*o40y~7{~gdBx!M~;vL0smKgfaW z2@cDGHZ=>NLUAhSn~>ciB$QV$f1CTeXuBF^h`Sj>mWqAk!y|JffHhwlbU2m45LejD zB$p>set9$*B^k-}82)jdlUK53oj2K4E#qWYY~dlPFJzzbQ?I1+i*mCK6CDuqIWkkX z#G#JuNxp8D`P_bcGQ<5_trZq##iF|kc24tD_?w^Wg!?HFoPsAmKx>r9e?7k`LR$3P zZNV8&XY_Z(`6_^=czEhk{h#z<-ShykgLINTE#(e7k>A&P}=l%d<_U`n4s zeGlIOqlWdUCI`TWa1VY=sFkF`x+_oW)fJJ9>;_FMfe%X_KV=n;KQZ-6QnJmj5Pd;+ zaAx!VrrTXbfzhjLkFPN78M>m*+!c8KrYPY50Z>Z^21h7m$ z=|`>zB?15df|DxONdXR%bk{cltdp$QH38R?;@3g}pOYonI|2EVVb~=Gn$G|L001~` BA$*{;1F*ZY12ug)lfJSze=MCOBK!hB9O&PdAGS;fmMjC&BAevB&+&LK zB~j2C{upfN&S2}R9U04%T*KGKY#7ydh-J94!*hGacV@PCj4 z*ApC;1#N0(K!xI1(2J1GEF_dye=wUHylA@`Wr(}mhs+iG$cJaoNS}JSVSY%Q|net6Ij%F4)3TQlH5_V(HJ5S)UoAD}f#f8?HD6(KEp z?$+Rpr!)E+;(QUnQan6$t^QAZpf8*`0qQKs(OOLNG8ti4Nowz9Q{54UK{{v7<2M9McH_avj004lq3$+m<4K;l^ zKQ}cu%_af>0DzMz*GT~nlXKTM0j-m(*EIpyli}Aw0ics4*gFCGlVI2-2Aj_S0002F C1}888 diff --git a/plugin.yml b/plugin.yml index 6586dea..32e1aeb 100644 --- a/plugin.yml +++ b/plugin.yml @@ -1,5 +1,5 @@ name: TradeCraft -version: AE-1.1b +version: AE-1.1 description: Setup shops with a combination of chest and sign. author: ArmEagle main: nl.armeagle.TradeCraft.TradeCraft From 346901ca61bf44120f565daa584a5f239ee06412 Mon Sep 17 00:00:00 2001 From: ArmEagle Date: Sun, 6 Nov 2011 14:31:08 +0100 Subject: [PATCH 066/100] Version 1.2. Using new configuration classes in most places. --- .classpath | 2 +- TradeCraft.jar | Bin 55435 -> 57724 bytes TradeCraft.properties | 2 +- nl/armeagle/TradeCraft/TradeCraft.java | 52 ++-- .../TradeCraftConfigurationFile.java | 231 +++++++++--------- .../TradeCraftConfigurationInfo.java | 46 +++- nl/armeagle/TradeCraft/TradeCraftItem.java | 3 + .../TradeCraft/TradeCraftLocalization.java | 111 ++------- .../TradeCraft/TradeCraftPropertiesFile.java | 31 ++- plugin.yml | 2 +- 10 files changed, 243 insertions(+), 237 deletions(-) diff --git a/.classpath b/.classpath index 4244016..373bf92 100644 --- a/.classpath +++ b/.classpath @@ -3,6 +3,6 @@ - + diff --git a/TradeCraft.jar b/TradeCraft.jar index 918d28dcfa1b15c65435701533aaa4e1427b28a2..0f8341b3ad934a0bc1c90a933d400cd3d7e4a2d5 100644 GIT binary patch delta 25720 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 Zh+#7jo8_mtR0vcEa&RL(AUNEI`!+W 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 configs = new HashMap(); + private final TradeCraftBlockListener blockListener = new TradeCraftBlockListener(this); private final TradeCraftPlayerListener playerListener = new TradeCraftPlayerListener(this); public TradeCraftPermissions permissions = new TradeCraftPermissions(this); @@ -582,34 +585,31 @@ private void displayCommandHelpText(Player player) { this.log(Level.INFO, "tcreload: "+ TradeCraftLocalization.get("TC_RELOAD")); } } - - /** - * Return the last modified time stamp of a resource with the given file path. - * @param filePath - * @return -1 in case of errors, or else the seconds since epoch at which point this resource was last modified. - */ - public static long resourceLastModified(String filePath) { - URL resource = TradeCraft.class.getResource(filePath); - if ( resource == null) { - return -1; - } - URLConnection resConn; + public void saveConfig() { + Logger.getLogger(JavaPlugin.class.getName()).log(Level.INFO, "saving config to: "+ this.getConfig("config").getFile().getAbsolutePath()); try { - resConn = resource.openConnection(); - } catch ( IOException ioe ) { - return -1; + this.getConfig("config").save(); + } catch (IOException ex) { + Logger.getLogger(JavaPlugin.class.getName()).log(Level.SEVERE, "Could not save config to " + this.getConfig("config").getFile().getName(), ex); + } + } + public StatefulYamlConfiguration getConfig() { + return this.getConfig("config"); + } + public StatefulYamlConfiguration getConfig(String name) { + if (name.indexOf(".") < 0) { + name += ".yml"; } - if ( resConn == null ) { - return -1; + if (this.configs.containsKey(name)) { + return this.configs.get(name); + } else { + File configFile = new File(this.getDataFolder(), name); + this.log(Level.INFO, configFile.getAbsolutePath()); + StatefulYamlConfiguration config = new StatefulYamlConfiguration(configFile); + this.configs.put(name, config); + return config; } - long lastModified = resConn.getLastModified(); - // calling getLastModified apparently opens an InputStream that should be closed - try { - resConn.getInputStream().close(); - } catch ( Exception e ) {} - // finally return the last modified time code - return lastModified; } public void log(Level level, String format, Object... args) { diff --git a/nl/armeagle/TradeCraft/TradeCraftConfigurationFile.java b/nl/armeagle/TradeCraft/TradeCraftConfigurationFile.java index 96dfd0e..df7c231 100644 --- a/nl/armeagle/TradeCraft/TradeCraftConfigurationFile.java +++ b/nl/armeagle/TradeCraft/TradeCraftConfigurationFile.java @@ -2,16 +2,20 @@ import java.io.BufferedReader; import java.io.File; -import java.io.FileOutputStream; import java.io.FileReader; import java.io.IOException; -import java.io.InputStream; import java.util.Arrays; import java.util.HashMap; +import java.util.Iterator; import java.util.Map; +import java.util.Map.Entry; import java.util.regex.Matcher; import java.util.regex.Pattern; +import org.bukkit.configuration.MemorySection; + +import nl.armeagle.Configuration.StatefulYamlConfiguration; + /** * The name of this class is a bit misleading. This class stores all the items and their default * trade rates that can be used in the game. The actual configuration of the plugin itself @@ -19,7 +23,6 @@ */ class TradeCraftConfigurationFile { private static final String fileName = TradeCraft.pluginName + ".txt"; - private static final String filePath = "plugins" + File.separator + TradeCraft.pluginName; private static final Pattern commentPattern = Pattern.compile("^\\s*#.*$"); private static final Pattern infoPattern = Pattern.compile( @@ -28,141 +31,143 @@ class TradeCraftConfigurationFile { "(?:,\\s*(\\d+)\\s*:\\s*(\\d+))?\\s*" + // buyAmount:buyValue "(?:,\\s*(\\d+)\\s*:\\s*(\\d+))?\\s*$"); // sellAmount:sellValue + private HashMap mapItemNames = new HashMap(); private final TradeCraft plugin; - private final Map infos = new HashMap(); // create an index from an TradeCraftItem (id;data) to the TradeCraftConfigurationInfo entry. This for configured name lookup based on id;data - private final Map TCitemInfoIndex = new HashMap(); - + private final Map TCitemInfoIndex = new HashMap(); + TradeCraftConfigurationFile(TradeCraft plugin) { this.plugin = plugin; } void load() { - // make folder in the plugins dir if it doesn't exist yet - File path = new File(filePath); - if ( !path.exists() ) { - path.mkdirs(); - } - path = null; + StatefulYamlConfiguration config = (StatefulYamlConfiguration) this.plugin.getConfig(); - // if file does not exist in this directory, copy it from the jar - File file = new File(filePath + File.separator + fileName); - if ( !file.exists() ) { - InputStream input = this.getClass().getResourceAsStream("/" + fileName); - if ( input != null ) { - FileOutputStream output = null; - - try { - output = new FileOutputStream(file); - byte[] buf = new byte[8192]; - int length = 0; - while ((length = input.read(buf)) > 0) { - output.write(buf, 0, length); + // if file exists, load the config to it once and then rename the old config + File file = new File(plugin.getDataFolder().getAbsolutePath(), fileName); + if ( file.exists() ) { + try { + FileReader reader = new FileReader(file); + BufferedReader configurationFile = new BufferedReader(reader); + + int lineNumber = 0; + String line; + + while ((line = configurationFile.readLine()) != null) { + lineNumber += 1; + + if (line.trim().equals("")) { + continue; + } + + Matcher commentMatcher = commentPattern.matcher(line); + + if (commentMatcher.matches()) { + continue; + } + + Matcher infoMatcher = infoPattern.matcher(line); + + if (!infoMatcher.matches()) { + plugin.log.warning( + "Failed to parse line number " + lineNumber + + " in " + file.getAbsolutePath() + + ": " + line); + continue; + } + + TradeCraftConfigurationInfo info = new TradeCraftConfigurationInfo(); + info.name = infoMatcher.group(1); + + // try to split ID and Data, separated by a semicolon mark + Matcher IdSplitData = TradeCraft.itemPatternIdSplitData.matcher(infoMatcher.group(2)); + + if (!IdSplitData.matches()) { + plugin.log.info( + "Failed to parse line number " + lineNumber + + " in " + file.getAbsolutePath() + + ": " + line); + continue; + } + + int id = Integer.parseInt(IdSplitData.group(1)); + if ( IdSplitData.group(2) != null ) { + short data = Short.parseShort(IdSplitData.group(2)); + info.type = new TradeCraftItem(id, data); + } else { + info.type = new TradeCraftItem(id); + } + + if (infoMatcher.group(3) != null) { + info.sellAmount = info.buyAmount = Integer.parseInt(infoMatcher.group(3)); + info.sellValue = info.buyValue = Integer.parseInt(infoMatcher.group(4)); + } + + if (infoMatcher.group(5) != null) { + info.sellAmount = Integer.parseInt(infoMatcher.group(5)); + info.sellValue = Integer.parseInt(infoMatcher.group(6)); } - } catch (IOException e) { - e.printStackTrace(); - } finally { - try { - if (input != null) { - input.close(); - } - } catch (IOException e) {} - try { - if (output != null) { - output.close(); - } - } catch (IOException e) {} +// config.set(info.name.toUpperCase(), info.toMemoryConfiguration()); + Iterator> iter = info.toMap().entrySet().iterator(); + while (iter.hasNext()) { + Map.Entry pairs = (Map.Entry)iter.next(); + if (!pairs.getKey().equals("name")) { + config.set(info.name +"."+ pairs.getKey(), pairs.getValue()); + } + } + +// TCitemInfoIndex.put(info.type, info.name); + } + configurationFile.close(); + reader.close(); + config.save(); + if (file.renameTo(new File(file.getAbsolutePath() + ".converted.to.config.yml"))) { + plugin.log.info("Converted old config to new style and renamed the old config file"); + } else { + plugin.log.info("FAILED to convert old config to new style"); } + + } catch (IOException e) { + plugin.log.severe("Error reading " + file.getAbsolutePath()); + } + } else { + try { + config.load(true); + } catch (IOException e) { + plugin.log.severe("Error loading plugin config file"); } } - try { - infos.clear(); - - BufferedReader configurationFile = new BufferedReader(new FileReader(filePath + File.separator + fileName)); - - int lineNumber = 0; - String line; - - while ((line = configurationFile.readLine()) != null) { - lineNumber += 1; - - if (line.trim().equals("")) { - continue; - } - - Matcher commentMatcher = commentPattern.matcher(line); - - if (commentMatcher.matches()) { - continue; - } - - Matcher infoMatcher = infoPattern.matcher(line); - - if (!infoMatcher.matches()) { - plugin.log.warning( - "Failed to parse line number " + lineNumber + - " in " + filePath + File.separator + fileName + - ": " + line); - continue; - } - - TradeCraftConfigurationInfo info = new TradeCraftConfigurationInfo(); - info.name = infoMatcher.group(1); - - // try to split ID and Data, separated by a semicolon mark - Matcher IdSplitData = TradeCraft.itemPatternIdSplitData.matcher(infoMatcher.group(2)); - - if (!IdSplitData.matches()) { - plugin.log.info( - "Failed to parse line number " + lineNumber + - " in " + filePath + File.separator + fileName + - ": " + line); - continue; - } - - int id = Integer.parseInt(IdSplitData.group(1)); - if ( IdSplitData.group(2) != null ) { - short data = Short.parseShort(IdSplitData.group(2)); - info.type = new TradeCraftItem(id, data); - } else { - info.type = new TradeCraftItem(id); - } - - if (infoMatcher.group(3) != null) { - info.sellAmount = info.buyAmount = Integer.parseInt(infoMatcher.group(3)); - info.sellValue = info.buyValue = Integer.parseInt(infoMatcher.group(4)); - } - - if (infoMatcher.group(5) != null) { - info.sellAmount = Integer.parseInt(infoMatcher.group(5)); - info.sellValue = Integer.parseInt(infoMatcher.group(6)); - } - - infos.put(info.name.toUpperCase(), info); - TCitemInfoIndex.put(info.type, info); - } - plugin.log.info("Loaded " + infos.size() + " configs"); - configurationFile.close(); - } catch (IOException e) { - plugin.log.warning("Error reading " + filePath + File.separator + fileName); - } + Iterator> iter = config.getValues(false).entrySet().iterator(); + while (iter.hasNext()) { + // store map of lowercase item names to key names in the configuration + Map.Entry entry = (Map.Entry)iter.next(); + this.mapItemNames.put(entry.getKey().toLowerCase(), entry.getKey()); + // store map of item types to key names + MemorySection section = (MemorySection) entry.getValue(); + TradeCraftItem tcItem = new TradeCraftItem(section.getInt("itemTypeId", 266), section.getInt("itemTypeData", 0)); + TCitemInfoIndex.put(tcItem, entry.getKey()); + } } public String[] getNames() { - String[] names = infos.keySet().toArray(new String[0]); + String[] names = plugin.getConfig().getKeys(false).toArray(new String[0]); Arrays.sort(names); return names; } public boolean isConfigured(String name) { - return infos.containsKey(name.toUpperCase()); + return this.mapItemNames.containsKey(name.toLowerCase()); } public TradeCraftConfigurationInfo get(String name) { - return infos.get(name.toUpperCase()); + // @todo, config code doesn't seem to like serialized objects, cannot seem to find the TradeCraftConfigurationInfo class + // though, the otherwise resulting ==: classpath lines aren't very user friendly anyway. +// return (TradeCraftConfigurationInfo) plugin.getConfig().get(name.toUpperCase()); + String itemName = this.mapItemNames.get(name.toLowerCase()); + return new TradeCraftConfigurationInfo(((MemorySection)plugin.getConfig().get(itemName)).getValues(false), name); } public TradeCraftConfigurationInfo get(int id) { return this.get(new TradeCraftItem(id)); @@ -171,6 +176,6 @@ public TradeCraftConfigurationInfo get(int id, short data) { return this.get(new TradeCraftItem(id, data)); } public TradeCraftConfigurationInfo get(TradeCraftItem item) { - return TCitemInfoIndex.get(item); + return this.get(TCitemInfoIndex.get(item)); } } \ No newline at end of file diff --git a/nl/armeagle/TradeCraft/TradeCraftConfigurationInfo.java b/nl/armeagle/TradeCraft/TradeCraftConfigurationInfo.java index 9b345ad..cd3d78a 100644 --- a/nl/armeagle/TradeCraft/TradeCraftConfigurationInfo.java +++ b/nl/armeagle/TradeCraft/TradeCraftConfigurationInfo.java @@ -1,10 +1,52 @@ package nl.armeagle.TradeCraft; -class TradeCraftConfigurationInfo { - public String name; +import java.util.LinkedHashMap; +import java.util.Map; +import org.bukkit.configuration.serialization.ConfigurationSerializable; +import org.bukkit.configuration.serialization.SerializableAs; + +@SerializableAs("TradeCraftConfiguration") +class TradeCraftConfigurationInfo implements ConfigurationSerializable { + public String name; public TradeCraftItem type; public int buyAmount; public int buyValue; public int sellAmount; public int sellValue; + + TradeCraftConfigurationInfo() { + } + + TradeCraftConfigurationInfo(Map map, String name) { + this.name = name; + this.type = new TradeCraftItem((Integer) map.get("itemTypeId"), (Integer) map.get("itemTypeData")); + this.buyAmount = (Integer) map.get("buyAmount"); + this.buyValue = (Integer) map.get("buyValue"); + this.sellAmount = (Integer) map.get("sellAmount"); + this.sellValue = (Integer) map.get("sellValue"); + } + TradeCraftConfigurationInfo(Map map) { + this.name = (String) map.get("name"); + this.type = new TradeCraftItem((Integer) map.get("itemTypeId"), (Integer) map.get("itemTypeData")); + this.buyAmount = (Integer) map.get("buyAmount"); + this.buyValue = (Integer) map.get("buyValue"); + this.sellAmount = (Integer) map.get("sellAmount"); + this.sellValue = (Integer) map.get("sellValue"); + } + + public Map toMap() { + LinkedHashMap map = new LinkedHashMap(); + map.put("name", this.name); + map.put("itemTypeId", this.type.id); + map.put("itemTypeData", this.type.data); + map.put("buyAmount", this.buyAmount); + map.put("buyValue", this.buyValue); + map.put("sellAmount", this.sellAmount); + map.put("sellValue", this.sellValue); + return map; + } + @Override + public Map serialize() { + return this.toMap(); + } } \ No newline at end of file diff --git a/nl/armeagle/TradeCraft/TradeCraftItem.java b/nl/armeagle/TradeCraft/TradeCraftItem.java index 978b188..d8155fa 100644 --- a/nl/armeagle/TradeCraft/TradeCraftItem.java +++ b/nl/armeagle/TradeCraft/TradeCraftItem.java @@ -12,6 +12,9 @@ public class TradeCraftItem implements Comparable { TradeCraftItem(int id) { this(id, (short)0); } + TradeCraftItem(int id, int data) { + this(id, new Integer(data).shortValue()); + } TradeCraftItem(int id, short data) { this.id = id; this.data = data; diff --git a/nl/armeagle/TradeCraft/TradeCraftLocalization.java b/nl/armeagle/TradeCraft/TradeCraftLocalization.java index 067e896..168a101 100644 --- a/nl/armeagle/TradeCraft/TradeCraftLocalization.java +++ b/nl/armeagle/TradeCraft/TradeCraftLocalization.java @@ -1,12 +1,8 @@ package nl.armeagle.TradeCraft; -import java.io.File; -import java.io.FileOutputStream; import java.io.IOException; -import java.io.InputStream; import java.util.logging.Level; - -import org.bukkit.util.config.Configuration; +import nl.armeagle.Configuration.StatefulYamlConfiguration; /** * @@ -19,90 +15,33 @@ */ public class TradeCraftLocalization { private static final String filePreName = TradeCraft.pluginName + ".%1$s.lang"; - private static final String filePath = "plugins" + File.separator + TradeCraft.pluginName; - - private TradeCraft plugin; - private String language; - private static Configuration localization; + private static StatefulYamlConfiguration localization; public TradeCraftLocalization(TradeCraft plugin) { - this.plugin = plugin; - this.language = TradeCraft.properties.getLanguage(); - - // make folder in the plugins dir if it doesn't exist yet - File path = new File(filePath); - if ( !path.exists() ) { - path.mkdirs(); - } - path = null; - - // If file does not exist in this directory, copy it from the jar. - // Or if the file in the jar was updated and the setting allows for automatic updating, then do so. - String fileName = String.format(TradeCraftLocalization.filePreName, this.language); - - File file = new File(filePath + File.separator + fileName); - - if ( !file.exists() - || TradeCraft.properties.autoUpdateLanguageFiles() - && TradeCraft.resourceLastModified("/"+ fileName) > file.lastModified() ) { - - InputStream input = this.getClass().getResourceAsStream("/" + fileName); - // If this file does not exist (not a default supported language), then revert back to the default language file. - // That file will exist in the jar for sure - if ( input == null ) { - this.plugin.log(Level.INFO, "%1$s was not found in %2$s.jar, using default language \"%3$s\" instead", - fileName, - TradeCraft.pluginName, - TradeCraftPropertiesFile.defaultLanguage); - this.language = TradeCraftPropertiesFile.defaultLanguage; - fileName = String.format(TradeCraftLocalization.filePreName, this.language); - file = new File(filePath + File.separator + fileName); - // check whether this file already exists, or else copy it over - if ( !file.exists() || TradeCraft.properties.autoUpdateLanguageFiles() - && TradeCraft.resourceLastModified("/"+ fileName) > file.lastModified()) { - input = this.getClass().getResourceAsStream("/" + fileName); - } - } - // if input is not null, then we need to copy the given file from the jar - if ( input != null ) { - if ( !file.exists() ) { - this.plugin.log(Level.INFO, "%1$s%2$s%3$s does not exist, creating...", - filePath, - File.separator, - fileName); - } else { - this.plugin.log(Level.INFO, "%1$s has a new version, updating...", - fileName); - } + String filename = String.format(TradeCraftLocalization.filePreName, TradeCraft.properties.getLanguage()); + TradeCraftLocalization.localization = plugin.getConfig(filename); + + try { + TradeCraftLocalization.localization.load(true); + } catch (IOException e) { + plugin.log(Level.SEVERE, "Failed to read file: %s", filename); + } + + String defaultFilename = String.format(TradeCraftLocalization.filePreName, "en"); + if (TradeCraftLocalization.localization.getKeys(false).isEmpty()) { + plugin.log(Level.INFO, "Language file %s does not exist or is empty, defaulting to %s", filename, defaultFilename); + } + TradeCraftLocalization.localization = plugin.getConfig(defaultFilename); + try { + TradeCraftLocalization.localization.load(true); + } catch (IOException e) { + plugin.log(Level.SEVERE, "Failed to read file: %s", defaultFilename); + } + + if (TradeCraftLocalization.localization.getKeys(false).isEmpty()) { + plugin.log(Level.SEVERE, "Default language file %s is also empty", defaultFilename); + } - FileOutputStream output = null; - - try { - output = new FileOutputStream(file); - byte[] buf = new byte[8192]; - int length = 0; - while ((length = input.read(buf)) > 0) { - output.write(buf, 0, length); - } - } catch (IOException e) { - e.printStackTrace(); - } finally { - try { - if (input != null) { - input.close(); - } - } catch (IOException e) {} - - try { - if (output != null) { - output.close(); - } - } catch (IOException e) {} - } - } - } - TradeCraftLocalization.localization = new Configuration(file); - TradeCraftLocalization.localization.load(); } public static String get(String key) { diff --git a/nl/armeagle/TradeCraft/TradeCraftPropertiesFile.java b/nl/armeagle/TradeCraft/TradeCraftPropertiesFile.java index 8f69f9f..7350034 100644 --- a/nl/armeagle/TradeCraft/TradeCraftPropertiesFile.java +++ b/nl/armeagle/TradeCraft/TradeCraftPropertiesFile.java @@ -4,9 +4,11 @@ import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; +import java.util.logging.Level; import org.bukkit.ChatColor; -import org.bukkit.util.config.Configuration; +import org.bukkit.configuration.InvalidConfigurationException; +import org.bukkit.configuration.file.YamlConfiguration; /** * This class, handles the actual configuration of the plugin. The TradeCraftConfiguration class @@ -18,7 +20,7 @@ public class TradeCraftPropertiesFile { public static final String defaultLanguage = "en"; private TradeCraft plugin; - private final Configuration properties; + private final YamlConfiguration properties; public TradeCraftPropertiesFile(TradeCraft plugin) { this.plugin = plugin; @@ -62,8 +64,23 @@ public TradeCraftPropertiesFile(TradeCraft plugin) { } } - properties = new Configuration(file); - properties.load(); + properties = new YamlConfiguration(); + try { + properties.load(file); + } catch (InvalidConfigurationException e) { + plugin.log(Level.SEVERE, "Failed to load file: %s", file.toURI()); + } catch (IOException e) { + plugin.log(Level.SEVERE, "Failed to read file: %s", file.toURI()); + } + } + + protected void save() { + File file = new File(filePath + File.separator + fileName); + try { + properties.save(file); + } catch (IOException e) { + this.plugin.log(Level.SEVERE, "Error saving to file: %s", file.toURI()); + } } public TradeCraftItem getCurrencyType(){ @@ -72,9 +89,9 @@ public TradeCraftItem getCurrencyType(){ return new TradeCraftItem(id, data); } public void setCurrencyType(TradeCraftItem item) { - properties.setProperty("currency-id", item.id); - properties.setProperty("currency-data", item.data); - properties.save(); + properties.set("currency-id", item.id); + properties.set("currency-data", item.data); + this.save(); } public boolean getNormalStackSizeUsed(){ return properties.getBoolean("normal-stack-size", true); diff --git a/plugin.yml b/plugin.yml index 32e1aeb..fb3fc9e 100644 --- a/plugin.yml +++ b/plugin.yml @@ -1,5 +1,5 @@ name: TradeCraft -version: AE-1.1 +version: AE-1.2 description: Setup shops with a combination of chest and sign. author: ArmEagle main: nl.armeagle.TradeCraft.TradeCraft From c2645d1bd02f8b8462122c7f0d33a411dfc30fb3 Mon Sep 17 00:00:00 2001 From: ArmEagle Date: Sun, 6 Nov 2011 15:23:24 +0100 Subject: [PATCH 067/100] forgot some --- TradeCraft.jar | Bin 57724 -> 57726 bytes config.yml | 153 ++++++++++++++++++ .../StatefulYamlConfiguration.java | 49 ++++++ plugin.yml | 2 +- 4 files changed, 203 insertions(+), 1 deletion(-) create mode 100644 config.yml create mode 100644 nl/armeagle/Configuration/StatefulYamlConfiguration.java diff --git a/TradeCraft.jar b/TradeCraft.jar index 0f8341b3ad934a0bc1c90a933d400cd3d7e4a2d5..6f93e15c4e6db2bdc90a415f58c0261122b60686 100644 GIT binary patch delta 637 zcmV-@0)qYg!UO)o1F%KA0t|JNN4qJ1bf}tkbf-2AiVj87xSfIy7wIGsxn{ zuw^>16d9-%*(C3Mj>mf`iGnup%V0~l20K^n$XKT220q=reP4g5Z>pATW0-bnbW6@_ zu!GVTo|zi}p;6+ANsRNLI%o#64p6pWSug7fy&IGXdPdw~l~f{^4j8bNV?q(0({ zpH2#;&>J3glw4eGhr0PMqHAg-qfZ?O;iy!xHr?_6`WSB=a5uX3C|sHZ~B z$rFX@X~U#b-o?_P96^WrD~Wtx<577Sa7PQgwD17bmk~20qQPovSjQUB97i$>z|4Th zyztKf-IHs)F(jLDhX0iuxPjn*uqhMeaKR= zk9>G!js&pwV~Y-_G8p0t+nMC@WXdm(CZi-HxdFpJ&U5lgcC7a%yQ*WH?20WsB=v>t zGk)robbe88wqc?JVm?P^>XtavvmMEo%`%_cPfupNf33B`!mLmsB>&)o)`@pMLiOPsF)Sc-?IF4g~ze<{~xyI|c@ln5~(l@W(~ z;v(8N*XQ2!tp1jUP(%}`4OTkFX#@=Y~J5=yQ?U$_v+f?D-64?s55s3p1&yy_&-of2MDPs ztS=`5004xumA!Z(4GeW=KdC6JFDC*30ECko;7I|RlVspG0XUPK;57nc&Xax47nADX XDgi5#72!Jpg_BX?B?czg00000T%R?~ delta 640 zcmV-`0)PGf!UO!m1F%KA0@!7fN4qJ1cBqSVbf-26iVQ{5xSfIy7wIGsxn{ zuw^>16bYyn*(C3Mj>mf`iGsH9%V0w{23uF{$ylc37QWnlxZZrK8nU%v+NH4_Ij_O? zN?Ukgt_Or#iDxD;&Vy>9?#Vhp*??u8+*IgYuS`%g;to4bl|UARB;TMB6h}6HCwTF* z3c4c5*lh*jadm#j=RBicy%g!gAq;xrdW`TEyRgfv-}(K~)mI1Axa|Kb$LXVbD%6xb zQK+6aOgiOVEFH=Lbf~_P$PYChl!qSoG{Z{^k3hYSm?04jR#VMdR)gj^keLBy20Z13 ze~##mTset9$*B^k-}82)jdlUK53oj2K4E#qVtY~eAf&t#wR zW3Qz1i*l0<6CDuqIWbeW#G#JuNxp8D>D+#LGQ-1LtrZq##iF|kc24tuT==V>>V$_e z5S)T%KR|1g$UVO)LR$3PZNV8&7xZ_;`67U&cz9~9{&)OKxh}gI>z<=ThykgLINlSh zX!o*RiDID}W$5-Pn9>(ex8XZr)UX`YG2hYT~^eIy8 Date: Sun, 6 Nov 2011 15:40:27 +0100 Subject: [PATCH 068/100] Changed item config file from default config.yml to items.yml, since the first should be used for plugin settings (need to convert the .properties file) --- nl/armeagle/TradeCraft/TradeCraft.java | 1 - .../TradeCraft/TradeCraftConfigurationFile.java | 13 +++++++++---- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/nl/armeagle/TradeCraft/TradeCraft.java b/nl/armeagle/TradeCraft/TradeCraft.java index f74bd6f..6f92187 100644 --- a/nl/armeagle/TradeCraft/TradeCraft.java +++ b/nl/armeagle/TradeCraft/TradeCraft.java @@ -605,7 +605,6 @@ public StatefulYamlConfiguration getConfig(String name) { return this.configs.get(name); } else { File configFile = new File(this.getDataFolder(), name); - this.log(Level.INFO, configFile.getAbsolutePath()); StatefulYamlConfiguration config = new StatefulYamlConfiguration(configFile); this.configs.put(name, config); return config; diff --git a/nl/armeagle/TradeCraft/TradeCraftConfigurationFile.java b/nl/armeagle/TradeCraft/TradeCraftConfigurationFile.java index df7c231..e42b5e9 100644 --- a/nl/armeagle/TradeCraft/TradeCraftConfigurationFile.java +++ b/nl/armeagle/TradeCraft/TradeCraftConfigurationFile.java @@ -23,6 +23,7 @@ */ class TradeCraftConfigurationFile { private static final String fileName = TradeCraft.pluginName + ".txt"; + private static final String configName = "items"; private static final Pattern commentPattern = Pattern.compile("^\\s*#.*$"); private static final Pattern infoPattern = Pattern.compile( @@ -40,9 +41,13 @@ class TradeCraftConfigurationFile { TradeCraftConfigurationFile(TradeCraft plugin) { this.plugin = plugin; } + + public StatefulYamlConfiguration getConfig() { + return this.plugin.getConfig(TradeCraftConfigurationFile.configName); + } void load() { - StatefulYamlConfiguration config = (StatefulYamlConfiguration) this.plugin.getConfig(); + StatefulYamlConfiguration config = (StatefulYamlConfiguration) this.getConfig(); // if file exists, load the config to it once and then rename the old config File file = new File(plugin.getDataFolder().getAbsolutePath(), fileName); @@ -123,7 +128,7 @@ void load() { configurationFile.close(); reader.close(); config.save(); - if (file.renameTo(new File(file.getAbsolutePath() + ".converted.to.config.yml"))) { + if (file.renameTo(new File(file.getAbsolutePath() + ".converted.to."+ TradeCraftConfigurationFile.configName +".yml"))) { plugin.log.info("Converted old config to new style and renamed the old config file"); } else { plugin.log.info("FAILED to convert old config to new style"); @@ -153,7 +158,7 @@ void load() { } public String[] getNames() { - String[] names = plugin.getConfig().getKeys(false).toArray(new String[0]); + String[] names = this.getConfig().getKeys(false).toArray(new String[0]); Arrays.sort(names); return names; } @@ -167,7 +172,7 @@ public TradeCraftConfigurationInfo get(String name) { // though, the otherwise resulting ==: classpath lines aren't very user friendly anyway. // return (TradeCraftConfigurationInfo) plugin.getConfig().get(name.toUpperCase()); String itemName = this.mapItemNames.get(name.toLowerCase()); - return new TradeCraftConfigurationInfo(((MemorySection)plugin.getConfig().get(itemName)).getValues(false), name); + return new TradeCraftConfigurationInfo(((MemorySection)this.getConfig().get(itemName)).getValues(false), name); } public TradeCraftConfigurationInfo get(int id) { return this.get(new TradeCraftItem(id)); From 39b4a6d0830e33c8f9c7bf91b1745dfdd89c322e Mon Sep 17 00:00:00 2001 From: ArmEagle Date: Wed, 7 Dec 2011 08:33:33 +0100 Subject: [PATCH 069/100] Preventing some exceptions when items/currency does not exist in the item config file. --- TradeCraft.jar | Bin 57726 -> 58055 bytes nl/armeagle/TradeCraft/TradeCraft.java | 7 ++++++- .../TradeCraftConfigurationFile.java | 6 +++++- .../TradeCraft/TradeCraftInfiniteShop.java | 5 ++++- plugin.yml | 2 +- 5 files changed, 16 insertions(+), 4 deletions(-) diff --git a/TradeCraft.jar b/TradeCraft.jar index 6f93e15c4e6db2bdc90a415f58c0261122b60686..a74e9038b7da481fc1d390b6a4e6703b1f75599e 100644 GIT binary patch delta 17755 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 delta 17392 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%0D2gVqG!SHX|k( zgj-k>y5M(i%~bW`O*aRSC*j*h65bX{ew=6@@I`ww`VcL$I0sAiLMAyhAI=^hGes~C zyw(POh*9?*Y4+~j{#QKt{OMy-yWvv+*wcEhtD)w|fF6ze+g7C=e(E`#^&a;0weBuf zL%;K3>1--ceTv8BA|C=LgN~4|LrR|`3LlEX0W?XB?;lkol8MR09UGw%{zELN;C`U;dw)jSL?s@Ut4V zM(unWg#Srt45{9M`rq7XH=XBz-G6=3(t7J8fGR zG>4g!jt7>nycaGVb)Uy$1=WD0)MBK~cn;T%mjvr0v0*$@jyf6`cfgJ->|7+U7n!R* ziy=JhKnoLVd&etKii3F8z5;eJU_@L~3gcLh{NDAJy?w3c+N>Ep@d?zrWC@g&icF26 zg`8Or6sc|v&E?|@hrRs__cPpA_QrOSgzsv)0b58#VI?o)> zX6D;wqHPy5d8TvdS2BLv@0~h2E7eXgTJ+Mu9=KHo5fN|5#`b~87F(szwsrqemHJpe zFS8hkx+l{}q*4~_Jvd-J{ou}QoH3TNkesaj+koJ->z#~8)iY&hg=yAB1U(=5W5@+- zz*(|efU-l&|FgtJ%t?0cX|JYPT-hvpa{5`L`M3T3)ArP_-FoQInuwC{gXc2man*k- zo?Bqg+7$fZiTjHlp`FiPD=l$8f0u2}#}c=C?>Xz4BD&g_>s|fdSFd?JwSITP-QX!4 zh6+;!+p8Zb-7>!!+mW79-B%XY{e9fVc>dWFhy|(=nT%`Yk?_AEk`z5egdzp55zK7?-oo z+po#>13YBBQe%z2Iuip!3kN85ZhpFNniTl(gu0vYVEVz$cwXS?3ij}!<;gy`JQP4f z%GaJ1wM%m|Ffd6lFqj|>FEcPm7*77^tUUSpEqQQS{CP{7X{rrSNM*9sZ7~JlaI-yf z%K%jWBfv~&phBz3t8Vi!0~-pHcixs}(g^|z8BAuoBL+53{*E-$(=f0N7qVC;Cqsp6 zfWpjKP+`8wr+|yFK+T7nK;g!0u&`f_&}8kqJYdHoXG&v)?Lbyb-ekdhDwC`4$}$7< z=;YaVrI{*%A&mH7iMgfnlB!1A{(_-X-Ngg|^@<9C%NfDY*k|@9Rz> zU{eiZ@5B_Hx5wBR7>opg7)7r|FIe$`K0%!TZ$>5&2FM8)ppd?EnCS zqA6|)SQFoDw#jd%@=bnq3u4Q8uYcDifwqJIU1^4*p=~BqgXrW__jy2t$K;#$fdRJw zEZn_NWU}r9NWj@VkY>_f0~W4%AOjA|*+3zo&0wM155yFJ#ftq>!>cD6flku|I?WQr zrr>Rp1s}R|0E--ZP#T+@@KB7&diUgthh|K(_D Date: Wed, 7 Dec 2011 22:23:47 +0100 Subject: [PATCH 070/100] some small changes and renamed file to items.yml --- TradeCraft.jar | Bin 58055 -> 58229 bytes TradeCraft.properties | 2 +- config.yml => items.yml | 2 +- .../TradeCraftConfigurationFile.java | 2 +- 4 files changed, 3 insertions(+), 3 deletions(-) rename config.yml => items.yml (94%) diff --git a/TradeCraft.jar b/TradeCraft.jar index a74e9038b7da481fc1d390b6a4e6703b1f75599e..a8abc8b5b82752192bff2fb4b7c24b01a07d9c01 100644 GIT binary patch delta 5298 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 delta 5123 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 u6=HmiNA#Bt|EVGX;Fk{nd3-1SUzG^ Date: Wed, 7 Dec 2011 22:51:22 +0100 Subject: [PATCH 071/100] Better items.yml now. Hopefully this works for everyone on 1337 now. version 1.2.2 --- TradeCraft.jar | Bin 58229 -> 58106 bytes items.yml | 251 +++++++----------- .../TradeCraftConfigurationInfo.java | 5 +- plugin.yml | 2 +- 4 files changed, 103 insertions(+), 155 deletions(-) diff --git a/TradeCraft.jar b/TradeCraft.jar index a8abc8b5b82752192bff2fb4b7c24b01a07d9c01..c447e9b49db49b7096de4b25ebf39df7cb97a676 100644 GIT binary patch delta 1775 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 bXc3W})c=18K;DdrC toMap() { public Map serialize() { return this.toMap(); } + public static TradeCraftConfigurationInfo deserialize(Map map) { + return new TradeCraftConfigurationInfo(map); + } } \ No newline at end of file diff --git a/plugin.yml b/plugin.yml index e32b79c..5a394f0 100644 --- a/plugin.yml +++ b/plugin.yml @@ -1,5 +1,5 @@ name: TradeCraft -version: AE-1.2.1 +version: AE-1.2.2 description: Setup shops with a combination of chest and sign. author: ArmEagle main: nl.armeagle.TradeCraft.TradeCraft From b072b722acd86c4056180ca84a57b14758194612 Mon Sep 17 00:00:00 2001 From: ArmEagle Date: Wed, 7 Dec 2011 23:35:45 +0100 Subject: [PATCH 072/100] version 1.2.3 Prevent NPE when currency is not in the items file (only to override the default item name). --- TradeCraft.jar | Bin 58106 -> 58129 bytes .../TradeCraftConfigurationFile.java | 4 ++++ plugin.yml | 2 +- 3 files changed, 5 insertions(+), 1 deletion(-) diff --git a/TradeCraft.jar b/TradeCraft.jar index c447e9b49db49b7096de4b25ebf39df7cb97a676..9f0620f8f81128e04f27931bc0c61b2da4382ddd 100644 GIT binary patch delta 4590 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 O1vaw6L)boh?e{OK1ep{7 delta 4588 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 W4p9S8NsuNY2bKs|$&0_A+x!cOJcc^} diff --git a/nl/armeagle/TradeCraft/TradeCraftConfigurationFile.java b/nl/armeagle/TradeCraft/TradeCraftConfigurationFile.java index 89c0049..4ec1749 100644 --- a/nl/armeagle/TradeCraft/TradeCraftConfigurationFile.java +++ b/nl/armeagle/TradeCraft/TradeCraftConfigurationFile.java @@ -168,6 +168,7 @@ public boolean isConfigured(String name) { } public TradeCraftConfigurationInfo get(String name) { + // @todo, config code doesn't seem to like serialized objects, cannot seem to find the TradeCraftConfigurationInfo class // though, the otherwise resulting ==: classpath lines aren't very user friendly anyway. // return (TradeCraftConfigurationInfo) plugin.getConfig().get(name.toUpperCase()); @@ -185,6 +186,9 @@ public TradeCraftConfigurationInfo get(int id, short data) { return this.get(new TradeCraftItem(id, data)); } public TradeCraftConfigurationInfo get(TradeCraftItem item) { + if (!TCitemInfoIndex.containsKey(item)) { + return null; + } return this.get(TCitemInfoIndex.get(item)); } } \ No newline at end of file diff --git a/plugin.yml b/plugin.yml index 5a394f0..bd589a3 100644 --- a/plugin.yml +++ b/plugin.yml @@ -1,5 +1,5 @@ name: TradeCraft -version: AE-1.2.2 +version: AE-1.2.3 description: Setup shops with a combination of chest and sign. author: ArmEagle main: nl.armeagle.TradeCraft.TradeCraft From a9176be4f406fddd8857911581f2d55a3ba6632f Mon Sep 17 00:00:00 2001 From: ArmEagle Date: Thu, 8 Dec 2011 20:31:53 +0100 Subject: [PATCH 073/100] Fixed usage line for the tcplayerperms command. --- plugin.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugin.yml b/plugin.yml index bd589a3..63c9108 100644 --- a/plugin.yml +++ b/plugin.yml @@ -30,7 +30,7 @@ commands: tcplayerperms: description: Show the permissions of the given player permission: TradeCraft.canQueryPlayer - usage: "Usage: /tccanplayer [player]" + usage: "Usage: /tcplayerperms [player]" permissions: TradeCraft.canBuy: description: Permission to buy from shops. From 582d939566eaccf4c9361556b0eec51e0da7c040 Mon Sep 17 00:00:00 2001 From: ArmEagle Date: Thu, 8 Dec 2011 22:00:20 +0100 Subject: [PATCH 074/100] version 1.2.4. Now using getDurability instead of getData again to support potions. --- .classpath | 2 +- TradeCraft.jar | Bin 58129 -> 58135 bytes nl/armeagle/TradeCraft/TradeCraftChest.java | 6 +++--- .../TradeCraft/TradeCraftRepairShop.java | 2 +- plugin.yml | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.classpath b/.classpath index 373bf92..d2516a4 100644 --- a/.classpath +++ b/.classpath @@ -3,6 +3,6 @@ - + diff --git a/TradeCraft.jar b/TradeCraft.jar index 9f0620f8f81128e04f27931bc0c61b2da4382ddd..26f829edfb7aa909546d721db09440e7f4effdaa 100644 GIT binary patch delta 4922 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�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 getNonCurrencyItems() { List items = new ArrayList(); for (ItemStack item : chest.getContents()) { if (item != null) { - short itemData = (item.getData() == null ? (short)0 : item.getData().getData()); + short itemData = (item.getData() == null ? (short)0 : item.getDurability()); if (item.getTypeId() != TradeCraft.currency.id || itemData != TradeCraft.currency.data) { items.add(item); } diff --git a/nl/armeagle/TradeCraft/TradeCraftRepairShop.java b/nl/armeagle/TradeCraft/TradeCraftRepairShop.java index 72a7cc0..f0d4b6b 100644 --- a/nl/armeagle/TradeCraft/TradeCraftRepairShop.java +++ b/nl/armeagle/TradeCraft/TradeCraftRepairShop.java @@ -48,7 +48,7 @@ public void handleRightClick(Player player) { chest.clear(); for (ItemStack item : items) { - short itemData = (item.getData() == null ? (short)0 : item.getData().getData()); + short itemData = (item.getData() == null ? (short)0 : item.getDurability()); chest.add(new TradeCraftItem(item.getTypeId(), itemData), 1); } diff --git a/plugin.yml b/plugin.yml index 63c9108..539fa5b 100644 --- a/plugin.yml +++ b/plugin.yml @@ -1,5 +1,5 @@ name: TradeCraft -version: AE-1.2.3 +version: AE-1.2.4 description: Setup shops with a combination of chest and sign. author: ArmEagle main: nl.armeagle.TradeCraft.TradeCraft From 1ebbdcc78e56f8f4f31857133d9698f494df2816 Mon Sep 17 00:00:00 2001 From: ArmEagle Date: Thu, 15 Dec 2011 20:34:27 +0100 Subject: [PATCH 075/100] tiny code cleanup --- nl/armeagle/TradeCraft/TradeCraft.java | 1 + 1 file changed, 1 insertion(+) diff --git a/nl/armeagle/TradeCraft/TradeCraft.java b/nl/armeagle/TradeCraft/TradeCraft.java index 7ca6b65..d967eb0 100644 --- a/nl/armeagle/TradeCraft/TradeCraft.java +++ b/nl/armeagle/TradeCraft/TradeCraft.java @@ -1,6 +1,7 @@ package nl.armeagle.TradeCraft; import java.io.*; + import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Date; From b379cf7f54db102648de9a8a552b1e229eddbadb Mon Sep 17 00:00:00 2001 From: ArmEagle Date: Thu, 15 Dec 2011 21:31:43 +0100 Subject: [PATCH 076/100] Better fixed bug: - Fixed bug in translation code, would still try to default to English language file --- .classpath | 2 +- TradeCraft.ge.lang | 68 ++++++++++++++++++ TradeCraft.jar | Bin 58135 -> 59998 bytes .../TradeCraft/TradeCraftLocalization.java | 4 +- plugin.yml | 2 +- 5 files changed, 73 insertions(+), 3 deletions(-) create mode 100644 TradeCraft.ge.lang diff --git a/.classpath b/.classpath index d2516a4..f3cb5d5 100644 --- a/.classpath +++ b/.classpath @@ -3,6 +3,6 @@ - + diff --git a/TradeCraft.ge.lang b/TradeCraft.ge.lang new file mode 100644 index 0000000..53f37bd --- /dev/null +++ b/TradeCraft.ge.lang @@ -0,0 +1,68 @@ +# TradeCraft.java +ERROR_IN_FORMAT_STRING: "Fehler im Format string:" +NO_PERMISSION_SET_CURRENCY: "Du hast keine Berechtigung die Währung umzustellen" +IS_NO_VALID_CURRENCY_USE_INSTEAD: "%1$s ist keine gültige Währung, nutze 'id[;data]' oder 'itemname'." +INVALID_CURRENCY: "Falsche Währung: %1$s" +CURRENCY_IS_SET_TO_A_IDDATA: "Währung umgestellt zu: %1$s (%2$s)" +CURRENCY_IS_A_IDDATA: "Währung ist: %1$s (%2$s)" +NO_SUCH_PLAYER: "Kein gültiger Spieler." +YOU_DONT_OWN_ANY_SHOPS: "Du hast keine Shops!" +A_DOES_NOT_OWN_ANY_SHOPS: "%1$s hat keine Shops." +YOUR_SHOPS: "Deine Shops:" +SHOPS_OF_A: "Shops von %1$s:" +ITEM: "Gegenstand" +AMOUNT: "Betrag" +POSSIBLE_COMMANDS_FOR_THE_PLUGIN: "Gültige Commands für %1$s Plugin:" +TC_HELP_THIS_TEXT: "Dieser Hilfetext" +TC_SHOPS: "Listet die Shops eines Spielers" +TC_CURRENCY_OPT_PARAM_GETSET_CURRENCY: "Währung setzen und anzeigen" +TC_CURRENCY_GET_CURRENCY: "Derzeitige Währung anzeigen" +TC_CAN_PLAYER: "Zeigt die Rechte eines Spielers" +TC_RELOAD: "Lädt das Script mit allen Einstellungen neu" +RESTARTING_PLUGIN: "Restarting %1$s" +RESTARTING_PLUGIN_DONE: "Restarting %1$s komplett" + +# TradeCraftBlockListener.java +ALL_ITEMS_MUST_BE_WITHDRAWN: "Alle Gegenstände und Währung müssen entnommen werden um die Kiste oder Schild zu zerstören!" +YOU_CANT_DESTROY_THIS_SIGN: "Du kannst dieses Schild nicht zerstören!" +YOU_CANT_DESTROY_THIS_CHEST: "Du kannst diese Kiste nicht zerstören!" +YOU_CANT_DESTROY_THIS_BLOCK_ATTACHED: "Du kannst diesen Block nicht zerstören, da ein Schild angebracht ist!" +YOU_CANT_CREATE_INF_SHOPS: "Du kannst keinen unbeschränkten Shop bauen!" +YOU_DONT_HAVE_PERM_CREATE_PLAYER_SHOP: "Du hast keine Berechtigung einen Spieler-Shop zu bauen!" + +# TradeCraftItemShop.java +THE_CHEST_HAS_MORE_THAN_ONE_TYPE: "Diese Kiste hat verschiedene Gegenstände!" +WITHDREW_X_A: "%1$d %2$s entnommen" +WITHDREW_X_CURRENCY_SHOP_STILL_HOLDS_Y_CURRENCY: "Das Maximum von %1$d %2$s entnommen, es sind jetzt noch %3$d %2$s im shop." +WITHDREW_X_A_SHOP_STILL_HOLDS_Y_A: "Das Maximum von %1$d %2$s entnommen, es sind jetzt noch %3$d %2$s im Shop." +THERE_IS_NOTHING_TO_WITHDRAW: "Nichts zum entnehmen vorhanden." +DEPOSITED_X_A: "%1$d %2$s eingezahlt." +YOU_CANT_DEPOSIT_THAT_HERE: "Du kannst dies hier nicht einzahlen!" +YOU_CAN_BUY_Y_A_FOR_X_B: "Du kannst %1$d %2$s für %3$d %4$s kaufen." +YOU_CAN_BUY_X_A_FOR_Y_B_UP_TO_Z: "Du kannst %1$d %2$s für %3$d %4$s kaufen, maximal %5$d." +YOU_CAN_SELL_X_A_FOR_Y_B: "Du kannst %1$d %2$s für %3$d %4$s verkaufen." +YOU_CAN_SELL_X_A_FOR_Y_B_UP_TO_Z: "Du kannst %1$d %2$s für %3$d %4$s verkaufen, maximal %5$d %4$s." +THIS_IS_AN_INFINITE_SHOP: "Dies ist ein unbegrenzter Shop" +YOU_ARE_NOT_ALLOWED_TO_BUY: "Du hast kein Recht in Shops zu kaufen!" +YOU_ARE_NO_ALLOWED_TO_SELL: "Du hast kein Recht in Shops zu verkaufen!" +YOU_CANT_SELL_THAT: "Dies kannst du hier nicht verkaufen!" +YOU_CANT_BUY_HERE: "Du kannst hier nicht kaufen!" +YOU_NEED_TO_SPEND_AT_LEAST_X_A_TO_GET_ANY_B: "Du musst mindestens %1$d %2$s einzahlen, um %3$s zu bekommen." +CANT_BUY_SHOP_HAS_NO_A_LEFT: "Du kannst nichts mehr kaufen, da dieser Shop keine %1$s mehr hat." +CANNOT_BUY_SHOP_ONLY_HAS_X_A: "Du kannst nichts mehr kaufen, da dieser Shop nur noch %1$d %2$s hat." +YOU_NEED_TO_SELL_AT_LEAST_X_A_TO_GET_ANY_B: "Du musst mindestens %1$d %2$s verkaufen, um %3$s zu bekommen." +CANNOT_SELL_SHOP_ONLY_HAS_X_A: "Du kannst nichts mehr verkaufen, da dieser Shop nur noch %1$d %2$s hat." +YOU_SOLD_X_A_FOR_Y_B: "Du hast %1$d %2$s für %3$d %4$s verkauft." +YOU_BOUGHT_X_A_FOR_Y_B: "Du hast %1$d %2$s für %3$d %4$s gekauft." +THIS_SHOP_ALWAYS_RETURNS_TOO_MUCH: "Dieser Shop gibt mehr aus als die Kiste beinhaltet, kontaktiere bitte den Shop-Besitzer." +THIS_SHOP_WOULD_RETURN_TOO_MANY_BUY_LESS: "Dieser Shop versucht mehr auszugeben als die Kiste beinhaltet, versuche weniger zu kaufen." +THIS_SHOP_ALWAYS_RETURNS_TOO_MUCH_CURRENCY: "Dieser Shop gibt mehr %1$s aus als die Kiste beinhaltet, kontaktiere bitte den Shop-Besitzer." +THIS_SHOP_WOULD_RETURN_TOO_MUCH_CURRENCY_SELL_LESS: "Dieser Shop versucht mehr %1$s auszugeben als die Kiste beinhaltet, versuche weniger zu verkaufen." + +# TradeCraftRepairShop.java +IT_COSTS_X_A_TO_REPAIR_AN_ITEM: "Es kostet %1$d %2$s um den Gegenstand zu reparieren." +WITH_THIS_MUCH_A_YOU_CAN_REPAIR_Y_ITEMS: "With this much %1$s you can repair %2$d items." +THAT_IS_NOT_ENOUGH_A: "That's not enough %1$s." +YOU_NEED_X_A_TO_REPAIR_ALL_THIS: "You need %1$d %2$s to repair all this." +YOU_REPAIRED_X_ITEMS_FOR_Y_B: "You repaired %1$d items for %2$d %3$s." +#Translation by Jobsti. Important: UTF-8 without BOM \ No newline at end of file diff --git a/TradeCraft.jar b/TradeCraft.jar index 26f829edfb7aa909546d721db09440e7f4effdaa..4677835035a34d94ea12f6754fa47e057b6f141a 100644 GIT binary patch delta 17009 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 diff --git a/nl/armeagle/TradeCraft/TradeCraftLocalization.java b/nl/armeagle/TradeCraft/TradeCraftLocalization.java index 168a101..052ac0d 100644 --- a/nl/armeagle/TradeCraft/TradeCraftLocalization.java +++ b/nl/armeagle/TradeCraft/TradeCraftLocalization.java @@ -30,7 +30,10 @@ public TradeCraftLocalization(TradeCraft plugin) { String defaultFilename = String.format(TradeCraftLocalization.filePreName, "en"); if (TradeCraftLocalization.localization.getKeys(false).isEmpty()) { plugin.log(Level.INFO, "Language file %s does not exist or is empty, defaulting to %s", filename, defaultFilename); + } else { + return; } + TradeCraftLocalization.localization = plugin.getConfig(defaultFilename); try { TradeCraftLocalization.localization.load(true); @@ -41,7 +44,6 @@ public TradeCraftLocalization(TradeCraft plugin) { if (TradeCraftLocalization.localization.getKeys(false).isEmpty()) { plugin.log(Level.SEVERE, "Default language file %s is also empty", defaultFilename); } - } public static String get(String key) { diff --git a/plugin.yml b/plugin.yml index 539fa5b..3003c15 100644 --- a/plugin.yml +++ b/plugin.yml @@ -1,5 +1,5 @@ name: TradeCraft -version: AE-1.2.4 +version: AE-1.2.5 description: Setup shops with a combination of chest and sign. author: ArmEagle main: nl.armeagle.TradeCraft.TradeCraft From 211476fd0a53a780b6e0d7556751003150dc2ae9 Mon Sep 17 00:00:00 2001 From: ArmEagle Date: Sun, 29 Jan 2012 11:52:01 +0100 Subject: [PATCH 077/100] Prevent NPE when an item is being looked up in the items config and does not exist in there. --- .../TradeCraft/TradeCraftConfigurationFile.java | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/nl/armeagle/TradeCraft/TradeCraftConfigurationFile.java b/nl/armeagle/TradeCraft/TradeCraftConfigurationFile.java index 4ec1749..b9af98b 100644 --- a/nl/armeagle/TradeCraft/TradeCraftConfigurationFile.java +++ b/nl/armeagle/TradeCraft/TradeCraftConfigurationFile.java @@ -173,11 +173,16 @@ public TradeCraftConfigurationInfo get(String name) { // though, the otherwise resulting ==: classpath lines aren't very user friendly anyway. // return (TradeCraftConfigurationInfo) plugin.getConfig().get(name.toUpperCase()); String itemName = this.mapItemNames.get(name.toLowerCase()); - if (null == itemName) { - return null; - } else { - return new TradeCraftConfigurationInfo(((MemorySection)this.getConfig().get(itemName)).getValues(false), name); + if (null != itemName) { + MemorySection item = ((MemorySection)this.getConfig().get(itemName)); + if (null != item) { + Map itemData = item.getValues(false); + if (null != itemData) { + return new TradeCraftConfigurationInfo(itemData, name); + } + } } + return null; } public TradeCraftConfigurationInfo get(int id) { return this.get(new TradeCraftItem(id)); From b48435dfef0aae64e1cb2c05d0bd7ef21890a5bf Mon Sep 17 00:00:00 2001 From: ArmEagle Date: Sun, 29 Jan 2012 15:54:40 +0100 Subject: [PATCH 078/100] Added setting to show shop location when using /tcshops: show-shop-location --- TradeCraft.properties | 4 +++- nl/armeagle/TradeCraft/TradeCraftPropertiesFile.java | 4 ++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/TradeCraft.properties b/TradeCraft.properties index 11dde7d..ca90e01 100644 --- a/TradeCraft.properties +++ b/TradeCraft.properties @@ -18,4 +18,6 @@ auto-update-language-files: true repair-shops-enabled: false repair-cost: 10 #to print debug messages or not -enable-debug-messages: false \ No newline at end of file +enable-debug-messages: false +#show the world and coordinates when using /tcshops +show-shop-location: false \ No newline at end of file diff --git a/nl/armeagle/TradeCraft/TradeCraftPropertiesFile.java b/nl/armeagle/TradeCraft/TradeCraftPropertiesFile.java index 7350034..8ed39e2 100644 --- a/nl/armeagle/TradeCraft/TradeCraftPropertiesFile.java +++ b/nl/armeagle/TradeCraft/TradeCraftPropertiesFile.java @@ -129,6 +129,10 @@ public boolean logShopUse() { return properties.getBoolean("log-shop-use", false); } + public boolean showShopLocation() { + return properties.getBoolean("show-shop-location", false); + } + public ChatColor getMessageTypeColor(TradeCraft.MessageTypes mtype) { switch (mtype) { case WITHDRAW: From f0efa4954c6e9cac212bd4c8e8a764afdb6866cb Mon Sep 17 00:00:00 2001 From: ArmEagle Date: Sun, 29 Jan 2012 15:56:16 +0100 Subject: [PATCH 079/100] Using new event system. Also listening on two priorities now, so we accept and listen other plugins canceling events we're interested in. --- nl/armeagle/TradeCraft/TradeCraft.java | 27 ++-- .../TradeCraft/TradeCraftBlockListener.java | 140 +++++++++++------- .../TradeCraft/TradeCraftDataFile.java | 7 +- .../TradeCraft/TradeCraftPlayerListener.java | 8 +- plugin.yml | 2 +- 5 files changed, 110 insertions(+), 74 deletions(-) diff --git a/nl/armeagle/TradeCraft/TradeCraft.java b/nl/armeagle/TradeCraft/TradeCraft.java index d967eb0..c960114 100644 --- a/nl/armeagle/TradeCraft/TradeCraft.java +++ b/nl/armeagle/TradeCraft/TradeCraft.java @@ -7,6 +7,7 @@ import java.util.Date; import java.util.HashMap; import java.util.IllegalFormatException; +import java.util.Map; import java.util.logging.Level; import java.util.logging.Logger; import java.util.regex.Matcher; @@ -25,8 +26,6 @@ import org.bukkit.command.CommandSender; import org.bukkit.command.ConsoleCommandSender; import org.bukkit.entity.Player; -import org.bukkit.event.Event.Priority; -import org.bukkit.event.Event.Type; import org.bukkit.inventory.ItemStack; import org.bukkit.material.MaterialData; import org.bukkit.plugin.PluginDescriptionFile; @@ -128,9 +127,8 @@ private void enable() { if ( !TradeCraft.hasRegisteredEventListeners ) { PluginManager pm = this.getServer().getPluginManager(); - pm.registerEvent(Type.PLAYER_INTERACT, playerListener, Priority.Normal, this); - pm.registerEvent(Type.SIGN_CHANGE, blockListener,Priority.Normal, this); - pm.registerEvent(Type.BLOCK_BREAK, blockListener,Priority.Normal, this); + pm.registerEvents(playerListener, this); + pm.registerEvents(blockListener, this); TradeCraft.hasRegisteredEventListeners = true; } @@ -272,7 +270,7 @@ public boolean onCommand(CommandSender sender, Command cmd, String label, String } void displayShops(String infoPlayerName, CommandSender displayTo, boolean otherQuery) { - ArrayList list = data.shopsOwned(infoPlayerName); + Map list = data.shopsOwned(infoPlayerName); if (list.size() == 0) { if ( otherQuery ) { // elevated player looking for other player's shops @@ -290,11 +288,18 @@ void displayShops(String infoPlayerName, CommandSender displayTo, boolean otherQ } else { displayTo.sendMessage(TradeCraftLocalization.get("YOUR_SHOPS")); } - for (TradeCraftDataInfo info : list) { - displayTo.sendMessage(TradeCraftLocalization.get("ITEM") +": "+ this.configuration.get(info.itemType).name +"("+ info.itemType.toShortString() +")" - +" "+ TradeCraftLocalization.get("AMOUNT") +": "+ info.itemAmount +" " - + this.getCurrencyName() +": "+ info.currencyAmount); - + for (Map.Entry entry : list.entrySet()) { + TradeCraftDataInfo info = entry.getValue(); + String message = ""; + if (TradeCraft.properties.showShopLocation()) { + String location = entry.getKey().replaceFirst(",", "(") +")"; + message += ChatColor.GRAY + location +" "+ ChatColor.WHITE; + } + message += TradeCraftLocalization.get("ITEM") +": "+ this.configuration.get(info.itemType).name +"("+ info.itemType.toShortString() +")" + +" "+ TradeCraftLocalization.get("AMOUNT") +": "+ info.itemAmount +" " + + this.getCurrencyName() +": "+ info.currencyAmount; + + displayTo.sendMessage(message); } } diff --git a/nl/armeagle/TradeCraft/TradeCraftBlockListener.java b/nl/armeagle/TradeCraft/TradeCraftBlockListener.java index 19d04d4..f1c5095 100644 --- a/nl/armeagle/TradeCraft/TradeCraftBlockListener.java +++ b/nl/armeagle/TradeCraft/TradeCraftBlockListener.java @@ -6,11 +6,12 @@ import org.bukkit.block.Sign; import org.bukkit.entity.Player; import org.bukkit.event.block.BlockBreakEvent; -import org.bukkit.event.block.BlockListener; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.Listener; import org.bukkit.event.block.SignChangeEvent; - -public class TradeCraftBlockListener extends BlockListener{ +public class TradeCraftBlockListener implements Listener{ private TradeCraft plugin; @@ -21,9 +22,18 @@ public class TradeCraftBlockListener extends BlockListener{ public void debug(String str){ plugin.getServer().broadcastMessage(str); } + + @EventHandler + public void onNormalBlockBreak(BlockBreakEvent e) { + this.onBlockBreak(e, EventPriority.NORMAL); + } + + @EventHandler(priority = EventPriority.MONITOR) + public void onMonitorBlockBreak(BlockBreakEvent e) { + this.onBlockBreak(e, EventPriority.MONITOR); + } - @Override - public void onBlockBreak(BlockBreakEvent e) { + private void onBlockBreak(BlockBreakEvent e, EventPriority p) { if ( !this.plugin.isEnabled() ) { return; } @@ -38,49 +48,47 @@ public void onBlockBreak(BlockBreakEvent e) { // Go through all shops in the list and check whether the player can destroy them all first. // Only if that is the case proceed to destroy. - for ( TradeCraftShop shop : shops ) { - if (!shop.playerCanDestroy(player) && !plugin.permissions.canDestroyShops(player) || - shop.shopCanBeWithdrawnFrom() ) { - // cannot destroy this shop, so cancel destruction, use distinct error messages - if ( shop.shopCanBeWithdrawnFrom() ) { - plugin.sendMessage(player, TradeCraftLocalization.get("ALL_ITEMS_MUST_BE_WITHDRAWN")); - } else { - if ( block.getType() == Material.WALL_SIGN ) { - plugin.sendMessage(player, TradeCraftLocalization.get("YOU_CANT_DESTROY_THIS_SIGN")); - } else if ( block.getType() == Material.CHEST ) { - plugin.sendMessage(player, TradeCraftLocalization.get("YOU_CANT_DESTROY_THIS_CHEST")); - } else { - plugin.sendMessage(player, TradeCraftLocalization.get("YOU_CANT_DESTROY_THIS_BLOCK_ATTACHED")); - } - } - stopDestruction(block,e); - return; - } + if (EventPriority.NORMAL == p) { + for ( TradeCraftShop shop : shops ) { + if (!shop.playerCanDestroy(player) && !plugin.permissions.canDestroyShops(player) || + shop.shopCanBeWithdrawnFrom() ) { + // cannot destroy this shop, so cancel destruction, use distinct error messages + if ( shop.shopCanBeWithdrawnFrom() ) { + plugin.sendMessage(player, TradeCraftLocalization.get("ALL_ITEMS_MUST_BE_WITHDRAWN")); + } else { + if ( block.getType() == Material.WALL_SIGN ) { + plugin.sendMessage(player, TradeCraftLocalization.get("YOU_CANT_DESTROY_THIS_SIGN")); + } else if ( block.getType() == Material.CHEST ) { + plugin.sendMessage(player, TradeCraftLocalization.get("YOU_CANT_DESTROY_THIS_CHEST")); + } else { + plugin.sendMessage(player, TradeCraftLocalization.get("YOU_CANT_DESTROY_THIS_BLOCK_ATTACHED")); + } + } + stopDestruction(block,e); + return; + } + } } - // player can destroy all shops, so proceed - for ( TradeCraftShop shop : shops ) { - plugin.data.deleteShop(shop); + + if (EventPriority.MONITOR == p) { + // player can destroy all shops, so proceed + for ( TradeCraftShop shop : shops ) { + plugin.data.deleteShop(shop); + } } - } + } - public void stopDestruction(Block b, BlockBreakEvent e){ - if(b.getState() instanceof Sign){ - Sign sign = (Sign)b.getState(); - String[] lines = sign.getLines(); - e.setCancelled(true); - for(int i = 0;i<4;i++){ - sign.setLine(i, lines[i]); - } - - sign.update(true); - return; - } else { - e.setCancelled(true); - } - + @EventHandler(priority = EventPriority.MONITOR) + public void onMonitorSignChange(SignChangeEvent e) { + this.onSignChange(e, EventPriority.MONITOR); } + + @EventHandler + public void onNormalSignChange(SignChangeEvent e) { + this.onSignChange(e, EventPriority.NORMAL); + } - public void onSignChange(SignChangeEvent e) { + public void onSignChange(SignChangeEvent e, EventPriority p) { if ( !this.plugin.isEnabled() ) { return; } @@ -119,22 +127,29 @@ public void onSignChange(SignChangeEvent e) { plugin.trace(player, "sign change, infinite chest of %s", itemName); return; } - - plugin.sendMessage(player, TradeCraftLocalization.get("YOU_CANT_CREATE_INF_SHOPS")); - e.setCancelled(true); - return; + + if (EventPriority.NORMAL == p) { + plugin.sendMessage(player, TradeCraftLocalization.get("YOU_CANT_CREATE_INF_SHOPS")); + e.setCancelled(true); + return; + } } // there is a buy rate, so this is a player owned shop - if ( !plugin.permissions.canMakePlayerShops(player)){ + + if (EventPriority.NORMAL == p && !plugin.permissions.canMakePlayerShops(player)){ plugin.sendMessage(player, TradeCraftLocalization.get("YOU_DONT_HAVE_PERM_CREATE_PLAYER_SHOP")); e.setCancelled(true); return; } - plugin.trace(player, "Setting owner of sign to: %s", ownerName); - // set the player name on the last line - e.setLine(3, "-"+ ownerName.substring(0, Math.min(ownerName.length(), 15)) +"-"); - plugin.data.createNewSign(ownerName, itemInfo, sign); + + if (EventPriority.MONITOR == p) { + plugin.trace(player, "Setting owner of sign to: %s", ownerName); + // set the player name on the last line + e.setLine(3, "-"+ ownerName.substring(0, Math.min(ownerName.length(), 15)) +"-"); + plugin.data.createNewSign(ownerName, itemInfo, sign); + } + /* if ( this.plugin.properties.getStrictPlayerShopOwnerNameRequired() ) { if (player.getName().equalsIgnoreCase(ownerName)) { @@ -148,7 +163,22 @@ public void onSignChange(SignChangeEvent e) { } } */ - - return; - } + } + + public void stopDestruction(Block b, BlockBreakEvent e){ + if(b.getState() instanceof Sign){ + Sign sign = (Sign)b.getState(); + String[] lines = sign.getLines(); + e.setCancelled(true); + for(int i = 0;i<4;i++){ + sign.setLine(i, lines[i]); + } + + sign.update(true); + return; + } else { + e.setCancelled(true); + } + + } } diff --git a/nl/armeagle/TradeCraft/TradeCraftDataFile.java b/nl/armeagle/TradeCraft/TradeCraftDataFile.java index 7456149..f94e880 100644 --- a/nl/armeagle/TradeCraft/TradeCraftDataFile.java +++ b/nl/armeagle/TradeCraft/TradeCraftDataFile.java @@ -6,7 +6,6 @@ import java.io.FileReader; import java.io.FileWriter; import java.io.IOException; -import java.util.ArrayList; import java.util.HashMap; import java.util.Map; import java.util.regex.Matcher; @@ -176,12 +175,12 @@ public void deleteShop(TradeCraftShop shop){ } } - public ArrayList shopsOwned(String name){ - ArrayList list = new ArrayList(); + public Map shopsOwned(String name){ + Map list = new HashMap(); for (String key : data.keySet()) { TradeCraftDataInfo info = data.get(key); if(info.ownerName.equalsIgnoreCase(name)){ - list.add(info); + list.put(key, info); } } diff --git a/nl/armeagle/TradeCraft/TradeCraftPlayerListener.java b/nl/armeagle/TradeCraft/TradeCraftPlayerListener.java index c76710e..8ba5d7d 100644 --- a/nl/armeagle/TradeCraft/TradeCraftPlayerListener.java +++ b/nl/armeagle/TradeCraft/TradeCraftPlayerListener.java @@ -2,19 +2,21 @@ import org.bukkit.block.Block; import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; import org.bukkit.event.block.Action; import org.bukkit.event.player.PlayerInteractEvent; -import org.bukkit.event.player.PlayerListener; +import org.bukkit.event.Listener; -public class TradeCraftPlayerListener extends PlayerListener{ +public class TradeCraftPlayerListener implements Listener{ private TradeCraft plugin; TradeCraftPlayerListener(TradeCraft plugin){ this.plugin = plugin; } - @Override + + @EventHandler public void onPlayerInteract(PlayerInteractEvent e) { if ( !this.plugin.isEnabled() ) { return; diff --git a/plugin.yml b/plugin.yml index 3003c15..e7a2921 100644 --- a/plugin.yml +++ b/plugin.yml @@ -1,5 +1,5 @@ name: TradeCraft -version: AE-1.2.5 +version: AE-1.3 description: Setup shops with a combination of chest and sign. author: ArmEagle main: nl.armeagle.TradeCraft.TradeCraft From ccd5c238f80368ef28ef9a9d8c9e82589065125a Mon Sep 17 00:00:00 2001 From: ArmEagle Date: Sun, 29 Jan 2012 15:58:00 +0100 Subject: [PATCH 080/100] removing .jar from source --- TradeCraft.jar | Bin 59998 -> 60574 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/TradeCraft.jar b/TradeCraft.jar index 4677835035a34d94ea12f6754fa47e057b6f141a..f777bbf1913eb254f19d8924fe3a0a484e26ae8e 100644 GIT binary patch delta 31064 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 A;s5{u delta 30517 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 From 8dbb873b84ce230ab4d02e88544d596c7ee6ac58 Mon Sep 17 00:00:00 2001 From: ArmEagle Date: Sun, 29 Jan 2012 16:07:49 +0100 Subject: [PATCH 081/100] commit .classpath --- .classpath | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.classpath b/.classpath index f3cb5d5..bf16426 100644 --- a/.classpath +++ b/.classpath @@ -3,6 +3,6 @@ - + From a1154e45343d80678e556d851d60c48f1febc7cc Mon Sep 17 00:00:00 2001 From: ArmEagle Date: Sun, 29 Jan 2012 17:10:53 +0100 Subject: [PATCH 082/100] Loading default config/language files from .jar differently now so on first use there will be no issues. --- nl/armeagle/Configuration/StatefulYamlConfiguration.java | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/nl/armeagle/Configuration/StatefulYamlConfiguration.java b/nl/armeagle/Configuration/StatefulYamlConfiguration.java index 25a2f1d..d716d69 100644 --- a/nl/armeagle/Configuration/StatefulYamlConfiguration.java +++ b/nl/armeagle/Configuration/StatefulYamlConfiguration.java @@ -32,8 +32,12 @@ public void load(boolean loadDefaults) throws IOException { InputStream defaultInput = this.getClass().getResourceAsStream("/" + file.getName()); if ( null != defaultInput ) { YamlConfiguration defaultConfig = YamlConfiguration.loadConfiguration(defaultInput); - this.setDefaults(defaultConfig); - this.options().copyDefaults(true); + + try { + this.loadFromString(defaultConfig.saveToString()); + } catch (InvalidConfigurationException e) { + Logger.getLogger(JavaPlugin.class.getName()).log(Level.SEVERE, "Invalid default configuation"); + } this.save(); } } From 4072ba397879f36a65f6ec2fe9e4ac00cac5a54d Mon Sep 17 00:00:00 2001 From: ArmEagle Date: Sun, 29 Jan 2012 17:11:15 +0100 Subject: [PATCH 083/100] untracking TradeCraft.jar --- TradeCraft.jar | Bin 60574 -> 62416 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/TradeCraft.jar b/TradeCraft.jar index f777bbf1913eb254f19d8924fe3a0a484e26ae8e..18eda55a205ec1874a384b81090407e4356ba4fc 100644 GIT binary patch delta 3763 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 N0~!z3{ew)S{{cgO&4mB} delta 1865 zcmYjRc|4p~8-2}U8AD|jTZBkyh9r|PsGW!`!}zR0sWp~bgN)c`zQi(PO?f3WNLpLf z7K~UDAu<#xV^EDrrwOvy8DptvHT6!u@0Z;B$GPWuo_l`xo<9zE4t|FRM?2WU5ja3{ zf5*|9(Oz&^9E}wQuYy5{5$7C()g*yoa1slzn~jEP`mxH*5fb|dF1T2N#AOsqcy>z` zsxP?gAz&wHpiUAATv?W7X?fIQpgD-!tjMzDBTU2~F32We zAx&f)iKW?&hC^r40}9hOG*8hY41({4*+kH@b3Jug8o+~tH{kkl}fkY(_``%^e}oY{L4j}CVJ-QR7#y5eZaq2T#M-{;jP>?q=ReOC2Q2xmx1eJ3j83_MtH zwtV5RWJU$n3vH$~VQ@=3J;%{QL8pGtIjr6!EOMxI1HG4D!-$kJxtBEFfnGNr@KpRAlP|!;6njvPws;H>7P;Tb2K=)`n z+xAjZzt>y1lw8b!3_n1!fN|8fI27+$d&tt1`Fls*j4b<|yur_ne$jk(igxdPYvbnI zzKWs9FJxYcQLrT&+l@`b{7Dawv?76)>2$oB%M*b8sDy-%w#ABDJ33!{${u> zKi252fJs-kR3XU!^!hfYHZ59aDD`tyE1J7bL#ZQbS_eLH=7R6(7e6%1Iq9Cyb~(ma z`j7JQEA`XZ*)zE83_GL+?~dS=Nxx>F^1uTM^>M@G_s28!2C&KJdMAurb6cFX0&{t~ z3o?$r%(j=--X|WqN1HicoG%e-r*H&@AaKcIWrVql$w%fxqoNhR(z$I zJ1K*5Bz1c5uH&e}Z9u1K#DArfV^TM%(c@d-uzj6aI;}(_r`wCJ@kMnV+oav1Czst0@CIJr>%i0QW$g&;i!(2-_ujn_o@Vi!-9@dP zeWiV?*1chNqH3r|N4N7vZU{Ym(5K(I<)5tX&ZBxfn%zTjLW?<$nX0<=3u7YwTBQP= zY)om+<=Vz04mq~x;XG&%)wSdvPx%i@EQUDv`_%>F?=@@ zSco1E7=q_`)12ksds`Y&R$6M_GIA6Nv!SuH#uvp9X7?EuEQMdsiy_hzjVxIHlV#$F zhRPcjEI5Bd94!XJ0sqf&PyUs)KG(*qeKQc;=}-;2?+pF zpXdR=VQ^KbDDo6UZ79RSCc&sl9B90O7P$-B?AOqI@Bkkt?_i4*hpwvZ@05KJ=~oc@ E7XcVF^Z)<= From 0d4a986b6f626320a45551f3825ee1173d4b5d4d Mon Sep 17 00:00:00 2001 From: ArmEagle Date: Sun, 29 Jan 2012 17:17:04 +0100 Subject: [PATCH 084/100] Added Swedish translation, thanks to cayeen. --- TradeCraft.sv.lang | 71 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 71 insertions(+) create mode 100644 TradeCraft.sv.lang diff --git a/TradeCraft.sv.lang b/TradeCraft.sv.lang new file mode 100644 index 0000000..717d4f6 --- /dev/null +++ b/TradeCraft.sv.lang @@ -0,0 +1,71 @@ +#Swedish translation thanks to cayeen (on dev.bukkit.org) +# TradeCraft.java +ERROR_IN_FORMAT_STRING: "There was an error in the format string:" +NO_PERMISSION_SET_CURRENCY: "Du har inte tillstånd att välja valutan!" +IS_NO_VALID_CURRENCY_USE_INSTEAD: "%1$s är inte ett okej val av föremål, använd 'id[;data]' or 'itemname'." +INVALID_CURRENCY: "Felaktigt valuta val: %1$s" +CURRENCY_IS_SET_TO_A_IDDATA: "Valutan är satt till: %1$s (%2$s)" +CURRENCY_IS_A_IDDATA: "Valutan är: %1$s (%2$s)" +NO_SUCH_PLAYER: "Det finns inte en sådan spelare." +YOU_DONT_OWN_ANY_SHOPS: "Du äger inga affärer!" +A_DOES_NOT_OWN_ANY_SHOPS: "%1$s Äger inga affärer." +YOUR_SHOPS: "Dina affärer:" +SHOPS_OF_A: "Affärer som ägs av %1$s:" +ITEM: "Föremål" +AMOUNT: "Mängd" +POSSIBLE_COMMANDS_FOR_THE_PLUGIN: "Tillgängliga kommandon för %1$s pluginet:" +TC_HELP_THIS_TEXT: "this help text" +TC_SHOPS: "listar alla affärer" +TC_PSHOPS: "listar alla affärer som spelaren äger" +TC_CURRENCY_OPT_PARAM_GETSET_CURRENCY: "få och välj valutan" +TC_CURRENCY_GET_CURRENCY: "få valutan ni använder" +TC_CAN_PLAYER: "visar tillåtelser som spelaren har" +TC_RELOAD: "startar om och med omstartningen laddas alla filer också om" +RESTARTING_PLUGIN: Startar om %1$s" +RESTARTING_PLUGIN_DONE: "Omstartningen av %1$s är färdig" + +# TradeCraftBlockListener.java +ALL_ITEMS_MUST_BE_WITHDRAWN: "Alla föremål och valutan måste ha tagits ut från affären innan du kan förstöra den!" +YOU_CANT_DESTROY_THIS_SIGN: "Du kan inte förstöra den skylten!" +YOU_CANT_DESTROY_THIS_CHEST: "Du kan inte förstöra den kistan!" +YOU_CANT_DESTROY_THIS_BLOCK_ATTACHED: "Du kan inte förstöra det blocket för det sitten en skylt på den!" +YOU_CANT_CREATE_INF_SHOPS: "Du kan inte skapa statliga affärer!" +YOU_DONT_HAVE_PERM_CREATE_PLAYER_SHOP: "Du har inte tillstånd att skapa en affär!" + +# TradeCraftItemShop.java +THE_CHEST_HAS_MORE_THAN_ONE_TYPE: "Den här kistan har mer än en sorts föremål i sig!" +WITHDREW_X_A: "Tog ut %1$d %2$s." +WITHDREW_X_CURRENCY_SHOP_STILL_HOLDS_Y_CURRENCY: "Tog ut det maximala antalet %1$d %2$s, det finns fortfarande %3$d %2$s i affären." +WITHDREW_X_A_SHOP_STILL_HOLDS_Y_A: "Tog ut det maximala antalet %1$d %2$s, det finns fortfarande %3$d %2$s i affären." +THERE_IS_NOTHING_TO_WITHDRAW: "Det finns inget att ta ut." +DEPOSITED_X_A: "Fyllde på med %1$d %2$s." +YOU_CANT_DEPOSIT_THAT_HERE: "Du kan inte fylla på det här!" +YOU_CAN_BUY_Y_A_FOR_X_B: "Du kan köpa %1$d %2$s för %3$d %4$s." +YOU_CAN_BUY_X_A_FOR_Y_B_UP_TO_Z: "Du kan köpa %1$d %2$s för %3$d %4$s, och upp till %5$d." +YOU_CAN_SELL_X_A_FOR_Y_B: "Du kan sälja %1$d %2$s för %3$d %4$s." +YOU_CAN_SELL_X_A_FOR_Y_B_UP_TO_Z: "Du kan sälja %1$d %2$s för %3$d %4$s, och upp till %5$d %4$s." +THIS_IS_AN_INFINITE_SHOP: "Detta är en oändlig butik" +YOU_ARE_NOT_ALLOWED_TO_BUY: "Du är inte tillåten att handla från affärer!" +YOU_ARE_NO_ALLOWED_TO_SELL: "Du är inte tillåten att sälja till affärer!" +YOU_CANT_SELL_THAT: "Du kan inte sälja det här!" +YOU_CANT_BUY_HERE: "Du kan inte köpa här!" +YOU_NEED_TO_SPEND_AT_LEAST_X_A_TO_GET_ANY_B: "Du måste spendera minst %1$d %2$s för att få några %3$s." +CANT_BUY_SHOP_HAS_NO_A_LEFT: "Du kan inte köpa det, affären har inge %1$s kvar." +CANNOT_BUY_SHOP_ONLY_HAS_X_A: "Kan inte köpa så många, affären har bara %1$d %2$s." +YOU_NEED_TO_SELL_AT_LEAST_X_A_TO_GET_ANY_B: "Du måste sälja minst %1$d %2$s för att få några %3$s." +CANNOT_SELL_SHOP_ONLY_HAS_X_A: "Du kan inte sälja affären har bara %1$d %2$s." +YOU_SOLD_X_A_FOR_Y_B: "Du sålde %1$d %2$s för %3$d %4$s." +SOLD_X_A_FOR_Y_B: "sålt %1$d %2$s för %3$d %4$s." +YOU_BOUGHT_X_A_FOR_Y_B: "Du köpte %1$d %2$s för %3$d %4$s." +BOUGHT_X_A_FOR_Y_B: "Köpt %1$d %2$s för %3$d %4$s." +THIS_SHOP_ALWAYS_RETURNS_TOO_MUCH: "Den här affären ger för mycket items varje gång, Kontakta en admin." +THIS_SHOP_WOULD_RETURN_TOO_MANY_BUY_LESS: "Du ger affären för mycket pengar testa handla med mindre." +THIS_SHOP_ALWAYS_RETURNS_TOO_MUCH_CURRENCY: "Den här affären ger alltid mer %1$s än vad den kan innehålla, Kontakta en admin." +THIS_SHOP_WOULD_RETURN_TOO_MUCH_CURRENCY_SELL_LESS: "Den här affären lämnar alltid ifrånsig mer %1$s än vad kistan kan innehålla, testa sälja lite mindre mängd med föremål." + +# TradeCraftRepairShop.java +IT_COSTS_X_A_TO_REPAIR_AN_ITEM: "It costs %1$d %2$s to repair an item." +WITH_THIS_MUCH_A_YOU_CAN_REPAIR_Y_ITEMS: "With this much %1$s you can repair %2$d items." +THAT_IS_NOT_ENOUGH_A: "That's not enough %1$s." +YOU_NEED_X_A_TO_REPAIR_ALL_THIS: "You need %1$d %2$s to repair all this." +YOU_REPAIRED_X_ITEMS_FOR_Y_B: "You repaired %1$d items for %2$d %3$s." From d8c87db33d01e6b40df17645bc706df51485e577 Mon Sep 17 00:00:00 2001 From: ArmEagle Date: Tue, 31 Jan 2012 21:22:37 +0100 Subject: [PATCH 085/100] Revert "Loading default config/language files from .jar differently now so on first use there will be no issues." This reverts commit a1154e45343d80678e556d851d60c48f1febc7cc. --- nl/armeagle/Configuration/StatefulYamlConfiguration.java | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/nl/armeagle/Configuration/StatefulYamlConfiguration.java b/nl/armeagle/Configuration/StatefulYamlConfiguration.java index d716d69..25a2f1d 100644 --- a/nl/armeagle/Configuration/StatefulYamlConfiguration.java +++ b/nl/armeagle/Configuration/StatefulYamlConfiguration.java @@ -32,12 +32,8 @@ public void load(boolean loadDefaults) throws IOException { InputStream defaultInput = this.getClass().getResourceAsStream("/" + file.getName()); if ( null != defaultInput ) { YamlConfiguration defaultConfig = YamlConfiguration.loadConfiguration(defaultInput); - - try { - this.loadFromString(defaultConfig.saveToString()); - } catch (InvalidConfigurationException e) { - Logger.getLogger(JavaPlugin.class.getName()).log(Level.SEVERE, "Invalid default configuation"); - } + this.setDefaults(defaultConfig); + this.options().copyDefaults(true); this.save(); } } From c55b7ddcdc550f2af1d958567d94f477412648e9 Mon Sep 17 00:00:00 2001 From: ArmEagle Date: Tue, 31 Jan 2012 22:14:20 +0100 Subject: [PATCH 086/100] Configuration fixes. --- TradeCraft.jar | Bin 62416 -> 62970 bytes .../StatefulYamlConfiguration.java | 39 +++++++++++++++--- .../TradeCraftConfigurationFile.java | 2 +- .../TradeCraft/TradeCraftLocalization.java | 4 +- plugin.yml | 2 +- 5 files changed, 38 insertions(+), 9 deletions(-) diff --git a/TradeCraft.jar b/TradeCraft.jar index 18eda55a205ec1874a384b81090407e4356ba4fc..764a8e4f3495f93e7074adc0bfc3daa76572f22d 100644 GIT binary patch delta 10045 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 Date: Mon, 6 Feb 2012 01:36:29 +0100 Subject: [PATCH 087/100] code rearranged --- .../StatefulYamlConfiguration.java | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/nl/armeagle/Configuration/StatefulYamlConfiguration.java b/nl/armeagle/Configuration/StatefulYamlConfiguration.java index eddb958..cc72966 100644 --- a/nl/armeagle/Configuration/StatefulYamlConfiguration.java +++ b/nl/armeagle/Configuration/StatefulYamlConfiguration.java @@ -47,13 +47,7 @@ public void load(boolean forceDefaults) throws IOException { } } - try { - this.loadFromString(baseConfig.saveToString()); - } catch (InvalidConfigurationException e) { - Logger.getLogger(JavaPlugin.class.getName()).log(Level.SEVERE, "Invalid configuation"); - } - - if (notLoaded || forceDefaults) { + if (notLoaded || forceDefaults) { InputStream defaultInput = this.getClass().getResourceAsStream("/" + file.getName()); if ( null != defaultInput ) { YamlConfiguration defaultConfig = YamlConfiguration.loadConfiguration(defaultInput); @@ -65,7 +59,14 @@ public void load(boolean forceDefaults) throws IOException { } this.save(); } - } + } else if (! notLoaded) { + try { + this.loadFromString(baseConfig.saveToString()); + } catch (InvalidConfigurationException e) { + Logger.getLogger(JavaPlugin.class.getName()).log(Level.SEVERE, "Invalid configuation"); + } + } + } public void save() throws IOException { From bc9553ea9ebaa73245334d6e1a974dfea443ee34 Mon Sep 17 00:00:00 2001 From: ArmEagle Date: Mon, 6 Feb 2012 01:43:45 +0100 Subject: [PATCH 088/100] actually checking for canceled state in monitor priority --- nl/armeagle/TradeCraft/TradeCraftBlockListener.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/nl/armeagle/TradeCraft/TradeCraftBlockListener.java b/nl/armeagle/TradeCraft/TradeCraftBlockListener.java index f1c5095..89fbdfa 100644 --- a/nl/armeagle/TradeCraft/TradeCraftBlockListener.java +++ b/nl/armeagle/TradeCraft/TradeCraftBlockListener.java @@ -70,7 +70,7 @@ private void onBlockBreak(BlockBreakEvent e, EventPriority p) { } } - if (EventPriority.MONITOR == p) { + if (EventPriority.MONITOR == p && ! e.isCancelled()) { // player can destroy all shops, so proceed for ( TradeCraftShop shop : shops ) { plugin.data.deleteShop(shop); @@ -143,7 +143,7 @@ public void onSignChange(SignChangeEvent e, EventPriority p) { } - if (EventPriority.MONITOR == p) { + if (EventPriority.MONITOR == p && !e.isCancelled()) { plugin.trace(player, "Setting owner of sign to: %s", ownerName); // set the player name on the last line e.setLine(3, "-"+ ownerName.substring(0, Math.min(ownerName.length(), 15)) +"-"); From d09a9ad0599b91abc9142c69d05a734ffa4f9a17 Mon Sep 17 00:00:00 2001 From: ArmEagle Date: Mon, 6 Feb 2012 01:45:03 +0100 Subject: [PATCH 089/100] version 1.3.2 for fixes --- plugin.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugin.yml b/plugin.yml index 9cf55c4..a1b0a3f 100644 --- a/plugin.yml +++ b/plugin.yml @@ -1,5 +1,5 @@ name: TradeCraft -version: AE-1.3.1 +version: AE-1.3.2 description: Setup shops with a combination of chest and sign. author: ArmEagle main: nl.armeagle.TradeCraft.TradeCraft From a1b5ab24e3c0f33214b2bab71c89c8c00cef80f7 Mon Sep 17 00:00:00 2001 From: ArmEagle Date: Sat, 3 Mar 2012 11:42:04 +0100 Subject: [PATCH 090/100] trying to untrack TradeCraft.jar --- TradeCraft.jar | Bin 62970 -> 63008 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/TradeCraft.jar b/TradeCraft.jar index 764a8e4f3495f93e7074adc0bfc3daa76572f22d..a459c7a80a04239494b2829e4a2eac3812fc8dc9 100644 GIT binary patch delta 5881 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 delta 5815 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 W>+t4lMZn<>{N|bi$bJ0Zss96$3-%EJ From c667e409b1d8f7d89bcbd360cc67f4dbc26a199a Mon Sep 17 00:00:00 2001 From: ArmEagle Date: Sat, 3 Mar 2012 12:09:43 +0100 Subject: [PATCH 091/100] Applied changes made by davidsiaw --- README.txt | 84 +++++++++++++++++++++++++++++++----------------------- 1 file changed, 49 insertions(+), 35 deletions(-) diff --git a/README.txt b/README.txt index b4e4125..03191a8 100644 --- a/README.txt +++ b/README.txt @@ -1,8 +1,7 @@ TradeCraft ========== -This is a plugin for Bukkit that allows you to buy and sell items using gold -ingots (bars) as a currency. +This is a plugin for Bukkit that allows you to buy and sell items using any currency. See also: http://forums.bukkit.org/threads/econ-tradecraft-ae-gold-based-economy.13797/ @@ -23,6 +22,8 @@ example file that comes with the plugin to see the properties you can set. Shops ===== +A manual with pictures here: http://dev.bukkit.org/server-mods/tradecraft-ae/pages/manual/ + Players buy and sell items at "shops". To create a shop, place a chest (single, not double) next to a wall. Place a @@ -62,14 +63,12 @@ Or, it could just say this: The item type has to be on a line all by itself and there can't be any spaces outside or inside the square brackets. Case isn't important. The item types are -defined by you in the TradeCraft.txt file. +defined by you in the TradeCraft.properties file. No state is maintained in infinite shops. Players can buy an infinite amount of items (assuming they have enough gold) from infinite shops. They can also sell an infinite amount of items (earning an infinite amount of gold). -TODO: Allow administrators to disable infinite shops using a command and -storing that setting in TradeCraft.properties. Player-owned shops ================== @@ -84,8 +83,8 @@ order to keep it in operation. The format for the text on a player-owned shop must look something like this: [Sand] -Buy for 32:1 -Sell for 48:1 +Buy 32 for 1 +Sell 48 for 1 -injektilo- The first line is the type of item bought and sold at that shop. This has to @@ -156,12 +155,43 @@ Configuration ============= To configure what can be traded and for how much (at infinite shops), you need -to edit TradeCraft.txt. The file should look like this: - -# Comments look like this. -Sand,12,32:1 -Diamond,264,1:64 -LGWool,35;8,10:1 +to edit items.yml. The file should look like this: + +Cobblestone: + itemTypeId: 4 + itemTypeData: 0 + buyAmount: 64 + buyValue: 1 + sellAmount: 64 + sellValue: 1 +Gravel: + itemTypeId: 13 + itemTypeData: 0 + buyAmount: 32 + buyValue: 1 + sellAmount: 32 + sellValue: 4 +Sand: + itemTypeId: 12 + itemTypeData: 0 + buyAmount: 32 + buyValue: 1 + sellAmount: 32 + sellValue: 1 +Dirt: + itemTypeId: 3 + itemTypeData: 0 + buyAmount: 32 + buyValue: 1 + sellAmount: 32 + sellValue: 1 +Coal: + itemTypeId: 263 + itemTypeData: 0 + buyAmount: 3 + buyValue: 1 + sellAmount: 4 + sellValue: 1 The first value is the name of the item as you want it to appear on your signs and in the messages the players see when they make their trades. @@ -172,33 +202,17 @@ values here: http://www.minecraftwiki.net/wiki/Data_values -The third value is the exchange rate. The number before the colon is how many -of that item needs to be sold to earn the number of gold specified as the -number after the colon. In the above example, players have to sell 32 sand -blocks to get a single gold. They have to pay 64 gold to buy a single diamond. - -You don't have to use the number 1 on either side of the colon. For example, -you could use a ratio like 3:2. That means that selling 3 items will get you 2 -gold. Selling 6 items will get you 4 gold. Likewise, spending 2 gold will get -you 3 items and spending 4 gold will get you 6 items. +The values in buyAmount and buyValue is the amount of that item and the +amount of currency respectively when the player is buying. Likewise, the +values in sellAmount is the exchange rate when the player is selling. +In the above example, players have to sell 4 Coal to get 1 currency. +They have to pay 1 currency to buy a 3 Coal. It's possible to configure separate exchange rates for buying and selling. If only a single ratio is specified, that ratio is used for both buying and selling. If two ratios are specified, the first is for buying and the second is for selling. -For example, to let players buy 32 sand for 1 gold, but only be able to sell 64 -sand for 1 gold, you would configure it like this: - -Sand,12,32:1,64:1 - -If you use a ratio of 0:0, that disables buying or selling of that item type. - -For example, to allow players to buy diamonds, but not sell them, you would -configure it like this: - -Diamond,264,1:64,0:0 - -If you click on a sign above an empty chest, you'll see a message saying what +If you right click on a sign above an empty chest, you'll see a message saying what the exchange rates for both buying and selling are. On player shops you will also get information about the amount of items and 'money' still in the shop. From a0ce9fda18ebe2a19ca7404bdd40ef181035b339 Mon Sep 17 00:00:00 2001 From: ArmEagle Date: Sun, 4 Mar 2012 11:29:11 +0100 Subject: [PATCH 092/100] version 1.4: - Now blocks piston events if it would move a sign or the block behind it. - Two new settings "player-world-shop-limit" (default 5) and "player-total-shop-limit" (default 10) that limit the amount of shops any player can have in a world and in total. For this two translation lines were added: WORLD_SHOP_LIMIT_X and TOTAL_SHOP_LIMIT_X. These aren't automatically taken over by the existing language files! --- .classpath | 2 +- TradeCraft.en.lang | 2 + TradeCraft.jar | Bin 63008 -> 64185 bytes TradeCraft.properties | 6 +- nl/armeagle/TradeCraft/TradeCraft.java | 23 +++-- .../TradeCraft/TradeCraftBlockListener.java | 78 ++++++++++++---- .../TradeCraft/TradeCraftDataFile.java | 84 ++++++++++++------ .../TradeCraft/TradeCraftDataInfo.java | 9 ++ .../TradeCraft/TradeCraftPropertiesFile.java | 7 ++ plugin.yml | 2 +- 10 files changed, 154 insertions(+), 59 deletions(-) diff --git a/.classpath b/.classpath index bf16426..131551b 100644 --- a/.classpath +++ b/.classpath @@ -3,6 +3,6 @@ - + diff --git a/TradeCraft.en.lang b/TradeCraft.en.lang index 02eb5aa..466047b 100644 --- a/TradeCraft.en.lang +++ b/TradeCraft.en.lang @@ -30,6 +30,8 @@ YOU_CANT_DESTROY_THIS_CHEST: "You can't destroy this chest!" YOU_CANT_DESTROY_THIS_BLOCK_ATTACHED: "You can't destroy this block because there are signs attached to it!" YOU_CANT_CREATE_INF_SHOPS: "You can't create infinite shops!" YOU_DONT_HAVE_PERM_CREATE_PLAYER_SHOP: "You do not have the permission to create a player shop!" +TOTAL_SHOP_LIMIT_X: "You already have the maximum amount of shops (%1$d)!" +WORLD_SHOP_LIMIT_X: "You already have the maximum amount of shops in this world (%1$d)!" # TradeCraftItemShop.java THE_CHEST_HAS_MORE_THAN_ONE_TYPE: "The chest has more than one type of item in it!" diff --git a/TradeCraft.jar b/TradeCraft.jar index a459c7a80a04239494b2829e4a2eac3812fc8dc9..7f521b2afaf263e2a0e5b1099b8a2d395e41a283 100644 GIT binary patch delta 30701 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 delta 29425 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 getShopsFromBlock(Player player, Block block) { } TradeCraftShop getShopFromSignOrChestBlock(Player player, Block block) { if (block.getType() == Material.CHEST) { - block = player.getWorld().getBlockAt(block.getX(), - block.getY() + 1, block.getZ()); + block = block.getWorld().getBlockAt( + block.getX(), + block.getY() + 1, + block.getZ() + ); } return getShopFromSignBlock(player, block); @@ -389,9 +396,9 @@ TradeCraftShop getShopFromSignBlock(Player player, Block block) { int y = block.getY(); int z = block.getZ(); - trace(player, "You clicked a sign at %d, %d, %d in world: %s.", x, y, z, player.getWorld().getName()); + trace(player, "You clicked a sign at %d, %d, %d in world: %s.", x, y, z, block.getWorld().getName()); - Sign sign = (Sign) player.getWorld().getBlockAt(x, y, z).getState(); + Sign sign = (Sign) block.getWorld().getBlockAt(x, y, z).getState(); // The sign at this location can be null if it was just destroyed. if (sign == null) { @@ -410,14 +417,14 @@ TradeCraftShop getShopFromSignBlock(Player player, Block block) { trace(player, "The item name on the sign is %s.", itemName); - Block blockBelowSign = player.getWorld().getBlockAt(x, y - 1, z); + Block blockBelowSign = block.getRelative(0, -1, 0); if (blockBelowSign.getType() != Material.CHEST) { trace(player, "There is no chest beneath the sign."); return null; } - Chest chest = (Chest) player.getWorld().getBlockAt(x, y - 1, z).getState(); + Chest chest = (Chest) blockBelowSign.getState(); if (itemName.toLowerCase().equals("repair")) { if (!properties.getRepairShopsEnabled()) { @@ -425,7 +432,7 @@ TradeCraftShop getShopFromSignBlock(Player player, Block block) { return null; } - if (!player.isOp()) { + if (player == null || !player.isOp()) { trace(player, "You can't use repair shops."); return null; } diff --git a/nl/armeagle/TradeCraft/TradeCraftBlockListener.java b/nl/armeagle/TradeCraft/TradeCraftBlockListener.java index 89fbdfa..9075fe3 100644 --- a/nl/armeagle/TradeCraft/TradeCraftBlockListener.java +++ b/nl/armeagle/TradeCraft/TradeCraftBlockListener.java @@ -1,11 +1,15 @@ package nl.armeagle.TradeCraft; import java.util.ArrayList; +import java.util.List; + import org.bukkit.Material; import org.bukkit.block.Block; import org.bukkit.block.Sign; import org.bukkit.entity.Player; import org.bukkit.event.block.BlockBreakEvent; +import org.bukkit.event.block.BlockPistonExtendEvent; +import org.bukkit.event.block.BlockPistonRetractEvent; import org.bukkit.event.EventHandler; import org.bukkit.event.EventPriority; import org.bukkit.event.Listener; @@ -88,24 +92,24 @@ public void onNormalSignChange(SignChangeEvent e) { this.onSignChange(e, EventPriority.NORMAL); } - public void onSignChange(SignChangeEvent e, EventPriority p) { + public void onSignChange(SignChangeEvent event, EventPriority priority) { if ( !this.plugin.isEnabled() ) { return; } // Check whether this block is (still) a sign. Can be revoked already in case the sign was temporary, // for example when the plugin SimpleSignEdit is being used. - if ( e.getBlock().getType() != Material.SIGN_POST && - e.getBlock().getType() != Material.WALL_SIGN ) { + if ( event.getBlock().getType() != Material.SIGN_POST && + event.getBlock().getType() != Material.WALL_SIGN ) { return; } - Sign sign = (Sign) e.getBlock().getState(); + Sign sign = (Sign) event.getBlock().getState(); - Player player = e.getPlayer(); + Player player = event.getPlayer(); String ownerName = player.getName(); - String itemName = plugin.getItemName(e.getLines()); + String itemName = plugin.getItemName(event.getLines()); if (itemName == null) { plugin.trace(player, "sign change, no item name, ignore"); @@ -119,8 +123,8 @@ public void onSignChange(SignChangeEvent e, EventPriority p) { return; } - TradeCraftExchangeRate buyRate = new TradeCraftExchangeRate(e.getLine(1)); - TradeCraftExchangeRate sellRate = new TradeCraftExchangeRate(e.getLine(2)); + TradeCraftExchangeRate buyRate = new TradeCraftExchangeRate(event.getLine(1)); + TradeCraftExchangeRate sellRate = new TradeCraftExchangeRate(event.getLine(2)); // no buy rate means this is an infinite shop if ( !buyRate.isValid() && !sellRate.isValid() ) { if (plugin.permissions.canMakeInfShops(player)){ @@ -128,25 +132,40 @@ public void onSignChange(SignChangeEvent e, EventPriority p) { return; } - if (EventPriority.NORMAL == p) { + if (EventPriority.NORMAL == priority) { plugin.sendMessage(player, TradeCraftLocalization.get("YOU_CANT_CREATE_INF_SHOPS")); - e.setCancelled(true); + event.setCancelled(true); return; } } // there is a buy rate, so this is a player owned shop - if (EventPriority.NORMAL == p && !plugin.permissions.canMakePlayerShops(player)){ + if (EventPriority.NORMAL == priority && !plugin.permissions.canMakePlayerShops(player)){ plugin.sendMessage(player, TradeCraftLocalization.get("YOU_DONT_HAVE_PERM_CREATE_PLAYER_SHOP")); - e.setCancelled(true); + event.setCancelled(true); return; } + // check whether the player doesn't have too many shops + if (EventPriority.NORMAL == priority) { + int totalShopLimit = TradeCraft.properties.getPlayerTotalShopLimit(); + int worldShopLimit = TradeCraft.properties.getPlayerWorldShopLimit(); + if (plugin.data.getPlayerShopCount(player) >= totalShopLimit) { + plugin.sendMessage(player, TradeCraftLocalization.get("TOTAL_SHOP_LIMIT_X"), totalShopLimit); + event.setCancelled(true); + return; + } else if (plugin.data.getPlayerShopCount(player, player.getWorld()) >= worldShopLimit) { + plugin.sendMessage(player, TradeCraftLocalization.get("WORLD_SHOP_LIMIT_X"), worldShopLimit); + event.setCancelled(true); + return; + } - if (EventPriority.MONITOR == p && !e.isCancelled()) { + } + + if (EventPriority.MONITOR == priority && !event.isCancelled()) { plugin.trace(player, "Setting owner of sign to: %s", ownerName); // set the player name on the last line - e.setLine(3, "-"+ ownerName.substring(0, Math.min(ownerName.length(), 15)) +"-"); + event.setLine(3, "-"+ ownerName.substring(0, Math.min(ownerName.length(), 15)) +"-"); plugin.data.createNewSign(ownerName, itemInfo, sign); } @@ -165,20 +184,41 @@ public void onSignChange(SignChangeEvent e, EventPriority p) { */ } + // prevent pistons from pushing away (the block behind) a sign. Block all retract and extend events that would move a block behind a sign, also for shop owners + @EventHandler + public void onNormalBlockPistonRetract(BlockPistonRetractEvent e) { + if (e.isSticky()) { + Block block = e.getRetractLocation().getBlock(); + ArrayList shops = plugin.getShopsFromBlock(null, block); + + if (shops.size() != 0) { + e.setCancelled(true); + } + } + } + @EventHandler + public void onNormalBlockPistonExtend(BlockPistonExtendEvent e) { + List blocks = e.getBlocks(); + for (Block block:blocks) { + ArrayList shops = plugin.getShopsFromBlock(null, block); + + if (shops.size() != 0) { + e.setCancelled(true); + return; + } + } + } + public void stopDestruction(Block b, BlockBreakEvent e){ if(b.getState() instanceof Sign){ Sign sign = (Sign)b.getState(); String[] lines = sign.getLines(); - e.setCancelled(true); for(int i = 0;i<4;i++){ sign.setLine(i, lines[i]); } sign.update(true); - return; - } else { - e.setCancelled(true); } - + e.setCancelled(true); } } diff --git a/nl/armeagle/TradeCraft/TradeCraftDataFile.java b/nl/armeagle/TradeCraft/TradeCraftDataFile.java index f94e880..2e4cb06 100644 --- a/nl/armeagle/TradeCraft/TradeCraftDataFile.java +++ b/nl/armeagle/TradeCraft/TradeCraftDataFile.java @@ -12,7 +12,9 @@ import java.util.regex.Pattern; import org.bukkit.Location; +import org.bukkit.World; import org.bukkit.block.Sign; +import org.bukkit.entity.Player; class TradeCraftDataFile { /* @@ -102,6 +104,7 @@ public void load() { TradeCraftDataInfo info = new TradeCraftDataInfo(); info.ownerName = ownerName; + info.worldName = worldName; info.itemAmount = itemAmount; info.currencyAmount = currencyAmount; @@ -175,11 +178,11 @@ public void deleteShop(TradeCraftShop shop){ } } - public Map shopsOwned(String name){ + public Map shopsOwned(String playerName){ Map list = new HashMap(); for (String key : data.keySet()) { TradeCraftDataInfo info = data.get(key); - if(info.ownerName.equalsIgnoreCase(name)){ + if(info.ownerName.equalsIgnoreCase(playerName)){ list.put(key, info); } } @@ -219,34 +222,39 @@ public int getCurrencyAmount(Sign sign) { } public void depositItems(String ownerName, Sign sign, TradeCraftItem itemType, int itemAmount) { + TradeCraftDataInfo info; String key = getKeyFromSign(sign); + if (data.containsKey(key)) { - TradeCraftDataInfo info = data.get(key); - info.ownerName = ownerName; // For old entries that don't have the name. - info.itemType = itemType; // For old entries that don't have the type. - info.itemAmount += itemAmount; + info = data.get(key); } else { - TradeCraftDataInfo info = new TradeCraftDataInfo(); - info.ownerName = ownerName; - info.itemType = itemType; - info.itemAmount = itemAmount; - data.put(key, info); + info = new TradeCraftDataInfo(); } + + info.ownerName = ownerName; + info.worldName = sign.getWorld().getName(); + info.itemType = itemType; + info.itemAmount += itemAmount; + data.put(key, info); + save(); } public void depositCurrency(String ownerName, Sign sign, int currencyAmount) { + TradeCraftDataInfo info; String key = getKeyFromSign(sign); + if (data.containsKey(key)) { - TradeCraftDataInfo info = data.get(key); - info.ownerName = ownerName; // For old entries that don't have the name. - info.currencyAmount += currencyAmount; + info = data.get(key); } else { - TradeCraftDataInfo info = new TradeCraftDataInfo(); - info.ownerName = ownerName; - info.currencyAmount = currencyAmount; - data.put(key, info); + info = new TradeCraftDataInfo(); } + + info.ownerName = ownerName; + info.worldName = sign.getWorld().getName(); + info.currencyAmount += currencyAmount; + data.put(key, info); + save(); } @@ -314,21 +322,39 @@ private String getKey(String world, int x, int y, int z) { } public void createNewSign(String ownerName, TradeCraftConfigurationInfo itemInfo, Sign sign) { + TradeCraftDataInfo info; String key = getKeyFromSign(sign); + if (data.containsKey(key)) { - TradeCraftDataInfo info = data.get(key); - info.ownerName = ownerName; - info.currencyAmount = 0; - info.itemAmount = 0; - info.itemType = itemInfo.type; + info = data.get(key); } else { - TradeCraftDataInfo info = new TradeCraftDataInfo(); - info.ownerName = ownerName; - info.currencyAmount = 0; - info.itemAmount = 0; - info.itemType = itemInfo.type; - data.put(key, info); + info = new TradeCraftDataInfo(); } + + info.ownerName = ownerName; + info.worldName = sign.getWorld().getName(); + info.currencyAmount = 0; + info.itemAmount = 0; + info.itemType = itemInfo.type; + data.put(key, info); + save(); } + + public int getPlayerShopCount(Player player, World world) { + Map list = this.shopsOwned(player.getName()); + int shopsOwnedCount = 0; + + for (String key : list.keySet()) { + TradeCraftDataInfo info = data.get(key); + if (info.worldName.equalsIgnoreCase(world.getName())) { + shopsOwnedCount++; + } + } + + return shopsOwnedCount; + } + public int getPlayerShopCount(Player player) { + return this.shopsOwned(player.getName()).size(); + } } \ No newline at end of file diff --git a/nl/armeagle/TradeCraft/TradeCraftDataInfo.java b/nl/armeagle/TradeCraft/TradeCraftDataInfo.java index d710ca4..18e50ea 100644 --- a/nl/armeagle/TradeCraft/TradeCraftDataInfo.java +++ b/nl/armeagle/TradeCraft/TradeCraftDataInfo.java @@ -2,7 +2,16 @@ class TradeCraftDataInfo { public String ownerName; + public String worldName; public TradeCraftItem itemType; public int itemAmount; public int currencyAmount; + + public TradeCraftDataInfo() { + this.ownerName = null; + this.worldName = null; + this.itemType = null; + this.itemAmount = 0; + this.currencyAmount = 0; + } } \ No newline at end of file diff --git a/nl/armeagle/TradeCraft/TradeCraftPropertiesFile.java b/nl/armeagle/TradeCraft/TradeCraftPropertiesFile.java index 8ed39e2..de95254 100644 --- a/nl/armeagle/TradeCraft/TradeCraftPropertiesFile.java +++ b/nl/armeagle/TradeCraft/TradeCraftPropertiesFile.java @@ -133,6 +133,13 @@ public boolean showShopLocation() { return properties.getBoolean("show-shop-location", false); } + public int getPlayerWorldShopLimit() { + return properties.getInt("player-world-shop-limit", 5); + } + public int getPlayerTotalShopLimit() { + return properties.getInt("player-total-shop-limit", 10); + } + public ChatColor getMessageTypeColor(TradeCraft.MessageTypes mtype) { switch (mtype) { case WITHDRAW: diff --git a/plugin.yml b/plugin.yml index a1b0a3f..2c5f311 100644 --- a/plugin.yml +++ b/plugin.yml @@ -1,5 +1,5 @@ name: TradeCraft -version: AE-1.3.2 +version: AE-1.4 description: Setup shops with a combination of chest and sign. author: ArmEagle main: nl.armeagle.TradeCraft.TradeCraft From 6adf7012541e7ad9b04063e12781866746abf3b9 Mon Sep 17 00:00:00 2001 From: armeagle Date: Sat, 11 Aug 2012 16:07:25 +0200 Subject: [PATCH 093/100] 1.4.1 - Moved all files into a source subdirectory - Removed support for old Permissions plugin, now only supporting Bukkit's built-in system. - Cleanup up tabs for whitespaces --- build.xml | 74 ++ manifest.mf | 3 + nbproject/build-impl.xml | 1042 +++++++++++++++++ nbproject/genfiles.properties | 8 + nbproject/project.properties | 75 ++ nbproject/project.xml | 13 + .../StatefulYamlConfiguration.java | 79 -- nl/armeagle/TradeCraft/TradeCraft.java | 656 ----------- .../TradeCraft/TradeCraftBlockListener.java | 224 ---- .../TradeCraftConfigurationFile.java | 199 ---- .../TradeCraftConfigurationInfo.java | 55 - nl/armeagle/TradeCraft/TradeCraftItem.java | 63 - .../TradeCraft/TradeCraftPermissions.java | 121 -- .../TradeCraft/TradeCraftPropertiesFile.java | 153 --- TradeCraft.en.lang => src/TradeCraft.en.lang | 0 TradeCraft.ge.lang => src/TradeCraft.ge.lang | 0 .../TradeCraft.properties | 0 TradeCraft.sv.lang => src/TradeCraft.sv.lang | 0 items.yml => src/items.yml | 0 .../StatefulYamlConfiguration.java | 79 ++ src/nl/armeagle/TradeCraft/TradeCraft.java | 652 +++++++++++ .../TradeCraft/TradeCraftBlockListener.java | 224 ++++ .../armeagle/TradeCraft/TradeCraftChest.java | 26 +- .../TradeCraftConfigurationFile.java | 199 ++++ .../TradeCraftConfigurationInfo.java | 55 + .../TradeCraft/TradeCraftDataFile.java | 146 +-- .../TradeCraft/TradeCraftDataInfo.java | 12 +- .../TradeCraft/TradeCraftExchangeRate.java | 18 +- .../TradeCraft/TradeCraftInfiniteShop.java | 4 +- .../armeagle/TradeCraft/TradeCraftItem.java | 63 + .../TradeCraft/TradeCraftItemShop.java | 294 ++--- .../TradeCraft/TradeCraftLocalization.java | 28 +- .../TradeCraft/TradeCraftPermissions.java | 68 ++ .../TradeCraft/TradeCraftPlayerListener.java | 52 +- .../TradeCraft/TradeCraftPlayerOwnedShop.java | 10 +- .../TradeCraft/TradeCraftPropertiesFile.java | 153 +++ .../TradeCraft/TradeCraftRepairShop.java | 22 +- .../armeagle/TradeCraft/TradeCraftShop.java | 10 +- plugin.yml => src/plugin.yml | 2 +- 39 files changed, 3020 insertions(+), 1862 deletions(-) create mode 100644 build.xml create mode 100644 manifest.mf create mode 100644 nbproject/build-impl.xml create mode 100644 nbproject/genfiles.properties create mode 100644 nbproject/project.properties create mode 100644 nbproject/project.xml delete mode 100644 nl/armeagle/Configuration/StatefulYamlConfiguration.java delete mode 100644 nl/armeagle/TradeCraft/TradeCraft.java delete mode 100644 nl/armeagle/TradeCraft/TradeCraftBlockListener.java delete mode 100644 nl/armeagle/TradeCraft/TradeCraftConfigurationFile.java delete mode 100644 nl/armeagle/TradeCraft/TradeCraftConfigurationInfo.java delete mode 100644 nl/armeagle/TradeCraft/TradeCraftItem.java delete mode 100644 nl/armeagle/TradeCraft/TradeCraftPermissions.java delete mode 100644 nl/armeagle/TradeCraft/TradeCraftPropertiesFile.java rename TradeCraft.en.lang => src/TradeCraft.en.lang (100%) rename TradeCraft.ge.lang => src/TradeCraft.ge.lang (100%) rename TradeCraft.properties => src/TradeCraft.properties (100%) rename TradeCraft.sv.lang => src/TradeCraft.sv.lang (100%) rename items.yml => src/items.yml (100%) create mode 100644 src/nl/armeagle/Configuration/StatefulYamlConfiguration.java create mode 100644 src/nl/armeagle/TradeCraft/TradeCraft.java create mode 100644 src/nl/armeagle/TradeCraft/TradeCraftBlockListener.java rename {nl => src/nl}/armeagle/TradeCraft/TradeCraftChest.java (75%) create mode 100644 src/nl/armeagle/TradeCraft/TradeCraftConfigurationFile.java create mode 100644 src/nl/armeagle/TradeCraft/TradeCraftConfigurationInfo.java rename {nl => src/nl}/armeagle/TradeCraft/TradeCraftDataFile.java (73%) rename {nl => src/nl}/armeagle/TradeCraft/TradeCraftDataInfo.java (61%) rename {nl => src/nl}/armeagle/TradeCraft/TradeCraftExchangeRate.java (52%) rename {nl => src/nl}/armeagle/TradeCraft/TradeCraftInfiniteShop.java (93%) create mode 100644 src/nl/armeagle/TradeCraft/TradeCraftItem.java rename {nl => src/nl}/armeagle/TradeCraft/TradeCraftItemShop.java (50%) rename {nl => src/nl}/armeagle/TradeCraft/TradeCraftLocalization.java (54%) create mode 100644 src/nl/armeagle/TradeCraft/TradeCraftPermissions.java rename {nl => src/nl}/armeagle/TradeCraft/TradeCraftPlayerListener.java (54%) rename {nl => src/nl}/armeagle/TradeCraft/TradeCraftPlayerOwnedShop.java (94%) create mode 100644 src/nl/armeagle/TradeCraft/TradeCraftPropertiesFile.java rename {nl => src/nl}/armeagle/TradeCraft/TradeCraftRepairShop.java (75%) rename {nl => src/nl}/armeagle/TradeCraft/TradeCraftShop.java (72%) rename plugin.yml => src/plugin.yml (99%) diff --git a/build.xml b/build.xml new file mode 100644 index 0000000..72a1205 --- /dev/null +++ b/build.xml @@ -0,0 +1,74 @@ + + + + + + + + + + + Builds, tests, and runs the project TradeCraft. + + + diff --git a/manifest.mf b/manifest.mf new file mode 100644 index 0000000..1574df4 --- /dev/null +++ b/manifest.mf @@ -0,0 +1,3 @@ +Manifest-Version: 1.0 +X-COMMENT: Main-Class will be added automatically by build + diff --git a/nbproject/build-impl.xml b/nbproject/build-impl.xml new file mode 100644 index 0000000..dcfb2dc --- /dev/null +++ b/nbproject/build-impl.xml @@ -0,0 +1,1042 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Must set src.src.dir + Must set build.dir + Must set dist.dir + Must set build.classes.dir + Must set dist.javadoc.dir + Must set build.test.classes.dir + Must set build.test.results.dir + Must set build.classes.excludes + Must set dist.jar + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Must set javac.includes + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Must set JVM to use for profiling in profiler.info.jvm + Must set profiler agent JVM arguments in profiler.info.jvmargs.agent + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Must select some files in the IDE or set javac.includes + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + To run this application from the command line without Ant, try: + + + + + + + java -cp "${run.classpath.with.dist.jar}" ${main.class} + + + + + + + + + + + + + + + + + + + + + + + + + To run this application from the command line without Ant, try: + + java -jar "${dist.jar.resolved}" + + + + + + + + + + + + + + + + + + + + + + + + + Must select one file in the IDE or set run.class + + + + Must select one file in the IDE or set run.class + + + + + + + + + + + + + + + + + + + + + + + Must select one file in the IDE or set debug.class + + + + + Must select one file in the IDE or set debug.class + + + + + Must set fix.includes + + + + + + + + + + + + + + + + + Must select one file in the IDE or set profile.class + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Must select some files in the IDE or set javac.includes + + + + + + + + + + + + + + + + + + Some tests failed; see details above. + + + + + + + + + Must select some files in the IDE or set test.includes + + + + Some tests failed; see details above. + + + + + Must select one file in the IDE or set test.class + + + + + + + + + + + + + + + + + + + + + + + + + + + Must select one file in the IDE or set applet.url + + + + + + + + + Must select one file in the IDE or set applet.url + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/nbproject/genfiles.properties b/nbproject/genfiles.properties new file mode 100644 index 0000000..928644b --- /dev/null +++ b/nbproject/genfiles.properties @@ -0,0 +1,8 @@ +build.xml.data.CRC32=ced48bff +build.xml.script.CRC32=3d45e660 +build.xml.stylesheet.CRC32=28e38971@1.50.3.46 +# This file is used by a NetBeans-based IDE to track changes in generated files such as build-impl.xml. +# Do not edit this file. You may delete it but then the IDE will never regenerate such files for you. +nbproject/build-impl.xml.data.CRC32=ced48bff +nbproject/build-impl.xml.script.CRC32=5c3459d4 +nbproject/build-impl.xml.stylesheet.CRC32=fcddb364@1.50.3.46 diff --git a/nbproject/project.properties b/nbproject/project.properties new file mode 100644 index 0000000..1c3c059 --- /dev/null +++ b/nbproject/project.properties @@ -0,0 +1,75 @@ +annotation.processing.enabled=true +annotation.processing.enabled.in.editor=false +annotation.processing.processors.list= +annotation.processing.run.all.processors=true +annotation.processing.source.output=${build.generated.sources.dir}/ap-source-output +application.title=TradeCraft +application.vendor=armeagle +build.classes.dir=${build.dir}/classes +build.classes.excludes=**/*.java,**/*.form +# This directory is removed when the project is cleaned: +build.dir=build +build.generated.dir=${build.dir}/generated +build.generated.sources.dir=${build.dir}/generated-sources +# Only compile against the classpath explicitly listed here: +build.sysclasspath=ignore +build.test.classes.dir=${build.dir}/test/classes +build.test.results.dir=${build.dir}/test/results +# Uncomment to specify the preferred debugger connection transport: +#debug.transport=dt_socket +debug.classpath=\ + ${run.classpath} +debug.test.classpath=\ + ${run.test.classpath} +# This directory is removed when the project is cleaned: +dist.dir=dist +dist.jar=${dist.dir}/TradeCraft.jar +dist.javadoc.dir=${dist.dir}/javadoc +endorsed.classpath= +excludes= +file.reference.bukkit-1.3.1-R1.0.jar=C:\\Games\\Minecraft\\Bukkit\\2317-1521\\bukkit-1.3.1-R1.0.jar +file.reference.NetBeansProjects-TradeCraft=. +includes=** +jar.compress=false +javac.classpath=\ + ${file.reference.bukkit-1.3.1-R1.0.jar} +# Space-separated list of extra javac options +javac.compilerargs= +javac.deprecation=false +javac.processorpath=\ + ${javac.classpath} +javac.source=1.7 +javac.target=1.7 +javac.test.classpath=\ + ${javac.classpath}:\ + ${build.classes.dir} +javac.test.processorpath=\ + ${javac.test.classpath} +javadoc.additionalparam= +javadoc.author=false +javadoc.encoding=${source.encoding} +javadoc.noindex=false +javadoc.nonavbar=false +javadoc.notree=false +javadoc.private=false +javadoc.splitindex=true +javadoc.use=true +javadoc.version=false +javadoc.windowtitle= +main.class= +manifest.file=manifest.mf +meta.inf.dir=${src.dir}/META-INF +mkdist.disabled=false +platform.active=default_platform +run.classpath=\ + ${javac.classpath}:\ + ${build.classes.dir} +# Space-separated list of JVM arguments used when running the project +# (you may also define separate properties like run-sys-prop.name=value instead of -Dname=value +# or test-sys-prop.name=value to set system properties for unit tests): +run.jvmargs= +run.test.classpath=\ + ${javac.test.classpath}:\ + ${build.test.classes.dir} +source.encoding=UTF-8 +src.src.dir=src diff --git a/nbproject/project.xml b/nbproject/project.xml new file mode 100644 index 0000000..8c0b8ff --- /dev/null +++ b/nbproject/project.xml @@ -0,0 +1,13 @@ + + + org.netbeans.modules.java.j2seproject + + + TradeCraft + + + + + + + diff --git a/nl/armeagle/Configuration/StatefulYamlConfiguration.java b/nl/armeagle/Configuration/StatefulYamlConfiguration.java deleted file mode 100644 index cc72966..0000000 --- a/nl/armeagle/Configuration/StatefulYamlConfiguration.java +++ /dev/null @@ -1,79 +0,0 @@ -package nl.armeagle.Configuration; - -import java.io.File; -import java.io.FileNotFoundException; -import java.io.IOException; -import java.io.InputStream; -import java.util.logging.Level; -import java.util.logging.Logger; - -import org.bukkit.Bukkit; -import org.bukkit.configuration.InvalidConfigurationException; -import org.bukkit.configuration.file.YamlConfiguration; -import org.bukkit.plugin.java.JavaPlugin; -import org.yaml.snakeyaml.error.YAMLException; - -public class StatefulYamlConfiguration extends YamlConfiguration { - protected File file; - - public StatefulYamlConfiguration(File file) { - this.file = file; - } - - public void load() throws IOException { - this.load(false); - } - public void load(boolean forceDefaults) throws IOException { - boolean notLoaded = false; - if (this.file == null) { - throw new IllegalArgumentException("File cannot be null"); - } - - YamlConfiguration baseConfig = new YamlConfiguration(); - - try { - baseConfig.load(this.file); - } catch (FileNotFoundException fnf) { - notLoaded = true; - } catch (IOException ex) { - Bukkit.getLogger().log(Level.SEVERE, "Cannot load " + this.file, ex); - } catch (InvalidConfigurationException ex) { - if (ex.getCause() instanceof YAMLException) { - Bukkit.getLogger().severe("Config file " + this.file + " isn't valid! " + ex.getCause()); - } else if ((ex.getCause() == null) || (ex.getCause() instanceof ClassCastException)) { - Bukkit.getLogger().severe("Config file " + this.file + " isn't valid!"); - } else { - Bukkit.getLogger().log(Level.SEVERE, "Cannot load " + this.file + ": " + ex.getCause().getClass(), ex); - } - } - - if (notLoaded || forceDefaults) { - InputStream defaultInput = this.getClass().getResourceAsStream("/" + file.getName()); - if ( null != defaultInput ) { - YamlConfiguration defaultConfig = YamlConfiguration.loadConfiguration(defaultInput); - - try { - this.loadFromString(defaultConfig.saveToString()); - } catch (InvalidConfigurationException e) { - Logger.getLogger(JavaPlugin.class.getName()).log(Level.SEVERE, "Invalid default configuation"); - } - this.save(); - } - } else if (! notLoaded) { - try { - this.loadFromString(baseConfig.saveToString()); - } catch (InvalidConfigurationException e) { - Logger.getLogger(JavaPlugin.class.getName()).log(Level.SEVERE, "Invalid configuation"); - } - } - - } - - public void save() throws IOException { - super.save(this.file); - } - - public File getFile() { - return this.file; - } -} diff --git a/nl/armeagle/TradeCraft/TradeCraft.java b/nl/armeagle/TradeCraft/TradeCraft.java deleted file mode 100644 index 8f00413..0000000 --- a/nl/armeagle/TradeCraft/TradeCraft.java +++ /dev/null @@ -1,656 +0,0 @@ -package nl.armeagle.TradeCraft; - -import java.io.*; - -import java.text.SimpleDateFormat; -import java.util.ArrayList; -import java.util.Date; -import java.util.HashMap; -import java.util.IllegalFormatException; -import java.util.Map; -import java.util.logging.Level; -import java.util.logging.Logger; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -import nl.armeagle.Configuration.StatefulYamlConfiguration; - -import org.bukkit.ChatColor; -import org.bukkit.Material; -import org.bukkit.Server; -import org.bukkit.block.Block; -import org.bukkit.block.BlockFace; -import org.bukkit.block.Chest; -import org.bukkit.block.Sign; -import org.bukkit.command.Command; -import org.bukkit.command.CommandSender; -import org.bukkit.command.ConsoleCommandSender; -import org.bukkit.entity.Player; -import org.bukkit.inventory.ItemStack; -import org.bukkit.material.MaterialData; -import org.bukkit.plugin.PluginDescriptionFile; -import org.bukkit.plugin.PluginManager; -import org.bukkit.plugin.java.JavaPlugin; - -import com.nijikokun.bukkit.Permissions.Permissions; - -public class TradeCraft extends JavaPlugin { - public static enum MessageTypes {WITHDRAW, DEPOSIT}; - - // The plugin name. - static final String pluginName = "TradeCraft"; - - public static final Pattern itemPatternIdSplitData = Pattern.compile("^(\\d+)(?:;(\\d+))?$"); - - private static enum Commands { - tcsetcurrency, - tcgetcurrency, - tcshops, - tcpshops, - tcreload, - tcplayerperms, - tchelp, - tc - }; - - // Stuff used to interact with the server. - final Logger log = Logger.getLogger("Minecraft"); - final Server server = this.getServer(); - - protected BufferedWriter usageLog = null; - // Objects used by the plugin. - static TradeCraftItem currency; - static TradeCraftPropertiesFile properties; - TradeCraftConfigurationFile configuration; - public TradeCraftLocalization localization; - TradeCraftDataFile data; - - private HashMap configs = new HashMap(); - - private final TradeCraftBlockListener blockListener = new TradeCraftBlockListener(this); - private final TradeCraftPlayerListener playerListener = new TradeCraftPlayerListener(this); - public TradeCraftPermissions permissions = new TradeCraftPermissions(this); - public Permissions permissionsPlugin = null; - public boolean permEnabled = false; - - // prevent the script from registering the event listeners multiple times (by dis-/enable) - public static boolean hasRegisteredEventListeners = false; - - public void onDisable() { - this.disable(); - } - private void disable() { - if ( this.usageLog != null ) { - try { - this.usageLog.close(); - } catch (IOException e) { - this.log(Level.WARNING, "Failed to close shop usage log file"); - } - } - properties = null; - configuration = null; - this.localization = null; - data.save(); - data = null; - } - - public void onEnable() { - this.enable(); - } - private void enable() { - properties = new TradeCraftPropertiesFile(this); - configuration = new TradeCraftConfigurationFile(this); - data = new TradeCraftDataFile(this); - this.localization = new TradeCraftLocalization(this); - - if ( TradeCraft.properties.logShopUse() ) { - File usageLogFile = new File(this.getDataFolder(), "shopUsage.log"); - try { - if ( !usageLogFile.exists() ) { - usageLogFile.createNewFile(); - } - if ( usageLogFile.canWrite() ) { - this.usageLog = new BufferedWriter(new FileWriter(usageLogFile, true)); - this.log(Level.INFO, "Writing shop usage to log file: "+ usageLogFile.toString()); - } else { - this.log(Level.WARNING, "Error opening shop usage log file: "+ usageLogFile.toString()); - } - } catch (IOException e) { - this.log(Level.WARNING, "Failed to open shop usage log file: "+ usageLogFile.toString()); - } - } - - configuration.load(); - data.load(); - currency = properties.getCurrencyType(); - permissions.setupPermissions(); - - if ( !TradeCraft.hasRegisteredEventListeners ) { - PluginManager pm = this.getServer().getPluginManager(); - pm.registerEvents(playerListener, this); - pm.registerEvents(blockListener, this); - TradeCraft.hasRegisteredEventListeners = true; - } - - PluginDescriptionFile pdfFile = this.getDescription(); - this.log.info(pdfFile.getName() + " version " + pdfFile.getVersion() + " is enabled!"); - - } - - @Override - public boolean onCommand(CommandSender sender, Command cmd, String label, String[] args) { - try { - TradeCraft.Commands command = TradeCraft.Commands.valueOf(cmd.getName()); - if (sender instanceof Player) { - Player p = (Player) sender; - switch (command) { - case tcsetcurrency: - if ( args.length == 1 && this.permissions.canSetCurrency(p) ) { - TradeCraftItem testCurrency = null; - // try to split ID and Data, separated by a semicolon mark - Matcher IdSplitData = TradeCraft.itemPatternIdSplitData.matcher(args[0]); - - if ( !IdSplitData.matches() ) { - // try to match the parameter to item names from the configuration - TradeCraftConfigurationInfo setCurr = this.configuration.get(args[0]); - if ( setCurr == null ) { - this.sendMessage(p, TradeCraftLocalization.get("IS_NO_VALID_CURRENCY_USE_INSTEAD"), - args[0]); - return false; - } else { - currency = setCurr.type; - } - } else { - try { - int cid = Integer.parseInt(IdSplitData.group(1)); - if ( IdSplitData.group(2) != null ) { - testCurrency = new TradeCraftItem(cid, Short.parseShort(IdSplitData.group(2))); - } else { - testCurrency = new TradeCraftItem(cid); - } - } catch ( NumberFormatException e ) { - this.sendMessage(p, TradeCraftLocalization.get("INVALID_CURRENCY"), - args[0]); - return false; - } - if ( this.configuration.get(testCurrency) != null ) { - currency = testCurrency; - } else { - this.sendMessage(p, TradeCraftLocalization.get("INVALID_CURRENCY"), - args[0]); - return false; - } - } - - TradeCraft.properties.setCurrencyType(currency); - this.sendMessage(p, TradeCraftLocalization.get("CURRENCY_IS_SET_TO_A_IDDATA"), - this.getCurrencyName(), - currency.toShortString()); - return true; - } - return true; - case tcgetcurrency: - this.sendMessage(p, TradeCraftLocalization.get("CURRENCY_IS_A_IDDATA"), - this.getCurrencyName(), - currency.toShortString()); - return true; - case tcshops: - // lookup own shows - displayShops(p.getName(), p, false); - return true; - case tcpshops: - // Check whether another parameter is passed, if so check whether - // the player can get information about other player's shops. - if ( this.permissions.canQueryOtherShops(p) ) { - if ( args.length == 1 ) { - // lookup other player's shops - displayShops(args[0], p, true); - } else { - this.sendMessage(p, cmd.getUsage()); - } - } - return true; - case tcreload: - if ( this.permissions.canReload(p)) { - this.sendMessage(p, TradeCraftLocalization.get("RESTARTING_PLUGIN"), TradeCraft.pluginName); - this.disable(); - this.enable(); - this.sendMessage(p, TradeCraftLocalization.get("RESTARTING_PLUGIN_DONE"), TradeCraft.pluginName); - } - return true; - case tcplayerperms: - if ( this.permissions.canQueryPlayer(p)) { - if ( args.length == 1 ) { - permissions.debug(sender, args[0]); - } else { - sender.sendMessage(cmd.getUsage()); - } - } - return true; - default: - displayCommandHelpText(p); - return true; - } - } else if ( sender instanceof ConsoleCommandSender ) { - switch (command) { - case tcplayerperms: - if ( args.length == 1 ) { - permissions.debug(sender, args[0]); - } else { - sender.sendMessage(cmd.getUsage()); - } - return true; - case tcpshops: - // Check whether another parameter is passed, if so check whether - // the player can get information about other player's shops. - if ( args.length == 1 ) { - // lookup other player's shops - displayShops(args[0], sender, true); - } else { - sender.sendMessage(cmd.getUsage()); - } - return true; - case tcreload: - sender.sendMessage(String.format(TradeCraftLocalization.get("RESTARTING_PLUGIN"), - TradeCraft.pluginName)); - this.disable(); - this.enable(); - sender.sendMessage(String.format(TradeCraftLocalization.get("RESTARTING_PLUGIN_DONE"), - TradeCraft.pluginName)); - return true; - default: - displayCommandHelpText(null); - return true; - } - } - } catch (IllegalArgumentException e) { - return false; - } - return false; - } - - void displayShops(String infoPlayerName, CommandSender displayTo, boolean otherQuery) { - Map list = data.shopsOwned(infoPlayerName); - if (list.size() == 0) { - if ( otherQuery ) { - // elevated player looking for other player's shops - displayTo.sendMessage(String.format(TradeCraftLocalization.get("A_DOES_NOT_OWN_ANY_SHOPS"), - infoPlayerName)); - } else { - displayTo.sendMessage(TradeCraftLocalization.get("YOU_DONT_OWN_ANY_SHOPS")); - } - return; - } - - if ( otherQuery ) { - displayTo.sendMessage(String.format(TradeCraftLocalization.get("SHOPS_OF_A"), - infoPlayerName)); - } else { - displayTo.sendMessage(TradeCraftLocalization.get("YOUR_SHOPS")); - } - for (Map.Entry entry : list.entrySet()) { - TradeCraftDataInfo info = entry.getValue(); - String message = ""; - if (TradeCraft.properties.showShopLocation()) { - String location = entry.getKey().replaceFirst(",", "(") +")"; - message += ChatColor.GRAY + location +" "+ ChatColor.WHITE; - } - message += TradeCraftLocalization.get("ITEM") +": "+ this.configuration.get(info.itemType).name +"("+ info.itemType.toShortString() +")" - +" "+ TradeCraftLocalization.get("AMOUNT") +": "+ info.itemAmount +" " - + this.getCurrencyName() +": "+ info.currencyAmount; - - displayTo.sendMessage(message); - } - - } - - void sendMessage(Player player, TradeCraft.MessageTypes messageType, String format, Object... args) { - try { - String message = String.format(format, args); - player.sendMessage(TradeCraft.properties.getMessageTypeColor(messageType) + message); - } catch ( IllegalFormatException e ) { - player.sendMessage(TradeCraftLocalization.get("ERROR_IN_FORMAT_STRING") +" "+ format); - } - } - - void sendMessage(Player player, String format, Object... args) { - try { - String message = String.format(format, args); - player.sendMessage(message); - } catch ( IllegalFormatException e ) { - player.sendMessage(TradeCraftLocalization.get("ERROR_IN_FORMAT_STRING") +" "+ format); - } - } - - void trace(Player player, String format, Object... args) { - if (properties.getEnableDebugMessages()) { - if (null != player) { - sendMessage(player, format, args); - } else { - this.log(Level.INFO, format, args); - } - } - } - /* - * When a block behind a shop sign is destroyed, the sign would be destroyed too. - * Check all side faces of this block, for a sign attached to this block. Then - * pass that sign block to getShopFromSignBlock. - * - * This should only be used for checking whether a normal block can be destroyed, for there not being any - * signs attached to it, or this block being a chest or sign itself. - * Since one block can - */ - ArrayList getShopsFromBlock(Player player, Block block) { - ArrayList shops = new ArrayList(); - // use other function(s) directly if applicable - if ( block.getType() == Material.CHEST || block.getType() == Material.WALL_SIGN ) { - TradeCraftShop shop = getShopFromSignOrChestBlock(player, block); - if ( shop != null ) { - shops.add(shop); - } - } else { - // go through all 4 faces of this block to check for a sign attached to this block - final BlockFace[] sides = {BlockFace.NORTH, BlockFace.EAST, BlockFace.SOUTH, BlockFace.WEST}; - for ( int index_sides = 0; index_sides < sides.length; index_sides++ ) { - BlockFace side = sides[index_sides]; - // get the block on that side - Block sideBlock = block.getRelative(side); - // check for it being a wall sign - if ( sideBlock.getType() == Material.WALL_SIGN ) { - // get the sign (extending MaterialData) for clean attached face checking - org.bukkit.material.Sign materialSign = new org.bukkit.material.Sign(Material.WALL_SIGN, sideBlock.getData()); - /* Now, for easy comparison, we'll compare to the direction the sign is facing. If that's - * the same as the current face (of the 4 we're looping through), then this sign is attached - * to the block that is being destroyed. - */ - if ( materialSign.getFacing() == side ) { - TradeCraftShop shop = this.getShopFromSignBlock(player, sideBlock); - if ( shop != null ) { - shops.add(shop); - } - } - - } - } - } - return shops; - } - TradeCraftShop getShopFromSignOrChestBlock(Player player, Block block) { - if (block.getType() == Material.CHEST) { - block = block.getWorld().getBlockAt( - block.getX(), - block.getY() + 1, - block.getZ() - ); - } - - return getShopFromSignBlock(player, block); - } - - TradeCraftShop getShopFromSignBlock(Player player, Block block) { - if (block.getType() != Material.WALL_SIGN) { - return null; - } - - int x = block.getX(); - int y = block.getY(); - int z = block.getZ(); - - trace(player, "You clicked a sign at %d, %d, %d in world: %s.", x, y, z, block.getWorld().getName()); - - Sign sign = (Sign) block.getWorld().getBlockAt(x, y, z).getState(); - - // The sign at this location can be null if it was just destroyed. - if (sign == null) { - trace(player, "The sign is no longer there."); - - return null; - } - - String itemName = getItemName(sign.getLines()); - - if (itemName == null) { - trace(player, "There is no item name on the sign."); - - return null; - } - - trace(player, "The item name on the sign is %s.", itemName); - - Block blockBelowSign = block.getRelative(0, -1, 0); - - if (blockBelowSign.getType() != Material.CHEST) { - trace(player, "There is no chest beneath the sign."); - return null; - } - - Chest chest = (Chest) blockBelowSign.getState(); - - if (itemName.toLowerCase().equals("repair")) { - if (!properties.getRepairShopsEnabled()) { - trace(player, "Repair shops are not enabled."); - return null; - } - - if (player == null || !player.isOp()) { - trace(player, "You can't use repair shops."); - return null; - } - - trace(player, "This is a repair shop."); - return new TradeCraftRepairShop(this, sign, chest); - } - - if (!configuration.isConfigured(itemName)) { - trace(player, - "The item name %s is not configured in the config file.", - itemName); - return null; - } - - // TODO change to use chest getOwner - //String ownerName = getOwnerName(sign.getLine(3)); - String ownerName = data.getOwnerOfSign(sign); - - if (ownerName == null) { - trace(player, "There is no owner name on the sign."); - - if (!properties.getInfiniteShopsEnabled()) { - trace(player, "Ininite shops are not enabled."); - return null; - } - - trace(player, "This is an infinite shop."); - try { - return new TradeCraftInfiniteShop(this, sign, chest); - } catch (Exception e) { - trace(player, e.getMessage()); - } - return null; - } - - trace(player, "The owner name on the sign is %s.", ownerName); - - if (!properties.getPlayerOwnedShopsEnabled()) { - trace(player, "Player-owned shops are not enabled."); - return null; - } - - trace(player, "This is a player-owned shop."); - return new TradeCraftPlayerOwnedShop(this, sign, chest, ownerName); - } - - String getItemName(String[] signLines) { - return getSpecialText(signLines, "[", "]"); - } - - String getOwnerName(String signLine) { - return getSpecialTextOnLine(signLine, "-", "-"); - } - - private String getSpecialText(String[] signLines, String prefix, String suffix) { - for (int i = 0; i < 4; i++) { - String text = getSpecialTextOnLine(signLines[i], prefix, suffix); - - if (text != null) { - return text; - } - } - - return null; - } - - private String getSpecialTextOnLine(String signLine, String prefix, String suffix) { - if (signLine == null) { - return null; - } - - signLine = signLine.trim(); - - if (signLine.startsWith(prefix) && signLine.endsWith(suffix) - && signLine.length() > 2) { - - String text = signLine.substring(1, signLine.length() - 1); - text = text.trim(); - if (text.equals("")) { - return null; - } - - return text; - } - - return null; - } - - TradeCraftExchangeRate getExchangeRate(String[] signLines, int lineNumber) { - if ( lineNumber < 0 || lineNumber >= signLines.length ) { - return null; - } - return getExchangeRate(signLines[lineNumber]); - } - TradeCraftExchangeRate getExchangeRate(String signLine) { - return new TradeCraftExchangeRate(signLine); } - - static int getMaxStackSize(int itemType) { - if ( TradeCraft.properties.getNormalStackSizeUsed() ) { - return Material.getMaterial(itemType).getMaxStackSize(); - } else { - return 64; - } - } - - /** - * Get a CamelCased string based on the current currency. - * - * @return a string representing the currency. - */ - public String getCurrencyName() { - // Try to get the name from the configuration file first - TradeCraftConfigurationInfo configInfo = this.configuration.get(TradeCraft.currency); - if ( configInfo != null ) { - return configInfo.name; - } else { - - ItemStack currencyStack = new ItemStack(TradeCraft.currency.id, 1, TradeCraft.currency.data); // weird that there's no Material.getMaterial(id, short/byte) - MaterialData currencyData = currencyStack.getType().getNewData((byte)TradeCraft.currency.data); - String currencyString; - if ( currencyData == null ) { - currencyString = currencyStack.getType().name(); - } else { - currencyString = currencyData.toString(); - } - - //String baseName = stack.getType().name(); - String[] words = currencyString.replace("null ", "").split("\\(")[0].split("[ _]{1}"); - String name = ""; - for ( int word_ind = 0; word_ind < words.length; word_ind++ ) { - String word = words[word_ind]; - if ( word_ind > 0 ) { - name = name.concat(" "); - } - name = name.concat(word.substring(0, 1).toUpperCase()).concat(word.substring(1).toLowerCase()); - } - return name; - } - } - - private void displayCommandHelpText(Player player) { - if ( player != null ) { - this.sendMessage(player, TradeCraftLocalization.get("POSSIBLE_COMMANDS_FOR_THE_PLUGIN"), - TradeCraft.pluginName); - this.sendMessage(player, "/tc[help]"+ ChatColor.GRAY +" "+ TradeCraftLocalization.get("TC_HELP_THIS_TEXT")); - this.sendMessage(player, "/tcshops"+ ChatColor.GRAY +" "+ TradeCraftLocalization.get("TC_SHOPS")); - if ( this.permissions.canQueryOtherShops(player) ) { - this.sendMessage(player, "/tcpshops [player]"+ ChatColor.GRAY +" "+ TradeCraftLocalization.get("TC_PSHOPS")); - } - this.sendMessage(player, "/tcgetcurrency"+ ChatColor.GRAY +" "+ TradeCraftLocalization.get("TC_CURRENCY_GET_CURRENCY")); - if ( this.permissions.canSetCurrency(player) ) { - this.sendMessage(player, "/tcsetcurrency [id[;data]]"+ ChatColor.GRAY +" "+ TradeCraftLocalization.get("TC_CURRENCY_OPT_PARAM_GETSET_CURRENCY")); - } - if ( this.permissions.canReload(player) ) { - this.sendMessage(player, "/tcreload"+ ChatColor.GRAY +" "+ TradeCraftLocalization.get("TC_RELOAD")); - } - if ( this.permissions.canQueryPlayer(player) ) { - this.sendMessage(player, "/tcplayerperms"+ ChatColor.GRAY +" "+ TradeCraftLocalization.get("TC_CAN_PLAYER")); - } - } else { - // console command help - this.log(Level.INFO, TradeCraftLocalization.get("POSSIBLE_COMMANDS_FOR_THE_PLUGIN"), - TradeCraft.pluginName); - this.log(Level.INFO, "tc[help]: "+ TradeCraftLocalization.get("TC_HELP_THIS_TEXT")); - this.log(Level.INFO, "tcplayerperms playername: "+ TradeCraftLocalization.get("TC_CAN_PLAYER")); - this.log(Level.INFO, "tcpshops [player]: "+ TradeCraftLocalization.get("TC_PSHOPS")); - this.log(Level.INFO, "tcreload: "+ TradeCraftLocalization.get("TC_RELOAD")); - } - } - - public void saveConfig() { - Logger.getLogger(JavaPlugin.class.getName()).log(Level.INFO, "saving config to: "+ this.getConfig("config").getFile().getAbsolutePath()); - try { - this.getConfig("config").save(); - } catch (IOException ex) { - Logger.getLogger(JavaPlugin.class.getName()).log(Level.SEVERE, "Could not save config to " + this.getConfig("config").getFile().getName(), ex); - } - } - public StatefulYamlConfiguration getConfig() { - return this.getConfig("config"); - } - public StatefulYamlConfiguration getConfig(String name) { - if (name.indexOf(".") < 0) { - name += ".yml"; - } - if (this.configs.containsKey(name)) { - return this.configs.get(name); - } else { - File configFile = new File(this.getDataFolder(), name); - StatefulYamlConfiguration config = new StatefulYamlConfiguration(configFile); - this.configs.put(name, config); - return config; - } - } - - public void log(Level level, String format, Object... args) { - this.log.log(level, TradeCraft.pluginName +": "+ String.format(format, args)); - } - - public void useLog(Player player, TradeCraftShop shop, String format, Object... args) { - if ( TradeCraft.properties.logShopUse() ) { - if ( this.usageLog != null ) { - try { - SimpleDateFormat formatter = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss"); - this.usageLog.write(String.format("%s %s \t%s %s\n", - formatter.format(new Date()), - shop.toString(), - player.getDisplayName(), - String.format(format, args))); - this.usageLog.flush(); - } catch (IOException e) { - this.log(Level.WARNING, "Failed to write to shop use log file"); - } - } else { - this.log(Level.INFO, "not written to log file"); - } - } - } - -} \ No newline at end of file diff --git a/nl/armeagle/TradeCraft/TradeCraftBlockListener.java b/nl/armeagle/TradeCraft/TradeCraftBlockListener.java deleted file mode 100644 index 9075fe3..0000000 --- a/nl/armeagle/TradeCraft/TradeCraftBlockListener.java +++ /dev/null @@ -1,224 +0,0 @@ -package nl.armeagle.TradeCraft; - -import java.util.ArrayList; -import java.util.List; - -import org.bukkit.Material; -import org.bukkit.block.Block; -import org.bukkit.block.Sign; -import org.bukkit.entity.Player; -import org.bukkit.event.block.BlockBreakEvent; -import org.bukkit.event.block.BlockPistonExtendEvent; -import org.bukkit.event.block.BlockPistonRetractEvent; -import org.bukkit.event.EventHandler; -import org.bukkit.event.EventPriority; -import org.bukkit.event.Listener; -import org.bukkit.event.block.SignChangeEvent; - -public class TradeCraftBlockListener implements Listener{ - - private TradeCraft plugin; - - TradeCraftBlockListener(TradeCraft plugin){ - this.plugin = plugin; - } - - public void debug(String str){ - plugin.getServer().broadcastMessage(str); - } - - @EventHandler - public void onNormalBlockBreak(BlockBreakEvent e) { - this.onBlockBreak(e, EventPriority.NORMAL); - } - - @EventHandler(priority = EventPriority.MONITOR) - public void onMonitorBlockBreak(BlockBreakEvent e) { - this.onBlockBreak(e, EventPriority.MONITOR); - } - - private void onBlockBreak(BlockBreakEvent e, EventPriority p) { - if ( !this.plugin.isEnabled() ) { - return; - } - - Player player = e.getPlayer(); - Block block = e.getBlock(); - ArrayList shops = plugin.getShopsFromBlock(player, block); - - if (shops.size() == 0) { - return; - } - - // Go through all shops in the list and check whether the player can destroy them all first. - // Only if that is the case proceed to destroy. - if (EventPriority.NORMAL == p) { - for ( TradeCraftShop shop : shops ) { - if (!shop.playerCanDestroy(player) && !plugin.permissions.canDestroyShops(player) || - shop.shopCanBeWithdrawnFrom() ) { - // cannot destroy this shop, so cancel destruction, use distinct error messages - if ( shop.shopCanBeWithdrawnFrom() ) { - plugin.sendMessage(player, TradeCraftLocalization.get("ALL_ITEMS_MUST_BE_WITHDRAWN")); - } else { - if ( block.getType() == Material.WALL_SIGN ) { - plugin.sendMessage(player, TradeCraftLocalization.get("YOU_CANT_DESTROY_THIS_SIGN")); - } else if ( block.getType() == Material.CHEST ) { - plugin.sendMessage(player, TradeCraftLocalization.get("YOU_CANT_DESTROY_THIS_CHEST")); - } else { - plugin.sendMessage(player, TradeCraftLocalization.get("YOU_CANT_DESTROY_THIS_BLOCK_ATTACHED")); - } - } - stopDestruction(block,e); - return; - } - } - } - - if (EventPriority.MONITOR == p && ! e.isCancelled()) { - // player can destroy all shops, so proceed - for ( TradeCraftShop shop : shops ) { - plugin.data.deleteShop(shop); - } - } - } - - @EventHandler(priority = EventPriority.MONITOR) - public void onMonitorSignChange(SignChangeEvent e) { - this.onSignChange(e, EventPriority.MONITOR); - } - - @EventHandler - public void onNormalSignChange(SignChangeEvent e) { - this.onSignChange(e, EventPriority.NORMAL); - } - - public void onSignChange(SignChangeEvent event, EventPriority priority) { - if ( !this.plugin.isEnabled() ) { - return; - } - - // Check whether this block is (still) a sign. Can be revoked already in case the sign was temporary, - // for example when the plugin SimpleSignEdit is being used. - if ( event.getBlock().getType() != Material.SIGN_POST && - event.getBlock().getType() != Material.WALL_SIGN ) { - return; - } - - Sign sign = (Sign) event.getBlock().getState(); - - Player player = event.getPlayer(); - String ownerName = player.getName(); - - String itemName = plugin.getItemName(event.getLines()); - - if (itemName == null) { - plugin.trace(player, "sign change, no item name, ignore"); - return; - } - // Check whether this is an existing item. Try to prevent as little normal sign placement as possible. - // Only treat signs with "[name]" as item line where "name" is found in the configuration (.txt) file. - TradeCraftConfigurationInfo itemInfo = plugin.configuration.get(itemName); - if ( itemInfo == null ) { - plugin.trace(player, "sign change, %s is not a valid item name, ignore this sign", itemName); - return; - } - - TradeCraftExchangeRate buyRate = new TradeCraftExchangeRate(event.getLine(1)); - TradeCraftExchangeRate sellRate = new TradeCraftExchangeRate(event.getLine(2)); - // no buy rate means this is an infinite shop - if ( !buyRate.isValid() && !sellRate.isValid() ) { - if (plugin.permissions.canMakeInfShops(player)){ - plugin.trace(player, "sign change, infinite chest of %s", itemName); - return; - } - - if (EventPriority.NORMAL == priority) { - plugin.sendMessage(player, TradeCraftLocalization.get("YOU_CANT_CREATE_INF_SHOPS")); - event.setCancelled(true); - return; - } - } - // there is a buy rate, so this is a player owned shop - - if (EventPriority.NORMAL == priority && !plugin.permissions.canMakePlayerShops(player)){ - plugin.sendMessage(player, TradeCraftLocalization.get("YOU_DONT_HAVE_PERM_CREATE_PLAYER_SHOP")); - event.setCancelled(true); - return; - } - - // check whether the player doesn't have too many shops - if (EventPriority.NORMAL == priority) { - int totalShopLimit = TradeCraft.properties.getPlayerTotalShopLimit(); - int worldShopLimit = TradeCraft.properties.getPlayerWorldShopLimit(); - if (plugin.data.getPlayerShopCount(player) >= totalShopLimit) { - plugin.sendMessage(player, TradeCraftLocalization.get("TOTAL_SHOP_LIMIT_X"), totalShopLimit); - event.setCancelled(true); - return; - } else if (plugin.data.getPlayerShopCount(player, player.getWorld()) >= worldShopLimit) { - plugin.sendMessage(player, TradeCraftLocalization.get("WORLD_SHOP_LIMIT_X"), worldShopLimit); - event.setCancelled(true); - return; - } - - } - - if (EventPriority.MONITOR == priority && !event.isCancelled()) { - plugin.trace(player, "Setting owner of sign to: %s", ownerName); - // set the player name on the last line - event.setLine(3, "-"+ ownerName.substring(0, Math.min(ownerName.length(), 15)) +"-"); - plugin.data.createNewSign(ownerName, itemInfo, sign); - } - - /* - if ( this.plugin.properties.getStrictPlayerShopOwnerNameRequired() ) { - if (player.getName().equalsIgnoreCase(ownerName)) { - plugin.data.setOwnerOfSign(player.getName(), sign); - return; - } - } else { - if (player.getName().startsWith(ownerName)) { - plugin.data.setOwnerOfSign(player.getName(), sign); - return; - } - } - */ - } - - // prevent pistons from pushing away (the block behind) a sign. Block all retract and extend events that would move a block behind a sign, also for shop owners - @EventHandler - public void onNormalBlockPistonRetract(BlockPistonRetractEvent e) { - if (e.isSticky()) { - Block block = e.getRetractLocation().getBlock(); - ArrayList shops = plugin.getShopsFromBlock(null, block); - - if (shops.size() != 0) { - e.setCancelled(true); - } - } - } - @EventHandler - public void onNormalBlockPistonExtend(BlockPistonExtendEvent e) { - List blocks = e.getBlocks(); - for (Block block:blocks) { - ArrayList shops = plugin.getShopsFromBlock(null, block); - - if (shops.size() != 0) { - e.setCancelled(true); - return; - } - } - } - - public void stopDestruction(Block b, BlockBreakEvent e){ - if(b.getState() instanceof Sign){ - Sign sign = (Sign)b.getState(); - String[] lines = sign.getLines(); - for(int i = 0;i<4;i++){ - sign.setLine(i, lines[i]); - } - - sign.update(true); - } - e.setCancelled(true); - } -} diff --git a/nl/armeagle/TradeCraft/TradeCraftConfigurationFile.java b/nl/armeagle/TradeCraft/TradeCraftConfigurationFile.java deleted file mode 100644 index 94dff0b..0000000 --- a/nl/armeagle/TradeCraft/TradeCraftConfigurationFile.java +++ /dev/null @@ -1,199 +0,0 @@ -package nl.armeagle.TradeCraft; - -import java.io.BufferedReader; -import java.io.File; -import java.io.FileReader; -import java.io.IOException; -import java.util.Arrays; -import java.util.HashMap; -import java.util.Iterator; -import java.util.Map; -import java.util.Map.Entry; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -import org.bukkit.configuration.MemorySection; - -import nl.armeagle.Configuration.StatefulYamlConfiguration; - -/** - * The name of this class is a bit misleading. This class stores all the items and their default - * trade rates that can be used in the game. The actual configuration of the plugin itself - * is handled by the TradeCraftProperties class. - */ -class TradeCraftConfigurationFile { - private static final String fileName = TradeCraft.pluginName + ".txt"; - private static final String configName = "items"; - - private static final Pattern commentPattern = Pattern.compile("^\\s*#.*$"); - private static final Pattern infoPattern = Pattern.compile( - "^\\s*([^,]+)\\s*," + // name - "\\s*(\\d+(?:;\\d+)?)\\s*" + // id[;data] (optional ;data) - "(?:,\\s*(\\d+)\\s*:\\s*(\\d+))?\\s*" + // buyAmount:buyValue - "(?:,\\s*(\\d+)\\s*:\\s*(\\d+))?\\s*$"); // sellAmount:sellValue - - private HashMap mapItemNames = new HashMap(); - - private final TradeCraft plugin; - // create an index from an TradeCraftItem (id;data) to the TradeCraftConfigurationInfo entry. This for configured name lookup based on id;data - private final Map TCitemInfoIndex = new HashMap(); - - TradeCraftConfigurationFile(TradeCraft plugin) { - this.plugin = plugin; - } - - public StatefulYamlConfiguration getConfig() { - return this.plugin.getConfig(TradeCraftConfigurationFile.configName); - } - - void load() { - StatefulYamlConfiguration config = (StatefulYamlConfiguration) this.getConfig(); - - // if file exists, load the config to it once and then rename the old config - File file = new File(plugin.getDataFolder().getAbsolutePath(), fileName); - if ( file.exists() ) { - try { - FileReader reader = new FileReader(file); - BufferedReader configurationFile = new BufferedReader(reader); - - int lineNumber = 0; - String line; - - while ((line = configurationFile.readLine()) != null) { - lineNumber += 1; - - if (line.trim().equals("")) { - continue; - } - - Matcher commentMatcher = commentPattern.matcher(line); - - if (commentMatcher.matches()) { - continue; - } - - Matcher infoMatcher = infoPattern.matcher(line); - - if (!infoMatcher.matches()) { - plugin.log.warning( - "Failed to parse line number " + lineNumber + - " in " + file.getAbsolutePath() + - ": " + line); - continue; - } - - TradeCraftConfigurationInfo info = new TradeCraftConfigurationInfo(); - info.name = infoMatcher.group(1); - - // try to split ID and Data, separated by a semicolon mark - Matcher IdSplitData = TradeCraft.itemPatternIdSplitData.matcher(infoMatcher.group(2)); - - if (!IdSplitData.matches()) { - plugin.log.info( - "Failed to parse line number " + lineNumber + - " in " + file.getAbsolutePath() + - ": " + line); - continue; - } - - int id = Integer.parseInt(IdSplitData.group(1)); - if ( IdSplitData.group(2) != null ) { - short data = Short.parseShort(IdSplitData.group(2)); - info.type = new TradeCraftItem(id, data); - } else { - info.type = new TradeCraftItem(id); - } - - if (infoMatcher.group(3) != null) { - info.sellAmount = info.buyAmount = Integer.parseInt(infoMatcher.group(3)); - info.sellValue = info.buyValue = Integer.parseInt(infoMatcher.group(4)); - } - - if (infoMatcher.group(5) != null) { - info.sellAmount = Integer.parseInt(infoMatcher.group(5)); - info.sellValue = Integer.parseInt(infoMatcher.group(6)); - } - -// config.set(info.name.toUpperCase(), info.toMemoryConfiguration()); - Iterator> iter = info.toMap().entrySet().iterator(); - while (iter.hasNext()) { - Map.Entry pairs = (Map.Entry)iter.next(); - if (!pairs.getKey().equals("name")) { - config.set(info.name +"."+ pairs.getKey(), pairs.getValue()); - } - } - -// TCitemInfoIndex.put(info.type, info.name); - } - configurationFile.close(); - reader.close(); - config.save(); - if (file.renameTo(new File(file.getAbsolutePath() + ".converted.to."+ TradeCraftConfigurationFile.configName +".yml"))) { - plugin.log.info("Converted old config to new style and renamed the old config file"); - } else { - plugin.log.severe("FAILED to convert old config to new style"); - } - - } catch (IOException e) { - plugin.log.severe("Error reading " + file.getAbsolutePath()); - } - } else { - try { - config.load(); - } catch (IOException e) { - plugin.log.severe("Error loading plugin config file"); - } - } - - Iterator> iter = config.getValues(false).entrySet().iterator(); - while (iter.hasNext()) { - // store map of lowercase item names to key names in the configuration - Map.Entry entry = (Map.Entry)iter.next(); - this.mapItemNames.put(entry.getKey().toLowerCase(), entry.getKey()); - // store map of item types to key names - MemorySection section = (MemorySection) entry.getValue(); - TradeCraftItem tcItem = new TradeCraftItem(section.getInt("itemTypeId", 266), section.getInt("itemTypeData", 0)); - TCitemInfoIndex.put(tcItem, entry.getKey()); - } - } - - public String[] getNames() { - String[] names = this.getConfig().getKeys(false).toArray(new String[0]); - Arrays.sort(names); - return names; - } - - public boolean isConfigured(String name) { - return this.mapItemNames.containsKey(name.toLowerCase()); - } - - public TradeCraftConfigurationInfo get(String name) { - - // @todo, config code doesn't seem to like serialized objects, cannot seem to find the TradeCraftConfigurationInfo class - // though, the otherwise resulting ==: classpath lines aren't very user friendly anyway. -// return (TradeCraftConfigurationInfo) plugin.getConfig().get(name.toUpperCase()); - String itemName = this.mapItemNames.get(name.toLowerCase()); - if (null != itemName) { - MemorySection item = ((MemorySection)this.getConfig().get(itemName)); - if (null != item) { - Map itemData = item.getValues(false); - if (null != itemData) { - return new TradeCraftConfigurationInfo(itemData, name); - } - } - } - return null; - } - public TradeCraftConfigurationInfo get(int id) { - return this.get(new TradeCraftItem(id)); - } - public TradeCraftConfigurationInfo get(int id, short data) { - return this.get(new TradeCraftItem(id, data)); - } - public TradeCraftConfigurationInfo get(TradeCraftItem item) { - if (!TCitemInfoIndex.containsKey(item)) { - return null; - } - return this.get(TCitemInfoIndex.get(item)); - } -} \ No newline at end of file diff --git a/nl/armeagle/TradeCraft/TradeCraftConfigurationInfo.java b/nl/armeagle/TradeCraft/TradeCraftConfigurationInfo.java deleted file mode 100644 index 671a524..0000000 --- a/nl/armeagle/TradeCraft/TradeCraftConfigurationInfo.java +++ /dev/null @@ -1,55 +0,0 @@ -package nl.armeagle.TradeCraft; - -import java.util.LinkedHashMap; -import java.util.Map; -import org.bukkit.configuration.serialization.ConfigurationSerializable; -import org.bukkit.configuration.serialization.SerializableAs; - -@SerializableAs("nl.armeagle.TradeCraft.TradeCraftConfigurationInfo") -class TradeCraftConfigurationInfo implements ConfigurationSerializable { - public String name; - public TradeCraftItem type; - public int buyAmount; - public int buyValue; - public int sellAmount; - public int sellValue; - - TradeCraftConfigurationInfo() { - } - - TradeCraftConfigurationInfo(Map map, String name) { - this.name = name; - this.type = new TradeCraftItem((Integer) map.get("itemTypeId"), (Integer) map.get("itemTypeData")); - this.buyAmount = (Integer) map.get("buyAmount"); - this.buyValue = (Integer) map.get("buyValue"); - this.sellAmount = (Integer) map.get("sellAmount"); - this.sellValue = (Integer) map.get("sellValue"); - } - TradeCraftConfigurationInfo(Map map) { - this.name = (String) map.get("name"); - this.type = new TradeCraftItem((Integer) map.get("itemTypeId"), (Integer) map.get("itemTypeData")); - this.buyAmount = (Integer) map.get("buyAmount"); - this.buyValue = (Integer) map.get("buyValue"); - this.sellAmount = (Integer) map.get("sellAmount"); - this.sellValue = (Integer) map.get("sellValue"); - } - - public Map toMap() { - LinkedHashMap map = new LinkedHashMap(); - map.put("name", this.name); - map.put("itemTypeId", this.type.id); - map.put("itemTypeData", this.type.data); - map.put("buyAmount", this.buyAmount); - map.put("buyValue", this.buyValue); - map.put("sellAmount", this.sellAmount); - map.put("sellValue", this.sellValue); - return map; - } - @Override - public Map serialize() { - return this.toMap(); - } - public static TradeCraftConfigurationInfo deserialize(Map map) { - return new TradeCraftConfigurationInfo(map); - } -} \ No newline at end of file diff --git a/nl/armeagle/TradeCraft/TradeCraftItem.java b/nl/armeagle/TradeCraft/TradeCraftItem.java deleted file mode 100644 index d8155fa..0000000 --- a/nl/armeagle/TradeCraft/TradeCraftItem.java +++ /dev/null @@ -1,63 +0,0 @@ -package nl.armeagle.TradeCraft; - -/** - * - * @author ArmEagle - * Store item type(ID) and optionally the data bit - */ -public class TradeCraftItem implements Comparable { - public int id; - public short data; - - TradeCraftItem(int id) { - this(id, (short)0); - } - TradeCraftItem(int id, int data) { - this(id, new Integer(data).shortValue()); - } - TradeCraftItem(int id, short data) { - this.id = id; - this.data = data; - } - - /** - * @param compare - * @throws NullPointerException if compare is null - * @return default < 0 > compare values - */ - @Override public int compareTo(TradeCraftItem compare) { - if ( this == compare ) { - return 0; - } - if ( this.id < compare.id ) { - return -1; - } else if ( this.id > compare.id ) { - return 1; - } else { - if ( this.data < compare.data ) { - return -1; - } else if ( this.data > compare.data ) { - return 1; - } else { - return 0; - } - } - } - @Override public boolean equals(Object compare) { - return (compare == null ? false : (compare instanceof TradeCraftItem? this.compareTo((TradeCraftItem)compare) == 0 : false)); - } - @Override public int hashCode() { - return this.id * 32768 + this.data; - } - - @Override public String toString() { - return "TradeCraftItem("+ this.id +";"+ this.data +")"; - } - public String toShortString() { - if ( this.data == 0 ) { - return String.valueOf(this.id); - } else { - return this.id +";"+ this.data; - } - } -} diff --git a/nl/armeagle/TradeCraft/TradeCraftPermissions.java b/nl/armeagle/TradeCraft/TradeCraftPermissions.java deleted file mode 100644 index ddf29f8..0000000 --- a/nl/armeagle/TradeCraft/TradeCraftPermissions.java +++ /dev/null @@ -1,121 +0,0 @@ -package nl.armeagle.TradeCraft; - -import org.bukkit.command.CommandSender; -import org.bukkit.entity.Player; -import org.bukkit.plugin.Plugin; - -import com.nijiko.permissions.PermissionHandler; -import com.nijikokun.bukkit.Permissions.Permissions; - -public class TradeCraftPermissions { - - PermissionHandler permHandler = null; - TradeCraft plugin; - - TradeCraftPermissions(TradeCraft plugin) { - this.plugin = plugin; - } - - public void setupPermissions() { - Plugin test = plugin.getServer().getPluginManager().getPlugin("Permissions"); - - if (permHandler == null) { - if (test != null) { - this.permHandler = ((Permissions)test).getHandler(); - plugin.permEnabled = true; - plugin.log.info("[TradeCraft] has recognized Permissions"); - } - } - } - - public boolean canBuy(Player p) { - if ( plugin.permEnabled ) { - return permHandler.has(p, "TradeCraft.canBuy"); - } else { - return p.hasPermission("TradeCraft.canBuy"); - } - } - - public boolean canSell(Player p) { - if ( plugin.permEnabled ) { - return permHandler.has(p, "TradeCraft.canSell"); - } else { - return p.hasPermission("TradeCraft.canSell"); - } - } - - public boolean canMakeInfShops(Player p) { - if ( plugin.permEnabled ) { - return permHandler.has(p, "TradeCraft.canMakeInfShops"); - } else { - return p.hasPermission("TradeCraft.canMakeInfShops"); - } - } - - public boolean canMakePlayerShops(Player p) { - if ( plugin.permEnabled ) { - return permHandler.has(p, "TradeCraft.canMakePlayerShops"); - } else { - return p.hasPermission("TradeCraft.canMakePlayerShops"); - } - } - - public boolean canDestroyShops(Player p) { - if ( plugin.permEnabled ) { - return permHandler.has(p, "TradeCraft.canDestroyShops"); - } else { - return p.hasPermission("TradeCraft.canDestroyShops"); - } - } - - public boolean canSetCurrency(Player p) { - if ( plugin.permEnabled ) { - return permHandler.has(p, "TradeCraft.canSetCurrency"); - } else { - return p.hasPermission("TradeCraft.canSetCurrency"); - } - } - - public boolean canReload(Player p) { - if ( plugin.permEnabled ) { - return permHandler.has(p, "TradeCraft.canReload"); - } else { - return p.hasPermission("TradeCraft.canReload"); - } - } - - public boolean canQueryOtherShops(Player p) { - if ( plugin.permEnabled ) { - return permHandler.has(p, "TradeCraft.canQueryOtherShops"); - } else { - return p.hasPermission("TradeCraft.canQueryOtherShops"); - } - } - - public boolean canQueryPlayer(Player p) { - if ( plugin.permEnabled ) { - return permHandler.has(p, "TradeCraft.canQueryPlayer"); - } else { - return p.hasPermission("TradeCraft.canQueryPlayer"); - } - } - - public void debug(CommandSender sender, String n){ - Player p = plugin.getServer().getPlayer(n); - if(p == null){ - plugin.getServer().broadcastMessage("/tc canPlayer used with a name of player who is not online."); - return; - } - String name = p.getName(); - sender.sendMessage("" + name + " has:"); - sender.sendMessage("canBuy " + canBuy(p)); - sender.sendMessage("canSell " + canSell(p)); - sender.sendMessage("canMakeInf " + canMakeInfShops(p)); - sender.sendMessage("canMakePersonal " + canMakePlayerShops(p)); - sender.sendMessage("canDestroy " + canDestroyShops(p)); - sender.sendMessage("canSetCurrency " + canSetCurrency(p)); - sender.sendMessage("canReload " + canReload(p)); - sender.sendMessage("canQueryOtherShops " + canQueryOtherShops(p)); - } - -} diff --git a/nl/armeagle/TradeCraft/TradeCraftPropertiesFile.java b/nl/armeagle/TradeCraft/TradeCraftPropertiesFile.java deleted file mode 100644 index de95254..0000000 --- a/nl/armeagle/TradeCraft/TradeCraftPropertiesFile.java +++ /dev/null @@ -1,153 +0,0 @@ -package nl.armeagle.TradeCraft; - -import java.io.File; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.util.logging.Level; - -import org.bukkit.ChatColor; -import org.bukkit.configuration.InvalidConfigurationException; -import org.bukkit.configuration.file.YamlConfiguration; - -/** - * This class, handles the actual configuration of the plugin. The TradeCraftConfiguration class - * actually handles all the items that can be used by the shops in the game. - */ -public class TradeCraftPropertiesFile { - private static final String fileName = TradeCraft.pluginName + ".properties"; - private static final String filePath = "plugins" + File.separator + TradeCraft.pluginName; - public static final String defaultLanguage = "en"; - - private TradeCraft plugin; - private final YamlConfiguration properties; - - public TradeCraftPropertiesFile(TradeCraft plugin) { - this.plugin = plugin; - // make folder in the plugins dir if it doesn't exist yet - File path = new File(filePath); - if ( !path.exists() ) { - path.mkdirs(); - } - path = null; - - // if file does not exist in this directory, copy it from the jar - File file = new File(filePath + File.separator + fileName); - if ( !file.exists() ) { - this.plugin.log.info(filePath + File.separator + fileName +" does not exist, creating..."); - InputStream input = this.getClass().getResourceAsStream("/" + fileName); - if ( input != null ) { - FileOutputStream output = null; - - try { - output = new FileOutputStream(file); - byte[] buf = new byte[8192]; - int length = 0; - while ((length = input.read(buf)) > 0) { - output.write(buf, 0, length); - } - } catch (IOException e) { - e.printStackTrace(); - } finally { - try { - if (input != null) { - input.close(); - } - } catch (IOException e) {} - - try { - if (output != null) { - output.close(); - } - } catch (IOException e) {} - } - } - } - - properties = new YamlConfiguration(); - try { - properties.load(file); - } catch (InvalidConfigurationException e) { - plugin.log(Level.SEVERE, "Failed to load file: %s", file.toURI()); - } catch (IOException e) { - plugin.log(Level.SEVERE, "Failed to read file: %s", file.toURI()); - } - } - - protected void save() { - File file = new File(filePath + File.separator + fileName); - try { - properties.save(file); - } catch (IOException e) { - this.plugin.log(Level.SEVERE, "Error saving to file: %s", file.toURI()); - } - } - - public TradeCraftItem getCurrencyType(){ - int id = properties.getInt("currency-id",266); - short data = (short)properties.getInt("currency-data",0); - return new TradeCraftItem(id, data); - } - public void setCurrencyType(TradeCraftItem item) { - properties.set("currency-id", item.id); - properties.set("currency-data", item.data); - this.save(); - } - public boolean getNormalStackSizeUsed(){ - return properties.getBoolean("normal-stack-size", true); - } - - public boolean getInfiniteShopsEnabled() { - return properties.getBoolean("infinite-shops-enabled", true); - } - - public boolean getPlayerOwnedShopsEnabled() { - return properties.getBoolean("player-owned-shops-enabled", true); - } - - public boolean getRepairShopsEnabled() { - return properties.getBoolean("repair-shops-enabled", false); - } - - public int getRepairCost() { - return properties.getInt("repair-cost", 0); - } - - public boolean getEnableDebugMessages() { - return properties.getBoolean("enable-debug-messages", false); - } - - public String getLanguage() { - return properties.getString("language", TradeCraftPropertiesFile.defaultLanguage); - } - - public boolean autoUpdateLanguageFiles() { - return properties.getBoolean("auto-update-language-files", true); - } - - public boolean logShopUse() { - return properties.getBoolean("log-shop-use", false); - } - - public boolean showShopLocation() { - return properties.getBoolean("show-shop-location", false); - } - - public int getPlayerWorldShopLimit() { - return properties.getInt("player-world-shop-limit", 5); - } - public int getPlayerTotalShopLimit() { - return properties.getInt("player-total-shop-limit", 10); - } - - public ChatColor getMessageTypeColor(TradeCraft.MessageTypes mtype) { - switch (mtype) { - case WITHDRAW: - return ChatColor.YELLOW; - case DEPOSIT: - return ChatColor.GRAY; - default: - return ChatColor.WHITE; - } - } -} diff --git a/TradeCraft.en.lang b/src/TradeCraft.en.lang similarity index 100% rename from TradeCraft.en.lang rename to src/TradeCraft.en.lang diff --git a/TradeCraft.ge.lang b/src/TradeCraft.ge.lang similarity index 100% rename from TradeCraft.ge.lang rename to src/TradeCraft.ge.lang diff --git a/TradeCraft.properties b/src/TradeCraft.properties similarity index 100% rename from TradeCraft.properties rename to src/TradeCraft.properties diff --git a/TradeCraft.sv.lang b/src/TradeCraft.sv.lang similarity index 100% rename from TradeCraft.sv.lang rename to src/TradeCraft.sv.lang diff --git a/items.yml b/src/items.yml similarity index 100% rename from items.yml rename to src/items.yml diff --git a/src/nl/armeagle/Configuration/StatefulYamlConfiguration.java b/src/nl/armeagle/Configuration/StatefulYamlConfiguration.java new file mode 100644 index 0000000..4e38c35 --- /dev/null +++ b/src/nl/armeagle/Configuration/StatefulYamlConfiguration.java @@ -0,0 +1,79 @@ +package nl.armeagle.Configuration; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStream; +import java.util.logging.Level; +import java.util.logging.Logger; + +import org.bukkit.Bukkit; +import org.bukkit.configuration.InvalidConfigurationException; +import org.bukkit.configuration.file.YamlConfiguration; +import org.bukkit.plugin.java.JavaPlugin; +import org.yaml.snakeyaml.error.YAMLException; + +public class StatefulYamlConfiguration extends YamlConfiguration { + protected File file; + + public StatefulYamlConfiguration(File file) { + this.file = file; + } + + public void load() throws IOException { + this.load(false); + } + public void load(boolean forceDefaults) throws IOException { + boolean notLoaded = false; + if (this.file == null) { + throw new IllegalArgumentException("File cannot be null"); + } + + YamlConfiguration baseConfig = new YamlConfiguration(); + + try { + baseConfig.load(this.file); + } catch (FileNotFoundException fnf) { + notLoaded = true; + } catch (IOException ex) { + Bukkit.getLogger().log(Level.SEVERE, "Cannot load " + this.file, ex); + } catch (InvalidConfigurationException ex) { + if (ex.getCause() instanceof YAMLException) { + Bukkit.getLogger().severe("Config file " + this.file + " isn't valid! " + ex.getCause()); + } else if ((ex.getCause() == null) || (ex.getCause() instanceof ClassCastException)) { + Bukkit.getLogger().severe("Config file " + this.file + " isn't valid!"); + } else { + Bukkit.getLogger().log(Level.SEVERE, "Cannot load " + this.file + ": " + ex.getCause().getClass(), ex); + } + } + + if (notLoaded || forceDefaults) { + InputStream defaultInput = this.getClass().getResourceAsStream("/" + file.getName()); + if ( null != defaultInput ) { + YamlConfiguration defaultConfig = YamlConfiguration.loadConfiguration(defaultInput); + + try { + this.loadFromString(defaultConfig.saveToString()); + } catch (InvalidConfigurationException e) { + Logger.getLogger(JavaPlugin.class.getName()).log(Level.SEVERE, "Invalid default configuation"); + } + this.save(); + } + } else if (! notLoaded) { + try { + this.loadFromString(baseConfig.saveToString()); + } catch (InvalidConfigurationException e) { + Logger.getLogger(JavaPlugin.class.getName()).log(Level.SEVERE, "Invalid configuation"); + } + } + + } + + public void save() throws IOException { + super.save(this.file); + } + + public File getFile() { + return this.file; + } +} diff --git a/src/nl/armeagle/TradeCraft/TradeCraft.java b/src/nl/armeagle/TradeCraft/TradeCraft.java new file mode 100644 index 0000000..aa122e1 --- /dev/null +++ b/src/nl/armeagle/TradeCraft/TradeCraft.java @@ -0,0 +1,652 @@ +package nl.armeagle.TradeCraft; + +import java.io.*; + +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Date; +import java.util.HashMap; +import java.util.IllegalFormatException; +import java.util.Map; +import java.util.logging.Level; +import java.util.logging.Logger; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import nl.armeagle.Configuration.StatefulYamlConfiguration; + +import org.bukkit.ChatColor; +import org.bukkit.Material; +import org.bukkit.Server; +import org.bukkit.block.Block; +import org.bukkit.block.BlockFace; +import org.bukkit.block.Chest; +import org.bukkit.block.Sign; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.command.ConsoleCommandSender; +import org.bukkit.entity.Player; +import org.bukkit.inventory.ItemStack; +import org.bukkit.material.MaterialData; +import org.bukkit.plugin.PluginDescriptionFile; +import org.bukkit.plugin.PluginManager; +import org.bukkit.plugin.java.JavaPlugin; + + +public class TradeCraft extends JavaPlugin { + public static enum MessageTypes {WITHDRAW, DEPOSIT}; + + // The plugin name. + static final String pluginName = "TradeCraft"; + + public static final Pattern itemPatternIdSplitData = Pattern.compile("^(\\d+)(?:;(\\d+))?$"); + + private static enum Commands { + tcsetcurrency, + tcgetcurrency, + tcshops, + tcpshops, + tcreload, + tcplayerperms, + tchelp, + tc + }; + + // Stuff used to interact with the server. + final Logger log = Logger.getLogger("Minecraft"); + final Server server = this.getServer(); + + protected BufferedWriter usageLog = null; + // Objects used by the plugin. + static TradeCraftItem currency; + static TradeCraftPropertiesFile properties; + TradeCraftConfigurationFile configuration; + public TradeCraftLocalization localization; + TradeCraftDataFile data; + + private HashMap configs = new HashMap(); + + private final TradeCraftBlockListener blockListener = new TradeCraftBlockListener(this); + private final TradeCraftPlayerListener playerListener = new TradeCraftPlayerListener(this); + public TradeCraftPermissions permissions = new TradeCraftPermissions(this); + + // prevent the script from registering the event listeners multiple times (by dis-/enable) + public static boolean hasRegisteredEventListeners = false; + + public void onDisable() { + this.disable(); + } + private void disable() { + if ( this.usageLog != null ) { + try { + this.usageLog.close(); + } catch (IOException e) { + this.log(Level.WARNING, "Failed to close shop usage log file"); + } + } + properties = null; + configuration = null; + this.localization = null; + data.save(); + data = null; + } + + public void onEnable() { + this.enable(); + } + private void enable() { + properties = new TradeCraftPropertiesFile(this); + configuration = new TradeCraftConfigurationFile(this); + data = new TradeCraftDataFile(this); + this.localization = new TradeCraftLocalization(this); + + if ( TradeCraft.properties.logShopUse() ) { + File usageLogFile = new File(this.getDataFolder(), "shopUsage.log"); + try { + if ( !usageLogFile.exists() ) { + usageLogFile.createNewFile(); + } + if ( usageLogFile.canWrite() ) { + this.usageLog = new BufferedWriter(new FileWriter(usageLogFile, true)); + this.log(Level.INFO, "Writing shop usage to log file: "+ usageLogFile.toString()); + } else { + this.log(Level.WARNING, "Error opening shop usage log file: "+ usageLogFile.toString()); + } + } catch (IOException e) { + this.log(Level.WARNING, "Failed to open shop usage log file: "+ usageLogFile.toString()); + } + } + + configuration.load(); + data.load(); + currency = properties.getCurrencyType(); + + if ( !TradeCraft.hasRegisteredEventListeners ) { + PluginManager pm = this.getServer().getPluginManager(); + pm.registerEvents(playerListener, this); + pm.registerEvents(blockListener, this); + TradeCraft.hasRegisteredEventListeners = true; + } + + PluginDescriptionFile pdfFile = this.getDescription(); + this.log.info(pdfFile.getName() + " version " + pdfFile.getVersion() + " is enabled!"); + + } + + @Override + public boolean onCommand(CommandSender sender, Command cmd, String label, String[] args) { + try { + TradeCraft.Commands command = TradeCraft.Commands.valueOf(cmd.getName()); + if (sender instanceof Player) { + Player p = (Player) sender; + switch (command) { + case tcsetcurrency: + if ( args.length == 1 && this.permissions.canSetCurrency(p) ) { + TradeCraftItem testCurrency = null; + // try to split ID and Data, separated by a semicolon mark + Matcher IdSplitData = TradeCraft.itemPatternIdSplitData.matcher(args[0]); + + if ( !IdSplitData.matches() ) { + // try to match the parameter to item names from the configuration + TradeCraftConfigurationInfo setCurr = this.configuration.get(args[0]); + if ( setCurr == null ) { + this.sendMessage(p, TradeCraftLocalization.get("IS_NO_VALID_CURRENCY_USE_INSTEAD"), + args[0]); + return false; + } else { + currency = setCurr.type; + } + } else { + try { + int cid = Integer.parseInt(IdSplitData.group(1)); + if ( IdSplitData.group(2) != null ) { + testCurrency = new TradeCraftItem(cid, Short.parseShort(IdSplitData.group(2))); + } else { + testCurrency = new TradeCraftItem(cid); + } + } catch ( NumberFormatException e ) { + this.sendMessage(p, TradeCraftLocalization.get("INVALID_CURRENCY"), + args[0]); + return false; + } + if ( this.configuration.get(testCurrency) != null ) { + currency = testCurrency; + } else { + this.sendMessage(p, TradeCraftLocalization.get("INVALID_CURRENCY"), + args[0]); + return false; + } + } + + TradeCraft.properties.setCurrencyType(currency); + this.sendMessage(p, TradeCraftLocalization.get("CURRENCY_IS_SET_TO_A_IDDATA"), + this.getCurrencyName(), + currency.toShortString()); + return true; + } + return true; + case tcgetcurrency: + this.sendMessage(p, TradeCraftLocalization.get("CURRENCY_IS_A_IDDATA"), + this.getCurrencyName(), + currency.toShortString()); + return true; + case tcshops: + // lookup own shows + displayShops(p.getName(), p, false); + return true; + case tcpshops: + // Check whether another parameter is passed, if so check whether + // the player can get information about other player's shops. + if ( this.permissions.canQueryOtherShops(p) ) { + if ( args.length == 1 ) { + // lookup other player's shops + displayShops(args[0], p, true); + } else { + this.sendMessage(p, cmd.getUsage()); + } + } + return true; + case tcreload: + if ( this.permissions.canReload(p)) { + this.sendMessage(p, TradeCraftLocalization.get("RESTARTING_PLUGIN"), TradeCraft.pluginName); + this.disable(); + this.enable(); + this.sendMessage(p, TradeCraftLocalization.get("RESTARTING_PLUGIN_DONE"), TradeCraft.pluginName); + } + return true; + case tcplayerperms: + if ( this.permissions.canQueryPlayer(p)) { + if ( args.length == 1 ) { + permissions.debug(sender, args[0]); + } else { + sender.sendMessage(cmd.getUsage()); + } + } + return true; + default: + displayCommandHelpText(p); + return true; + } + } else if ( sender instanceof ConsoleCommandSender ) { + switch (command) { + case tcplayerperms: + if ( args.length == 1 ) { + permissions.debug(sender, args[0]); + } else { + sender.sendMessage(cmd.getUsage()); + } + return true; + case tcpshops: + // Check whether another parameter is passed, if so check whether + // the player can get information about other player's shops. + if ( args.length == 1 ) { + // lookup other player's shops + displayShops(args[0], sender, true); + } else { + sender.sendMessage(cmd.getUsage()); + } + return true; + case tcreload: + sender.sendMessage(String.format(TradeCraftLocalization.get("RESTARTING_PLUGIN"), + TradeCraft.pluginName)); + this.disable(); + this.enable(); + sender.sendMessage(String.format(TradeCraftLocalization.get("RESTARTING_PLUGIN_DONE"), + TradeCraft.pluginName)); + return true; + default: + displayCommandHelpText(null); + return true; + } + } + } catch (IllegalArgumentException e) { + return false; + } + return false; + } + + void displayShops(String infoPlayerName, CommandSender displayTo, boolean otherQuery) { + Map list = data.shopsOwned(infoPlayerName); + if (list.size() == 0) { + if ( otherQuery ) { + // elevated player looking for other player's shops + displayTo.sendMessage(String.format(TradeCraftLocalization.get("A_DOES_NOT_OWN_ANY_SHOPS"), + infoPlayerName)); + } else { + displayTo.sendMessage(TradeCraftLocalization.get("YOU_DONT_OWN_ANY_SHOPS")); + } + return; + } + + if ( otherQuery ) { + displayTo.sendMessage(String.format(TradeCraftLocalization.get("SHOPS_OF_A"), + infoPlayerName)); + } else { + displayTo.sendMessage(TradeCraftLocalization.get("YOUR_SHOPS")); + } + for (Map.Entry entry : list.entrySet()) { + TradeCraftDataInfo info = entry.getValue(); + String message = ""; + if (TradeCraft.properties.showShopLocation()) { + String location = entry.getKey().replaceFirst(",", "(") +")"; + message += ChatColor.GRAY + location +" "+ ChatColor.WHITE; + } + message += TradeCraftLocalization.get("ITEM") +": "+ this.configuration.get(info.itemType).name +"("+ info.itemType.toShortString() +")" + +" "+ TradeCraftLocalization.get("AMOUNT") +": "+ info.itemAmount +" " + + this.getCurrencyName() +": "+ info.currencyAmount; + + displayTo.sendMessage(message); + } + + } + + void sendMessage(Player player, TradeCraft.MessageTypes messageType, String format, Object... args) { + try { + String message = String.format(format, args); + player.sendMessage(TradeCraft.properties.getMessageTypeColor(messageType) + message); + } catch ( IllegalFormatException e ) { + player.sendMessage(TradeCraftLocalization.get("ERROR_IN_FORMAT_STRING") +" "+ format); + } + } + + void sendMessage(Player player, String format, Object... args) { + try { + String message = String.format(format, args); + player.sendMessage(message); + } catch ( IllegalFormatException e ) { + player.sendMessage(TradeCraftLocalization.get("ERROR_IN_FORMAT_STRING") +" "+ format); + } + } + + void trace(Player player, String format, Object... args) { + if (properties.getEnableDebugMessages()) { + if (null != player) { + sendMessage(player, format, args); + } else { + this.log(Level.INFO, format, args); + } + } + } + /* + * When a block behind a shop sign is destroyed, the sign would be destroyed too. + * Check all side faces of this block, for a sign attached to this block. Then + * pass that sign block to getShopFromSignBlock. + * + * This should only be used for checking whether a normal block can be destroyed, for there not being any + * signs attached to it, or this block being a chest or sign itself. + * Since one block can + */ + ArrayList getShopsFromBlock(Player player, Block block) { + ArrayList shops = new ArrayList(); + // use other function(s) directly if applicable + if ( block.getType() == Material.CHEST || block.getType() == Material.WALL_SIGN ) { + TradeCraftShop shop = getShopFromSignOrChestBlock(player, block); + if ( shop != null ) { + shops.add(shop); + } + } else { + // go through all 4 faces of this block to check for a sign attached to this block + final BlockFace[] sides = {BlockFace.NORTH, BlockFace.EAST, BlockFace.SOUTH, BlockFace.WEST}; + for ( int index_sides = 0; index_sides < sides.length; index_sides++ ) { + BlockFace side = sides[index_sides]; + // get the block on that side + Block sideBlock = block.getRelative(side); + // check for it being a wall sign + if ( sideBlock.getType() == Material.WALL_SIGN ) { + // get the sign (extending MaterialData) for clean attached face checking + org.bukkit.material.Sign materialSign = new org.bukkit.material.Sign(Material.WALL_SIGN, sideBlock.getData()); + /* Now, for easy comparison, we'll compare to the direction the sign is facing. If that's + * the same as the current face (of the 4 we're looping through), then this sign is attached + * to the block that is being destroyed. + */ + if ( materialSign.getFacing() == side ) { + TradeCraftShop shop = this.getShopFromSignBlock(player, sideBlock); + if ( shop != null ) { + shops.add(shop); + } + } + + } + } + } + return shops; + } + TradeCraftShop getShopFromSignOrChestBlock(Player player, Block block) { + if (block.getType() == Material.CHEST) { + block = block.getWorld().getBlockAt( + block.getX(), + block.getY() + 1, + block.getZ() + ); + } + + return getShopFromSignBlock(player, block); + } + + TradeCraftShop getShopFromSignBlock(Player player, Block block) { + if (block.getType() != Material.WALL_SIGN) { + return null; + } + + int x = block.getX(); + int y = block.getY(); + int z = block.getZ(); + + trace(player, "You clicked a sign at %d, %d, %d in world: %s.", x, y, z, block.getWorld().getName()); + + Sign sign = (Sign) block.getWorld().getBlockAt(x, y, z).getState(); + + // The sign at this location can be null if it was just destroyed. + if (sign == null) { + trace(player, "The sign is no longer there."); + + return null; + } + + String itemName = getItemName(sign.getLines()); + + if (itemName == null) { + trace(player, "There is no item name on the sign."); + + return null; + } + + trace(player, "The item name on the sign is %s.", itemName); + + Block blockBelowSign = block.getRelative(0, -1, 0); + + if (blockBelowSign.getType() != Material.CHEST) { + trace(player, "There is no chest beneath the sign."); + return null; + } + + Chest chest = (Chest) blockBelowSign.getState(); + + if (itemName.toLowerCase().equals("repair")) { + if (!properties.getRepairShopsEnabled()) { + trace(player, "Repair shops are not enabled."); + return null; + } + + if (player == null || !player.isOp()) { + trace(player, "You can't use repair shops."); + return null; + } + + trace(player, "This is a repair shop."); + return new TradeCraftRepairShop(this, sign, chest); + } + + if (!configuration.isConfigured(itemName)) { + trace(player, + "The item name %s is not configured in the config file.", + itemName); + return null; + } + + // TODO change to use chest getOwner + //String ownerName = getOwnerName(sign.getLine(3)); + String ownerName = data.getOwnerOfSign(sign); + + if (ownerName == null) { + trace(player, "There is no owner name on the sign."); + + if (!properties.getInfiniteShopsEnabled()) { + trace(player, "Ininite shops are not enabled."); + return null; + } + + trace(player, "This is an infinite shop."); + try { + return new TradeCraftInfiniteShop(this, sign, chest); + } catch (Exception e) { + trace(player, e.getMessage()); + } + return null; + } + + trace(player, "The owner name on the sign is %s.", ownerName); + + if (!properties.getPlayerOwnedShopsEnabled()) { + trace(player, "Player-owned shops are not enabled."); + return null; + } + + trace(player, "This is a player-owned shop."); + return new TradeCraftPlayerOwnedShop(this, sign, chest, ownerName); + } + + String getItemName(String[] signLines) { + return getSpecialText(signLines, "[", "]"); + } + + String getOwnerName(String signLine) { + return getSpecialTextOnLine(signLine, "-", "-"); + } + + private String getSpecialText(String[] signLines, String prefix, String suffix) { + for (int i = 0; i < 4; i++) { + String text = getSpecialTextOnLine(signLines[i], prefix, suffix); + + if (text != null) { + return text; + } + } + + return null; + } + + private String getSpecialTextOnLine(String signLine, String prefix, String suffix) { + if (signLine == null) { + return null; + } + + signLine = signLine.trim(); + + if (signLine.startsWith(prefix) && signLine.endsWith(suffix) + && signLine.length() > 2) { + + String text = signLine.substring(1, signLine.length() - 1); + text = text.trim(); + if (text.equals("")) { + return null; + } + + return text; + } + + return null; + } + + TradeCraftExchangeRate getExchangeRate(String[] signLines, int lineNumber) { + if ( lineNumber < 0 || lineNumber >= signLines.length ) { + return null; + } + return getExchangeRate(signLines[lineNumber]); + } + TradeCraftExchangeRate getExchangeRate(String signLine) { + return new TradeCraftExchangeRate(signLine); } + + static int getMaxStackSize(int itemType) { + if ( TradeCraft.properties.getNormalStackSizeUsed() ) { + return Material.getMaterial(itemType).getMaxStackSize(); + } else { + return 64; + } + } + + /** + * Get a CamelCased string based on the current currency. + * + * @return a string representing the currency. + */ + public String getCurrencyName() { + // Try to get the name from the configuration file first + TradeCraftConfigurationInfo configInfo = this.configuration.get(TradeCraft.currency); + if ( configInfo != null ) { + return configInfo.name; + } else { + + ItemStack currencyStack = new ItemStack(TradeCraft.currency.id, 1, TradeCraft.currency.data); // weird that there's no Material.getMaterial(id, short/byte) + MaterialData currencyData = currencyStack.getType().getNewData((byte)TradeCraft.currency.data); + String currencyString; + if ( currencyData == null ) { + currencyString = currencyStack.getType().name(); + } else { + currencyString = currencyData.toString(); + } + + //String baseName = stack.getType().name(); + String[] words = currencyString.replace("null ", "").split("\\(")[0].split("[ _]{1}"); + String name = ""; + for ( int word_ind = 0; word_ind < words.length; word_ind++ ) { + String word = words[word_ind]; + if ( word_ind > 0 ) { + name = name.concat(" "); + } + name = name.concat(word.substring(0, 1).toUpperCase()).concat(word.substring(1).toLowerCase()); + } + return name; + } + } + + private void displayCommandHelpText(Player player) { + if ( player != null ) { + this.sendMessage(player, TradeCraftLocalization.get("POSSIBLE_COMMANDS_FOR_THE_PLUGIN"), + TradeCraft.pluginName); + this.sendMessage(player, "/tc[help]"+ ChatColor.GRAY +" "+ TradeCraftLocalization.get("TC_HELP_THIS_TEXT")); + this.sendMessage(player, "/tcshops"+ ChatColor.GRAY +" "+ TradeCraftLocalization.get("TC_SHOPS")); + if ( this.permissions.canQueryOtherShops(player) ) { + this.sendMessage(player, "/tcpshops [player]"+ ChatColor.GRAY +" "+ TradeCraftLocalization.get("TC_PSHOPS")); + } + this.sendMessage(player, "/tcgetcurrency"+ ChatColor.GRAY +" "+ TradeCraftLocalization.get("TC_CURRENCY_GET_CURRENCY")); + if ( this.permissions.canSetCurrency(player) ) { + this.sendMessage(player, "/tcsetcurrency [id[;data]]"+ ChatColor.GRAY +" "+ TradeCraftLocalization.get("TC_CURRENCY_OPT_PARAM_GETSET_CURRENCY")); + } + if ( this.permissions.canReload(player) ) { + this.sendMessage(player, "/tcreload"+ ChatColor.GRAY +" "+ TradeCraftLocalization.get("TC_RELOAD")); + } + if ( this.permissions.canQueryPlayer(player) ) { + this.sendMessage(player, "/tcplayerperms"+ ChatColor.GRAY +" "+ TradeCraftLocalization.get("TC_CAN_PLAYER")); + } + } else { + // console command help + this.log(Level.INFO, TradeCraftLocalization.get("POSSIBLE_COMMANDS_FOR_THE_PLUGIN"), + TradeCraft.pluginName); + this.log(Level.INFO, "tc[help]: "+ TradeCraftLocalization.get("TC_HELP_THIS_TEXT")); + this.log(Level.INFO, "tcplayerperms playername: "+ TradeCraftLocalization.get("TC_CAN_PLAYER")); + this.log(Level.INFO, "tcpshops [player]: "+ TradeCraftLocalization.get("TC_PSHOPS")); + this.log(Level.INFO, "tcreload: "+ TradeCraftLocalization.get("TC_RELOAD")); + } + } + + public void saveConfig() { + Logger.getLogger(JavaPlugin.class.getName()).log(Level.INFO, "saving config to: "+ this.getConfig("config").getFile().getAbsolutePath()); + try { + this.getConfig("config").save(); + } catch (IOException ex) { + Logger.getLogger(JavaPlugin.class.getName()).log(Level.SEVERE, "Could not save config to " + this.getConfig("config").getFile().getName(), ex); + } + } + public StatefulYamlConfiguration getConfig() { + return this.getConfig("config"); + } + public StatefulYamlConfiguration getConfig(String name) { + if (name.indexOf(".") < 0) { + name += ".yml"; + } + if (this.configs.containsKey(name)) { + return this.configs.get(name); + } else { + File configFile = new File(this.getDataFolder(), name); + StatefulYamlConfiguration config = new StatefulYamlConfiguration(configFile); + this.configs.put(name, config); + return config; + } + } + + public void log(Level level, String format, Object... args) { + this.log.log(level, TradeCraft.pluginName +": "+ String.format(format, args)); + } + + public void useLog(Player player, TradeCraftShop shop, String format, Object... args) { + if ( TradeCraft.properties.logShopUse() ) { + if ( this.usageLog != null ) { + try { + SimpleDateFormat formatter = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss"); + this.usageLog.write(String.format("%s %s \t%s %s\n", + formatter.format(new Date()), + shop.toString(), + player.getDisplayName(), + String.format(format, args))); + this.usageLog.flush(); + } catch (IOException e) { + this.log(Level.WARNING, "Failed to write to shop use log file"); + } + } else { + this.log(Level.INFO, "not written to log file"); + } + } + } + +} \ No newline at end of file diff --git a/src/nl/armeagle/TradeCraft/TradeCraftBlockListener.java b/src/nl/armeagle/TradeCraft/TradeCraftBlockListener.java new file mode 100644 index 0000000..10d8e3b --- /dev/null +++ b/src/nl/armeagle/TradeCraft/TradeCraftBlockListener.java @@ -0,0 +1,224 @@ +package nl.armeagle.TradeCraft; + +import java.util.ArrayList; +import java.util.List; + +import org.bukkit.Material; +import org.bukkit.block.Block; +import org.bukkit.block.Sign; +import org.bukkit.entity.Player; +import org.bukkit.event.block.BlockBreakEvent; +import org.bukkit.event.block.BlockPistonExtendEvent; +import org.bukkit.event.block.BlockPistonRetractEvent; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.Listener; +import org.bukkit.event.block.SignChangeEvent; + +public class TradeCraftBlockListener implements Listener{ + + private TradeCraft plugin; + + TradeCraftBlockListener(TradeCraft plugin){ + this.plugin = plugin; + } + + public void debug(String str){ + plugin.getServer().broadcastMessage(str); + } + + @EventHandler + public void onNormalBlockBreak(BlockBreakEvent e) { + this.onBlockBreak(e, EventPriority.NORMAL); + } + + @EventHandler(priority = EventPriority.MONITOR) + public void onMonitorBlockBreak(BlockBreakEvent e) { + this.onBlockBreak(e, EventPriority.MONITOR); + } + + private void onBlockBreak(BlockBreakEvent e, EventPriority p) { + if ( !this.plugin.isEnabled() ) { + return; + } + + Player player = e.getPlayer(); + Block block = e.getBlock(); + ArrayList shops = plugin.getShopsFromBlock(player, block); + + if (shops.size() == 0) { + return; + } + + // Go through all shops in the list and check whether the player can destroy them all first. + // Only if that is the case proceed to destroy. + if (EventPriority.NORMAL == p) { + for ( TradeCraftShop shop : shops ) { + if (!shop.playerCanDestroy(player) && !plugin.permissions.canDestroyShops(player) || + shop.shopCanBeWithdrawnFrom() ) { + // cannot destroy this shop, so cancel destruction, use distinct error messages + if ( shop.shopCanBeWithdrawnFrom() ) { + plugin.sendMessage(player, TradeCraftLocalization.get("ALL_ITEMS_MUST_BE_WITHDRAWN")); + } else { + if ( block.getType() == Material.WALL_SIGN ) { + plugin.sendMessage(player, TradeCraftLocalization.get("YOU_CANT_DESTROY_THIS_SIGN")); + } else if ( block.getType() == Material.CHEST ) { + plugin.sendMessage(player, TradeCraftLocalization.get("YOU_CANT_DESTROY_THIS_CHEST")); + } else { + plugin.sendMessage(player, TradeCraftLocalization.get("YOU_CANT_DESTROY_THIS_BLOCK_ATTACHED")); + } + } + stopDestruction(block,e); + return; + } + } + } + + if (EventPriority.MONITOR == p && ! e.isCancelled()) { + // player can destroy all shops, so proceed + for ( TradeCraftShop shop : shops ) { + plugin.data.deleteShop(shop); + } + } + } + + @EventHandler(priority = EventPriority.MONITOR) + public void onMonitorSignChange(SignChangeEvent e) { + this.onSignChange(e, EventPriority.MONITOR); + } + + @EventHandler + public void onNormalSignChange(SignChangeEvent e) { + this.onSignChange(e, EventPriority.NORMAL); + } + + public void onSignChange(SignChangeEvent event, EventPriority priority) { + if ( !this.plugin.isEnabled() ) { + return; + } + + // Check whether this block is (still) a sign. Can be revoked already in case the sign was temporary, + // for example when the plugin SimpleSignEdit is being used. + if ( event.getBlock().getType() != Material.SIGN_POST && + event.getBlock().getType() != Material.WALL_SIGN ) { + return; + } + + Sign sign = (Sign) event.getBlock().getState(); + + Player player = event.getPlayer(); + String ownerName = player.getName(); + + String itemName = plugin.getItemName(event.getLines()); + + if (itemName == null) { + plugin.trace(player, "sign change, no item name, ignore"); + return; + } + // Check whether this is an existing item. Try to prevent as little normal sign placement as possible. + // Only treat signs with "[name]" as item line where "name" is found in the configuration (.txt) file. + TradeCraftConfigurationInfo itemInfo = plugin.configuration.get(itemName); + if ( itemInfo == null ) { + plugin.trace(player, "sign change, %s is not a valid item name, ignore this sign", itemName); + return; + } + + TradeCraftExchangeRate buyRate = new TradeCraftExchangeRate(event.getLine(1)); + TradeCraftExchangeRate sellRate = new TradeCraftExchangeRate(event.getLine(2)); + // no buy rate means this is an infinite shop + if ( !buyRate.isValid() && !sellRate.isValid() ) { + if (plugin.permissions.canMakeInfShops(player)){ + plugin.trace(player, "sign change, infinite chest of %s", itemName); + return; + } + + if (EventPriority.NORMAL == priority) { + plugin.sendMessage(player, TradeCraftLocalization.get("YOU_CANT_CREATE_INF_SHOPS")); + event.setCancelled(true); + return; + } + } + // there is a buy rate, so this is a player owned shop + + if (EventPriority.NORMAL == priority && !plugin.permissions.canMakePlayerShops(player)){ + plugin.sendMessage(player, TradeCraftLocalization.get("YOU_DONT_HAVE_PERM_CREATE_PLAYER_SHOP")); + event.setCancelled(true); + return; + } + + // check whether the player doesn't have too many shops + if (EventPriority.NORMAL == priority) { + int totalShopLimit = TradeCraft.properties.getPlayerTotalShopLimit(); + int worldShopLimit = TradeCraft.properties.getPlayerWorldShopLimit(); + if (plugin.data.getPlayerShopCount(player) >= totalShopLimit) { + plugin.sendMessage(player, TradeCraftLocalization.get("TOTAL_SHOP_LIMIT_X"), totalShopLimit); + event.setCancelled(true); + return; + } else if (plugin.data.getPlayerShopCount(player, player.getWorld()) >= worldShopLimit) { + plugin.sendMessage(player, TradeCraftLocalization.get("WORLD_SHOP_LIMIT_X"), worldShopLimit); + event.setCancelled(true); + return; + } + + } + + if (EventPriority.MONITOR == priority && !event.isCancelled()) { + plugin.trace(player, "Setting owner of sign to: %s", ownerName); + // set the player name on the last line + event.setLine(3, "-"+ ownerName.substring(0, Math.min(ownerName.length(), 15)) +"-"); + plugin.data.createNewSign(ownerName, itemInfo, sign); + } + + /* + if ( this.plugin.properties.getStrictPlayerShopOwnerNameRequired() ) { + if (player.getName().equalsIgnoreCase(ownerName)) { + plugin.data.setOwnerOfSign(player.getName(), sign); + return; + } + } else { + if (player.getName().startsWith(ownerName)) { + plugin.data.setOwnerOfSign(player.getName(), sign); + return; + } + } + */ + } + + // prevent pistons from pushing away (the block behind) a sign. Block all retract and extend events that would move a block behind a sign, also for shop owners + @EventHandler + public void onNormalBlockPistonRetract(BlockPistonRetractEvent e) { + if (e.isSticky()) { + Block block = e.getRetractLocation().getBlock(); + ArrayList shops = plugin.getShopsFromBlock(null, block); + + if (shops.size() != 0) { + e.setCancelled(true); + } + } + } + @EventHandler + public void onNormalBlockPistonExtend(BlockPistonExtendEvent e) { + List blocks = e.getBlocks(); + for (Block block:blocks) { + ArrayList shops = plugin.getShopsFromBlock(null, block); + + if (shops.size() != 0) { + e.setCancelled(true); + return; + } + } + } + + public void stopDestruction(Block b, BlockBreakEvent e){ + if(b.getState() instanceof Sign){ + Sign sign = (Sign)b.getState(); + String[] lines = sign.getLines(); + for(int i = 0;i<4;i++){ + sign.setLine(i, lines[i]); + } + + sign.update(true); + } + e.setCancelled(true); + } +} diff --git a/nl/armeagle/TradeCraft/TradeCraftChest.java b/src/nl/armeagle/TradeCraft/TradeCraftChest.java similarity index 75% rename from nl/armeagle/TradeCraft/TradeCraftChest.java rename to src/nl/armeagle/TradeCraft/TradeCraftChest.java index d089909..0f8655f 100644 --- a/nl/armeagle/TradeCraft/TradeCraftChest.java +++ b/src/nl/armeagle/TradeCraft/TradeCraftChest.java @@ -17,16 +17,16 @@ public TradeCraftChest(Chest c) { chest = c.getInventory(); for (ItemStack item : chest.getContents()) { - if(item != null){ - short itemData = (item.getData() == null ? (short)0 : item.getDurability()); - if(type.id != 0 && (type.id != item.getTypeId() || type.data != itemData) ){ - diffFlag = true; - return; - } - type.id = item.getTypeId(); - type.data = itemData; - total += item.getAmount(); - } + if(item != null){ + short itemData = (item.getData() == null ? (short)0 : item.getDurability()); + if(type.id != 0 && (type.id != item.getTypeId() || type.data != itemData) ){ + diffFlag = true; + return; + } + type.id = item.getTypeId(); + type.data = itemData; + total += item.getAmount(); + } } } @@ -66,7 +66,7 @@ public int getAmountOfCurrencyInChest() { int amount = 0; for (ItemStack item : ((Inventory)chest).getContents()) { if (item != null) { - short itemData = (item.getData() == null ? (short)0 : item.getDurability()); + short itemData = (item.getData() == null ? (short)0 : item.getDurability()); if (item.getTypeId() == TradeCraft.currency.id && itemData == TradeCraft.currency.data) { amount += item.getAmount(); } @@ -79,7 +79,7 @@ public List getNonCurrencyItems() { List items = new ArrayList(); for (ItemStack item : chest.getContents()) { if (item != null) { - short itemData = (item.getData() == null ? (short)0 : item.getDurability()); + short itemData = (item.getData() == null ? (short)0 : item.getDurability()); if (item.getTypeId() != TradeCraft.currency.id || itemData != TradeCraft.currency.data) { items.add(item); } @@ -89,6 +89,6 @@ public List getNonCurrencyItems() { } public int getSize() { - return (this.chest == null ? 0 : this.chest.getSize()); + return (this.chest == null ? 0 : this.chest.getSize()); } } diff --git a/src/nl/armeagle/TradeCraft/TradeCraftConfigurationFile.java b/src/nl/armeagle/TradeCraft/TradeCraftConfigurationFile.java new file mode 100644 index 0000000..589ab2b --- /dev/null +++ b/src/nl/armeagle/TradeCraft/TradeCraftConfigurationFile.java @@ -0,0 +1,199 @@ +package nl.armeagle.TradeCraft; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileReader; +import java.io.IOException; +import java.util.Arrays; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; +import java.util.Map.Entry; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import org.bukkit.configuration.MemorySection; + +import nl.armeagle.Configuration.StatefulYamlConfiguration; + +/** + * The name of this class is a bit misleading. This class stores all the items and their default + * trade rates that can be used in the game. The actual configuration of the plugin itself + * is handled by the TradeCraftProperties class. + */ +class TradeCraftConfigurationFile { + private static final String fileName = TradeCraft.pluginName + ".txt"; + private static final String configName = "items"; + + private static final Pattern commentPattern = Pattern.compile("^\\s*#.*$"); + private static final Pattern infoPattern = Pattern.compile( + "^\\s*([^,]+)\\s*," + // name + "\\s*(\\d+(?:;\\d+)?)\\s*" + // id[;data] (optional ;data) + "(?:,\\s*(\\d+)\\s*:\\s*(\\d+))?\\s*" + // buyAmount:buyValue + "(?:,\\s*(\\d+)\\s*:\\s*(\\d+))?\\s*$"); // sellAmount:sellValue + + private HashMap mapItemNames = new HashMap(); + + private final TradeCraft plugin; + // create an index from an TradeCraftItem (id;data) to the TradeCraftConfigurationInfo entry. This for configured name lookup based on id;data + private final Map TCitemInfoIndex = new HashMap(); + + TradeCraftConfigurationFile(TradeCraft plugin) { + this.plugin = plugin; + } + + public StatefulYamlConfiguration getConfig() { + return this.plugin.getConfig(TradeCraftConfigurationFile.configName); + } + + void load() { + StatefulYamlConfiguration config = (StatefulYamlConfiguration) this.getConfig(); + + // if file exists, load the config to it once and then rename the old config + File file = new File(plugin.getDataFolder().getAbsolutePath(), fileName); + if ( file.exists() ) { + try { + FileReader reader = new FileReader(file); + BufferedReader configurationFile = new BufferedReader(reader); + + int lineNumber = 0; + String line; + + while ((line = configurationFile.readLine()) != null) { + lineNumber += 1; + + if (line.trim().equals("")) { + continue; + } + + Matcher commentMatcher = commentPattern.matcher(line); + + if (commentMatcher.matches()) { + continue; + } + + Matcher infoMatcher = infoPattern.matcher(line); + + if (!infoMatcher.matches()) { + plugin.log.warning( + "Failed to parse line number " + lineNumber + + " in " + file.getAbsolutePath() + + ": " + line); + continue; + } + + TradeCraftConfigurationInfo info = new TradeCraftConfigurationInfo(); + info.name = infoMatcher.group(1); + + // try to split ID and Data, separated by a semicolon mark + Matcher IdSplitData = TradeCraft.itemPatternIdSplitData.matcher(infoMatcher.group(2)); + + if (!IdSplitData.matches()) { + plugin.log.info( + "Failed to parse line number " + lineNumber + + " in " + file.getAbsolutePath() + + ": " + line); + continue; + } + + int id = Integer.parseInt(IdSplitData.group(1)); + if ( IdSplitData.group(2) != null ) { + short data = Short.parseShort(IdSplitData.group(2)); + info.type = new TradeCraftItem(id, data); + } else { + info.type = new TradeCraftItem(id); + } + + if (infoMatcher.group(3) != null) { + info.sellAmount = info.buyAmount = Integer.parseInt(infoMatcher.group(3)); + info.sellValue = info.buyValue = Integer.parseInt(infoMatcher.group(4)); + } + + if (infoMatcher.group(5) != null) { + info.sellAmount = Integer.parseInt(infoMatcher.group(5)); + info.sellValue = Integer.parseInt(infoMatcher.group(6)); + } + +// config.set(info.name.toUpperCase(), info.toMemoryConfiguration()); + Iterator> iter = info.toMap().entrySet().iterator(); + while (iter.hasNext()) { + Map.Entry pairs = (Map.Entry)iter.next(); + if (!pairs.getKey().equals("name")) { + config.set(info.name +"."+ pairs.getKey(), pairs.getValue()); + } + } + +// TCitemInfoIndex.put(info.type, info.name); + } + configurationFile.close(); + reader.close(); + config.save(); + if (file.renameTo(new File(file.getAbsolutePath() + ".converted.to."+ TradeCraftConfigurationFile.configName +".yml"))) { + plugin.log.info("Converted old config to new style and renamed the old config file"); + } else { + plugin.log.severe("FAILED to convert old config to new style"); + } + + } catch (IOException e) { + plugin.log.severe("Error reading " + file.getAbsolutePath()); + } + } else { + try { + config.load(); + } catch (IOException e) { + plugin.log.severe("Error loading plugin config file"); + } + } + + Iterator> iter = config.getValues(false).entrySet().iterator(); + while (iter.hasNext()) { + // store map of lowercase item names to key names in the configuration + Map.Entry entry = (Map.Entry)iter.next(); + this.mapItemNames.put(entry.getKey().toLowerCase(), entry.getKey()); + // store map of item types to key names + MemorySection section = (MemorySection) entry.getValue(); + TradeCraftItem tcItem = new TradeCraftItem(section.getInt("itemTypeId", 266), section.getInt("itemTypeData", 0)); + TCitemInfoIndex.put(tcItem, entry.getKey()); + } + } + + public String[] getNames() { + String[] names = this.getConfig().getKeys(false).toArray(new String[0]); + Arrays.sort(names); + return names; + } + + public boolean isConfigured(String name) { + return this.mapItemNames.containsKey(name.toLowerCase()); + } + + public TradeCraftConfigurationInfo get(String name) { + + // @todo, config code doesn't seem to like serialized objects, cannot seem to find the TradeCraftConfigurationInfo class + // though, the otherwise resulting ==: classpath lines aren't very user friendly anyway. +// return (TradeCraftConfigurationInfo) plugin.getConfig().get(name.toUpperCase()); + String itemName = this.mapItemNames.get(name.toLowerCase()); + if (null != itemName) { + MemorySection item = ((MemorySection)this.getConfig().get(itemName)); + if (null != item) { + Map itemData = item.getValues(false); + if (null != itemData) { + return new TradeCraftConfigurationInfo(itemData, name); + } + } + } + return null; + } + public TradeCraftConfigurationInfo get(int id) { + return this.get(new TradeCraftItem(id)); + } + public TradeCraftConfigurationInfo get(int id, short data) { + return this.get(new TradeCraftItem(id, data)); + } + public TradeCraftConfigurationInfo get(TradeCraftItem item) { + if (!TCitemInfoIndex.containsKey(item)) { + return null; + } + return this.get(TCitemInfoIndex.get(item)); + } +} \ No newline at end of file diff --git a/src/nl/armeagle/TradeCraft/TradeCraftConfigurationInfo.java b/src/nl/armeagle/TradeCraft/TradeCraftConfigurationInfo.java new file mode 100644 index 0000000..7b62336 --- /dev/null +++ b/src/nl/armeagle/TradeCraft/TradeCraftConfigurationInfo.java @@ -0,0 +1,55 @@ +package nl.armeagle.TradeCraft; + +import java.util.LinkedHashMap; +import java.util.Map; +import org.bukkit.configuration.serialization.ConfigurationSerializable; +import org.bukkit.configuration.serialization.SerializableAs; + +@SerializableAs("nl.armeagle.TradeCraft.TradeCraftConfigurationInfo") +class TradeCraftConfigurationInfo implements ConfigurationSerializable { + public String name; + public TradeCraftItem type; + public int buyAmount; + public int buyValue; + public int sellAmount; + public int sellValue; + + TradeCraftConfigurationInfo() { + } + + TradeCraftConfigurationInfo(Map map, String name) { + this.name = name; + this.type = new TradeCraftItem((Integer) map.get("itemTypeId"), (Integer) map.get("itemTypeData")); + this.buyAmount = (Integer) map.get("buyAmount"); + this.buyValue = (Integer) map.get("buyValue"); + this.sellAmount = (Integer) map.get("sellAmount"); + this.sellValue = (Integer) map.get("sellValue"); + } + TradeCraftConfigurationInfo(Map map) { + this.name = (String) map.get("name"); + this.type = new TradeCraftItem((Integer) map.get("itemTypeId"), (Integer) map.get("itemTypeData")); + this.buyAmount = (Integer) map.get("buyAmount"); + this.buyValue = (Integer) map.get("buyValue"); + this.sellAmount = (Integer) map.get("sellAmount"); + this.sellValue = (Integer) map.get("sellValue"); + } + + public Map toMap() { + LinkedHashMap map = new LinkedHashMap(); + map.put("name", this.name); + map.put("itemTypeId", this.type.id); + map.put("itemTypeData", this.type.data); + map.put("buyAmount", this.buyAmount); + map.put("buyValue", this.buyValue); + map.put("sellAmount", this.sellAmount); + map.put("sellValue", this.sellValue); + return map; + } + @Override + public Map serialize() { + return this.toMap(); + } + public static TradeCraftConfigurationInfo deserialize(Map map) { + return new TradeCraftConfigurationInfo(map); + } +} \ No newline at end of file diff --git a/nl/armeagle/TradeCraft/TradeCraftDataFile.java b/src/nl/armeagle/TradeCraft/TradeCraftDataFile.java similarity index 73% rename from nl/armeagle/TradeCraft/TradeCraftDataFile.java rename to src/nl/armeagle/TradeCraft/TradeCraftDataFile.java index 2e4cb06..29f7dbd 100644 --- a/nl/armeagle/TradeCraft/TradeCraftDataFile.java +++ b/src/nl/armeagle/TradeCraft/TradeCraftDataFile.java @@ -17,13 +17,13 @@ import org.bukkit.entity.Player; class TradeCraftDataFile { - /* - * As of version 1.0.5 there is support for multiple worlds. - * Newly created shops will add the world name to the information stored. - * Old shops will be converted when first interacted with. - */ + /* + * As of version 1.0.5 there is support for multiple worlds. + * Newly created shops will add the world name to the information stored. + * Old shops will be converted when first interacted with. + */ - private static final String fileName = "plugins" + File.separator + TradeCraft.pluginName+ File.separator + TradeCraft.pluginName + ".data"; + private static final String fileName = "plugins" + File.separator + TradeCraft.pluginName+ File.separator + TradeCraft.pluginName + ".data"; private static final Pattern infoPatternNoWorld = Pattern.compile( "^\\s*([^,]+)\\s*," + // ownerName "\\s*(-?\\d+)\\s*,\\s*(-?\\d+)\\s*,\\s*(-?\\d+)\\s*," + // x,y,z @@ -50,7 +50,7 @@ class TradeCraftDataFile { public void load() { try { - dFile.createNewFile(); + dFile.createNewFile(); data.clear(); BufferedReader reader = new BufferedReader(new FileReader(fileName)); @@ -82,15 +82,15 @@ public void load() { itemAmount = Integer.parseInt(infoMatcher2.group(6)); currencyAmount = Integer.parseInt(infoMatcher2.group(7)); } else { - // support for multiple worlds - Matcher infoMatcher3 = infoPatternWorld.matcher(line); - if ( !infoMatcher3.matches()) { - plugin.log.warning( + // support for multiple worlds + Matcher infoMatcher3 = infoPatternWorld.matcher(line); + if ( !infoMatcher3.matches()) { + plugin.log.warning( "Failed to parse line number " + lineNumber + " in " + fileName + ": " + line); continue; - } + } ownerName = infoMatcher3.group(1); worldName = infoMatcher3.group(2); @@ -116,12 +116,12 @@ public void load() { if ( IdSplitData.matches() ) { int itemId = Integer.parseInt(IdSplitData.group(1)); if ( IdSplitData.group(2) != null ) { - info.itemType = new TradeCraftItem(itemId, Short.parseShort(IdSplitData.group(2))); + info.itemType = new TradeCraftItem(itemId, Short.parseShort(IdSplitData.group(2))); } else { - info.itemType = new TradeCraftItem(itemId); + info.itemType = new TradeCraftItem(itemId); } } else { - plugin.log.warning( + plugin.log.warning( "Failed to parse line number " + lineNumber + " in " + fileName + ": " + line); @@ -138,13 +138,13 @@ public void load() { } public void save() { - if ( ! this.wasLoaded ) { - this.plugin.log.severe("TradeCraft: failed to load data file when plugin was enabled, will not save to prevent loss of items."); - // The failure should have been such that no interaction with shops would have been possible, so no items should have been lost since the plugin was - // loaded till this save point. TODO, make sure that no items are lost, when save is actually called after motations, even though that situation - // should never possibly occur. - return; - } + if ( ! this.wasLoaded ) { + this.plugin.log.severe("TradeCraft: failed to load data file when plugin was enabled, will not save to prevent loss of items."); + // The failure should have been such that no interaction with shops would have been possible, so no items should have been lost since the plugin was + // loaded till this save point. TODO, make sure that no items are lost, when save is actually called after motations, even though that situation + // should never possibly occur. + return; + } try { BufferedWriter writer = new BufferedWriter(new FileWriter(fileName)); @@ -170,24 +170,24 @@ public void save() { } public void deleteShop(TradeCraftShop shop){ - Location l = shop.sign.getBlock().getLocation(); - String key = getKey(shop.sign.getWorld().getName(), l.getBlockX(),l.getBlockY(),l.getBlockZ()); - if(data.containsKey(key)){ - data.remove(key); - save(); - } + Location l = shop.sign.getBlock().getLocation(); + String key = getKey(shop.sign.getWorld().getName(), l.getBlockX(),l.getBlockY(),l.getBlockZ()); + if(data.containsKey(key)){ + data.remove(key); + save(); + } } public Map shopsOwned(String playerName){ - Map list = new HashMap(); - for (String key : data.keySet()) { - TradeCraftDataInfo info = data.get(key); - if(info.ownerName.equalsIgnoreCase(playerName)){ - list.put(key, info); - } - } - - return list; + Map list = new HashMap(); + for (String key : data.keySet()) { + TradeCraftDataInfo info = data.get(key); + if(info.ownerName.equalsIgnoreCase(playerName)){ + list.put(key, info); + } + } + + return list; } public void setOwnerOfSign(String ownerName, Sign sign) { @@ -241,7 +241,7 @@ public void depositItems(String ownerName, Sign sign, TradeCraftItem itemType, i } public void depositCurrency(String ownerName, Sign sign, int currencyAmount) { - TradeCraftDataInfo info; + TradeCraftDataInfo info; String key = getKeyFromSign(sign); if (data.containsKey(key)) { @@ -301,30 +301,30 @@ private String getKeyFromSign(Sign sign) { String keyWithWorld = getKey(sign.getWorld().getName(), sign.getX(), sign.getY(), sign.getZ()); // convert old style keys (without world name) to new style and return the new key if ( !data.containsKey(keyWithWorld) ) { - // try the old style key, without the world part - String keyWithoutWorld = getKey(null, sign.getX(), sign.getY(), sign.getZ()); - if ( data.containsKey(keyWithoutWorld) ) { - TradeCraftDataInfo shopInfo = data.get(keyWithoutWorld); - data.remove(keyWithoutWorld); - data.put(keyWithWorld, shopInfo); - } + // try the old style key, without the world part + String keyWithoutWorld = getKey(null, sign.getX(), sign.getY(), sign.getZ()); + if ( data.containsKey(keyWithoutWorld) ) { + TradeCraftDataInfo shopInfo = data.get(keyWithoutWorld); + data.remove(keyWithoutWorld); + data.put(keyWithWorld, shopInfo); + } } return keyWithWorld; } private String getKey(String world, int x, int y, int z) { - // support for multiple words now, optionally accepting a world passed on. - if ( world == null ) { - return x + "," + y + "," + z; - } else { - return world + "," + x + "," + y + "," + z; - } + // support for multiple words now, optionally accepting a world passed on. + if ( world == null ) { + return x + "," + y + "," + z; + } else { + return world + "," + x + "," + y + "," + z; + } } - public void createNewSign(String ownerName, TradeCraftConfigurationInfo itemInfo, Sign sign) { - TradeCraftDataInfo info; - String key = getKeyFromSign(sign); - + public void createNewSign(String ownerName, TradeCraftConfigurationInfo itemInfo, Sign sign) { + TradeCraftDataInfo info; + String key = getKeyFromSign(sign); + if (data.containsKey(key)) { info = data.get(key); } else { @@ -339,22 +339,22 @@ public void createNewSign(String ownerName, TradeCraftConfigurationInfo itemInfo data.put(key, info); save(); - } - - public int getPlayerShopCount(Player player, World world) { - Map list = this.shopsOwned(player.getName()); - int shopsOwnedCount = 0; - - for (String key : list.keySet()) { - TradeCraftDataInfo info = data.get(key); - if (info.worldName.equalsIgnoreCase(world.getName())) { - shopsOwnedCount++; - } - } - - return shopsOwnedCount; - } - public int getPlayerShopCount(Player player) { - return this.shopsOwned(player.getName()).size(); - } + } + + public int getPlayerShopCount(Player player, World world) { + Map list = this.shopsOwned(player.getName()); + int shopsOwnedCount = 0; + + for (String key : list.keySet()) { + TradeCraftDataInfo info = data.get(key); + if (info.worldName.equalsIgnoreCase(world.getName())) { + shopsOwnedCount++; + } + } + + return shopsOwnedCount; + } + public int getPlayerShopCount(Player player) { + return this.shopsOwned(player.getName()).size(); + } } \ No newline at end of file diff --git a/nl/armeagle/TradeCraft/TradeCraftDataInfo.java b/src/nl/armeagle/TradeCraft/TradeCraftDataInfo.java similarity index 61% rename from nl/armeagle/TradeCraft/TradeCraftDataInfo.java rename to src/nl/armeagle/TradeCraft/TradeCraftDataInfo.java index 18e50ea..e9dce65 100644 --- a/nl/armeagle/TradeCraft/TradeCraftDataInfo.java +++ b/src/nl/armeagle/TradeCraft/TradeCraftDataInfo.java @@ -8,10 +8,10 @@ class TradeCraftDataInfo { public int currencyAmount; public TradeCraftDataInfo() { - this.ownerName = null; - this.worldName = null; - this.itemType = null; - this.itemAmount = 0; - this.currencyAmount = 0; - } + this.ownerName = null; + this.worldName = null; + this.itemType = null; + this.itemAmount = 0; + this.currencyAmount = 0; + } } \ No newline at end of file diff --git a/nl/armeagle/TradeCraft/TradeCraftExchangeRate.java b/src/nl/armeagle/TradeCraft/TradeCraftExchangeRate.java similarity index 52% rename from nl/armeagle/TradeCraft/TradeCraftExchangeRate.java rename to src/nl/armeagle/TradeCraft/TradeCraftExchangeRate.java index a63d6e7..5fd3ae0 100644 --- a/nl/armeagle/TradeCraft/TradeCraftExchangeRate.java +++ b/src/nl/armeagle/TradeCraft/TradeCraftExchangeRate.java @@ -10,18 +10,18 @@ public class TradeCraftExchangeRate { private static final Pattern ratePattern = Pattern.compile("(\\d+)\\D+(\\d+)\\s*"); TradeCraftExchangeRate(String signLine) { - Matcher matcher = ratePattern.matcher(signLine); + Matcher matcher = ratePattern.matcher(signLine); - if (matcher.find()) { - this.amount = Integer.parseInt(matcher.group(1)); - this.value = Integer.parseInt(matcher.group(2)); - } else { - this.amount = 0; - this.value = 0; - } + if (matcher.find()) { + this.amount = Integer.parseInt(matcher.group(1)); + this.value = Integer.parseInt(matcher.group(2)); + } else { + this.amount = 0; + this.value = 0; + } } public boolean isValid() { - return this.amount != 0; + return this.amount != 0; } } diff --git a/nl/armeagle/TradeCraft/TradeCraftInfiniteShop.java b/src/nl/armeagle/TradeCraft/TradeCraftInfiniteShop.java similarity index 93% rename from nl/armeagle/TradeCraft/TradeCraftInfiniteShop.java rename to src/nl/armeagle/TradeCraft/TradeCraftInfiniteShop.java index b717bf2..94786d5 100644 --- a/nl/armeagle/TradeCraft/TradeCraftInfiniteShop.java +++ b/src/nl/armeagle/TradeCraft/TradeCraftInfiniteShop.java @@ -13,12 +13,12 @@ public TradeCraftInfiniteShop(TradeCraft plugin, Sign sign, Chest chest) throws String itemName = plugin.getItemName(sign.getLines()); configurationInfo = plugin.configuration.get(itemName); if (null == configurationInfo) { - throw new Exception("Invalid item name on sign: "+ itemName); + throw new Exception("Invalid item name on sign: "+ itemName); } } public boolean playerCanDestroy(Player player) { - return plugin.permissions.canDestroyShops(player); + return plugin.permissions.canDestroyShops(player); } public boolean shopCanBeWithdrawnFrom() { diff --git a/src/nl/armeagle/TradeCraft/TradeCraftItem.java b/src/nl/armeagle/TradeCraft/TradeCraftItem.java new file mode 100644 index 0000000..9bb32f5 --- /dev/null +++ b/src/nl/armeagle/TradeCraft/TradeCraftItem.java @@ -0,0 +1,63 @@ +package nl.armeagle.TradeCraft; + +/** + * + * @author ArmEagle + * Store item type(ID) and optionally the data bit + */ +public class TradeCraftItem implements Comparable { + public int id; + public short data; + + TradeCraftItem(int id) { + this(id, (short)0); + } + TradeCraftItem(int id, int data) { + this(id, new Integer(data).shortValue()); + } + TradeCraftItem(int id, short data) { + this.id = id; + this.data = data; + } + + /** + * @param compare + * @throws NullPointerException if compare is null + * @return default < 0 > compare values + */ + @Override public int compareTo(TradeCraftItem compare) { + if ( this == compare ) { + return 0; + } + if ( this.id < compare.id ) { + return -1; + } else if ( this.id > compare.id ) { + return 1; + } else { + if ( this.data < compare.data ) { + return -1; + } else if ( this.data > compare.data ) { + return 1; + } else { + return 0; + } + } + } + @Override public boolean equals(Object compare) { + return (compare == null ? false : (compare instanceof TradeCraftItem? this.compareTo((TradeCraftItem)compare) == 0 : false)); + } + @Override public int hashCode() { + return this.id * 32768 + this.data; + } + + @Override public String toString() { + return "TradeCraftItem("+ this.id +";"+ this.data +")"; + } + public String toShortString() { + if ( this.data == 0 ) { + return String.valueOf(this.id); + } else { + return this.id +";"+ this.data; + } + } +} diff --git a/nl/armeagle/TradeCraft/TradeCraftItemShop.java b/src/nl/armeagle/TradeCraft/TradeCraftItemShop.java similarity index 50% rename from nl/armeagle/TradeCraft/TradeCraftItemShop.java rename to src/nl/armeagle/TradeCraft/TradeCraftItemShop.java index 09b5fde..ad65463 100644 --- a/nl/armeagle/TradeCraft/TradeCraftItemShop.java +++ b/src/nl/armeagle/TradeCraft/TradeCraftItemShop.java @@ -27,61 +27,61 @@ private void handleOwnerClick(Player player) { if (getChestItemCount() == 0) { int currencyAmount = withdrawCurrency(); if (currencyAmount > 0) { - // limit amount of currency dropped into the chest to the max amount it can hold - int maxCurrencyChestCanHold = chest.getSize() * TradeCraft.getMaxStackSize(TradeCraft.currency.id); - if ( currencyAmount > maxCurrencyChestCanHold ) { - populateChest(TradeCraft.currency, maxCurrencyChestCanHold); // fill to the max - depositCurrency(currencyAmount - maxCurrencyChestCanHold); // return remaining money back to shop data - plugin.sendMessage(player, TradeCraft.MessageTypes.WITHDRAW, - TradeCraftLocalization.get("WITHDREW_X_CURRENCY_SHOP_STILL_HOLDS_Y_CURRENCY"), - maxCurrencyChestCanHold, - plugin.getCurrencyName(), - currencyAmount - maxCurrencyChestCanHold); - plugin.useLog(player, this, - TradeCraftLocalization.get("WITHDREW_X_CURRENCY_SHOP_STILL_HOLDS_Y_CURRENCY"), - maxCurrencyChestCanHold, - plugin.getCurrencyName(), - currencyAmount - maxCurrencyChestCanHold); - } else { - populateChest(TradeCraft.currency, currencyAmount); - plugin.sendMessage(player, TradeCraft.MessageTypes.WITHDRAW, - TradeCraftLocalization.get("WITHDREW_X_A"), - currencyAmount, - plugin.getCurrencyName()); - plugin.useLog(player, this, - TradeCraftLocalization.get("WITHDREW_X_A"), - currencyAmount, - plugin.getCurrencyName()); - } + // limit amount of currency dropped into the chest to the max amount it can hold + int maxCurrencyChestCanHold = chest.getSize() * TradeCraft.getMaxStackSize(TradeCraft.currency.id); + if ( currencyAmount > maxCurrencyChestCanHold ) { + populateChest(TradeCraft.currency, maxCurrencyChestCanHold); // fill to the max + depositCurrency(currencyAmount - maxCurrencyChestCanHold); // return remaining money back to shop data + plugin.sendMessage(player, TradeCraft.MessageTypes.WITHDRAW, + TradeCraftLocalization.get("WITHDREW_X_CURRENCY_SHOP_STILL_HOLDS_Y_CURRENCY"), + maxCurrencyChestCanHold, + plugin.getCurrencyName(), + currencyAmount - maxCurrencyChestCanHold); + plugin.useLog(player, this, + TradeCraftLocalization.get("WITHDREW_X_CURRENCY_SHOP_STILL_HOLDS_Y_CURRENCY"), + maxCurrencyChestCanHold, + plugin.getCurrencyName(), + currencyAmount - maxCurrencyChestCanHold); + } else { + populateChest(TradeCraft.currency, currencyAmount); + plugin.sendMessage(player, TradeCraft.MessageTypes.WITHDRAW, + TradeCraftLocalization.get("WITHDREW_X_A"), + currencyAmount, + plugin.getCurrencyName()); + plugin.useLog(player, this, + TradeCraftLocalization.get("WITHDREW_X_A"), + currencyAmount, + plugin.getCurrencyName()); + } } else { - // limit amount of items dropped into the chest + // limit amount of items dropped into the chest int itemAmount = withdrawItems(); if (itemAmount > 0) { - int maxItemsChestCanHold = chest.getSize() * TradeCraft.getMaxStackSize(getItemType().id); - if ( itemAmount > maxItemsChestCanHold ) { - populateChest(getItemType(), maxItemsChestCanHold); - depositItems(itemAmount - maxItemsChestCanHold); - plugin.sendMessage(player, TradeCraft.MessageTypes.WITHDRAW, - TradeCraftLocalization.get("WITHDREW_X_A_SHOP_STILL_HOLDS_Y_A"), - maxItemsChestCanHold, - getItemName(), - itemAmount - maxItemsChestCanHold); - plugin.useLog(player, this, - TradeCraftLocalization.get("WITHDREW_X_A_SHOP_STILL_HOLDS_Y_A"), - maxItemsChestCanHold, - getItemName(), - itemAmount - maxItemsChestCanHold); - } else { - populateChest(getItemType(), itemAmount); - plugin.sendMessage(player, TradeCraft.MessageTypes.WITHDRAW, - TradeCraftLocalization.get("WITHDREW_X_A"), - itemAmount, - getItemName()); - plugin.useLog(player, this, - TradeCraftLocalization.get("WITHDREW_X_A"), - itemAmount, - getItemName()); - } + int maxItemsChestCanHold = chest.getSize() * TradeCraft.getMaxStackSize(getItemType().id); + if ( itemAmount > maxItemsChestCanHold ) { + populateChest(getItemType(), maxItemsChestCanHold); + depositItems(itemAmount - maxItemsChestCanHold); + plugin.sendMessage(player, TradeCraft.MessageTypes.WITHDRAW, + TradeCraftLocalization.get("WITHDREW_X_A_SHOP_STILL_HOLDS_Y_A"), + maxItemsChestCanHold, + getItemName(), + itemAmount - maxItemsChestCanHold); + plugin.useLog(player, this, + TradeCraftLocalization.get("WITHDREW_X_A_SHOP_STILL_HOLDS_Y_A"), + maxItemsChestCanHold, + getItemName(), + itemAmount - maxItemsChestCanHold); + } else { + populateChest(getItemType(), itemAmount); + plugin.sendMessage(player, TradeCraft.MessageTypes.WITHDRAW, + TradeCraftLocalization.get("WITHDREW_X_A"), + itemAmount, + getItemName()); + plugin.useLog(player, this, + TradeCraftLocalization.get("WITHDREW_X_A"), + itemAmount, + getItemName()); + } } else { plugin.sendMessage(player, TradeCraftLocalization.get("THERE_IS_NOTHING_TO_WITHDRAW")); } @@ -89,61 +89,61 @@ private void handleOwnerClick(Player player) { } else if ( getChestItemType().compareTo(TradeCraft.currency) == 0 ) { depositCurrency(getChestItemCount()); plugin.sendMessage(player, TradeCraft.MessageTypes.DEPOSIT, - TradeCraftLocalization.get("DEPOSITED_X_A"), - getChestItemCount(), - plugin.getCurrencyName()); + TradeCraftLocalization.get("DEPOSITED_X_A"), + getChestItemCount(), + plugin.getCurrencyName()); plugin.useLog(player, this, - TradeCraftLocalization.get("DEPOSITED_X_A"), - getChestItemCount(), - plugin.getCurrencyName()); + TradeCraftLocalization.get("DEPOSITED_X_A"), + getChestItemCount(), + plugin.getCurrencyName()); populateChest(new TradeCraftItem(0), 0); int itemAmount = withdrawItems(); if (itemAmount > 0) { - int maxItemsChestCanHold = chest.getSize() * TradeCraft.getMaxStackSize(getChestItemType().id); - if ( itemAmount > maxItemsChestCanHold ) { - populateChest(getItemType(), maxItemsChestCanHold); - depositItems(itemAmount - maxItemsChestCanHold); + int maxItemsChestCanHold = chest.getSize() * TradeCraft.getMaxStackSize(getChestItemType().id); + if ( itemAmount > maxItemsChestCanHold ) { + populateChest(getItemType(), maxItemsChestCanHold); + depositItems(itemAmount - maxItemsChestCanHold); plugin.sendMessage(player, TradeCraft.MessageTypes.WITHDRAW, - TradeCraftLocalization.get("WITHDREW_X_A_SHOP_STILL_HOLDS_Y_A"), - maxItemsChestCanHold, - getItemName(), - itemAmount - maxItemsChestCanHold); + TradeCraftLocalization.get("WITHDREW_X_A_SHOP_STILL_HOLDS_Y_A"), + maxItemsChestCanHold, + getItemName(), + itemAmount - maxItemsChestCanHold); plugin.useLog(player, this, - TradeCraftLocalization.get("WITHDREW_X_A_SHOP_STILL_HOLDS_Y_A"), - maxItemsChestCanHold, - getItemName(), - itemAmount - maxItemsChestCanHold); - } else { + TradeCraftLocalization.get("WITHDREW_X_A_SHOP_STILL_HOLDS_Y_A"), + maxItemsChestCanHold, + getItemName(), + itemAmount - maxItemsChestCanHold); + } else { populateChest(getItemType(), itemAmount); plugin.sendMessage(player, TradeCraft.MessageTypes.WITHDRAW, - TradeCraftLocalization.get("WITHDREW_X_A"), - itemAmount, - getItemName()); + TradeCraftLocalization.get("WITHDREW_X_A"), + itemAmount, + getItemName()); plugin.useLog(player, this, - TradeCraftLocalization.get("WITHDREW_X_A"), - itemAmount, - getItemName()); - } + TradeCraftLocalization.get("WITHDREW_X_A"), + itemAmount, + getItemName()); + } } } else if ( getChestItemType().compareTo(getItemType()) == 0 ) { depositItems(getChestItemCount()); populateChest(new TradeCraftItem(0), 0); plugin.sendMessage(player, TradeCraft.MessageTypes.DEPOSIT, - TradeCraftLocalization.get("DEPOSITED_X_A"), - getChestItemCount(), - getItemName()); + TradeCraftLocalization.get("DEPOSITED_X_A"), + getChestItemCount(), + getItemName()); plugin.useLog(player, this, - TradeCraftLocalization.get("DEPOSITED_X_A"), - getChestItemCount(), - getItemName()); + TradeCraftLocalization.get("DEPOSITED_X_A"), + getChestItemCount(), + getItemName()); } else { plugin.sendMessage(player, TradeCraftLocalization.get("YOU_CANT_DEPOSIT_THAT_HERE")); } } private void handlePatronClick(Player player) { - - + + boolean playerCanBuy= (plugin.permissions.canBuy(player)); boolean playerCanSell = plugin.permissions.canSell(player); @@ -156,45 +156,45 @@ private void handlePatronClick(Player player) { if (getChestItemCount() == 0) { if (playerCanBuy && playerCanBuy()) { - if ( this instanceof TradeCraftInfiniteShop ) { - plugin.sendMessage(player, - TradeCraftLocalization.get("YOU_CAN_BUY_Y_A_FOR_X_B"), - getBuyAmount(), - getItemName(), - getBuyValue(), - plugin.getCurrencyName()); - } else { - plugin.sendMessage(player, - TradeCraftLocalization.get("YOU_CAN_BUY_X_A_FOR_Y_B_UP_TO_Z"), - getBuyAmount(), - getItemName(), - getBuyValue(), - plugin.getCurrencyName(), - this.getItemsInShop()); - } + if ( this instanceof TradeCraftInfiniteShop ) { + plugin.sendMessage(player, + TradeCraftLocalization.get("YOU_CAN_BUY_Y_A_FOR_X_B"), + getBuyAmount(), + getItemName(), + getBuyValue(), + plugin.getCurrencyName()); + } else { + plugin.sendMessage(player, + TradeCraftLocalization.get("YOU_CAN_BUY_X_A_FOR_Y_B_UP_TO_Z"), + getBuyAmount(), + getItemName(), + getBuyValue(), + plugin.getCurrencyName(), + this.getItemsInShop()); + } } if (playerCanSell && playerCanSell()) { - if ( this instanceof TradeCraftInfiniteShop ) { - plugin.sendMessage(player, - TradeCraftLocalization.get("YOU_CAN_SELL_X_A_FOR_Y_B"), - getSellAmount(), - getItemName(), - getSellValue(), - plugin.getCurrencyName()); - } else { - plugin.sendMessage(player, - TradeCraftLocalization.get("YOU_CAN_SELL_X_A_FOR_Y_B_UP_TO_Z"), - getSellAmount(), - getItemName(), - getSellValue(), - plugin.getCurrencyName(), - this.getCurrencyInShop()); - } + if ( this instanceof TradeCraftInfiniteShop ) { + plugin.sendMessage(player, + TradeCraftLocalization.get("YOU_CAN_SELL_X_A_FOR_Y_B"), + getSellAmount(), + getItemName(), + getSellValue(), + plugin.getCurrencyName()); + } else { + plugin.sendMessage(player, + TradeCraftLocalization.get("YOU_CAN_SELL_X_A_FOR_Y_B_UP_TO_Z"), + getSellAmount(), + getItemName(), + getSellValue(), + plugin.getCurrencyName(), + this.getCurrencyInShop()); + } } if ( this instanceof TradeCraftInfiniteShop ) { - plugin.sendMessage(player, TradeCraftLocalization.get("THIS_IS_AN_INFINITE_SHOP")); + plugin.sendMessage(player, TradeCraftLocalization.get("THIS_IS_AN_INFINITE_SHOP")); } return; } @@ -225,10 +225,10 @@ private void playerWantsToBuy(Player player) { int currencyPlayerWantsToSpend = getChestItemCount(); int amountPlayerWantsToBuy = ((currencyPlayerWantsToSpend - (currencyPlayerWantsToSpend % getBuyValue()) ) / getBuyValue()) * getBuyAmount(); - if ( getBuyAmount() > this.chest.getSize() * TradeCraft.getMaxStackSize(getItemType().id) ) { - plugin.sendMessage(player, TradeCraftLocalization.get("THIS_SHOP_ALWAYS_RETURNS_TOO_MUCH")); - return; - } + if ( getBuyAmount() > this.chest.getSize() * TradeCraft.getMaxStackSize(getItemType().id) ) { + plugin.sendMessage(player, TradeCraftLocalization.get("THIS_SHOP_ALWAYS_RETURNS_TOO_MUCH")); + return; + } if (amountPlayerWantsToBuy == 0) { plugin.sendMessage(player, @@ -240,26 +240,26 @@ private void playerWantsToBuy(Player player) { } if (amountPlayerWantsToBuy > getItemsInShop()) { - if ( getItemsInShop() == 0 ) { - plugin.sendMessage(player, - TradeCraftLocalization.get("CANT_BUY_SHOP_HAS_NO_A_LEFT"), - getItemName()); - } else { - plugin.sendMessage(player, - TradeCraftLocalization.get("CANNOT_BUY_SHOP_ONLY_HAS_X_A"), - getItemsInShop(), - getItemName()); - } - return; + if ( getItemsInShop() == 0 ) { + plugin.sendMessage(player, + TradeCraftLocalization.get("CANT_BUY_SHOP_HAS_NO_A_LEFT"), + getItemName()); + } else { + plugin.sendMessage(player, + TradeCraftLocalization.get("CANNOT_BUY_SHOP_ONLY_HAS_X_A"), + getItemsInShop(), + getItemName()); + } + return; } int requiredCurrencyForThatAmount = amountPlayerWantsToBuy * getBuyValue() / getBuyAmount(); if ( Math.ceil( (currencyPlayerWantsToSpend - requiredCurrencyForThatAmount) / TradeCraft.getMaxStackSize(TradeCraft.currency.id)) - + Math.ceil( amountPlayerWantsToBuy / TradeCraft.getMaxStackSize(getItemType().id)) - > this.chest.getSize() ) { - plugin.sendMessage(player, TradeCraftLocalization.get("THIS_SHOP_WOULD_RETURN_TOO_MANY_BUY_LESS")); - return; + + Math.ceil( amountPlayerWantsToBuy / TradeCraft.getMaxStackSize(getItemType().id)) + > this.chest.getSize() ) { + plugin.sendMessage(player, TradeCraftLocalization.get("THIS_SHOP_WOULD_RETURN_TOO_MANY_BUY_LESS")); + return; } updateItemAndCurrencyAmounts(-amountPlayerWantsToBuy, requiredCurrencyForThatAmount); @@ -270,13 +270,13 @@ private void playerWantsToBuy(Player player) { chest.update(); plugin.sendMessage(player, - TradeCraftLocalization.get("YOU_BOUGHT_X_A_FOR_Y_B"), + TradeCraftLocalization.get("YOU_BOUGHT_X_A_FOR_Y_B"), amountPlayerWantsToBuy, getItemName(), requiredCurrencyForThatAmount, plugin.getCurrencyName()); plugin.useLog(player, this, - TradeCraftLocalization.get("BOUGHT_X_A_FOR_Y_B"), + TradeCraftLocalization.get("BOUGHT_X_A_FOR_Y_B"), amountPlayerWantsToBuy, getItemName(), requiredCurrencyForThatAmount, @@ -292,10 +292,10 @@ private void playerWantsToSell(Player player) { int amountPlayerWantsToSell = getChestItemCount(); int currencyPlayerShouldReceive = ((amountPlayerWantsToSell - (amountPlayerWantsToSell % getSellAmount())) / getSellAmount()) * getSellValue(); // prevent too much currency (more than can fit in a chest) to be given to the customer - if ( getSellValue() > this.chest.getSize() * TradeCraft.getMaxStackSize(TradeCraft.currency.id) ) { - plugin.sendMessage(player, TradeCraftLocalization.get("THIS_SHOP_ALWAYS_RETURNS_TOO_MUCH_CURRENCY"), this.plugin.getCurrencyName()); - return; - } + if ( getSellValue() > this.chest.getSize() * TradeCraft.getMaxStackSize(TradeCraft.currency.id) ) { + plugin.sendMessage(player, TradeCraftLocalization.get("THIS_SHOP_ALWAYS_RETURNS_TOO_MUCH_CURRENCY"), this.plugin.getCurrencyName()); + return; + } if (currencyPlayerShouldReceive == 0) { plugin.sendMessage(player, @@ -318,10 +318,10 @@ private void playerWantsToSell(Player player) { // prevent too much items+currency stacks to end up in the chest if ( Math.ceil( (amountPlayerWantsToSell - amountThatCanBeSold)/ TradeCraft.getMaxStackSize(getItemType().id) ) - + Math.ceil( currencyPlayerShouldReceive / TradeCraft.getMaxStackSize(TradeCraft.currency.id) ) + + Math.ceil( currencyPlayerShouldReceive / TradeCraft.getMaxStackSize(TradeCraft.currency.id) ) > chest.getSize() ) { - plugin.sendMessage(player, TradeCraftLocalization.get("THIS_SHOP_WOULD_RETURN_TOO_MUCH_CURRENCY_SELL_LESS"), this.plugin.getCurrencyName()); - return; + plugin.sendMessage(player, TradeCraftLocalization.get("THIS_SHOP_WOULD_RETURN_TOO_MUCH_CURRENCY_SELL_LESS"), this.plugin.getCurrencyName()); + return; } updateItemAndCurrencyAmounts(amountThatCanBeSold, -currencyPlayerShouldReceive); diff --git a/nl/armeagle/TradeCraft/TradeCraftLocalization.java b/src/nl/armeagle/TradeCraft/TradeCraftLocalization.java similarity index 54% rename from nl/armeagle/TradeCraft/TradeCraftLocalization.java rename to src/nl/armeagle/TradeCraft/TradeCraftLocalization.java index ffd2044..a85f769 100644 --- a/nl/armeagle/TradeCraft/TradeCraftLocalization.java +++ b/src/nl/armeagle/TradeCraft/TradeCraftLocalization.java @@ -14,39 +14,39 @@ * such other language file to be used. */ public class TradeCraftLocalization { - private static final String filePreName = TradeCraft.pluginName + ".%1$s.lang"; + private static final String filePreName = TradeCraft.pluginName + ".%1$s.lang"; private static StatefulYamlConfiguration localization; - public TradeCraftLocalization(TradeCraft plugin) { - String filename = String.format(TradeCraftLocalization.filePreName, TradeCraft.properties.getLanguage()); + public TradeCraftLocalization(TradeCraft plugin) { + String filename = String.format(TradeCraftLocalization.filePreName, TradeCraft.properties.getLanguage()); TradeCraftLocalization.localization = plugin.getConfig(filename); try { - TradeCraftLocalization.localization.load(); + TradeCraftLocalization.localization.load(); } catch (IOException e) { - plugin.log(Level.SEVERE, "Failed to read file: %s", filename); + plugin.log(Level.SEVERE, "Failed to read file: %s", filename); } String defaultFilename = String.format(TradeCraftLocalization.filePreName, "en"); if (TradeCraftLocalization.localization.getKeys(false).isEmpty()) { - plugin.log(Level.INFO, "Language file %s does not exist or is empty, defaulting to %s", filename, defaultFilename); + plugin.log(Level.INFO, "Language file %s does not exist or is empty, defaulting to %s", filename, defaultFilename); } else { - return; + return; } TradeCraftLocalization.localization = plugin.getConfig(defaultFilename); try { - TradeCraftLocalization.localization.load(); + TradeCraftLocalization.localization.load(); } catch (IOException e) { - plugin.log(Level.SEVERE, "Failed to read file: %s", defaultFilename); + plugin.log(Level.SEVERE, "Failed to read file: %s", defaultFilename); } if (TradeCraftLocalization.localization.getKeys(false).isEmpty()) { - plugin.log(Level.SEVERE, "Default language file %s is also empty", defaultFilename); + plugin.log(Level.SEVERE, "Default language file %s is also empty", defaultFilename); } - } + } - public static String get(String key) { - return TradeCraftLocalization.localization.getString(key, "key error \""+ key +"\""); - } + public static String get(String key) { + return TradeCraftLocalization.localization.getString(key, "key error \""+ key +"\""); + } } diff --git a/src/nl/armeagle/TradeCraft/TradeCraftPermissions.java b/src/nl/armeagle/TradeCraft/TradeCraftPermissions.java new file mode 100644 index 0000000..2471728 --- /dev/null +++ b/src/nl/armeagle/TradeCraft/TradeCraftPermissions.java @@ -0,0 +1,68 @@ +package nl.armeagle.TradeCraft; + +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; +import org.bukkit.plugin.Plugin; + +public class TradeCraftPermissions { + TradeCraft plugin; + + TradeCraftPermissions(TradeCraft plugin) { + this.plugin = plugin; + } + + public boolean canBuy(Player p) { + return p.hasPermission("TradeCraft.canBuy"); + } + + public boolean canSell(Player p) { + return p.hasPermission("TradeCraft.canSell"); + } + + public boolean canMakeInfShops(Player p) { + return p.hasPermission("TradeCraft.canMakeInfShops"); + } + + public boolean canMakePlayerShops(Player p) { + return p.hasPermission("TradeCraft.canMakePlayerShops"); + } + + public boolean canDestroyShops(Player p) { + return p.hasPermission("TradeCraft.canDestroyShops"); + } + + public boolean canSetCurrency(Player p) { + return p.hasPermission("TradeCraft.canSetCurrency"); + } + + public boolean canReload(Player p) { + return p.hasPermission("TradeCraft.canReload"); + } + + public boolean canQueryOtherShops(Player p) { + return p.hasPermission("TradeCraft.canQueryOtherShops"); + } + + public boolean canQueryPlayer(Player p) { + return p.hasPermission("TradeCraft.canQueryPlayer"); + } + + public void debug(CommandSender sender, String n){ + Player p = plugin.getServer().getPlayer(n); + if(p == null){ + plugin.getServer().broadcastMessage("/tc canPlayer used with a name of player who is not online."); + return; + } + String name = p.getName(); + sender.sendMessage("" + name + " has:"); + sender.sendMessage("canBuy " + canBuy(p)); + sender.sendMessage("canSell " + canSell(p)); + sender.sendMessage("canMakeInf " + canMakeInfShops(p)); + sender.sendMessage("canMakePersonal " + canMakePlayerShops(p)); + sender.sendMessage("canDestroy " + canDestroyShops(p)); + sender.sendMessage("canSetCurrency " + canSetCurrency(p)); + sender.sendMessage("canReload " + canReload(p)); + sender.sendMessage("canQueryOtherShops " + canQueryOtherShops(p)); + } + +} diff --git a/nl/armeagle/TradeCraft/TradeCraftPlayerListener.java b/src/nl/armeagle/TradeCraft/TradeCraftPlayerListener.java similarity index 54% rename from nl/armeagle/TradeCraft/TradeCraftPlayerListener.java rename to src/nl/armeagle/TradeCraft/TradeCraftPlayerListener.java index 8ba5d7d..9bc6bef 100644 --- a/nl/armeagle/TradeCraft/TradeCraftPlayerListener.java +++ b/src/nl/armeagle/TradeCraft/TradeCraftPlayerListener.java @@ -10,34 +10,34 @@ public class TradeCraftPlayerListener implements Listener{ - private TradeCraft plugin; - - TradeCraftPlayerListener(TradeCraft plugin){ - this.plugin = plugin; - } - - @EventHandler - public void onPlayerInteract(PlayerInteractEvent e) { - if ( !this.plugin.isEnabled() ) { - return; - } - if ( e.getAction() == Action.RIGHT_CLICK_BLOCK ) { - Block blockClicked = e.getClickedBlock(); - Player player = e.getPlayer(); - - TradeCraftShop shop = plugin.getShopFromSignBlock(player, blockClicked); - - if (shop == null) { - return; - } + private TradeCraft plugin; + + TradeCraftPlayerListener(TradeCraft plugin){ + this.plugin = plugin; + } + + @EventHandler + public void onPlayerInteract(PlayerInteractEvent e) { + if ( !this.plugin.isEnabled() ) { + return; + } + if ( e.getAction() == Action.RIGHT_CLICK_BLOCK ) { + Block blockClicked = e.getClickedBlock(); + Player player = e.getPlayer(); + + TradeCraftShop shop = plugin.getShopFromSignBlock(player, blockClicked); + + if (shop == null) { + return; + } - shop.handleRightClick(player); - e.setCancelled(true); - } - } - + shop.handleRightClick(player); + e.setCancelled(true); + } + } + @SuppressWarnings("unused") - private void displayItems(Player player) { + private void displayItems(Player player) { String[] names = plugin.configuration.getNames(); StringBuilder sb = new StringBuilder(); for (String name : names) { diff --git a/nl/armeagle/TradeCraft/TradeCraftPlayerOwnedShop.java b/src/nl/armeagle/TradeCraft/TradeCraftPlayerOwnedShop.java similarity index 94% rename from nl/armeagle/TradeCraft/TradeCraftPlayerOwnedShop.java rename to src/nl/armeagle/TradeCraft/TradeCraftPlayerOwnedShop.java index e453e64..69ecd5e 100644 --- a/nl/armeagle/TradeCraft/TradeCraftPlayerOwnedShop.java +++ b/src/nl/armeagle/TradeCraft/TradeCraftPlayerOwnedShop.java @@ -30,11 +30,11 @@ public boolean shopCanBeWithdrawnFrom() { } public boolean isOwnedByPlayer(Player player) { - if ( ownerName == null ) { - return false; - } else { - return player.getName().equals(ownerName); - } + if ( ownerName == null ) { + return false; + } else { + return player.getName().equals(ownerName); + } } public TradeCraftItem getItemType() { diff --git a/src/nl/armeagle/TradeCraft/TradeCraftPropertiesFile.java b/src/nl/armeagle/TradeCraft/TradeCraftPropertiesFile.java new file mode 100644 index 0000000..028704d --- /dev/null +++ b/src/nl/armeagle/TradeCraft/TradeCraftPropertiesFile.java @@ -0,0 +1,153 @@ +package nl.armeagle.TradeCraft; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.util.logging.Level; + +import org.bukkit.ChatColor; +import org.bukkit.configuration.InvalidConfigurationException; +import org.bukkit.configuration.file.YamlConfiguration; + +/** + * This class, handles the actual configuration of the plugin. The TradeCraftConfiguration class + * actually handles all the items that can be used by the shops in the game. + */ +public class TradeCraftPropertiesFile { + private static final String fileName = TradeCraft.pluginName + ".properties"; + private static final String filePath = "plugins" + File.separator + TradeCraft.pluginName; + public static final String defaultLanguage = "en"; + + private TradeCraft plugin; + private final YamlConfiguration properties; + + public TradeCraftPropertiesFile(TradeCraft plugin) { + this.plugin = plugin; + // make folder in the plugins dir if it doesn't exist yet + File path = new File(filePath); + if ( !path.exists() ) { + path.mkdirs(); + } + path = null; + + // if file does not exist in this directory, copy it from the jar + File file = new File(filePath + File.separator + fileName); + if ( !file.exists() ) { + this.plugin.log.info(filePath + File.separator + fileName +" does not exist, creating..."); + InputStream input = this.getClass().getResourceAsStream("/" + fileName); + if ( input != null ) { + FileOutputStream output = null; + + try { + output = new FileOutputStream(file); + byte[] buf = new byte[8192]; + int length = 0; + while ((length = input.read(buf)) > 0) { + output.write(buf, 0, length); + } + } catch (IOException e) { + e.printStackTrace(); + } finally { + try { + if (input != null) { + input.close(); + } + } catch (IOException e) {} + + try { + if (output != null) { + output.close(); + } + } catch (IOException e) {} + } + } + } + + properties = new YamlConfiguration(); + try { + properties.load(file); + } catch (InvalidConfigurationException e) { + plugin.log(Level.SEVERE, "Failed to load file: %s", file.toURI()); + } catch (IOException e) { + plugin.log(Level.SEVERE, "Failed to read file: %s", file.toURI()); + } + } + + protected void save() { + File file = new File(filePath + File.separator + fileName); + try { + properties.save(file); + } catch (IOException e) { + this.plugin.log(Level.SEVERE, "Error saving to file: %s", file.toURI()); + } + } + + public TradeCraftItem getCurrencyType(){ + int id = properties.getInt("currency-id",266); + short data = (short)properties.getInt("currency-data",0); + return new TradeCraftItem(id, data); + } + public void setCurrencyType(TradeCraftItem item) { + properties.set("currency-id", item.id); + properties.set("currency-data", item.data); + this.save(); + } + public boolean getNormalStackSizeUsed(){ + return properties.getBoolean("normal-stack-size", true); + } + + public boolean getInfiniteShopsEnabled() { + return properties.getBoolean("infinite-shops-enabled", true); + } + + public boolean getPlayerOwnedShopsEnabled() { + return properties.getBoolean("player-owned-shops-enabled", true); + } + + public boolean getRepairShopsEnabled() { + return properties.getBoolean("repair-shops-enabled", false); + } + + public int getRepairCost() { + return properties.getInt("repair-cost", 0); + } + + public boolean getEnableDebugMessages() { + return properties.getBoolean("enable-debug-messages", false); + } + + public String getLanguage() { + return properties.getString("language", TradeCraftPropertiesFile.defaultLanguage); + } + + public boolean autoUpdateLanguageFiles() { + return properties.getBoolean("auto-update-language-files", true); + } + + public boolean logShopUse() { + return properties.getBoolean("log-shop-use", false); + } + + public boolean showShopLocation() { + return properties.getBoolean("show-shop-location", false); + } + + public int getPlayerWorldShopLimit() { + return properties.getInt("player-world-shop-limit", 5); + } + public int getPlayerTotalShopLimit() { + return properties.getInt("player-total-shop-limit", 10); + } + + public ChatColor getMessageTypeColor(TradeCraft.MessageTypes mtype) { + switch (mtype) { + case WITHDRAW: + return ChatColor.YELLOW; + case DEPOSIT: + return ChatColor.GRAY; + default: + return ChatColor.WHITE; + } + } +} diff --git a/nl/armeagle/TradeCraft/TradeCraftRepairShop.java b/src/nl/armeagle/TradeCraft/TradeCraftRepairShop.java similarity index 75% rename from nl/armeagle/TradeCraft/TradeCraftRepairShop.java rename to src/nl/armeagle/TradeCraft/TradeCraftRepairShop.java index f0d4b6b..438f769 100644 --- a/nl/armeagle/TradeCraft/TradeCraftRepairShop.java +++ b/src/nl/armeagle/TradeCraft/TradeCraftRepairShop.java @@ -20,8 +20,8 @@ public void handleRightClick(Player player) { if (currencyAmount == 0 && items.size() == 0) { plugin.sendMessage(player, TradeCraftLocalization.get("IT_COSTS_X_A_TO_REPAIR_AN_ITEM"), - repairCost, - plugin.getCurrencyName()); + repairCost, + plugin.getCurrencyName()); return; } @@ -29,26 +29,26 @@ public void handleRightClick(Player player) { if (items.size() == 0) { plugin.sendMessage(player, TradeCraftLocalization.get("WITH_THIS_MUCH_A_YOU_CAN_REPAIR_Y_ITEMS"), - plugin.getCurrencyName(), - currencyAmount / repairCost); + plugin.getCurrencyName(), + currencyAmount / repairCost); return; } if (currencyAmount < actualCost) { if (currencyAmount > 0) { plugin.sendMessage(player, TradeCraftLocalization.get("THAT_IS_NOT_ENOUGH_A"), - plugin.getCurrencyName()); + plugin.getCurrencyName()); } plugin.sendMessage(player, TradeCraftLocalization.get("YOU_NEED_X_A_TO_REPAIR_ALL_THIS"), - actualCost, - plugin.getCurrencyName()); + actualCost, + plugin.getCurrencyName()); return; } chest.clear(); for (ItemStack item : items) { - short itemData = (item.getData() == null ? (short)0 : item.getDurability()); + short itemData = (item.getData() == null ? (short)0 : item.getDurability()); chest.add(new TradeCraftItem(item.getTypeId(), itemData), 1); } @@ -58,9 +58,9 @@ public void handleRightClick(Player player) { chest.update(); plugin.sendMessage(player, TradeCraftLocalization.get("YOU_REPAIRED_X_ITEMS_FOR_Y_B"), - items.size(), - actualCost, - plugin.getCurrencyName()); + items.size(), + actualCost, + plugin.getCurrencyName()); } public boolean playerCanDestroy(Player player) { diff --git a/nl/armeagle/TradeCraft/TradeCraftShop.java b/src/nl/armeagle/TradeCraft/TradeCraftShop.java similarity index 72% rename from nl/armeagle/TradeCraft/TradeCraftShop.java rename to src/nl/armeagle/TradeCraft/TradeCraftShop.java index b66a611..f537991 100644 --- a/nl/armeagle/TradeCraft/TradeCraftShop.java +++ b/src/nl/armeagle/TradeCraft/TradeCraftShop.java @@ -22,10 +22,10 @@ public TradeCraftShop(TradeCraft plugin, Sign sign, Chest chest) { public abstract boolean shopCanBeWithdrawnFrom(); public String toString() { - return String.format("Shop(%s:%d,%d,%d)", - this.sign.getWorld().getName(), - this.sign.getX(), - this.sign.getY(), - this.sign.getZ()); + return String.format("Shop(%s:%d,%d,%d)", + this.sign.getWorld().getName(), + this.sign.getX(), + this.sign.getY(), + this.sign.getZ()); } } diff --git a/plugin.yml b/src/plugin.yml similarity index 99% rename from plugin.yml rename to src/plugin.yml index 2c5f311..7528caa 100644 --- a/plugin.yml +++ b/src/plugin.yml @@ -1,5 +1,5 @@ name: TradeCraft -version: AE-1.4 +version: AE-1.4.1 description: Setup shops with a combination of chest and sign. author: ArmEagle main: nl.armeagle.TradeCraft.TradeCraft From 59979ad5d52b701038f08668c7ad0ec9638e53b4 Mon Sep 17 00:00:00 2001 From: armeagle Date: Sat, 11 Aug 2012 17:54:31 +0200 Subject: [PATCH 094/100] Revert "1.4.1 - Moved all files into a source subdirectory - Removed support for old Permissions plugin, now only supporting Bukkit's built-in system. - Cleanup up tabs for whitespaces" This reverts commit 6adf7012541e7ad9b04063e12781866746abf3b9. --- src/TradeCraft.en.lang => TradeCraft.en.lang | 0 src/TradeCraft.ge.lang => TradeCraft.ge.lang | 0 ...eCraft.properties => TradeCraft.properties | 0 src/TradeCraft.sv.lang => TradeCraft.sv.lang | 0 build.xml | 74 -- src/items.yml => items.yml | 0 manifest.mf | 3 - nbproject/build-impl.xml | 1042 ----------------- nbproject/genfiles.properties | 8 - nbproject/project.properties | 75 -- nbproject/project.xml | 13 - .../StatefulYamlConfiguration.java | 79 ++ nl/armeagle/TradeCraft/TradeCraft.java | 656 +++++++++++ .../TradeCraft/TradeCraftBlockListener.java | 224 ++++ .../armeagle/TradeCraft/TradeCraftChest.java | 26 +- .../TradeCraftConfigurationFile.java | 199 ++++ .../TradeCraftConfigurationInfo.java | 55 + .../TradeCraft/TradeCraftDataFile.java | 146 +-- .../TradeCraft/TradeCraftDataInfo.java | 12 +- .../TradeCraft/TradeCraftExchangeRate.java | 18 +- .../TradeCraft/TradeCraftInfiniteShop.java | 4 +- nl/armeagle/TradeCraft/TradeCraftItem.java | 63 + .../TradeCraft/TradeCraftItemShop.java | 294 ++--- .../TradeCraft/TradeCraftLocalization.java | 28 +- .../TradeCraft/TradeCraftPermissions.java | 121 ++ .../TradeCraft/TradeCraftPlayerListener.java | 52 +- .../TradeCraft/TradeCraftPlayerOwnedShop.java | 10 +- .../TradeCraft/TradeCraftPropertiesFile.java | 153 +++ .../TradeCraft/TradeCraftRepairShop.java | 22 +- .../armeagle/TradeCraft/TradeCraftShop.java | 10 +- src/plugin.yml => plugin.yml | 2 +- .../StatefulYamlConfiguration.java | 79 -- src/nl/armeagle/TradeCraft/TradeCraft.java | 652 ----------- .../TradeCraft/TradeCraftBlockListener.java | 224 ---- .../TradeCraftConfigurationFile.java | 199 ---- .../TradeCraftConfigurationInfo.java | 55 - .../armeagle/TradeCraft/TradeCraftItem.java | 63 - .../TradeCraft/TradeCraftPermissions.java | 68 -- .../TradeCraft/TradeCraftPropertiesFile.java | 153 --- 39 files changed, 1862 insertions(+), 3020 deletions(-) rename src/TradeCraft.en.lang => TradeCraft.en.lang (100%) rename src/TradeCraft.ge.lang => TradeCraft.ge.lang (100%) rename src/TradeCraft.properties => TradeCraft.properties (100%) rename src/TradeCraft.sv.lang => TradeCraft.sv.lang (100%) delete mode 100644 build.xml rename src/items.yml => items.yml (100%) delete mode 100644 manifest.mf delete mode 100644 nbproject/build-impl.xml delete mode 100644 nbproject/genfiles.properties delete mode 100644 nbproject/project.properties delete mode 100644 nbproject/project.xml create mode 100644 nl/armeagle/Configuration/StatefulYamlConfiguration.java create mode 100644 nl/armeagle/TradeCraft/TradeCraft.java create mode 100644 nl/armeagle/TradeCraft/TradeCraftBlockListener.java rename {src/nl => nl}/armeagle/TradeCraft/TradeCraftChest.java (75%) create mode 100644 nl/armeagle/TradeCraft/TradeCraftConfigurationFile.java create mode 100644 nl/armeagle/TradeCraft/TradeCraftConfigurationInfo.java rename {src/nl => nl}/armeagle/TradeCraft/TradeCraftDataFile.java (73%) rename {src/nl => nl}/armeagle/TradeCraft/TradeCraftDataInfo.java (61%) rename {src/nl => nl}/armeagle/TradeCraft/TradeCraftExchangeRate.java (52%) rename {src/nl => nl}/armeagle/TradeCraft/TradeCraftInfiniteShop.java (93%) create mode 100644 nl/armeagle/TradeCraft/TradeCraftItem.java rename {src/nl => nl}/armeagle/TradeCraft/TradeCraftItemShop.java (50%) rename {src/nl => nl}/armeagle/TradeCraft/TradeCraftLocalization.java (54%) create mode 100644 nl/armeagle/TradeCraft/TradeCraftPermissions.java rename {src/nl => nl}/armeagle/TradeCraft/TradeCraftPlayerListener.java (54%) rename {src/nl => nl}/armeagle/TradeCraft/TradeCraftPlayerOwnedShop.java (94%) create mode 100644 nl/armeagle/TradeCraft/TradeCraftPropertiesFile.java rename {src/nl => nl}/armeagle/TradeCraft/TradeCraftRepairShop.java (75%) rename {src/nl => nl}/armeagle/TradeCraft/TradeCraftShop.java (72%) rename src/plugin.yml => plugin.yml (99%) delete mode 100644 src/nl/armeagle/Configuration/StatefulYamlConfiguration.java delete mode 100644 src/nl/armeagle/TradeCraft/TradeCraft.java delete mode 100644 src/nl/armeagle/TradeCraft/TradeCraftBlockListener.java delete mode 100644 src/nl/armeagle/TradeCraft/TradeCraftConfigurationFile.java delete mode 100644 src/nl/armeagle/TradeCraft/TradeCraftConfigurationInfo.java delete mode 100644 src/nl/armeagle/TradeCraft/TradeCraftItem.java delete mode 100644 src/nl/armeagle/TradeCraft/TradeCraftPermissions.java delete mode 100644 src/nl/armeagle/TradeCraft/TradeCraftPropertiesFile.java diff --git a/src/TradeCraft.en.lang b/TradeCraft.en.lang similarity index 100% rename from src/TradeCraft.en.lang rename to TradeCraft.en.lang diff --git a/src/TradeCraft.ge.lang b/TradeCraft.ge.lang similarity index 100% rename from src/TradeCraft.ge.lang rename to TradeCraft.ge.lang diff --git a/src/TradeCraft.properties b/TradeCraft.properties similarity index 100% rename from src/TradeCraft.properties rename to TradeCraft.properties diff --git a/src/TradeCraft.sv.lang b/TradeCraft.sv.lang similarity index 100% rename from src/TradeCraft.sv.lang rename to TradeCraft.sv.lang diff --git a/build.xml b/build.xml deleted file mode 100644 index 72a1205..0000000 --- a/build.xml +++ /dev/null @@ -1,74 +0,0 @@ - - - - - - - - - - - Builds, tests, and runs the project TradeCraft. - - - diff --git a/src/items.yml b/items.yml similarity index 100% rename from src/items.yml rename to items.yml diff --git a/manifest.mf b/manifest.mf deleted file mode 100644 index 1574df4..0000000 --- a/manifest.mf +++ /dev/null @@ -1,3 +0,0 @@ -Manifest-Version: 1.0 -X-COMMENT: Main-Class will be added automatically by build - diff --git a/nbproject/build-impl.xml b/nbproject/build-impl.xml deleted file mode 100644 index dcfb2dc..0000000 --- a/nbproject/build-impl.xml +++ /dev/null @@ -1,1042 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Must set src.src.dir - Must set build.dir - Must set dist.dir - Must set build.classes.dir - Must set dist.javadoc.dir - Must set build.test.classes.dir - Must set build.test.results.dir - Must set build.classes.excludes - Must set dist.jar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Must set javac.includes - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Must set JVM to use for profiling in profiler.info.jvm - Must set profiler agent JVM arguments in profiler.info.jvmargs.agent - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Must select some files in the IDE or set javac.includes - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - To run this application from the command line without Ant, try: - - - - - - - java -cp "${run.classpath.with.dist.jar}" ${main.class} - - - - - - - - - - - - - - - - - - - - - - - - - To run this application from the command line without Ant, try: - - java -jar "${dist.jar.resolved}" - - - - - - - - - - - - - - - - - - - - - - - - - Must select one file in the IDE or set run.class - - - - Must select one file in the IDE or set run.class - - - - - - - - - - - - - - - - - - - - - - - Must select one file in the IDE or set debug.class - - - - - Must select one file in the IDE or set debug.class - - - - - Must set fix.includes - - - - - - - - - - - - - - - - - Must select one file in the IDE or set profile.class - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Must select some files in the IDE or set javac.includes - - - - - - - - - - - - - - - - - - Some tests failed; see details above. - - - - - - - - - Must select some files in the IDE or set test.includes - - - - Some tests failed; see details above. - - - - - Must select one file in the IDE or set test.class - - - - - - - - - - - - - - - - - - - - - - - - - - - Must select one file in the IDE or set applet.url - - - - - - - - - Must select one file in the IDE or set applet.url - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/nbproject/genfiles.properties b/nbproject/genfiles.properties deleted file mode 100644 index 928644b..0000000 --- a/nbproject/genfiles.properties +++ /dev/null @@ -1,8 +0,0 @@ -build.xml.data.CRC32=ced48bff -build.xml.script.CRC32=3d45e660 -build.xml.stylesheet.CRC32=28e38971@1.50.3.46 -# This file is used by a NetBeans-based IDE to track changes in generated files such as build-impl.xml. -# Do not edit this file. You may delete it but then the IDE will never regenerate such files for you. -nbproject/build-impl.xml.data.CRC32=ced48bff -nbproject/build-impl.xml.script.CRC32=5c3459d4 -nbproject/build-impl.xml.stylesheet.CRC32=fcddb364@1.50.3.46 diff --git a/nbproject/project.properties b/nbproject/project.properties deleted file mode 100644 index 1c3c059..0000000 --- a/nbproject/project.properties +++ /dev/null @@ -1,75 +0,0 @@ -annotation.processing.enabled=true -annotation.processing.enabled.in.editor=false -annotation.processing.processors.list= -annotation.processing.run.all.processors=true -annotation.processing.source.output=${build.generated.sources.dir}/ap-source-output -application.title=TradeCraft -application.vendor=armeagle -build.classes.dir=${build.dir}/classes -build.classes.excludes=**/*.java,**/*.form -# This directory is removed when the project is cleaned: -build.dir=build -build.generated.dir=${build.dir}/generated -build.generated.sources.dir=${build.dir}/generated-sources -# Only compile against the classpath explicitly listed here: -build.sysclasspath=ignore -build.test.classes.dir=${build.dir}/test/classes -build.test.results.dir=${build.dir}/test/results -# Uncomment to specify the preferred debugger connection transport: -#debug.transport=dt_socket -debug.classpath=\ - ${run.classpath} -debug.test.classpath=\ - ${run.test.classpath} -# This directory is removed when the project is cleaned: -dist.dir=dist -dist.jar=${dist.dir}/TradeCraft.jar -dist.javadoc.dir=${dist.dir}/javadoc -endorsed.classpath= -excludes= -file.reference.bukkit-1.3.1-R1.0.jar=C:\\Games\\Minecraft\\Bukkit\\2317-1521\\bukkit-1.3.1-R1.0.jar -file.reference.NetBeansProjects-TradeCraft=. -includes=** -jar.compress=false -javac.classpath=\ - ${file.reference.bukkit-1.3.1-R1.0.jar} -# Space-separated list of extra javac options -javac.compilerargs= -javac.deprecation=false -javac.processorpath=\ - ${javac.classpath} -javac.source=1.7 -javac.target=1.7 -javac.test.classpath=\ - ${javac.classpath}:\ - ${build.classes.dir} -javac.test.processorpath=\ - ${javac.test.classpath} -javadoc.additionalparam= -javadoc.author=false -javadoc.encoding=${source.encoding} -javadoc.noindex=false -javadoc.nonavbar=false -javadoc.notree=false -javadoc.private=false -javadoc.splitindex=true -javadoc.use=true -javadoc.version=false -javadoc.windowtitle= -main.class= -manifest.file=manifest.mf -meta.inf.dir=${src.dir}/META-INF -mkdist.disabled=false -platform.active=default_platform -run.classpath=\ - ${javac.classpath}:\ - ${build.classes.dir} -# Space-separated list of JVM arguments used when running the project -# (you may also define separate properties like run-sys-prop.name=value instead of -Dname=value -# or test-sys-prop.name=value to set system properties for unit tests): -run.jvmargs= -run.test.classpath=\ - ${javac.test.classpath}:\ - ${build.test.classes.dir} -source.encoding=UTF-8 -src.src.dir=src diff --git a/nbproject/project.xml b/nbproject/project.xml deleted file mode 100644 index 8c0b8ff..0000000 --- a/nbproject/project.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - org.netbeans.modules.java.j2seproject - - - TradeCraft - - - - - - - diff --git a/nl/armeagle/Configuration/StatefulYamlConfiguration.java b/nl/armeagle/Configuration/StatefulYamlConfiguration.java new file mode 100644 index 0000000..cc72966 --- /dev/null +++ b/nl/armeagle/Configuration/StatefulYamlConfiguration.java @@ -0,0 +1,79 @@ +package nl.armeagle.Configuration; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStream; +import java.util.logging.Level; +import java.util.logging.Logger; + +import org.bukkit.Bukkit; +import org.bukkit.configuration.InvalidConfigurationException; +import org.bukkit.configuration.file.YamlConfiguration; +import org.bukkit.plugin.java.JavaPlugin; +import org.yaml.snakeyaml.error.YAMLException; + +public class StatefulYamlConfiguration extends YamlConfiguration { + protected File file; + + public StatefulYamlConfiguration(File file) { + this.file = file; + } + + public void load() throws IOException { + this.load(false); + } + public void load(boolean forceDefaults) throws IOException { + boolean notLoaded = false; + if (this.file == null) { + throw new IllegalArgumentException("File cannot be null"); + } + + YamlConfiguration baseConfig = new YamlConfiguration(); + + try { + baseConfig.load(this.file); + } catch (FileNotFoundException fnf) { + notLoaded = true; + } catch (IOException ex) { + Bukkit.getLogger().log(Level.SEVERE, "Cannot load " + this.file, ex); + } catch (InvalidConfigurationException ex) { + if (ex.getCause() instanceof YAMLException) { + Bukkit.getLogger().severe("Config file " + this.file + " isn't valid! " + ex.getCause()); + } else if ((ex.getCause() == null) || (ex.getCause() instanceof ClassCastException)) { + Bukkit.getLogger().severe("Config file " + this.file + " isn't valid!"); + } else { + Bukkit.getLogger().log(Level.SEVERE, "Cannot load " + this.file + ": " + ex.getCause().getClass(), ex); + } + } + + if (notLoaded || forceDefaults) { + InputStream defaultInput = this.getClass().getResourceAsStream("/" + file.getName()); + if ( null != defaultInput ) { + YamlConfiguration defaultConfig = YamlConfiguration.loadConfiguration(defaultInput); + + try { + this.loadFromString(defaultConfig.saveToString()); + } catch (InvalidConfigurationException e) { + Logger.getLogger(JavaPlugin.class.getName()).log(Level.SEVERE, "Invalid default configuation"); + } + this.save(); + } + } else if (! notLoaded) { + try { + this.loadFromString(baseConfig.saveToString()); + } catch (InvalidConfigurationException e) { + Logger.getLogger(JavaPlugin.class.getName()).log(Level.SEVERE, "Invalid configuation"); + } + } + + } + + public void save() throws IOException { + super.save(this.file); + } + + public File getFile() { + return this.file; + } +} diff --git a/nl/armeagle/TradeCraft/TradeCraft.java b/nl/armeagle/TradeCraft/TradeCraft.java new file mode 100644 index 0000000..8f00413 --- /dev/null +++ b/nl/armeagle/TradeCraft/TradeCraft.java @@ -0,0 +1,656 @@ +package nl.armeagle.TradeCraft; + +import java.io.*; + +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Date; +import java.util.HashMap; +import java.util.IllegalFormatException; +import java.util.Map; +import java.util.logging.Level; +import java.util.logging.Logger; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import nl.armeagle.Configuration.StatefulYamlConfiguration; + +import org.bukkit.ChatColor; +import org.bukkit.Material; +import org.bukkit.Server; +import org.bukkit.block.Block; +import org.bukkit.block.BlockFace; +import org.bukkit.block.Chest; +import org.bukkit.block.Sign; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.command.ConsoleCommandSender; +import org.bukkit.entity.Player; +import org.bukkit.inventory.ItemStack; +import org.bukkit.material.MaterialData; +import org.bukkit.plugin.PluginDescriptionFile; +import org.bukkit.plugin.PluginManager; +import org.bukkit.plugin.java.JavaPlugin; + +import com.nijikokun.bukkit.Permissions.Permissions; + +public class TradeCraft extends JavaPlugin { + public static enum MessageTypes {WITHDRAW, DEPOSIT}; + + // The plugin name. + static final String pluginName = "TradeCraft"; + + public static final Pattern itemPatternIdSplitData = Pattern.compile("^(\\d+)(?:;(\\d+))?$"); + + private static enum Commands { + tcsetcurrency, + tcgetcurrency, + tcshops, + tcpshops, + tcreload, + tcplayerperms, + tchelp, + tc + }; + + // Stuff used to interact with the server. + final Logger log = Logger.getLogger("Minecraft"); + final Server server = this.getServer(); + + protected BufferedWriter usageLog = null; + // Objects used by the plugin. + static TradeCraftItem currency; + static TradeCraftPropertiesFile properties; + TradeCraftConfigurationFile configuration; + public TradeCraftLocalization localization; + TradeCraftDataFile data; + + private HashMap configs = new HashMap(); + + private final TradeCraftBlockListener blockListener = new TradeCraftBlockListener(this); + private final TradeCraftPlayerListener playerListener = new TradeCraftPlayerListener(this); + public TradeCraftPermissions permissions = new TradeCraftPermissions(this); + public Permissions permissionsPlugin = null; + public boolean permEnabled = false; + + // prevent the script from registering the event listeners multiple times (by dis-/enable) + public static boolean hasRegisteredEventListeners = false; + + public void onDisable() { + this.disable(); + } + private void disable() { + if ( this.usageLog != null ) { + try { + this.usageLog.close(); + } catch (IOException e) { + this.log(Level.WARNING, "Failed to close shop usage log file"); + } + } + properties = null; + configuration = null; + this.localization = null; + data.save(); + data = null; + } + + public void onEnable() { + this.enable(); + } + private void enable() { + properties = new TradeCraftPropertiesFile(this); + configuration = new TradeCraftConfigurationFile(this); + data = new TradeCraftDataFile(this); + this.localization = new TradeCraftLocalization(this); + + if ( TradeCraft.properties.logShopUse() ) { + File usageLogFile = new File(this.getDataFolder(), "shopUsage.log"); + try { + if ( !usageLogFile.exists() ) { + usageLogFile.createNewFile(); + } + if ( usageLogFile.canWrite() ) { + this.usageLog = new BufferedWriter(new FileWriter(usageLogFile, true)); + this.log(Level.INFO, "Writing shop usage to log file: "+ usageLogFile.toString()); + } else { + this.log(Level.WARNING, "Error opening shop usage log file: "+ usageLogFile.toString()); + } + } catch (IOException e) { + this.log(Level.WARNING, "Failed to open shop usage log file: "+ usageLogFile.toString()); + } + } + + configuration.load(); + data.load(); + currency = properties.getCurrencyType(); + permissions.setupPermissions(); + + if ( !TradeCraft.hasRegisteredEventListeners ) { + PluginManager pm = this.getServer().getPluginManager(); + pm.registerEvents(playerListener, this); + pm.registerEvents(blockListener, this); + TradeCraft.hasRegisteredEventListeners = true; + } + + PluginDescriptionFile pdfFile = this.getDescription(); + this.log.info(pdfFile.getName() + " version " + pdfFile.getVersion() + " is enabled!"); + + } + + @Override + public boolean onCommand(CommandSender sender, Command cmd, String label, String[] args) { + try { + TradeCraft.Commands command = TradeCraft.Commands.valueOf(cmd.getName()); + if (sender instanceof Player) { + Player p = (Player) sender; + switch (command) { + case tcsetcurrency: + if ( args.length == 1 && this.permissions.canSetCurrency(p) ) { + TradeCraftItem testCurrency = null; + // try to split ID and Data, separated by a semicolon mark + Matcher IdSplitData = TradeCraft.itemPatternIdSplitData.matcher(args[0]); + + if ( !IdSplitData.matches() ) { + // try to match the parameter to item names from the configuration + TradeCraftConfigurationInfo setCurr = this.configuration.get(args[0]); + if ( setCurr == null ) { + this.sendMessage(p, TradeCraftLocalization.get("IS_NO_VALID_CURRENCY_USE_INSTEAD"), + args[0]); + return false; + } else { + currency = setCurr.type; + } + } else { + try { + int cid = Integer.parseInt(IdSplitData.group(1)); + if ( IdSplitData.group(2) != null ) { + testCurrency = new TradeCraftItem(cid, Short.parseShort(IdSplitData.group(2))); + } else { + testCurrency = new TradeCraftItem(cid); + } + } catch ( NumberFormatException e ) { + this.sendMessage(p, TradeCraftLocalization.get("INVALID_CURRENCY"), + args[0]); + return false; + } + if ( this.configuration.get(testCurrency) != null ) { + currency = testCurrency; + } else { + this.sendMessage(p, TradeCraftLocalization.get("INVALID_CURRENCY"), + args[0]); + return false; + } + } + + TradeCraft.properties.setCurrencyType(currency); + this.sendMessage(p, TradeCraftLocalization.get("CURRENCY_IS_SET_TO_A_IDDATA"), + this.getCurrencyName(), + currency.toShortString()); + return true; + } + return true; + case tcgetcurrency: + this.sendMessage(p, TradeCraftLocalization.get("CURRENCY_IS_A_IDDATA"), + this.getCurrencyName(), + currency.toShortString()); + return true; + case tcshops: + // lookup own shows + displayShops(p.getName(), p, false); + return true; + case tcpshops: + // Check whether another parameter is passed, if so check whether + // the player can get information about other player's shops. + if ( this.permissions.canQueryOtherShops(p) ) { + if ( args.length == 1 ) { + // lookup other player's shops + displayShops(args[0], p, true); + } else { + this.sendMessage(p, cmd.getUsage()); + } + } + return true; + case tcreload: + if ( this.permissions.canReload(p)) { + this.sendMessage(p, TradeCraftLocalization.get("RESTARTING_PLUGIN"), TradeCraft.pluginName); + this.disable(); + this.enable(); + this.sendMessage(p, TradeCraftLocalization.get("RESTARTING_PLUGIN_DONE"), TradeCraft.pluginName); + } + return true; + case tcplayerperms: + if ( this.permissions.canQueryPlayer(p)) { + if ( args.length == 1 ) { + permissions.debug(sender, args[0]); + } else { + sender.sendMessage(cmd.getUsage()); + } + } + return true; + default: + displayCommandHelpText(p); + return true; + } + } else if ( sender instanceof ConsoleCommandSender ) { + switch (command) { + case tcplayerperms: + if ( args.length == 1 ) { + permissions.debug(sender, args[0]); + } else { + sender.sendMessage(cmd.getUsage()); + } + return true; + case tcpshops: + // Check whether another parameter is passed, if so check whether + // the player can get information about other player's shops. + if ( args.length == 1 ) { + // lookup other player's shops + displayShops(args[0], sender, true); + } else { + sender.sendMessage(cmd.getUsage()); + } + return true; + case tcreload: + sender.sendMessage(String.format(TradeCraftLocalization.get("RESTARTING_PLUGIN"), + TradeCraft.pluginName)); + this.disable(); + this.enable(); + sender.sendMessage(String.format(TradeCraftLocalization.get("RESTARTING_PLUGIN_DONE"), + TradeCraft.pluginName)); + return true; + default: + displayCommandHelpText(null); + return true; + } + } + } catch (IllegalArgumentException e) { + return false; + } + return false; + } + + void displayShops(String infoPlayerName, CommandSender displayTo, boolean otherQuery) { + Map list = data.shopsOwned(infoPlayerName); + if (list.size() == 0) { + if ( otherQuery ) { + // elevated player looking for other player's shops + displayTo.sendMessage(String.format(TradeCraftLocalization.get("A_DOES_NOT_OWN_ANY_SHOPS"), + infoPlayerName)); + } else { + displayTo.sendMessage(TradeCraftLocalization.get("YOU_DONT_OWN_ANY_SHOPS")); + } + return; + } + + if ( otherQuery ) { + displayTo.sendMessage(String.format(TradeCraftLocalization.get("SHOPS_OF_A"), + infoPlayerName)); + } else { + displayTo.sendMessage(TradeCraftLocalization.get("YOUR_SHOPS")); + } + for (Map.Entry entry : list.entrySet()) { + TradeCraftDataInfo info = entry.getValue(); + String message = ""; + if (TradeCraft.properties.showShopLocation()) { + String location = entry.getKey().replaceFirst(",", "(") +")"; + message += ChatColor.GRAY + location +" "+ ChatColor.WHITE; + } + message += TradeCraftLocalization.get("ITEM") +": "+ this.configuration.get(info.itemType).name +"("+ info.itemType.toShortString() +")" + +" "+ TradeCraftLocalization.get("AMOUNT") +": "+ info.itemAmount +" " + + this.getCurrencyName() +": "+ info.currencyAmount; + + displayTo.sendMessage(message); + } + + } + + void sendMessage(Player player, TradeCraft.MessageTypes messageType, String format, Object... args) { + try { + String message = String.format(format, args); + player.sendMessage(TradeCraft.properties.getMessageTypeColor(messageType) + message); + } catch ( IllegalFormatException e ) { + player.sendMessage(TradeCraftLocalization.get("ERROR_IN_FORMAT_STRING") +" "+ format); + } + } + + void sendMessage(Player player, String format, Object... args) { + try { + String message = String.format(format, args); + player.sendMessage(message); + } catch ( IllegalFormatException e ) { + player.sendMessage(TradeCraftLocalization.get("ERROR_IN_FORMAT_STRING") +" "+ format); + } + } + + void trace(Player player, String format, Object... args) { + if (properties.getEnableDebugMessages()) { + if (null != player) { + sendMessage(player, format, args); + } else { + this.log(Level.INFO, format, args); + } + } + } + /* + * When a block behind a shop sign is destroyed, the sign would be destroyed too. + * Check all side faces of this block, for a sign attached to this block. Then + * pass that sign block to getShopFromSignBlock. + * + * This should only be used for checking whether a normal block can be destroyed, for there not being any + * signs attached to it, or this block being a chest or sign itself. + * Since one block can + */ + ArrayList getShopsFromBlock(Player player, Block block) { + ArrayList shops = new ArrayList(); + // use other function(s) directly if applicable + if ( block.getType() == Material.CHEST || block.getType() == Material.WALL_SIGN ) { + TradeCraftShop shop = getShopFromSignOrChestBlock(player, block); + if ( shop != null ) { + shops.add(shop); + } + } else { + // go through all 4 faces of this block to check for a sign attached to this block + final BlockFace[] sides = {BlockFace.NORTH, BlockFace.EAST, BlockFace.SOUTH, BlockFace.WEST}; + for ( int index_sides = 0; index_sides < sides.length; index_sides++ ) { + BlockFace side = sides[index_sides]; + // get the block on that side + Block sideBlock = block.getRelative(side); + // check for it being a wall sign + if ( sideBlock.getType() == Material.WALL_SIGN ) { + // get the sign (extending MaterialData) for clean attached face checking + org.bukkit.material.Sign materialSign = new org.bukkit.material.Sign(Material.WALL_SIGN, sideBlock.getData()); + /* Now, for easy comparison, we'll compare to the direction the sign is facing. If that's + * the same as the current face (of the 4 we're looping through), then this sign is attached + * to the block that is being destroyed. + */ + if ( materialSign.getFacing() == side ) { + TradeCraftShop shop = this.getShopFromSignBlock(player, sideBlock); + if ( shop != null ) { + shops.add(shop); + } + } + + } + } + } + return shops; + } + TradeCraftShop getShopFromSignOrChestBlock(Player player, Block block) { + if (block.getType() == Material.CHEST) { + block = block.getWorld().getBlockAt( + block.getX(), + block.getY() + 1, + block.getZ() + ); + } + + return getShopFromSignBlock(player, block); + } + + TradeCraftShop getShopFromSignBlock(Player player, Block block) { + if (block.getType() != Material.WALL_SIGN) { + return null; + } + + int x = block.getX(); + int y = block.getY(); + int z = block.getZ(); + + trace(player, "You clicked a sign at %d, %d, %d in world: %s.", x, y, z, block.getWorld().getName()); + + Sign sign = (Sign) block.getWorld().getBlockAt(x, y, z).getState(); + + // The sign at this location can be null if it was just destroyed. + if (sign == null) { + trace(player, "The sign is no longer there."); + + return null; + } + + String itemName = getItemName(sign.getLines()); + + if (itemName == null) { + trace(player, "There is no item name on the sign."); + + return null; + } + + trace(player, "The item name on the sign is %s.", itemName); + + Block blockBelowSign = block.getRelative(0, -1, 0); + + if (blockBelowSign.getType() != Material.CHEST) { + trace(player, "There is no chest beneath the sign."); + return null; + } + + Chest chest = (Chest) blockBelowSign.getState(); + + if (itemName.toLowerCase().equals("repair")) { + if (!properties.getRepairShopsEnabled()) { + trace(player, "Repair shops are not enabled."); + return null; + } + + if (player == null || !player.isOp()) { + trace(player, "You can't use repair shops."); + return null; + } + + trace(player, "This is a repair shop."); + return new TradeCraftRepairShop(this, sign, chest); + } + + if (!configuration.isConfigured(itemName)) { + trace(player, + "The item name %s is not configured in the config file.", + itemName); + return null; + } + + // TODO change to use chest getOwner + //String ownerName = getOwnerName(sign.getLine(3)); + String ownerName = data.getOwnerOfSign(sign); + + if (ownerName == null) { + trace(player, "There is no owner name on the sign."); + + if (!properties.getInfiniteShopsEnabled()) { + trace(player, "Ininite shops are not enabled."); + return null; + } + + trace(player, "This is an infinite shop."); + try { + return new TradeCraftInfiniteShop(this, sign, chest); + } catch (Exception e) { + trace(player, e.getMessage()); + } + return null; + } + + trace(player, "The owner name on the sign is %s.", ownerName); + + if (!properties.getPlayerOwnedShopsEnabled()) { + trace(player, "Player-owned shops are not enabled."); + return null; + } + + trace(player, "This is a player-owned shop."); + return new TradeCraftPlayerOwnedShop(this, sign, chest, ownerName); + } + + String getItemName(String[] signLines) { + return getSpecialText(signLines, "[", "]"); + } + + String getOwnerName(String signLine) { + return getSpecialTextOnLine(signLine, "-", "-"); + } + + private String getSpecialText(String[] signLines, String prefix, String suffix) { + for (int i = 0; i < 4; i++) { + String text = getSpecialTextOnLine(signLines[i], prefix, suffix); + + if (text != null) { + return text; + } + } + + return null; + } + + private String getSpecialTextOnLine(String signLine, String prefix, String suffix) { + if (signLine == null) { + return null; + } + + signLine = signLine.trim(); + + if (signLine.startsWith(prefix) && signLine.endsWith(suffix) + && signLine.length() > 2) { + + String text = signLine.substring(1, signLine.length() - 1); + text = text.trim(); + if (text.equals("")) { + return null; + } + + return text; + } + + return null; + } + + TradeCraftExchangeRate getExchangeRate(String[] signLines, int lineNumber) { + if ( lineNumber < 0 || lineNumber >= signLines.length ) { + return null; + } + return getExchangeRate(signLines[lineNumber]); + } + TradeCraftExchangeRate getExchangeRate(String signLine) { + return new TradeCraftExchangeRate(signLine); } + + static int getMaxStackSize(int itemType) { + if ( TradeCraft.properties.getNormalStackSizeUsed() ) { + return Material.getMaterial(itemType).getMaxStackSize(); + } else { + return 64; + } + } + + /** + * Get a CamelCased string based on the current currency. + * + * @return a string representing the currency. + */ + public String getCurrencyName() { + // Try to get the name from the configuration file first + TradeCraftConfigurationInfo configInfo = this.configuration.get(TradeCraft.currency); + if ( configInfo != null ) { + return configInfo.name; + } else { + + ItemStack currencyStack = new ItemStack(TradeCraft.currency.id, 1, TradeCraft.currency.data); // weird that there's no Material.getMaterial(id, short/byte) + MaterialData currencyData = currencyStack.getType().getNewData((byte)TradeCraft.currency.data); + String currencyString; + if ( currencyData == null ) { + currencyString = currencyStack.getType().name(); + } else { + currencyString = currencyData.toString(); + } + + //String baseName = stack.getType().name(); + String[] words = currencyString.replace("null ", "").split("\\(")[0].split("[ _]{1}"); + String name = ""; + for ( int word_ind = 0; word_ind < words.length; word_ind++ ) { + String word = words[word_ind]; + if ( word_ind > 0 ) { + name = name.concat(" "); + } + name = name.concat(word.substring(0, 1).toUpperCase()).concat(word.substring(1).toLowerCase()); + } + return name; + } + } + + private void displayCommandHelpText(Player player) { + if ( player != null ) { + this.sendMessage(player, TradeCraftLocalization.get("POSSIBLE_COMMANDS_FOR_THE_PLUGIN"), + TradeCraft.pluginName); + this.sendMessage(player, "/tc[help]"+ ChatColor.GRAY +" "+ TradeCraftLocalization.get("TC_HELP_THIS_TEXT")); + this.sendMessage(player, "/tcshops"+ ChatColor.GRAY +" "+ TradeCraftLocalization.get("TC_SHOPS")); + if ( this.permissions.canQueryOtherShops(player) ) { + this.sendMessage(player, "/tcpshops [player]"+ ChatColor.GRAY +" "+ TradeCraftLocalization.get("TC_PSHOPS")); + } + this.sendMessage(player, "/tcgetcurrency"+ ChatColor.GRAY +" "+ TradeCraftLocalization.get("TC_CURRENCY_GET_CURRENCY")); + if ( this.permissions.canSetCurrency(player) ) { + this.sendMessage(player, "/tcsetcurrency [id[;data]]"+ ChatColor.GRAY +" "+ TradeCraftLocalization.get("TC_CURRENCY_OPT_PARAM_GETSET_CURRENCY")); + } + if ( this.permissions.canReload(player) ) { + this.sendMessage(player, "/tcreload"+ ChatColor.GRAY +" "+ TradeCraftLocalization.get("TC_RELOAD")); + } + if ( this.permissions.canQueryPlayer(player) ) { + this.sendMessage(player, "/tcplayerperms"+ ChatColor.GRAY +" "+ TradeCraftLocalization.get("TC_CAN_PLAYER")); + } + } else { + // console command help + this.log(Level.INFO, TradeCraftLocalization.get("POSSIBLE_COMMANDS_FOR_THE_PLUGIN"), + TradeCraft.pluginName); + this.log(Level.INFO, "tc[help]: "+ TradeCraftLocalization.get("TC_HELP_THIS_TEXT")); + this.log(Level.INFO, "tcplayerperms playername: "+ TradeCraftLocalization.get("TC_CAN_PLAYER")); + this.log(Level.INFO, "tcpshops [player]: "+ TradeCraftLocalization.get("TC_PSHOPS")); + this.log(Level.INFO, "tcreload: "+ TradeCraftLocalization.get("TC_RELOAD")); + } + } + + public void saveConfig() { + Logger.getLogger(JavaPlugin.class.getName()).log(Level.INFO, "saving config to: "+ this.getConfig("config").getFile().getAbsolutePath()); + try { + this.getConfig("config").save(); + } catch (IOException ex) { + Logger.getLogger(JavaPlugin.class.getName()).log(Level.SEVERE, "Could not save config to " + this.getConfig("config").getFile().getName(), ex); + } + } + public StatefulYamlConfiguration getConfig() { + return this.getConfig("config"); + } + public StatefulYamlConfiguration getConfig(String name) { + if (name.indexOf(".") < 0) { + name += ".yml"; + } + if (this.configs.containsKey(name)) { + return this.configs.get(name); + } else { + File configFile = new File(this.getDataFolder(), name); + StatefulYamlConfiguration config = new StatefulYamlConfiguration(configFile); + this.configs.put(name, config); + return config; + } + } + + public void log(Level level, String format, Object... args) { + this.log.log(level, TradeCraft.pluginName +": "+ String.format(format, args)); + } + + public void useLog(Player player, TradeCraftShop shop, String format, Object... args) { + if ( TradeCraft.properties.logShopUse() ) { + if ( this.usageLog != null ) { + try { + SimpleDateFormat formatter = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss"); + this.usageLog.write(String.format("%s %s \t%s %s\n", + formatter.format(new Date()), + shop.toString(), + player.getDisplayName(), + String.format(format, args))); + this.usageLog.flush(); + } catch (IOException e) { + this.log(Level.WARNING, "Failed to write to shop use log file"); + } + } else { + this.log(Level.INFO, "not written to log file"); + } + } + } + +} \ No newline at end of file diff --git a/nl/armeagle/TradeCraft/TradeCraftBlockListener.java b/nl/armeagle/TradeCraft/TradeCraftBlockListener.java new file mode 100644 index 0000000..9075fe3 --- /dev/null +++ b/nl/armeagle/TradeCraft/TradeCraftBlockListener.java @@ -0,0 +1,224 @@ +package nl.armeagle.TradeCraft; + +import java.util.ArrayList; +import java.util.List; + +import org.bukkit.Material; +import org.bukkit.block.Block; +import org.bukkit.block.Sign; +import org.bukkit.entity.Player; +import org.bukkit.event.block.BlockBreakEvent; +import org.bukkit.event.block.BlockPistonExtendEvent; +import org.bukkit.event.block.BlockPistonRetractEvent; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.Listener; +import org.bukkit.event.block.SignChangeEvent; + +public class TradeCraftBlockListener implements Listener{ + + private TradeCraft plugin; + + TradeCraftBlockListener(TradeCraft plugin){ + this.plugin = plugin; + } + + public void debug(String str){ + plugin.getServer().broadcastMessage(str); + } + + @EventHandler + public void onNormalBlockBreak(BlockBreakEvent e) { + this.onBlockBreak(e, EventPriority.NORMAL); + } + + @EventHandler(priority = EventPriority.MONITOR) + public void onMonitorBlockBreak(BlockBreakEvent e) { + this.onBlockBreak(e, EventPriority.MONITOR); + } + + private void onBlockBreak(BlockBreakEvent e, EventPriority p) { + if ( !this.plugin.isEnabled() ) { + return; + } + + Player player = e.getPlayer(); + Block block = e.getBlock(); + ArrayList shops = plugin.getShopsFromBlock(player, block); + + if (shops.size() == 0) { + return; + } + + // Go through all shops in the list and check whether the player can destroy them all first. + // Only if that is the case proceed to destroy. + if (EventPriority.NORMAL == p) { + for ( TradeCraftShop shop : shops ) { + if (!shop.playerCanDestroy(player) && !plugin.permissions.canDestroyShops(player) || + shop.shopCanBeWithdrawnFrom() ) { + // cannot destroy this shop, so cancel destruction, use distinct error messages + if ( shop.shopCanBeWithdrawnFrom() ) { + plugin.sendMessage(player, TradeCraftLocalization.get("ALL_ITEMS_MUST_BE_WITHDRAWN")); + } else { + if ( block.getType() == Material.WALL_SIGN ) { + plugin.sendMessage(player, TradeCraftLocalization.get("YOU_CANT_DESTROY_THIS_SIGN")); + } else if ( block.getType() == Material.CHEST ) { + plugin.sendMessage(player, TradeCraftLocalization.get("YOU_CANT_DESTROY_THIS_CHEST")); + } else { + plugin.sendMessage(player, TradeCraftLocalization.get("YOU_CANT_DESTROY_THIS_BLOCK_ATTACHED")); + } + } + stopDestruction(block,e); + return; + } + } + } + + if (EventPriority.MONITOR == p && ! e.isCancelled()) { + // player can destroy all shops, so proceed + for ( TradeCraftShop shop : shops ) { + plugin.data.deleteShop(shop); + } + } + } + + @EventHandler(priority = EventPriority.MONITOR) + public void onMonitorSignChange(SignChangeEvent e) { + this.onSignChange(e, EventPriority.MONITOR); + } + + @EventHandler + public void onNormalSignChange(SignChangeEvent e) { + this.onSignChange(e, EventPriority.NORMAL); + } + + public void onSignChange(SignChangeEvent event, EventPriority priority) { + if ( !this.plugin.isEnabled() ) { + return; + } + + // Check whether this block is (still) a sign. Can be revoked already in case the sign was temporary, + // for example when the plugin SimpleSignEdit is being used. + if ( event.getBlock().getType() != Material.SIGN_POST && + event.getBlock().getType() != Material.WALL_SIGN ) { + return; + } + + Sign sign = (Sign) event.getBlock().getState(); + + Player player = event.getPlayer(); + String ownerName = player.getName(); + + String itemName = plugin.getItemName(event.getLines()); + + if (itemName == null) { + plugin.trace(player, "sign change, no item name, ignore"); + return; + } + // Check whether this is an existing item. Try to prevent as little normal sign placement as possible. + // Only treat signs with "[name]" as item line where "name" is found in the configuration (.txt) file. + TradeCraftConfigurationInfo itemInfo = plugin.configuration.get(itemName); + if ( itemInfo == null ) { + plugin.trace(player, "sign change, %s is not a valid item name, ignore this sign", itemName); + return; + } + + TradeCraftExchangeRate buyRate = new TradeCraftExchangeRate(event.getLine(1)); + TradeCraftExchangeRate sellRate = new TradeCraftExchangeRate(event.getLine(2)); + // no buy rate means this is an infinite shop + if ( !buyRate.isValid() && !sellRate.isValid() ) { + if (plugin.permissions.canMakeInfShops(player)){ + plugin.trace(player, "sign change, infinite chest of %s", itemName); + return; + } + + if (EventPriority.NORMAL == priority) { + plugin.sendMessage(player, TradeCraftLocalization.get("YOU_CANT_CREATE_INF_SHOPS")); + event.setCancelled(true); + return; + } + } + // there is a buy rate, so this is a player owned shop + + if (EventPriority.NORMAL == priority && !plugin.permissions.canMakePlayerShops(player)){ + plugin.sendMessage(player, TradeCraftLocalization.get("YOU_DONT_HAVE_PERM_CREATE_PLAYER_SHOP")); + event.setCancelled(true); + return; + } + + // check whether the player doesn't have too many shops + if (EventPriority.NORMAL == priority) { + int totalShopLimit = TradeCraft.properties.getPlayerTotalShopLimit(); + int worldShopLimit = TradeCraft.properties.getPlayerWorldShopLimit(); + if (plugin.data.getPlayerShopCount(player) >= totalShopLimit) { + plugin.sendMessage(player, TradeCraftLocalization.get("TOTAL_SHOP_LIMIT_X"), totalShopLimit); + event.setCancelled(true); + return; + } else if (plugin.data.getPlayerShopCount(player, player.getWorld()) >= worldShopLimit) { + plugin.sendMessage(player, TradeCraftLocalization.get("WORLD_SHOP_LIMIT_X"), worldShopLimit); + event.setCancelled(true); + return; + } + + } + + if (EventPriority.MONITOR == priority && !event.isCancelled()) { + plugin.trace(player, "Setting owner of sign to: %s", ownerName); + // set the player name on the last line + event.setLine(3, "-"+ ownerName.substring(0, Math.min(ownerName.length(), 15)) +"-"); + plugin.data.createNewSign(ownerName, itemInfo, sign); + } + + /* + if ( this.plugin.properties.getStrictPlayerShopOwnerNameRequired() ) { + if (player.getName().equalsIgnoreCase(ownerName)) { + plugin.data.setOwnerOfSign(player.getName(), sign); + return; + } + } else { + if (player.getName().startsWith(ownerName)) { + plugin.data.setOwnerOfSign(player.getName(), sign); + return; + } + } + */ + } + + // prevent pistons from pushing away (the block behind) a sign. Block all retract and extend events that would move a block behind a sign, also for shop owners + @EventHandler + public void onNormalBlockPistonRetract(BlockPistonRetractEvent e) { + if (e.isSticky()) { + Block block = e.getRetractLocation().getBlock(); + ArrayList shops = plugin.getShopsFromBlock(null, block); + + if (shops.size() != 0) { + e.setCancelled(true); + } + } + } + @EventHandler + public void onNormalBlockPistonExtend(BlockPistonExtendEvent e) { + List blocks = e.getBlocks(); + for (Block block:blocks) { + ArrayList shops = plugin.getShopsFromBlock(null, block); + + if (shops.size() != 0) { + e.setCancelled(true); + return; + } + } + } + + public void stopDestruction(Block b, BlockBreakEvent e){ + if(b.getState() instanceof Sign){ + Sign sign = (Sign)b.getState(); + String[] lines = sign.getLines(); + for(int i = 0;i<4;i++){ + sign.setLine(i, lines[i]); + } + + sign.update(true); + } + e.setCancelled(true); + } +} diff --git a/src/nl/armeagle/TradeCraft/TradeCraftChest.java b/nl/armeagle/TradeCraft/TradeCraftChest.java similarity index 75% rename from src/nl/armeagle/TradeCraft/TradeCraftChest.java rename to nl/armeagle/TradeCraft/TradeCraftChest.java index 0f8655f..d089909 100644 --- a/src/nl/armeagle/TradeCraft/TradeCraftChest.java +++ b/nl/armeagle/TradeCraft/TradeCraftChest.java @@ -17,16 +17,16 @@ public TradeCraftChest(Chest c) { chest = c.getInventory(); for (ItemStack item : chest.getContents()) { - if(item != null){ - short itemData = (item.getData() == null ? (short)0 : item.getDurability()); - if(type.id != 0 && (type.id != item.getTypeId() || type.data != itemData) ){ - diffFlag = true; - return; - } - type.id = item.getTypeId(); - type.data = itemData; - total += item.getAmount(); - } + if(item != null){ + short itemData = (item.getData() == null ? (short)0 : item.getDurability()); + if(type.id != 0 && (type.id != item.getTypeId() || type.data != itemData) ){ + diffFlag = true; + return; + } + type.id = item.getTypeId(); + type.data = itemData; + total += item.getAmount(); + } } } @@ -66,7 +66,7 @@ public int getAmountOfCurrencyInChest() { int amount = 0; for (ItemStack item : ((Inventory)chest).getContents()) { if (item != null) { - short itemData = (item.getData() == null ? (short)0 : item.getDurability()); + short itemData = (item.getData() == null ? (short)0 : item.getDurability()); if (item.getTypeId() == TradeCraft.currency.id && itemData == TradeCraft.currency.data) { amount += item.getAmount(); } @@ -79,7 +79,7 @@ public List getNonCurrencyItems() { List items = new ArrayList(); for (ItemStack item : chest.getContents()) { if (item != null) { - short itemData = (item.getData() == null ? (short)0 : item.getDurability()); + short itemData = (item.getData() == null ? (short)0 : item.getDurability()); if (item.getTypeId() != TradeCraft.currency.id || itemData != TradeCraft.currency.data) { items.add(item); } @@ -89,6 +89,6 @@ public List getNonCurrencyItems() { } public int getSize() { - return (this.chest == null ? 0 : this.chest.getSize()); + return (this.chest == null ? 0 : this.chest.getSize()); } } diff --git a/nl/armeagle/TradeCraft/TradeCraftConfigurationFile.java b/nl/armeagle/TradeCraft/TradeCraftConfigurationFile.java new file mode 100644 index 0000000..94dff0b --- /dev/null +++ b/nl/armeagle/TradeCraft/TradeCraftConfigurationFile.java @@ -0,0 +1,199 @@ +package nl.armeagle.TradeCraft; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileReader; +import java.io.IOException; +import java.util.Arrays; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; +import java.util.Map.Entry; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import org.bukkit.configuration.MemorySection; + +import nl.armeagle.Configuration.StatefulYamlConfiguration; + +/** + * The name of this class is a bit misleading. This class stores all the items and their default + * trade rates that can be used in the game. The actual configuration of the plugin itself + * is handled by the TradeCraftProperties class. + */ +class TradeCraftConfigurationFile { + private static final String fileName = TradeCraft.pluginName + ".txt"; + private static final String configName = "items"; + + private static final Pattern commentPattern = Pattern.compile("^\\s*#.*$"); + private static final Pattern infoPattern = Pattern.compile( + "^\\s*([^,]+)\\s*," + // name + "\\s*(\\d+(?:;\\d+)?)\\s*" + // id[;data] (optional ;data) + "(?:,\\s*(\\d+)\\s*:\\s*(\\d+))?\\s*" + // buyAmount:buyValue + "(?:,\\s*(\\d+)\\s*:\\s*(\\d+))?\\s*$"); // sellAmount:sellValue + + private HashMap mapItemNames = new HashMap(); + + private final TradeCraft plugin; + // create an index from an TradeCraftItem (id;data) to the TradeCraftConfigurationInfo entry. This for configured name lookup based on id;data + private final Map TCitemInfoIndex = new HashMap(); + + TradeCraftConfigurationFile(TradeCraft plugin) { + this.plugin = plugin; + } + + public StatefulYamlConfiguration getConfig() { + return this.plugin.getConfig(TradeCraftConfigurationFile.configName); + } + + void load() { + StatefulYamlConfiguration config = (StatefulYamlConfiguration) this.getConfig(); + + // if file exists, load the config to it once and then rename the old config + File file = new File(plugin.getDataFolder().getAbsolutePath(), fileName); + if ( file.exists() ) { + try { + FileReader reader = new FileReader(file); + BufferedReader configurationFile = new BufferedReader(reader); + + int lineNumber = 0; + String line; + + while ((line = configurationFile.readLine()) != null) { + lineNumber += 1; + + if (line.trim().equals("")) { + continue; + } + + Matcher commentMatcher = commentPattern.matcher(line); + + if (commentMatcher.matches()) { + continue; + } + + Matcher infoMatcher = infoPattern.matcher(line); + + if (!infoMatcher.matches()) { + plugin.log.warning( + "Failed to parse line number " + lineNumber + + " in " + file.getAbsolutePath() + + ": " + line); + continue; + } + + TradeCraftConfigurationInfo info = new TradeCraftConfigurationInfo(); + info.name = infoMatcher.group(1); + + // try to split ID and Data, separated by a semicolon mark + Matcher IdSplitData = TradeCraft.itemPatternIdSplitData.matcher(infoMatcher.group(2)); + + if (!IdSplitData.matches()) { + plugin.log.info( + "Failed to parse line number " + lineNumber + + " in " + file.getAbsolutePath() + + ": " + line); + continue; + } + + int id = Integer.parseInt(IdSplitData.group(1)); + if ( IdSplitData.group(2) != null ) { + short data = Short.parseShort(IdSplitData.group(2)); + info.type = new TradeCraftItem(id, data); + } else { + info.type = new TradeCraftItem(id); + } + + if (infoMatcher.group(3) != null) { + info.sellAmount = info.buyAmount = Integer.parseInt(infoMatcher.group(3)); + info.sellValue = info.buyValue = Integer.parseInt(infoMatcher.group(4)); + } + + if (infoMatcher.group(5) != null) { + info.sellAmount = Integer.parseInt(infoMatcher.group(5)); + info.sellValue = Integer.parseInt(infoMatcher.group(6)); + } + +// config.set(info.name.toUpperCase(), info.toMemoryConfiguration()); + Iterator> iter = info.toMap().entrySet().iterator(); + while (iter.hasNext()) { + Map.Entry pairs = (Map.Entry)iter.next(); + if (!pairs.getKey().equals("name")) { + config.set(info.name +"."+ pairs.getKey(), pairs.getValue()); + } + } + +// TCitemInfoIndex.put(info.type, info.name); + } + configurationFile.close(); + reader.close(); + config.save(); + if (file.renameTo(new File(file.getAbsolutePath() + ".converted.to."+ TradeCraftConfigurationFile.configName +".yml"))) { + plugin.log.info("Converted old config to new style and renamed the old config file"); + } else { + plugin.log.severe("FAILED to convert old config to new style"); + } + + } catch (IOException e) { + plugin.log.severe("Error reading " + file.getAbsolutePath()); + } + } else { + try { + config.load(); + } catch (IOException e) { + plugin.log.severe("Error loading plugin config file"); + } + } + + Iterator> iter = config.getValues(false).entrySet().iterator(); + while (iter.hasNext()) { + // store map of lowercase item names to key names in the configuration + Map.Entry entry = (Map.Entry)iter.next(); + this.mapItemNames.put(entry.getKey().toLowerCase(), entry.getKey()); + // store map of item types to key names + MemorySection section = (MemorySection) entry.getValue(); + TradeCraftItem tcItem = new TradeCraftItem(section.getInt("itemTypeId", 266), section.getInt("itemTypeData", 0)); + TCitemInfoIndex.put(tcItem, entry.getKey()); + } + } + + public String[] getNames() { + String[] names = this.getConfig().getKeys(false).toArray(new String[0]); + Arrays.sort(names); + return names; + } + + public boolean isConfigured(String name) { + return this.mapItemNames.containsKey(name.toLowerCase()); + } + + public TradeCraftConfigurationInfo get(String name) { + + // @todo, config code doesn't seem to like serialized objects, cannot seem to find the TradeCraftConfigurationInfo class + // though, the otherwise resulting ==: classpath lines aren't very user friendly anyway. +// return (TradeCraftConfigurationInfo) plugin.getConfig().get(name.toUpperCase()); + String itemName = this.mapItemNames.get(name.toLowerCase()); + if (null != itemName) { + MemorySection item = ((MemorySection)this.getConfig().get(itemName)); + if (null != item) { + Map itemData = item.getValues(false); + if (null != itemData) { + return new TradeCraftConfigurationInfo(itemData, name); + } + } + } + return null; + } + public TradeCraftConfigurationInfo get(int id) { + return this.get(new TradeCraftItem(id)); + } + public TradeCraftConfigurationInfo get(int id, short data) { + return this.get(new TradeCraftItem(id, data)); + } + public TradeCraftConfigurationInfo get(TradeCraftItem item) { + if (!TCitemInfoIndex.containsKey(item)) { + return null; + } + return this.get(TCitemInfoIndex.get(item)); + } +} \ No newline at end of file diff --git a/nl/armeagle/TradeCraft/TradeCraftConfigurationInfo.java b/nl/armeagle/TradeCraft/TradeCraftConfigurationInfo.java new file mode 100644 index 0000000..671a524 --- /dev/null +++ b/nl/armeagle/TradeCraft/TradeCraftConfigurationInfo.java @@ -0,0 +1,55 @@ +package nl.armeagle.TradeCraft; + +import java.util.LinkedHashMap; +import java.util.Map; +import org.bukkit.configuration.serialization.ConfigurationSerializable; +import org.bukkit.configuration.serialization.SerializableAs; + +@SerializableAs("nl.armeagle.TradeCraft.TradeCraftConfigurationInfo") +class TradeCraftConfigurationInfo implements ConfigurationSerializable { + public String name; + public TradeCraftItem type; + public int buyAmount; + public int buyValue; + public int sellAmount; + public int sellValue; + + TradeCraftConfigurationInfo() { + } + + TradeCraftConfigurationInfo(Map map, String name) { + this.name = name; + this.type = new TradeCraftItem((Integer) map.get("itemTypeId"), (Integer) map.get("itemTypeData")); + this.buyAmount = (Integer) map.get("buyAmount"); + this.buyValue = (Integer) map.get("buyValue"); + this.sellAmount = (Integer) map.get("sellAmount"); + this.sellValue = (Integer) map.get("sellValue"); + } + TradeCraftConfigurationInfo(Map map) { + this.name = (String) map.get("name"); + this.type = new TradeCraftItem((Integer) map.get("itemTypeId"), (Integer) map.get("itemTypeData")); + this.buyAmount = (Integer) map.get("buyAmount"); + this.buyValue = (Integer) map.get("buyValue"); + this.sellAmount = (Integer) map.get("sellAmount"); + this.sellValue = (Integer) map.get("sellValue"); + } + + public Map toMap() { + LinkedHashMap map = new LinkedHashMap(); + map.put("name", this.name); + map.put("itemTypeId", this.type.id); + map.put("itemTypeData", this.type.data); + map.put("buyAmount", this.buyAmount); + map.put("buyValue", this.buyValue); + map.put("sellAmount", this.sellAmount); + map.put("sellValue", this.sellValue); + return map; + } + @Override + public Map serialize() { + return this.toMap(); + } + public static TradeCraftConfigurationInfo deserialize(Map map) { + return new TradeCraftConfigurationInfo(map); + } +} \ No newline at end of file diff --git a/src/nl/armeagle/TradeCraft/TradeCraftDataFile.java b/nl/armeagle/TradeCraft/TradeCraftDataFile.java similarity index 73% rename from src/nl/armeagle/TradeCraft/TradeCraftDataFile.java rename to nl/armeagle/TradeCraft/TradeCraftDataFile.java index 29f7dbd..2e4cb06 100644 --- a/src/nl/armeagle/TradeCraft/TradeCraftDataFile.java +++ b/nl/armeagle/TradeCraft/TradeCraftDataFile.java @@ -17,13 +17,13 @@ import org.bukkit.entity.Player; class TradeCraftDataFile { - /* - * As of version 1.0.5 there is support for multiple worlds. - * Newly created shops will add the world name to the information stored. - * Old shops will be converted when first interacted with. - */ + /* + * As of version 1.0.5 there is support for multiple worlds. + * Newly created shops will add the world name to the information stored. + * Old shops will be converted when first interacted with. + */ - private static final String fileName = "plugins" + File.separator + TradeCraft.pluginName+ File.separator + TradeCraft.pluginName + ".data"; + private static final String fileName = "plugins" + File.separator + TradeCraft.pluginName+ File.separator + TradeCraft.pluginName + ".data"; private static final Pattern infoPatternNoWorld = Pattern.compile( "^\\s*([^,]+)\\s*," + // ownerName "\\s*(-?\\d+)\\s*,\\s*(-?\\d+)\\s*,\\s*(-?\\d+)\\s*," + // x,y,z @@ -50,7 +50,7 @@ class TradeCraftDataFile { public void load() { try { - dFile.createNewFile(); + dFile.createNewFile(); data.clear(); BufferedReader reader = new BufferedReader(new FileReader(fileName)); @@ -82,15 +82,15 @@ public void load() { itemAmount = Integer.parseInt(infoMatcher2.group(6)); currencyAmount = Integer.parseInt(infoMatcher2.group(7)); } else { - // support for multiple worlds - Matcher infoMatcher3 = infoPatternWorld.matcher(line); - if ( !infoMatcher3.matches()) { - plugin.log.warning( + // support for multiple worlds + Matcher infoMatcher3 = infoPatternWorld.matcher(line); + if ( !infoMatcher3.matches()) { + plugin.log.warning( "Failed to parse line number " + lineNumber + " in " + fileName + ": " + line); continue; - } + } ownerName = infoMatcher3.group(1); worldName = infoMatcher3.group(2); @@ -116,12 +116,12 @@ public void load() { if ( IdSplitData.matches() ) { int itemId = Integer.parseInt(IdSplitData.group(1)); if ( IdSplitData.group(2) != null ) { - info.itemType = new TradeCraftItem(itemId, Short.parseShort(IdSplitData.group(2))); + info.itemType = new TradeCraftItem(itemId, Short.parseShort(IdSplitData.group(2))); } else { - info.itemType = new TradeCraftItem(itemId); + info.itemType = new TradeCraftItem(itemId); } } else { - plugin.log.warning( + plugin.log.warning( "Failed to parse line number " + lineNumber + " in " + fileName + ": " + line); @@ -138,13 +138,13 @@ public void load() { } public void save() { - if ( ! this.wasLoaded ) { - this.plugin.log.severe("TradeCraft: failed to load data file when plugin was enabled, will not save to prevent loss of items."); - // The failure should have been such that no interaction with shops would have been possible, so no items should have been lost since the plugin was - // loaded till this save point. TODO, make sure that no items are lost, when save is actually called after motations, even though that situation - // should never possibly occur. - return; - } + if ( ! this.wasLoaded ) { + this.plugin.log.severe("TradeCraft: failed to load data file when plugin was enabled, will not save to prevent loss of items."); + // The failure should have been such that no interaction with shops would have been possible, so no items should have been lost since the plugin was + // loaded till this save point. TODO, make sure that no items are lost, when save is actually called after motations, even though that situation + // should never possibly occur. + return; + } try { BufferedWriter writer = new BufferedWriter(new FileWriter(fileName)); @@ -170,24 +170,24 @@ public void save() { } public void deleteShop(TradeCraftShop shop){ - Location l = shop.sign.getBlock().getLocation(); - String key = getKey(shop.sign.getWorld().getName(), l.getBlockX(),l.getBlockY(),l.getBlockZ()); - if(data.containsKey(key)){ - data.remove(key); - save(); - } + Location l = shop.sign.getBlock().getLocation(); + String key = getKey(shop.sign.getWorld().getName(), l.getBlockX(),l.getBlockY(),l.getBlockZ()); + if(data.containsKey(key)){ + data.remove(key); + save(); + } } public Map shopsOwned(String playerName){ - Map list = new HashMap(); - for (String key : data.keySet()) { - TradeCraftDataInfo info = data.get(key); - if(info.ownerName.equalsIgnoreCase(playerName)){ - list.put(key, info); - } - } - - return list; + Map list = new HashMap(); + for (String key : data.keySet()) { + TradeCraftDataInfo info = data.get(key); + if(info.ownerName.equalsIgnoreCase(playerName)){ + list.put(key, info); + } + } + + return list; } public void setOwnerOfSign(String ownerName, Sign sign) { @@ -241,7 +241,7 @@ public void depositItems(String ownerName, Sign sign, TradeCraftItem itemType, i } public void depositCurrency(String ownerName, Sign sign, int currencyAmount) { - TradeCraftDataInfo info; + TradeCraftDataInfo info; String key = getKeyFromSign(sign); if (data.containsKey(key)) { @@ -301,30 +301,30 @@ private String getKeyFromSign(Sign sign) { String keyWithWorld = getKey(sign.getWorld().getName(), sign.getX(), sign.getY(), sign.getZ()); // convert old style keys (without world name) to new style and return the new key if ( !data.containsKey(keyWithWorld) ) { - // try the old style key, without the world part - String keyWithoutWorld = getKey(null, sign.getX(), sign.getY(), sign.getZ()); - if ( data.containsKey(keyWithoutWorld) ) { - TradeCraftDataInfo shopInfo = data.get(keyWithoutWorld); - data.remove(keyWithoutWorld); - data.put(keyWithWorld, shopInfo); - } + // try the old style key, without the world part + String keyWithoutWorld = getKey(null, sign.getX(), sign.getY(), sign.getZ()); + if ( data.containsKey(keyWithoutWorld) ) { + TradeCraftDataInfo shopInfo = data.get(keyWithoutWorld); + data.remove(keyWithoutWorld); + data.put(keyWithWorld, shopInfo); + } } return keyWithWorld; } private String getKey(String world, int x, int y, int z) { - // support for multiple words now, optionally accepting a world passed on. - if ( world == null ) { - return x + "," + y + "," + z; - } else { - return world + "," + x + "," + y + "," + z; - } + // support for multiple words now, optionally accepting a world passed on. + if ( world == null ) { + return x + "," + y + "," + z; + } else { + return world + "," + x + "," + y + "," + z; + } } - public void createNewSign(String ownerName, TradeCraftConfigurationInfo itemInfo, Sign sign) { - TradeCraftDataInfo info; - String key = getKeyFromSign(sign); - + public void createNewSign(String ownerName, TradeCraftConfigurationInfo itemInfo, Sign sign) { + TradeCraftDataInfo info; + String key = getKeyFromSign(sign); + if (data.containsKey(key)) { info = data.get(key); } else { @@ -339,22 +339,22 @@ public void createNewSign(String ownerName, TradeCraftConfigurationInfo itemInfo data.put(key, info); save(); - } - - public int getPlayerShopCount(Player player, World world) { - Map list = this.shopsOwned(player.getName()); - int shopsOwnedCount = 0; - - for (String key : list.keySet()) { - TradeCraftDataInfo info = data.get(key); - if (info.worldName.equalsIgnoreCase(world.getName())) { - shopsOwnedCount++; - } - } - - return shopsOwnedCount; - } - public int getPlayerShopCount(Player player) { - return this.shopsOwned(player.getName()).size(); - } + } + + public int getPlayerShopCount(Player player, World world) { + Map list = this.shopsOwned(player.getName()); + int shopsOwnedCount = 0; + + for (String key : list.keySet()) { + TradeCraftDataInfo info = data.get(key); + if (info.worldName.equalsIgnoreCase(world.getName())) { + shopsOwnedCount++; + } + } + + return shopsOwnedCount; + } + public int getPlayerShopCount(Player player) { + return this.shopsOwned(player.getName()).size(); + } } \ No newline at end of file diff --git a/src/nl/armeagle/TradeCraft/TradeCraftDataInfo.java b/nl/armeagle/TradeCraft/TradeCraftDataInfo.java similarity index 61% rename from src/nl/armeagle/TradeCraft/TradeCraftDataInfo.java rename to nl/armeagle/TradeCraft/TradeCraftDataInfo.java index e9dce65..18e50ea 100644 --- a/src/nl/armeagle/TradeCraft/TradeCraftDataInfo.java +++ b/nl/armeagle/TradeCraft/TradeCraftDataInfo.java @@ -8,10 +8,10 @@ class TradeCraftDataInfo { public int currencyAmount; public TradeCraftDataInfo() { - this.ownerName = null; - this.worldName = null; - this.itemType = null; - this.itemAmount = 0; - this.currencyAmount = 0; - } + this.ownerName = null; + this.worldName = null; + this.itemType = null; + this.itemAmount = 0; + this.currencyAmount = 0; + } } \ No newline at end of file diff --git a/src/nl/armeagle/TradeCraft/TradeCraftExchangeRate.java b/nl/armeagle/TradeCraft/TradeCraftExchangeRate.java similarity index 52% rename from src/nl/armeagle/TradeCraft/TradeCraftExchangeRate.java rename to nl/armeagle/TradeCraft/TradeCraftExchangeRate.java index 5fd3ae0..a63d6e7 100644 --- a/src/nl/armeagle/TradeCraft/TradeCraftExchangeRate.java +++ b/nl/armeagle/TradeCraft/TradeCraftExchangeRate.java @@ -10,18 +10,18 @@ public class TradeCraftExchangeRate { private static final Pattern ratePattern = Pattern.compile("(\\d+)\\D+(\\d+)\\s*"); TradeCraftExchangeRate(String signLine) { - Matcher matcher = ratePattern.matcher(signLine); + Matcher matcher = ratePattern.matcher(signLine); - if (matcher.find()) { - this.amount = Integer.parseInt(matcher.group(1)); - this.value = Integer.parseInt(matcher.group(2)); - } else { - this.amount = 0; - this.value = 0; - } + if (matcher.find()) { + this.amount = Integer.parseInt(matcher.group(1)); + this.value = Integer.parseInt(matcher.group(2)); + } else { + this.amount = 0; + this.value = 0; + } } public boolean isValid() { - return this.amount != 0; + return this.amount != 0; } } diff --git a/src/nl/armeagle/TradeCraft/TradeCraftInfiniteShop.java b/nl/armeagle/TradeCraft/TradeCraftInfiniteShop.java similarity index 93% rename from src/nl/armeagle/TradeCraft/TradeCraftInfiniteShop.java rename to nl/armeagle/TradeCraft/TradeCraftInfiniteShop.java index 94786d5..b717bf2 100644 --- a/src/nl/armeagle/TradeCraft/TradeCraftInfiniteShop.java +++ b/nl/armeagle/TradeCraft/TradeCraftInfiniteShop.java @@ -13,12 +13,12 @@ public TradeCraftInfiniteShop(TradeCraft plugin, Sign sign, Chest chest) throws String itemName = plugin.getItemName(sign.getLines()); configurationInfo = plugin.configuration.get(itemName); if (null == configurationInfo) { - throw new Exception("Invalid item name on sign: "+ itemName); + throw new Exception("Invalid item name on sign: "+ itemName); } } public boolean playerCanDestroy(Player player) { - return plugin.permissions.canDestroyShops(player); + return plugin.permissions.canDestroyShops(player); } public boolean shopCanBeWithdrawnFrom() { diff --git a/nl/armeagle/TradeCraft/TradeCraftItem.java b/nl/armeagle/TradeCraft/TradeCraftItem.java new file mode 100644 index 0000000..d8155fa --- /dev/null +++ b/nl/armeagle/TradeCraft/TradeCraftItem.java @@ -0,0 +1,63 @@ +package nl.armeagle.TradeCraft; + +/** + * + * @author ArmEagle + * Store item type(ID) and optionally the data bit + */ +public class TradeCraftItem implements Comparable { + public int id; + public short data; + + TradeCraftItem(int id) { + this(id, (short)0); + } + TradeCraftItem(int id, int data) { + this(id, new Integer(data).shortValue()); + } + TradeCraftItem(int id, short data) { + this.id = id; + this.data = data; + } + + /** + * @param compare + * @throws NullPointerException if compare is null + * @return default < 0 > compare values + */ + @Override public int compareTo(TradeCraftItem compare) { + if ( this == compare ) { + return 0; + } + if ( this.id < compare.id ) { + return -1; + } else if ( this.id > compare.id ) { + return 1; + } else { + if ( this.data < compare.data ) { + return -1; + } else if ( this.data > compare.data ) { + return 1; + } else { + return 0; + } + } + } + @Override public boolean equals(Object compare) { + return (compare == null ? false : (compare instanceof TradeCraftItem? this.compareTo((TradeCraftItem)compare) == 0 : false)); + } + @Override public int hashCode() { + return this.id * 32768 + this.data; + } + + @Override public String toString() { + return "TradeCraftItem("+ this.id +";"+ this.data +")"; + } + public String toShortString() { + if ( this.data == 0 ) { + return String.valueOf(this.id); + } else { + return this.id +";"+ this.data; + } + } +} diff --git a/src/nl/armeagle/TradeCraft/TradeCraftItemShop.java b/nl/armeagle/TradeCraft/TradeCraftItemShop.java similarity index 50% rename from src/nl/armeagle/TradeCraft/TradeCraftItemShop.java rename to nl/armeagle/TradeCraft/TradeCraftItemShop.java index ad65463..09b5fde 100644 --- a/src/nl/armeagle/TradeCraft/TradeCraftItemShop.java +++ b/nl/armeagle/TradeCraft/TradeCraftItemShop.java @@ -27,61 +27,61 @@ private void handleOwnerClick(Player player) { if (getChestItemCount() == 0) { int currencyAmount = withdrawCurrency(); if (currencyAmount > 0) { - // limit amount of currency dropped into the chest to the max amount it can hold - int maxCurrencyChestCanHold = chest.getSize() * TradeCraft.getMaxStackSize(TradeCraft.currency.id); - if ( currencyAmount > maxCurrencyChestCanHold ) { - populateChest(TradeCraft.currency, maxCurrencyChestCanHold); // fill to the max - depositCurrency(currencyAmount - maxCurrencyChestCanHold); // return remaining money back to shop data - plugin.sendMessage(player, TradeCraft.MessageTypes.WITHDRAW, - TradeCraftLocalization.get("WITHDREW_X_CURRENCY_SHOP_STILL_HOLDS_Y_CURRENCY"), - maxCurrencyChestCanHold, - plugin.getCurrencyName(), - currencyAmount - maxCurrencyChestCanHold); - plugin.useLog(player, this, - TradeCraftLocalization.get("WITHDREW_X_CURRENCY_SHOP_STILL_HOLDS_Y_CURRENCY"), - maxCurrencyChestCanHold, - plugin.getCurrencyName(), - currencyAmount - maxCurrencyChestCanHold); - } else { - populateChest(TradeCraft.currency, currencyAmount); - plugin.sendMessage(player, TradeCraft.MessageTypes.WITHDRAW, - TradeCraftLocalization.get("WITHDREW_X_A"), - currencyAmount, - plugin.getCurrencyName()); - plugin.useLog(player, this, - TradeCraftLocalization.get("WITHDREW_X_A"), - currencyAmount, - plugin.getCurrencyName()); - } + // limit amount of currency dropped into the chest to the max amount it can hold + int maxCurrencyChestCanHold = chest.getSize() * TradeCraft.getMaxStackSize(TradeCraft.currency.id); + if ( currencyAmount > maxCurrencyChestCanHold ) { + populateChest(TradeCraft.currency, maxCurrencyChestCanHold); // fill to the max + depositCurrency(currencyAmount - maxCurrencyChestCanHold); // return remaining money back to shop data + plugin.sendMessage(player, TradeCraft.MessageTypes.WITHDRAW, + TradeCraftLocalization.get("WITHDREW_X_CURRENCY_SHOP_STILL_HOLDS_Y_CURRENCY"), + maxCurrencyChestCanHold, + plugin.getCurrencyName(), + currencyAmount - maxCurrencyChestCanHold); + plugin.useLog(player, this, + TradeCraftLocalization.get("WITHDREW_X_CURRENCY_SHOP_STILL_HOLDS_Y_CURRENCY"), + maxCurrencyChestCanHold, + plugin.getCurrencyName(), + currencyAmount - maxCurrencyChestCanHold); + } else { + populateChest(TradeCraft.currency, currencyAmount); + plugin.sendMessage(player, TradeCraft.MessageTypes.WITHDRAW, + TradeCraftLocalization.get("WITHDREW_X_A"), + currencyAmount, + plugin.getCurrencyName()); + plugin.useLog(player, this, + TradeCraftLocalization.get("WITHDREW_X_A"), + currencyAmount, + plugin.getCurrencyName()); + } } else { - // limit amount of items dropped into the chest + // limit amount of items dropped into the chest int itemAmount = withdrawItems(); if (itemAmount > 0) { - int maxItemsChestCanHold = chest.getSize() * TradeCraft.getMaxStackSize(getItemType().id); - if ( itemAmount > maxItemsChestCanHold ) { - populateChest(getItemType(), maxItemsChestCanHold); - depositItems(itemAmount - maxItemsChestCanHold); - plugin.sendMessage(player, TradeCraft.MessageTypes.WITHDRAW, - TradeCraftLocalization.get("WITHDREW_X_A_SHOP_STILL_HOLDS_Y_A"), - maxItemsChestCanHold, - getItemName(), - itemAmount - maxItemsChestCanHold); - plugin.useLog(player, this, - TradeCraftLocalization.get("WITHDREW_X_A_SHOP_STILL_HOLDS_Y_A"), - maxItemsChestCanHold, - getItemName(), - itemAmount - maxItemsChestCanHold); - } else { - populateChest(getItemType(), itemAmount); - plugin.sendMessage(player, TradeCraft.MessageTypes.WITHDRAW, - TradeCraftLocalization.get("WITHDREW_X_A"), - itemAmount, - getItemName()); - plugin.useLog(player, this, - TradeCraftLocalization.get("WITHDREW_X_A"), - itemAmount, - getItemName()); - } + int maxItemsChestCanHold = chest.getSize() * TradeCraft.getMaxStackSize(getItemType().id); + if ( itemAmount > maxItemsChestCanHold ) { + populateChest(getItemType(), maxItemsChestCanHold); + depositItems(itemAmount - maxItemsChestCanHold); + plugin.sendMessage(player, TradeCraft.MessageTypes.WITHDRAW, + TradeCraftLocalization.get("WITHDREW_X_A_SHOP_STILL_HOLDS_Y_A"), + maxItemsChestCanHold, + getItemName(), + itemAmount - maxItemsChestCanHold); + plugin.useLog(player, this, + TradeCraftLocalization.get("WITHDREW_X_A_SHOP_STILL_HOLDS_Y_A"), + maxItemsChestCanHold, + getItemName(), + itemAmount - maxItemsChestCanHold); + } else { + populateChest(getItemType(), itemAmount); + plugin.sendMessage(player, TradeCraft.MessageTypes.WITHDRAW, + TradeCraftLocalization.get("WITHDREW_X_A"), + itemAmount, + getItemName()); + plugin.useLog(player, this, + TradeCraftLocalization.get("WITHDREW_X_A"), + itemAmount, + getItemName()); + } } else { plugin.sendMessage(player, TradeCraftLocalization.get("THERE_IS_NOTHING_TO_WITHDRAW")); } @@ -89,61 +89,61 @@ private void handleOwnerClick(Player player) { } else if ( getChestItemType().compareTo(TradeCraft.currency) == 0 ) { depositCurrency(getChestItemCount()); plugin.sendMessage(player, TradeCraft.MessageTypes.DEPOSIT, - TradeCraftLocalization.get("DEPOSITED_X_A"), - getChestItemCount(), - plugin.getCurrencyName()); + TradeCraftLocalization.get("DEPOSITED_X_A"), + getChestItemCount(), + plugin.getCurrencyName()); plugin.useLog(player, this, - TradeCraftLocalization.get("DEPOSITED_X_A"), - getChestItemCount(), - plugin.getCurrencyName()); + TradeCraftLocalization.get("DEPOSITED_X_A"), + getChestItemCount(), + plugin.getCurrencyName()); populateChest(new TradeCraftItem(0), 0); int itemAmount = withdrawItems(); if (itemAmount > 0) { - int maxItemsChestCanHold = chest.getSize() * TradeCraft.getMaxStackSize(getChestItemType().id); - if ( itemAmount > maxItemsChestCanHold ) { - populateChest(getItemType(), maxItemsChestCanHold); - depositItems(itemAmount - maxItemsChestCanHold); + int maxItemsChestCanHold = chest.getSize() * TradeCraft.getMaxStackSize(getChestItemType().id); + if ( itemAmount > maxItemsChestCanHold ) { + populateChest(getItemType(), maxItemsChestCanHold); + depositItems(itemAmount - maxItemsChestCanHold); plugin.sendMessage(player, TradeCraft.MessageTypes.WITHDRAW, - TradeCraftLocalization.get("WITHDREW_X_A_SHOP_STILL_HOLDS_Y_A"), - maxItemsChestCanHold, - getItemName(), - itemAmount - maxItemsChestCanHold); + TradeCraftLocalization.get("WITHDREW_X_A_SHOP_STILL_HOLDS_Y_A"), + maxItemsChestCanHold, + getItemName(), + itemAmount - maxItemsChestCanHold); plugin.useLog(player, this, - TradeCraftLocalization.get("WITHDREW_X_A_SHOP_STILL_HOLDS_Y_A"), - maxItemsChestCanHold, - getItemName(), - itemAmount - maxItemsChestCanHold); - } else { + TradeCraftLocalization.get("WITHDREW_X_A_SHOP_STILL_HOLDS_Y_A"), + maxItemsChestCanHold, + getItemName(), + itemAmount - maxItemsChestCanHold); + } else { populateChest(getItemType(), itemAmount); plugin.sendMessage(player, TradeCraft.MessageTypes.WITHDRAW, - TradeCraftLocalization.get("WITHDREW_X_A"), - itemAmount, - getItemName()); + TradeCraftLocalization.get("WITHDREW_X_A"), + itemAmount, + getItemName()); plugin.useLog(player, this, - TradeCraftLocalization.get("WITHDREW_X_A"), - itemAmount, - getItemName()); - } + TradeCraftLocalization.get("WITHDREW_X_A"), + itemAmount, + getItemName()); + } } } else if ( getChestItemType().compareTo(getItemType()) == 0 ) { depositItems(getChestItemCount()); populateChest(new TradeCraftItem(0), 0); plugin.sendMessage(player, TradeCraft.MessageTypes.DEPOSIT, - TradeCraftLocalization.get("DEPOSITED_X_A"), - getChestItemCount(), - getItemName()); + TradeCraftLocalization.get("DEPOSITED_X_A"), + getChestItemCount(), + getItemName()); plugin.useLog(player, this, - TradeCraftLocalization.get("DEPOSITED_X_A"), - getChestItemCount(), - getItemName()); + TradeCraftLocalization.get("DEPOSITED_X_A"), + getChestItemCount(), + getItemName()); } else { plugin.sendMessage(player, TradeCraftLocalization.get("YOU_CANT_DEPOSIT_THAT_HERE")); } } private void handlePatronClick(Player player) { - - + + boolean playerCanBuy= (plugin.permissions.canBuy(player)); boolean playerCanSell = plugin.permissions.canSell(player); @@ -156,45 +156,45 @@ private void handlePatronClick(Player player) { if (getChestItemCount() == 0) { if (playerCanBuy && playerCanBuy()) { - if ( this instanceof TradeCraftInfiniteShop ) { - plugin.sendMessage(player, - TradeCraftLocalization.get("YOU_CAN_BUY_Y_A_FOR_X_B"), - getBuyAmount(), - getItemName(), - getBuyValue(), - plugin.getCurrencyName()); - } else { - plugin.sendMessage(player, - TradeCraftLocalization.get("YOU_CAN_BUY_X_A_FOR_Y_B_UP_TO_Z"), - getBuyAmount(), - getItemName(), - getBuyValue(), - plugin.getCurrencyName(), - this.getItemsInShop()); - } + if ( this instanceof TradeCraftInfiniteShop ) { + plugin.sendMessage(player, + TradeCraftLocalization.get("YOU_CAN_BUY_Y_A_FOR_X_B"), + getBuyAmount(), + getItemName(), + getBuyValue(), + plugin.getCurrencyName()); + } else { + plugin.sendMessage(player, + TradeCraftLocalization.get("YOU_CAN_BUY_X_A_FOR_Y_B_UP_TO_Z"), + getBuyAmount(), + getItemName(), + getBuyValue(), + plugin.getCurrencyName(), + this.getItemsInShop()); + } } if (playerCanSell && playerCanSell()) { - if ( this instanceof TradeCraftInfiniteShop ) { - plugin.sendMessage(player, - TradeCraftLocalization.get("YOU_CAN_SELL_X_A_FOR_Y_B"), - getSellAmount(), - getItemName(), - getSellValue(), - plugin.getCurrencyName()); - } else { - plugin.sendMessage(player, - TradeCraftLocalization.get("YOU_CAN_SELL_X_A_FOR_Y_B_UP_TO_Z"), - getSellAmount(), - getItemName(), - getSellValue(), - plugin.getCurrencyName(), - this.getCurrencyInShop()); - } + if ( this instanceof TradeCraftInfiniteShop ) { + plugin.sendMessage(player, + TradeCraftLocalization.get("YOU_CAN_SELL_X_A_FOR_Y_B"), + getSellAmount(), + getItemName(), + getSellValue(), + plugin.getCurrencyName()); + } else { + plugin.sendMessage(player, + TradeCraftLocalization.get("YOU_CAN_SELL_X_A_FOR_Y_B_UP_TO_Z"), + getSellAmount(), + getItemName(), + getSellValue(), + plugin.getCurrencyName(), + this.getCurrencyInShop()); + } } if ( this instanceof TradeCraftInfiniteShop ) { - plugin.sendMessage(player, TradeCraftLocalization.get("THIS_IS_AN_INFINITE_SHOP")); + plugin.sendMessage(player, TradeCraftLocalization.get("THIS_IS_AN_INFINITE_SHOP")); } return; } @@ -225,10 +225,10 @@ private void playerWantsToBuy(Player player) { int currencyPlayerWantsToSpend = getChestItemCount(); int amountPlayerWantsToBuy = ((currencyPlayerWantsToSpend - (currencyPlayerWantsToSpend % getBuyValue()) ) / getBuyValue()) * getBuyAmount(); - if ( getBuyAmount() > this.chest.getSize() * TradeCraft.getMaxStackSize(getItemType().id) ) { - plugin.sendMessage(player, TradeCraftLocalization.get("THIS_SHOP_ALWAYS_RETURNS_TOO_MUCH")); - return; - } + if ( getBuyAmount() > this.chest.getSize() * TradeCraft.getMaxStackSize(getItemType().id) ) { + plugin.sendMessage(player, TradeCraftLocalization.get("THIS_SHOP_ALWAYS_RETURNS_TOO_MUCH")); + return; + } if (amountPlayerWantsToBuy == 0) { plugin.sendMessage(player, @@ -240,26 +240,26 @@ private void playerWantsToBuy(Player player) { } if (amountPlayerWantsToBuy > getItemsInShop()) { - if ( getItemsInShop() == 0 ) { - plugin.sendMessage(player, - TradeCraftLocalization.get("CANT_BUY_SHOP_HAS_NO_A_LEFT"), - getItemName()); - } else { - plugin.sendMessage(player, - TradeCraftLocalization.get("CANNOT_BUY_SHOP_ONLY_HAS_X_A"), - getItemsInShop(), - getItemName()); - } - return; + if ( getItemsInShop() == 0 ) { + plugin.sendMessage(player, + TradeCraftLocalization.get("CANT_BUY_SHOP_HAS_NO_A_LEFT"), + getItemName()); + } else { + plugin.sendMessage(player, + TradeCraftLocalization.get("CANNOT_BUY_SHOP_ONLY_HAS_X_A"), + getItemsInShop(), + getItemName()); + } + return; } int requiredCurrencyForThatAmount = amountPlayerWantsToBuy * getBuyValue() / getBuyAmount(); if ( Math.ceil( (currencyPlayerWantsToSpend - requiredCurrencyForThatAmount) / TradeCraft.getMaxStackSize(TradeCraft.currency.id)) - + Math.ceil( amountPlayerWantsToBuy / TradeCraft.getMaxStackSize(getItemType().id)) - > this.chest.getSize() ) { - plugin.sendMessage(player, TradeCraftLocalization.get("THIS_SHOP_WOULD_RETURN_TOO_MANY_BUY_LESS")); - return; + + Math.ceil( amountPlayerWantsToBuy / TradeCraft.getMaxStackSize(getItemType().id)) + > this.chest.getSize() ) { + plugin.sendMessage(player, TradeCraftLocalization.get("THIS_SHOP_WOULD_RETURN_TOO_MANY_BUY_LESS")); + return; } updateItemAndCurrencyAmounts(-amountPlayerWantsToBuy, requiredCurrencyForThatAmount); @@ -270,13 +270,13 @@ private void playerWantsToBuy(Player player) { chest.update(); plugin.sendMessage(player, - TradeCraftLocalization.get("YOU_BOUGHT_X_A_FOR_Y_B"), + TradeCraftLocalization.get("YOU_BOUGHT_X_A_FOR_Y_B"), amountPlayerWantsToBuy, getItemName(), requiredCurrencyForThatAmount, plugin.getCurrencyName()); plugin.useLog(player, this, - TradeCraftLocalization.get("BOUGHT_X_A_FOR_Y_B"), + TradeCraftLocalization.get("BOUGHT_X_A_FOR_Y_B"), amountPlayerWantsToBuy, getItemName(), requiredCurrencyForThatAmount, @@ -292,10 +292,10 @@ private void playerWantsToSell(Player player) { int amountPlayerWantsToSell = getChestItemCount(); int currencyPlayerShouldReceive = ((amountPlayerWantsToSell - (amountPlayerWantsToSell % getSellAmount())) / getSellAmount()) * getSellValue(); // prevent too much currency (more than can fit in a chest) to be given to the customer - if ( getSellValue() > this.chest.getSize() * TradeCraft.getMaxStackSize(TradeCraft.currency.id) ) { - plugin.sendMessage(player, TradeCraftLocalization.get("THIS_SHOP_ALWAYS_RETURNS_TOO_MUCH_CURRENCY"), this.plugin.getCurrencyName()); - return; - } + if ( getSellValue() > this.chest.getSize() * TradeCraft.getMaxStackSize(TradeCraft.currency.id) ) { + plugin.sendMessage(player, TradeCraftLocalization.get("THIS_SHOP_ALWAYS_RETURNS_TOO_MUCH_CURRENCY"), this.plugin.getCurrencyName()); + return; + } if (currencyPlayerShouldReceive == 0) { plugin.sendMessage(player, @@ -318,10 +318,10 @@ private void playerWantsToSell(Player player) { // prevent too much items+currency stacks to end up in the chest if ( Math.ceil( (amountPlayerWantsToSell - amountThatCanBeSold)/ TradeCraft.getMaxStackSize(getItemType().id) ) - + Math.ceil( currencyPlayerShouldReceive / TradeCraft.getMaxStackSize(TradeCraft.currency.id) ) + + Math.ceil( currencyPlayerShouldReceive / TradeCraft.getMaxStackSize(TradeCraft.currency.id) ) > chest.getSize() ) { - plugin.sendMessage(player, TradeCraftLocalization.get("THIS_SHOP_WOULD_RETURN_TOO_MUCH_CURRENCY_SELL_LESS"), this.plugin.getCurrencyName()); - return; + plugin.sendMessage(player, TradeCraftLocalization.get("THIS_SHOP_WOULD_RETURN_TOO_MUCH_CURRENCY_SELL_LESS"), this.plugin.getCurrencyName()); + return; } updateItemAndCurrencyAmounts(amountThatCanBeSold, -currencyPlayerShouldReceive); diff --git a/src/nl/armeagle/TradeCraft/TradeCraftLocalization.java b/nl/armeagle/TradeCraft/TradeCraftLocalization.java similarity index 54% rename from src/nl/armeagle/TradeCraft/TradeCraftLocalization.java rename to nl/armeagle/TradeCraft/TradeCraftLocalization.java index a85f769..ffd2044 100644 --- a/src/nl/armeagle/TradeCraft/TradeCraftLocalization.java +++ b/nl/armeagle/TradeCraft/TradeCraftLocalization.java @@ -14,39 +14,39 @@ * such other language file to be used. */ public class TradeCraftLocalization { - private static final String filePreName = TradeCraft.pluginName + ".%1$s.lang"; + private static final String filePreName = TradeCraft.pluginName + ".%1$s.lang"; private static StatefulYamlConfiguration localization; - public TradeCraftLocalization(TradeCraft plugin) { - String filename = String.format(TradeCraftLocalization.filePreName, TradeCraft.properties.getLanguage()); + public TradeCraftLocalization(TradeCraft plugin) { + String filename = String.format(TradeCraftLocalization.filePreName, TradeCraft.properties.getLanguage()); TradeCraftLocalization.localization = plugin.getConfig(filename); try { - TradeCraftLocalization.localization.load(); + TradeCraftLocalization.localization.load(); } catch (IOException e) { - plugin.log(Level.SEVERE, "Failed to read file: %s", filename); + plugin.log(Level.SEVERE, "Failed to read file: %s", filename); } String defaultFilename = String.format(TradeCraftLocalization.filePreName, "en"); if (TradeCraftLocalization.localization.getKeys(false).isEmpty()) { - plugin.log(Level.INFO, "Language file %s does not exist or is empty, defaulting to %s", filename, defaultFilename); + plugin.log(Level.INFO, "Language file %s does not exist or is empty, defaulting to %s", filename, defaultFilename); } else { - return; + return; } TradeCraftLocalization.localization = plugin.getConfig(defaultFilename); try { - TradeCraftLocalization.localization.load(); + TradeCraftLocalization.localization.load(); } catch (IOException e) { - plugin.log(Level.SEVERE, "Failed to read file: %s", defaultFilename); + plugin.log(Level.SEVERE, "Failed to read file: %s", defaultFilename); } if (TradeCraftLocalization.localization.getKeys(false).isEmpty()) { - plugin.log(Level.SEVERE, "Default language file %s is also empty", defaultFilename); + plugin.log(Level.SEVERE, "Default language file %s is also empty", defaultFilename); } - } + } - public static String get(String key) { - return TradeCraftLocalization.localization.getString(key, "key error \""+ key +"\""); - } + public static String get(String key) { + return TradeCraftLocalization.localization.getString(key, "key error \""+ key +"\""); + } } diff --git a/nl/armeagle/TradeCraft/TradeCraftPermissions.java b/nl/armeagle/TradeCraft/TradeCraftPermissions.java new file mode 100644 index 0000000..ddf29f8 --- /dev/null +++ b/nl/armeagle/TradeCraft/TradeCraftPermissions.java @@ -0,0 +1,121 @@ +package nl.armeagle.TradeCraft; + +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; +import org.bukkit.plugin.Plugin; + +import com.nijiko.permissions.PermissionHandler; +import com.nijikokun.bukkit.Permissions.Permissions; + +public class TradeCraftPermissions { + + PermissionHandler permHandler = null; + TradeCraft plugin; + + TradeCraftPermissions(TradeCraft plugin) { + this.plugin = plugin; + } + + public void setupPermissions() { + Plugin test = plugin.getServer().getPluginManager().getPlugin("Permissions"); + + if (permHandler == null) { + if (test != null) { + this.permHandler = ((Permissions)test).getHandler(); + plugin.permEnabled = true; + plugin.log.info("[TradeCraft] has recognized Permissions"); + } + } + } + + public boolean canBuy(Player p) { + if ( plugin.permEnabled ) { + return permHandler.has(p, "TradeCraft.canBuy"); + } else { + return p.hasPermission("TradeCraft.canBuy"); + } + } + + public boolean canSell(Player p) { + if ( plugin.permEnabled ) { + return permHandler.has(p, "TradeCraft.canSell"); + } else { + return p.hasPermission("TradeCraft.canSell"); + } + } + + public boolean canMakeInfShops(Player p) { + if ( plugin.permEnabled ) { + return permHandler.has(p, "TradeCraft.canMakeInfShops"); + } else { + return p.hasPermission("TradeCraft.canMakeInfShops"); + } + } + + public boolean canMakePlayerShops(Player p) { + if ( plugin.permEnabled ) { + return permHandler.has(p, "TradeCraft.canMakePlayerShops"); + } else { + return p.hasPermission("TradeCraft.canMakePlayerShops"); + } + } + + public boolean canDestroyShops(Player p) { + if ( plugin.permEnabled ) { + return permHandler.has(p, "TradeCraft.canDestroyShops"); + } else { + return p.hasPermission("TradeCraft.canDestroyShops"); + } + } + + public boolean canSetCurrency(Player p) { + if ( plugin.permEnabled ) { + return permHandler.has(p, "TradeCraft.canSetCurrency"); + } else { + return p.hasPermission("TradeCraft.canSetCurrency"); + } + } + + public boolean canReload(Player p) { + if ( plugin.permEnabled ) { + return permHandler.has(p, "TradeCraft.canReload"); + } else { + return p.hasPermission("TradeCraft.canReload"); + } + } + + public boolean canQueryOtherShops(Player p) { + if ( plugin.permEnabled ) { + return permHandler.has(p, "TradeCraft.canQueryOtherShops"); + } else { + return p.hasPermission("TradeCraft.canQueryOtherShops"); + } + } + + public boolean canQueryPlayer(Player p) { + if ( plugin.permEnabled ) { + return permHandler.has(p, "TradeCraft.canQueryPlayer"); + } else { + return p.hasPermission("TradeCraft.canQueryPlayer"); + } + } + + public void debug(CommandSender sender, String n){ + Player p = plugin.getServer().getPlayer(n); + if(p == null){ + plugin.getServer().broadcastMessage("/tc canPlayer used with a name of player who is not online."); + return; + } + String name = p.getName(); + sender.sendMessage("" + name + " has:"); + sender.sendMessage("canBuy " + canBuy(p)); + sender.sendMessage("canSell " + canSell(p)); + sender.sendMessage("canMakeInf " + canMakeInfShops(p)); + sender.sendMessage("canMakePersonal " + canMakePlayerShops(p)); + sender.sendMessage("canDestroy " + canDestroyShops(p)); + sender.sendMessage("canSetCurrency " + canSetCurrency(p)); + sender.sendMessage("canReload " + canReload(p)); + sender.sendMessage("canQueryOtherShops " + canQueryOtherShops(p)); + } + +} diff --git a/src/nl/armeagle/TradeCraft/TradeCraftPlayerListener.java b/nl/armeagle/TradeCraft/TradeCraftPlayerListener.java similarity index 54% rename from src/nl/armeagle/TradeCraft/TradeCraftPlayerListener.java rename to nl/armeagle/TradeCraft/TradeCraftPlayerListener.java index 9bc6bef..8ba5d7d 100644 --- a/src/nl/armeagle/TradeCraft/TradeCraftPlayerListener.java +++ b/nl/armeagle/TradeCraft/TradeCraftPlayerListener.java @@ -10,34 +10,34 @@ public class TradeCraftPlayerListener implements Listener{ - private TradeCraft plugin; - - TradeCraftPlayerListener(TradeCraft plugin){ - this.plugin = plugin; - } - - @EventHandler - public void onPlayerInteract(PlayerInteractEvent e) { - if ( !this.plugin.isEnabled() ) { - return; - } - if ( e.getAction() == Action.RIGHT_CLICK_BLOCK ) { - Block blockClicked = e.getClickedBlock(); - Player player = e.getPlayer(); - - TradeCraftShop shop = plugin.getShopFromSignBlock(player, blockClicked); - - if (shop == null) { - return; - } + private TradeCraft plugin; + + TradeCraftPlayerListener(TradeCraft plugin){ + this.plugin = plugin; + } + + @EventHandler + public void onPlayerInteract(PlayerInteractEvent e) { + if ( !this.plugin.isEnabled() ) { + return; + } + if ( e.getAction() == Action.RIGHT_CLICK_BLOCK ) { + Block blockClicked = e.getClickedBlock(); + Player player = e.getPlayer(); + + TradeCraftShop shop = plugin.getShopFromSignBlock(player, blockClicked); + + if (shop == null) { + return; + } - shop.handleRightClick(player); - e.setCancelled(true); - } - } - + shop.handleRightClick(player); + e.setCancelled(true); + } + } + @SuppressWarnings("unused") - private void displayItems(Player player) { + private void displayItems(Player player) { String[] names = plugin.configuration.getNames(); StringBuilder sb = new StringBuilder(); for (String name : names) { diff --git a/src/nl/armeagle/TradeCraft/TradeCraftPlayerOwnedShop.java b/nl/armeagle/TradeCraft/TradeCraftPlayerOwnedShop.java similarity index 94% rename from src/nl/armeagle/TradeCraft/TradeCraftPlayerOwnedShop.java rename to nl/armeagle/TradeCraft/TradeCraftPlayerOwnedShop.java index 69ecd5e..e453e64 100644 --- a/src/nl/armeagle/TradeCraft/TradeCraftPlayerOwnedShop.java +++ b/nl/armeagle/TradeCraft/TradeCraftPlayerOwnedShop.java @@ -30,11 +30,11 @@ public boolean shopCanBeWithdrawnFrom() { } public boolean isOwnedByPlayer(Player player) { - if ( ownerName == null ) { - return false; - } else { - return player.getName().equals(ownerName); - } + if ( ownerName == null ) { + return false; + } else { + return player.getName().equals(ownerName); + } } public TradeCraftItem getItemType() { diff --git a/nl/armeagle/TradeCraft/TradeCraftPropertiesFile.java b/nl/armeagle/TradeCraft/TradeCraftPropertiesFile.java new file mode 100644 index 0000000..de95254 --- /dev/null +++ b/nl/armeagle/TradeCraft/TradeCraftPropertiesFile.java @@ -0,0 +1,153 @@ +package nl.armeagle.TradeCraft; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.util.logging.Level; + +import org.bukkit.ChatColor; +import org.bukkit.configuration.InvalidConfigurationException; +import org.bukkit.configuration.file.YamlConfiguration; + +/** + * This class, handles the actual configuration of the plugin. The TradeCraftConfiguration class + * actually handles all the items that can be used by the shops in the game. + */ +public class TradeCraftPropertiesFile { + private static final String fileName = TradeCraft.pluginName + ".properties"; + private static final String filePath = "plugins" + File.separator + TradeCraft.pluginName; + public static final String defaultLanguage = "en"; + + private TradeCraft plugin; + private final YamlConfiguration properties; + + public TradeCraftPropertiesFile(TradeCraft plugin) { + this.plugin = plugin; + // make folder in the plugins dir if it doesn't exist yet + File path = new File(filePath); + if ( !path.exists() ) { + path.mkdirs(); + } + path = null; + + // if file does not exist in this directory, copy it from the jar + File file = new File(filePath + File.separator + fileName); + if ( !file.exists() ) { + this.plugin.log.info(filePath + File.separator + fileName +" does not exist, creating..."); + InputStream input = this.getClass().getResourceAsStream("/" + fileName); + if ( input != null ) { + FileOutputStream output = null; + + try { + output = new FileOutputStream(file); + byte[] buf = new byte[8192]; + int length = 0; + while ((length = input.read(buf)) > 0) { + output.write(buf, 0, length); + } + } catch (IOException e) { + e.printStackTrace(); + } finally { + try { + if (input != null) { + input.close(); + } + } catch (IOException e) {} + + try { + if (output != null) { + output.close(); + } + } catch (IOException e) {} + } + } + } + + properties = new YamlConfiguration(); + try { + properties.load(file); + } catch (InvalidConfigurationException e) { + plugin.log(Level.SEVERE, "Failed to load file: %s", file.toURI()); + } catch (IOException e) { + plugin.log(Level.SEVERE, "Failed to read file: %s", file.toURI()); + } + } + + protected void save() { + File file = new File(filePath + File.separator + fileName); + try { + properties.save(file); + } catch (IOException e) { + this.plugin.log(Level.SEVERE, "Error saving to file: %s", file.toURI()); + } + } + + public TradeCraftItem getCurrencyType(){ + int id = properties.getInt("currency-id",266); + short data = (short)properties.getInt("currency-data",0); + return new TradeCraftItem(id, data); + } + public void setCurrencyType(TradeCraftItem item) { + properties.set("currency-id", item.id); + properties.set("currency-data", item.data); + this.save(); + } + public boolean getNormalStackSizeUsed(){ + return properties.getBoolean("normal-stack-size", true); + } + + public boolean getInfiniteShopsEnabled() { + return properties.getBoolean("infinite-shops-enabled", true); + } + + public boolean getPlayerOwnedShopsEnabled() { + return properties.getBoolean("player-owned-shops-enabled", true); + } + + public boolean getRepairShopsEnabled() { + return properties.getBoolean("repair-shops-enabled", false); + } + + public int getRepairCost() { + return properties.getInt("repair-cost", 0); + } + + public boolean getEnableDebugMessages() { + return properties.getBoolean("enable-debug-messages", false); + } + + public String getLanguage() { + return properties.getString("language", TradeCraftPropertiesFile.defaultLanguage); + } + + public boolean autoUpdateLanguageFiles() { + return properties.getBoolean("auto-update-language-files", true); + } + + public boolean logShopUse() { + return properties.getBoolean("log-shop-use", false); + } + + public boolean showShopLocation() { + return properties.getBoolean("show-shop-location", false); + } + + public int getPlayerWorldShopLimit() { + return properties.getInt("player-world-shop-limit", 5); + } + public int getPlayerTotalShopLimit() { + return properties.getInt("player-total-shop-limit", 10); + } + + public ChatColor getMessageTypeColor(TradeCraft.MessageTypes mtype) { + switch (mtype) { + case WITHDRAW: + return ChatColor.YELLOW; + case DEPOSIT: + return ChatColor.GRAY; + default: + return ChatColor.WHITE; + } + } +} diff --git a/src/nl/armeagle/TradeCraft/TradeCraftRepairShop.java b/nl/armeagle/TradeCraft/TradeCraftRepairShop.java similarity index 75% rename from src/nl/armeagle/TradeCraft/TradeCraftRepairShop.java rename to nl/armeagle/TradeCraft/TradeCraftRepairShop.java index 438f769..f0d4b6b 100644 --- a/src/nl/armeagle/TradeCraft/TradeCraftRepairShop.java +++ b/nl/armeagle/TradeCraft/TradeCraftRepairShop.java @@ -20,8 +20,8 @@ public void handleRightClick(Player player) { if (currencyAmount == 0 && items.size() == 0) { plugin.sendMessage(player, TradeCraftLocalization.get("IT_COSTS_X_A_TO_REPAIR_AN_ITEM"), - repairCost, - plugin.getCurrencyName()); + repairCost, + plugin.getCurrencyName()); return; } @@ -29,26 +29,26 @@ public void handleRightClick(Player player) { if (items.size() == 0) { plugin.sendMessage(player, TradeCraftLocalization.get("WITH_THIS_MUCH_A_YOU_CAN_REPAIR_Y_ITEMS"), - plugin.getCurrencyName(), - currencyAmount / repairCost); + plugin.getCurrencyName(), + currencyAmount / repairCost); return; } if (currencyAmount < actualCost) { if (currencyAmount > 0) { plugin.sendMessage(player, TradeCraftLocalization.get("THAT_IS_NOT_ENOUGH_A"), - plugin.getCurrencyName()); + plugin.getCurrencyName()); } plugin.sendMessage(player, TradeCraftLocalization.get("YOU_NEED_X_A_TO_REPAIR_ALL_THIS"), - actualCost, - plugin.getCurrencyName()); + actualCost, + plugin.getCurrencyName()); return; } chest.clear(); for (ItemStack item : items) { - short itemData = (item.getData() == null ? (short)0 : item.getDurability()); + short itemData = (item.getData() == null ? (short)0 : item.getDurability()); chest.add(new TradeCraftItem(item.getTypeId(), itemData), 1); } @@ -58,9 +58,9 @@ public void handleRightClick(Player player) { chest.update(); plugin.sendMessage(player, TradeCraftLocalization.get("YOU_REPAIRED_X_ITEMS_FOR_Y_B"), - items.size(), - actualCost, - plugin.getCurrencyName()); + items.size(), + actualCost, + plugin.getCurrencyName()); } public boolean playerCanDestroy(Player player) { diff --git a/src/nl/armeagle/TradeCraft/TradeCraftShop.java b/nl/armeagle/TradeCraft/TradeCraftShop.java similarity index 72% rename from src/nl/armeagle/TradeCraft/TradeCraftShop.java rename to nl/armeagle/TradeCraft/TradeCraftShop.java index f537991..b66a611 100644 --- a/src/nl/armeagle/TradeCraft/TradeCraftShop.java +++ b/nl/armeagle/TradeCraft/TradeCraftShop.java @@ -22,10 +22,10 @@ public TradeCraftShop(TradeCraft plugin, Sign sign, Chest chest) { public abstract boolean shopCanBeWithdrawnFrom(); public String toString() { - return String.format("Shop(%s:%d,%d,%d)", - this.sign.getWorld().getName(), - this.sign.getX(), - this.sign.getY(), - this.sign.getZ()); + return String.format("Shop(%s:%d,%d,%d)", + this.sign.getWorld().getName(), + this.sign.getX(), + this.sign.getY(), + this.sign.getZ()); } } diff --git a/src/plugin.yml b/plugin.yml similarity index 99% rename from src/plugin.yml rename to plugin.yml index 7528caa..2c5f311 100644 --- a/src/plugin.yml +++ b/plugin.yml @@ -1,5 +1,5 @@ name: TradeCraft -version: AE-1.4.1 +version: AE-1.4 description: Setup shops with a combination of chest and sign. author: ArmEagle main: nl.armeagle.TradeCraft.TradeCraft diff --git a/src/nl/armeagle/Configuration/StatefulYamlConfiguration.java b/src/nl/armeagle/Configuration/StatefulYamlConfiguration.java deleted file mode 100644 index 4e38c35..0000000 --- a/src/nl/armeagle/Configuration/StatefulYamlConfiguration.java +++ /dev/null @@ -1,79 +0,0 @@ -package nl.armeagle.Configuration; - -import java.io.File; -import java.io.FileNotFoundException; -import java.io.IOException; -import java.io.InputStream; -import java.util.logging.Level; -import java.util.logging.Logger; - -import org.bukkit.Bukkit; -import org.bukkit.configuration.InvalidConfigurationException; -import org.bukkit.configuration.file.YamlConfiguration; -import org.bukkit.plugin.java.JavaPlugin; -import org.yaml.snakeyaml.error.YAMLException; - -public class StatefulYamlConfiguration extends YamlConfiguration { - protected File file; - - public StatefulYamlConfiguration(File file) { - this.file = file; - } - - public void load() throws IOException { - this.load(false); - } - public void load(boolean forceDefaults) throws IOException { - boolean notLoaded = false; - if (this.file == null) { - throw new IllegalArgumentException("File cannot be null"); - } - - YamlConfiguration baseConfig = new YamlConfiguration(); - - try { - baseConfig.load(this.file); - } catch (FileNotFoundException fnf) { - notLoaded = true; - } catch (IOException ex) { - Bukkit.getLogger().log(Level.SEVERE, "Cannot load " + this.file, ex); - } catch (InvalidConfigurationException ex) { - if (ex.getCause() instanceof YAMLException) { - Bukkit.getLogger().severe("Config file " + this.file + " isn't valid! " + ex.getCause()); - } else if ((ex.getCause() == null) || (ex.getCause() instanceof ClassCastException)) { - Bukkit.getLogger().severe("Config file " + this.file + " isn't valid!"); - } else { - Bukkit.getLogger().log(Level.SEVERE, "Cannot load " + this.file + ": " + ex.getCause().getClass(), ex); - } - } - - if (notLoaded || forceDefaults) { - InputStream defaultInput = this.getClass().getResourceAsStream("/" + file.getName()); - if ( null != defaultInput ) { - YamlConfiguration defaultConfig = YamlConfiguration.loadConfiguration(defaultInput); - - try { - this.loadFromString(defaultConfig.saveToString()); - } catch (InvalidConfigurationException e) { - Logger.getLogger(JavaPlugin.class.getName()).log(Level.SEVERE, "Invalid default configuation"); - } - this.save(); - } - } else if (! notLoaded) { - try { - this.loadFromString(baseConfig.saveToString()); - } catch (InvalidConfigurationException e) { - Logger.getLogger(JavaPlugin.class.getName()).log(Level.SEVERE, "Invalid configuation"); - } - } - - } - - public void save() throws IOException { - super.save(this.file); - } - - public File getFile() { - return this.file; - } -} diff --git a/src/nl/armeagle/TradeCraft/TradeCraft.java b/src/nl/armeagle/TradeCraft/TradeCraft.java deleted file mode 100644 index aa122e1..0000000 --- a/src/nl/armeagle/TradeCraft/TradeCraft.java +++ /dev/null @@ -1,652 +0,0 @@ -package nl.armeagle.TradeCraft; - -import java.io.*; - -import java.text.SimpleDateFormat; -import java.util.ArrayList; -import java.util.Date; -import java.util.HashMap; -import java.util.IllegalFormatException; -import java.util.Map; -import java.util.logging.Level; -import java.util.logging.Logger; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -import nl.armeagle.Configuration.StatefulYamlConfiguration; - -import org.bukkit.ChatColor; -import org.bukkit.Material; -import org.bukkit.Server; -import org.bukkit.block.Block; -import org.bukkit.block.BlockFace; -import org.bukkit.block.Chest; -import org.bukkit.block.Sign; -import org.bukkit.command.Command; -import org.bukkit.command.CommandSender; -import org.bukkit.command.ConsoleCommandSender; -import org.bukkit.entity.Player; -import org.bukkit.inventory.ItemStack; -import org.bukkit.material.MaterialData; -import org.bukkit.plugin.PluginDescriptionFile; -import org.bukkit.plugin.PluginManager; -import org.bukkit.plugin.java.JavaPlugin; - - -public class TradeCraft extends JavaPlugin { - public static enum MessageTypes {WITHDRAW, DEPOSIT}; - - // The plugin name. - static final String pluginName = "TradeCraft"; - - public static final Pattern itemPatternIdSplitData = Pattern.compile("^(\\d+)(?:;(\\d+))?$"); - - private static enum Commands { - tcsetcurrency, - tcgetcurrency, - tcshops, - tcpshops, - tcreload, - tcplayerperms, - tchelp, - tc - }; - - // Stuff used to interact with the server. - final Logger log = Logger.getLogger("Minecraft"); - final Server server = this.getServer(); - - protected BufferedWriter usageLog = null; - // Objects used by the plugin. - static TradeCraftItem currency; - static TradeCraftPropertiesFile properties; - TradeCraftConfigurationFile configuration; - public TradeCraftLocalization localization; - TradeCraftDataFile data; - - private HashMap configs = new HashMap(); - - private final TradeCraftBlockListener blockListener = new TradeCraftBlockListener(this); - private final TradeCraftPlayerListener playerListener = new TradeCraftPlayerListener(this); - public TradeCraftPermissions permissions = new TradeCraftPermissions(this); - - // prevent the script from registering the event listeners multiple times (by dis-/enable) - public static boolean hasRegisteredEventListeners = false; - - public void onDisable() { - this.disable(); - } - private void disable() { - if ( this.usageLog != null ) { - try { - this.usageLog.close(); - } catch (IOException e) { - this.log(Level.WARNING, "Failed to close shop usage log file"); - } - } - properties = null; - configuration = null; - this.localization = null; - data.save(); - data = null; - } - - public void onEnable() { - this.enable(); - } - private void enable() { - properties = new TradeCraftPropertiesFile(this); - configuration = new TradeCraftConfigurationFile(this); - data = new TradeCraftDataFile(this); - this.localization = new TradeCraftLocalization(this); - - if ( TradeCraft.properties.logShopUse() ) { - File usageLogFile = new File(this.getDataFolder(), "shopUsage.log"); - try { - if ( !usageLogFile.exists() ) { - usageLogFile.createNewFile(); - } - if ( usageLogFile.canWrite() ) { - this.usageLog = new BufferedWriter(new FileWriter(usageLogFile, true)); - this.log(Level.INFO, "Writing shop usage to log file: "+ usageLogFile.toString()); - } else { - this.log(Level.WARNING, "Error opening shop usage log file: "+ usageLogFile.toString()); - } - } catch (IOException e) { - this.log(Level.WARNING, "Failed to open shop usage log file: "+ usageLogFile.toString()); - } - } - - configuration.load(); - data.load(); - currency = properties.getCurrencyType(); - - if ( !TradeCraft.hasRegisteredEventListeners ) { - PluginManager pm = this.getServer().getPluginManager(); - pm.registerEvents(playerListener, this); - pm.registerEvents(blockListener, this); - TradeCraft.hasRegisteredEventListeners = true; - } - - PluginDescriptionFile pdfFile = this.getDescription(); - this.log.info(pdfFile.getName() + " version " + pdfFile.getVersion() + " is enabled!"); - - } - - @Override - public boolean onCommand(CommandSender sender, Command cmd, String label, String[] args) { - try { - TradeCraft.Commands command = TradeCraft.Commands.valueOf(cmd.getName()); - if (sender instanceof Player) { - Player p = (Player) sender; - switch (command) { - case tcsetcurrency: - if ( args.length == 1 && this.permissions.canSetCurrency(p) ) { - TradeCraftItem testCurrency = null; - // try to split ID and Data, separated by a semicolon mark - Matcher IdSplitData = TradeCraft.itemPatternIdSplitData.matcher(args[0]); - - if ( !IdSplitData.matches() ) { - // try to match the parameter to item names from the configuration - TradeCraftConfigurationInfo setCurr = this.configuration.get(args[0]); - if ( setCurr == null ) { - this.sendMessage(p, TradeCraftLocalization.get("IS_NO_VALID_CURRENCY_USE_INSTEAD"), - args[0]); - return false; - } else { - currency = setCurr.type; - } - } else { - try { - int cid = Integer.parseInt(IdSplitData.group(1)); - if ( IdSplitData.group(2) != null ) { - testCurrency = new TradeCraftItem(cid, Short.parseShort(IdSplitData.group(2))); - } else { - testCurrency = new TradeCraftItem(cid); - } - } catch ( NumberFormatException e ) { - this.sendMessage(p, TradeCraftLocalization.get("INVALID_CURRENCY"), - args[0]); - return false; - } - if ( this.configuration.get(testCurrency) != null ) { - currency = testCurrency; - } else { - this.sendMessage(p, TradeCraftLocalization.get("INVALID_CURRENCY"), - args[0]); - return false; - } - } - - TradeCraft.properties.setCurrencyType(currency); - this.sendMessage(p, TradeCraftLocalization.get("CURRENCY_IS_SET_TO_A_IDDATA"), - this.getCurrencyName(), - currency.toShortString()); - return true; - } - return true; - case tcgetcurrency: - this.sendMessage(p, TradeCraftLocalization.get("CURRENCY_IS_A_IDDATA"), - this.getCurrencyName(), - currency.toShortString()); - return true; - case tcshops: - // lookup own shows - displayShops(p.getName(), p, false); - return true; - case tcpshops: - // Check whether another parameter is passed, if so check whether - // the player can get information about other player's shops. - if ( this.permissions.canQueryOtherShops(p) ) { - if ( args.length == 1 ) { - // lookup other player's shops - displayShops(args[0], p, true); - } else { - this.sendMessage(p, cmd.getUsage()); - } - } - return true; - case tcreload: - if ( this.permissions.canReload(p)) { - this.sendMessage(p, TradeCraftLocalization.get("RESTARTING_PLUGIN"), TradeCraft.pluginName); - this.disable(); - this.enable(); - this.sendMessage(p, TradeCraftLocalization.get("RESTARTING_PLUGIN_DONE"), TradeCraft.pluginName); - } - return true; - case tcplayerperms: - if ( this.permissions.canQueryPlayer(p)) { - if ( args.length == 1 ) { - permissions.debug(sender, args[0]); - } else { - sender.sendMessage(cmd.getUsage()); - } - } - return true; - default: - displayCommandHelpText(p); - return true; - } - } else if ( sender instanceof ConsoleCommandSender ) { - switch (command) { - case tcplayerperms: - if ( args.length == 1 ) { - permissions.debug(sender, args[0]); - } else { - sender.sendMessage(cmd.getUsage()); - } - return true; - case tcpshops: - // Check whether another parameter is passed, if so check whether - // the player can get information about other player's shops. - if ( args.length == 1 ) { - // lookup other player's shops - displayShops(args[0], sender, true); - } else { - sender.sendMessage(cmd.getUsage()); - } - return true; - case tcreload: - sender.sendMessage(String.format(TradeCraftLocalization.get("RESTARTING_PLUGIN"), - TradeCraft.pluginName)); - this.disable(); - this.enable(); - sender.sendMessage(String.format(TradeCraftLocalization.get("RESTARTING_PLUGIN_DONE"), - TradeCraft.pluginName)); - return true; - default: - displayCommandHelpText(null); - return true; - } - } - } catch (IllegalArgumentException e) { - return false; - } - return false; - } - - void displayShops(String infoPlayerName, CommandSender displayTo, boolean otherQuery) { - Map list = data.shopsOwned(infoPlayerName); - if (list.size() == 0) { - if ( otherQuery ) { - // elevated player looking for other player's shops - displayTo.sendMessage(String.format(TradeCraftLocalization.get("A_DOES_NOT_OWN_ANY_SHOPS"), - infoPlayerName)); - } else { - displayTo.sendMessage(TradeCraftLocalization.get("YOU_DONT_OWN_ANY_SHOPS")); - } - return; - } - - if ( otherQuery ) { - displayTo.sendMessage(String.format(TradeCraftLocalization.get("SHOPS_OF_A"), - infoPlayerName)); - } else { - displayTo.sendMessage(TradeCraftLocalization.get("YOUR_SHOPS")); - } - for (Map.Entry entry : list.entrySet()) { - TradeCraftDataInfo info = entry.getValue(); - String message = ""; - if (TradeCraft.properties.showShopLocation()) { - String location = entry.getKey().replaceFirst(",", "(") +")"; - message += ChatColor.GRAY + location +" "+ ChatColor.WHITE; - } - message += TradeCraftLocalization.get("ITEM") +": "+ this.configuration.get(info.itemType).name +"("+ info.itemType.toShortString() +")" - +" "+ TradeCraftLocalization.get("AMOUNT") +": "+ info.itemAmount +" " - + this.getCurrencyName() +": "+ info.currencyAmount; - - displayTo.sendMessage(message); - } - - } - - void sendMessage(Player player, TradeCraft.MessageTypes messageType, String format, Object... args) { - try { - String message = String.format(format, args); - player.sendMessage(TradeCraft.properties.getMessageTypeColor(messageType) + message); - } catch ( IllegalFormatException e ) { - player.sendMessage(TradeCraftLocalization.get("ERROR_IN_FORMAT_STRING") +" "+ format); - } - } - - void sendMessage(Player player, String format, Object... args) { - try { - String message = String.format(format, args); - player.sendMessage(message); - } catch ( IllegalFormatException e ) { - player.sendMessage(TradeCraftLocalization.get("ERROR_IN_FORMAT_STRING") +" "+ format); - } - } - - void trace(Player player, String format, Object... args) { - if (properties.getEnableDebugMessages()) { - if (null != player) { - sendMessage(player, format, args); - } else { - this.log(Level.INFO, format, args); - } - } - } - /* - * When a block behind a shop sign is destroyed, the sign would be destroyed too. - * Check all side faces of this block, for a sign attached to this block. Then - * pass that sign block to getShopFromSignBlock. - * - * This should only be used for checking whether a normal block can be destroyed, for there not being any - * signs attached to it, or this block being a chest or sign itself. - * Since one block can - */ - ArrayList getShopsFromBlock(Player player, Block block) { - ArrayList shops = new ArrayList(); - // use other function(s) directly if applicable - if ( block.getType() == Material.CHEST || block.getType() == Material.WALL_SIGN ) { - TradeCraftShop shop = getShopFromSignOrChestBlock(player, block); - if ( shop != null ) { - shops.add(shop); - } - } else { - // go through all 4 faces of this block to check for a sign attached to this block - final BlockFace[] sides = {BlockFace.NORTH, BlockFace.EAST, BlockFace.SOUTH, BlockFace.WEST}; - for ( int index_sides = 0; index_sides < sides.length; index_sides++ ) { - BlockFace side = sides[index_sides]; - // get the block on that side - Block sideBlock = block.getRelative(side); - // check for it being a wall sign - if ( sideBlock.getType() == Material.WALL_SIGN ) { - // get the sign (extending MaterialData) for clean attached face checking - org.bukkit.material.Sign materialSign = new org.bukkit.material.Sign(Material.WALL_SIGN, sideBlock.getData()); - /* Now, for easy comparison, we'll compare to the direction the sign is facing. If that's - * the same as the current face (of the 4 we're looping through), then this sign is attached - * to the block that is being destroyed. - */ - if ( materialSign.getFacing() == side ) { - TradeCraftShop shop = this.getShopFromSignBlock(player, sideBlock); - if ( shop != null ) { - shops.add(shop); - } - } - - } - } - } - return shops; - } - TradeCraftShop getShopFromSignOrChestBlock(Player player, Block block) { - if (block.getType() == Material.CHEST) { - block = block.getWorld().getBlockAt( - block.getX(), - block.getY() + 1, - block.getZ() - ); - } - - return getShopFromSignBlock(player, block); - } - - TradeCraftShop getShopFromSignBlock(Player player, Block block) { - if (block.getType() != Material.WALL_SIGN) { - return null; - } - - int x = block.getX(); - int y = block.getY(); - int z = block.getZ(); - - trace(player, "You clicked a sign at %d, %d, %d in world: %s.", x, y, z, block.getWorld().getName()); - - Sign sign = (Sign) block.getWorld().getBlockAt(x, y, z).getState(); - - // The sign at this location can be null if it was just destroyed. - if (sign == null) { - trace(player, "The sign is no longer there."); - - return null; - } - - String itemName = getItemName(sign.getLines()); - - if (itemName == null) { - trace(player, "There is no item name on the sign."); - - return null; - } - - trace(player, "The item name on the sign is %s.", itemName); - - Block blockBelowSign = block.getRelative(0, -1, 0); - - if (blockBelowSign.getType() != Material.CHEST) { - trace(player, "There is no chest beneath the sign."); - return null; - } - - Chest chest = (Chest) blockBelowSign.getState(); - - if (itemName.toLowerCase().equals("repair")) { - if (!properties.getRepairShopsEnabled()) { - trace(player, "Repair shops are not enabled."); - return null; - } - - if (player == null || !player.isOp()) { - trace(player, "You can't use repair shops."); - return null; - } - - trace(player, "This is a repair shop."); - return new TradeCraftRepairShop(this, sign, chest); - } - - if (!configuration.isConfigured(itemName)) { - trace(player, - "The item name %s is not configured in the config file.", - itemName); - return null; - } - - // TODO change to use chest getOwner - //String ownerName = getOwnerName(sign.getLine(3)); - String ownerName = data.getOwnerOfSign(sign); - - if (ownerName == null) { - trace(player, "There is no owner name on the sign."); - - if (!properties.getInfiniteShopsEnabled()) { - trace(player, "Ininite shops are not enabled."); - return null; - } - - trace(player, "This is an infinite shop."); - try { - return new TradeCraftInfiniteShop(this, sign, chest); - } catch (Exception e) { - trace(player, e.getMessage()); - } - return null; - } - - trace(player, "The owner name on the sign is %s.", ownerName); - - if (!properties.getPlayerOwnedShopsEnabled()) { - trace(player, "Player-owned shops are not enabled."); - return null; - } - - trace(player, "This is a player-owned shop."); - return new TradeCraftPlayerOwnedShop(this, sign, chest, ownerName); - } - - String getItemName(String[] signLines) { - return getSpecialText(signLines, "[", "]"); - } - - String getOwnerName(String signLine) { - return getSpecialTextOnLine(signLine, "-", "-"); - } - - private String getSpecialText(String[] signLines, String prefix, String suffix) { - for (int i = 0; i < 4; i++) { - String text = getSpecialTextOnLine(signLines[i], prefix, suffix); - - if (text != null) { - return text; - } - } - - return null; - } - - private String getSpecialTextOnLine(String signLine, String prefix, String suffix) { - if (signLine == null) { - return null; - } - - signLine = signLine.trim(); - - if (signLine.startsWith(prefix) && signLine.endsWith(suffix) - && signLine.length() > 2) { - - String text = signLine.substring(1, signLine.length() - 1); - text = text.trim(); - if (text.equals("")) { - return null; - } - - return text; - } - - return null; - } - - TradeCraftExchangeRate getExchangeRate(String[] signLines, int lineNumber) { - if ( lineNumber < 0 || lineNumber >= signLines.length ) { - return null; - } - return getExchangeRate(signLines[lineNumber]); - } - TradeCraftExchangeRate getExchangeRate(String signLine) { - return new TradeCraftExchangeRate(signLine); } - - static int getMaxStackSize(int itemType) { - if ( TradeCraft.properties.getNormalStackSizeUsed() ) { - return Material.getMaterial(itemType).getMaxStackSize(); - } else { - return 64; - } - } - - /** - * Get a CamelCased string based on the current currency. - * - * @return a string representing the currency. - */ - public String getCurrencyName() { - // Try to get the name from the configuration file first - TradeCraftConfigurationInfo configInfo = this.configuration.get(TradeCraft.currency); - if ( configInfo != null ) { - return configInfo.name; - } else { - - ItemStack currencyStack = new ItemStack(TradeCraft.currency.id, 1, TradeCraft.currency.data); // weird that there's no Material.getMaterial(id, short/byte) - MaterialData currencyData = currencyStack.getType().getNewData((byte)TradeCraft.currency.data); - String currencyString; - if ( currencyData == null ) { - currencyString = currencyStack.getType().name(); - } else { - currencyString = currencyData.toString(); - } - - //String baseName = stack.getType().name(); - String[] words = currencyString.replace("null ", "").split("\\(")[0].split("[ _]{1}"); - String name = ""; - for ( int word_ind = 0; word_ind < words.length; word_ind++ ) { - String word = words[word_ind]; - if ( word_ind > 0 ) { - name = name.concat(" "); - } - name = name.concat(word.substring(0, 1).toUpperCase()).concat(word.substring(1).toLowerCase()); - } - return name; - } - } - - private void displayCommandHelpText(Player player) { - if ( player != null ) { - this.sendMessage(player, TradeCraftLocalization.get("POSSIBLE_COMMANDS_FOR_THE_PLUGIN"), - TradeCraft.pluginName); - this.sendMessage(player, "/tc[help]"+ ChatColor.GRAY +" "+ TradeCraftLocalization.get("TC_HELP_THIS_TEXT")); - this.sendMessage(player, "/tcshops"+ ChatColor.GRAY +" "+ TradeCraftLocalization.get("TC_SHOPS")); - if ( this.permissions.canQueryOtherShops(player) ) { - this.sendMessage(player, "/tcpshops [player]"+ ChatColor.GRAY +" "+ TradeCraftLocalization.get("TC_PSHOPS")); - } - this.sendMessage(player, "/tcgetcurrency"+ ChatColor.GRAY +" "+ TradeCraftLocalization.get("TC_CURRENCY_GET_CURRENCY")); - if ( this.permissions.canSetCurrency(player) ) { - this.sendMessage(player, "/tcsetcurrency [id[;data]]"+ ChatColor.GRAY +" "+ TradeCraftLocalization.get("TC_CURRENCY_OPT_PARAM_GETSET_CURRENCY")); - } - if ( this.permissions.canReload(player) ) { - this.sendMessage(player, "/tcreload"+ ChatColor.GRAY +" "+ TradeCraftLocalization.get("TC_RELOAD")); - } - if ( this.permissions.canQueryPlayer(player) ) { - this.sendMessage(player, "/tcplayerperms"+ ChatColor.GRAY +" "+ TradeCraftLocalization.get("TC_CAN_PLAYER")); - } - } else { - // console command help - this.log(Level.INFO, TradeCraftLocalization.get("POSSIBLE_COMMANDS_FOR_THE_PLUGIN"), - TradeCraft.pluginName); - this.log(Level.INFO, "tc[help]: "+ TradeCraftLocalization.get("TC_HELP_THIS_TEXT")); - this.log(Level.INFO, "tcplayerperms playername: "+ TradeCraftLocalization.get("TC_CAN_PLAYER")); - this.log(Level.INFO, "tcpshops [player]: "+ TradeCraftLocalization.get("TC_PSHOPS")); - this.log(Level.INFO, "tcreload: "+ TradeCraftLocalization.get("TC_RELOAD")); - } - } - - public void saveConfig() { - Logger.getLogger(JavaPlugin.class.getName()).log(Level.INFO, "saving config to: "+ this.getConfig("config").getFile().getAbsolutePath()); - try { - this.getConfig("config").save(); - } catch (IOException ex) { - Logger.getLogger(JavaPlugin.class.getName()).log(Level.SEVERE, "Could not save config to " + this.getConfig("config").getFile().getName(), ex); - } - } - public StatefulYamlConfiguration getConfig() { - return this.getConfig("config"); - } - public StatefulYamlConfiguration getConfig(String name) { - if (name.indexOf(".") < 0) { - name += ".yml"; - } - if (this.configs.containsKey(name)) { - return this.configs.get(name); - } else { - File configFile = new File(this.getDataFolder(), name); - StatefulYamlConfiguration config = new StatefulYamlConfiguration(configFile); - this.configs.put(name, config); - return config; - } - } - - public void log(Level level, String format, Object... args) { - this.log.log(level, TradeCraft.pluginName +": "+ String.format(format, args)); - } - - public void useLog(Player player, TradeCraftShop shop, String format, Object... args) { - if ( TradeCraft.properties.logShopUse() ) { - if ( this.usageLog != null ) { - try { - SimpleDateFormat formatter = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss"); - this.usageLog.write(String.format("%s %s \t%s %s\n", - formatter.format(new Date()), - shop.toString(), - player.getDisplayName(), - String.format(format, args))); - this.usageLog.flush(); - } catch (IOException e) { - this.log(Level.WARNING, "Failed to write to shop use log file"); - } - } else { - this.log(Level.INFO, "not written to log file"); - } - } - } - -} \ No newline at end of file diff --git a/src/nl/armeagle/TradeCraft/TradeCraftBlockListener.java b/src/nl/armeagle/TradeCraft/TradeCraftBlockListener.java deleted file mode 100644 index 10d8e3b..0000000 --- a/src/nl/armeagle/TradeCraft/TradeCraftBlockListener.java +++ /dev/null @@ -1,224 +0,0 @@ -package nl.armeagle.TradeCraft; - -import java.util.ArrayList; -import java.util.List; - -import org.bukkit.Material; -import org.bukkit.block.Block; -import org.bukkit.block.Sign; -import org.bukkit.entity.Player; -import org.bukkit.event.block.BlockBreakEvent; -import org.bukkit.event.block.BlockPistonExtendEvent; -import org.bukkit.event.block.BlockPistonRetractEvent; -import org.bukkit.event.EventHandler; -import org.bukkit.event.EventPriority; -import org.bukkit.event.Listener; -import org.bukkit.event.block.SignChangeEvent; - -public class TradeCraftBlockListener implements Listener{ - - private TradeCraft plugin; - - TradeCraftBlockListener(TradeCraft plugin){ - this.plugin = plugin; - } - - public void debug(String str){ - plugin.getServer().broadcastMessage(str); - } - - @EventHandler - public void onNormalBlockBreak(BlockBreakEvent e) { - this.onBlockBreak(e, EventPriority.NORMAL); - } - - @EventHandler(priority = EventPriority.MONITOR) - public void onMonitorBlockBreak(BlockBreakEvent e) { - this.onBlockBreak(e, EventPriority.MONITOR); - } - - private void onBlockBreak(BlockBreakEvent e, EventPriority p) { - if ( !this.plugin.isEnabled() ) { - return; - } - - Player player = e.getPlayer(); - Block block = e.getBlock(); - ArrayList shops = plugin.getShopsFromBlock(player, block); - - if (shops.size() == 0) { - return; - } - - // Go through all shops in the list and check whether the player can destroy them all first. - // Only if that is the case proceed to destroy. - if (EventPriority.NORMAL == p) { - for ( TradeCraftShop shop : shops ) { - if (!shop.playerCanDestroy(player) && !plugin.permissions.canDestroyShops(player) || - shop.shopCanBeWithdrawnFrom() ) { - // cannot destroy this shop, so cancel destruction, use distinct error messages - if ( shop.shopCanBeWithdrawnFrom() ) { - plugin.sendMessage(player, TradeCraftLocalization.get("ALL_ITEMS_MUST_BE_WITHDRAWN")); - } else { - if ( block.getType() == Material.WALL_SIGN ) { - plugin.sendMessage(player, TradeCraftLocalization.get("YOU_CANT_DESTROY_THIS_SIGN")); - } else if ( block.getType() == Material.CHEST ) { - plugin.sendMessage(player, TradeCraftLocalization.get("YOU_CANT_DESTROY_THIS_CHEST")); - } else { - plugin.sendMessage(player, TradeCraftLocalization.get("YOU_CANT_DESTROY_THIS_BLOCK_ATTACHED")); - } - } - stopDestruction(block,e); - return; - } - } - } - - if (EventPriority.MONITOR == p && ! e.isCancelled()) { - // player can destroy all shops, so proceed - for ( TradeCraftShop shop : shops ) { - plugin.data.deleteShop(shop); - } - } - } - - @EventHandler(priority = EventPriority.MONITOR) - public void onMonitorSignChange(SignChangeEvent e) { - this.onSignChange(e, EventPriority.MONITOR); - } - - @EventHandler - public void onNormalSignChange(SignChangeEvent e) { - this.onSignChange(e, EventPriority.NORMAL); - } - - public void onSignChange(SignChangeEvent event, EventPriority priority) { - if ( !this.plugin.isEnabled() ) { - return; - } - - // Check whether this block is (still) a sign. Can be revoked already in case the sign was temporary, - // for example when the plugin SimpleSignEdit is being used. - if ( event.getBlock().getType() != Material.SIGN_POST && - event.getBlock().getType() != Material.WALL_SIGN ) { - return; - } - - Sign sign = (Sign) event.getBlock().getState(); - - Player player = event.getPlayer(); - String ownerName = player.getName(); - - String itemName = plugin.getItemName(event.getLines()); - - if (itemName == null) { - plugin.trace(player, "sign change, no item name, ignore"); - return; - } - // Check whether this is an existing item. Try to prevent as little normal sign placement as possible. - // Only treat signs with "[name]" as item line where "name" is found in the configuration (.txt) file. - TradeCraftConfigurationInfo itemInfo = plugin.configuration.get(itemName); - if ( itemInfo == null ) { - plugin.trace(player, "sign change, %s is not a valid item name, ignore this sign", itemName); - return; - } - - TradeCraftExchangeRate buyRate = new TradeCraftExchangeRate(event.getLine(1)); - TradeCraftExchangeRate sellRate = new TradeCraftExchangeRate(event.getLine(2)); - // no buy rate means this is an infinite shop - if ( !buyRate.isValid() && !sellRate.isValid() ) { - if (plugin.permissions.canMakeInfShops(player)){ - plugin.trace(player, "sign change, infinite chest of %s", itemName); - return; - } - - if (EventPriority.NORMAL == priority) { - plugin.sendMessage(player, TradeCraftLocalization.get("YOU_CANT_CREATE_INF_SHOPS")); - event.setCancelled(true); - return; - } - } - // there is a buy rate, so this is a player owned shop - - if (EventPriority.NORMAL == priority && !plugin.permissions.canMakePlayerShops(player)){ - plugin.sendMessage(player, TradeCraftLocalization.get("YOU_DONT_HAVE_PERM_CREATE_PLAYER_SHOP")); - event.setCancelled(true); - return; - } - - // check whether the player doesn't have too many shops - if (EventPriority.NORMAL == priority) { - int totalShopLimit = TradeCraft.properties.getPlayerTotalShopLimit(); - int worldShopLimit = TradeCraft.properties.getPlayerWorldShopLimit(); - if (plugin.data.getPlayerShopCount(player) >= totalShopLimit) { - plugin.sendMessage(player, TradeCraftLocalization.get("TOTAL_SHOP_LIMIT_X"), totalShopLimit); - event.setCancelled(true); - return; - } else if (plugin.data.getPlayerShopCount(player, player.getWorld()) >= worldShopLimit) { - plugin.sendMessage(player, TradeCraftLocalization.get("WORLD_SHOP_LIMIT_X"), worldShopLimit); - event.setCancelled(true); - return; - } - - } - - if (EventPriority.MONITOR == priority && !event.isCancelled()) { - plugin.trace(player, "Setting owner of sign to: %s", ownerName); - // set the player name on the last line - event.setLine(3, "-"+ ownerName.substring(0, Math.min(ownerName.length(), 15)) +"-"); - plugin.data.createNewSign(ownerName, itemInfo, sign); - } - - /* - if ( this.plugin.properties.getStrictPlayerShopOwnerNameRequired() ) { - if (player.getName().equalsIgnoreCase(ownerName)) { - plugin.data.setOwnerOfSign(player.getName(), sign); - return; - } - } else { - if (player.getName().startsWith(ownerName)) { - plugin.data.setOwnerOfSign(player.getName(), sign); - return; - } - } - */ - } - - // prevent pistons from pushing away (the block behind) a sign. Block all retract and extend events that would move a block behind a sign, also for shop owners - @EventHandler - public void onNormalBlockPistonRetract(BlockPistonRetractEvent e) { - if (e.isSticky()) { - Block block = e.getRetractLocation().getBlock(); - ArrayList shops = plugin.getShopsFromBlock(null, block); - - if (shops.size() != 0) { - e.setCancelled(true); - } - } - } - @EventHandler - public void onNormalBlockPistonExtend(BlockPistonExtendEvent e) { - List blocks = e.getBlocks(); - for (Block block:blocks) { - ArrayList shops = plugin.getShopsFromBlock(null, block); - - if (shops.size() != 0) { - e.setCancelled(true); - return; - } - } - } - - public void stopDestruction(Block b, BlockBreakEvent e){ - if(b.getState() instanceof Sign){ - Sign sign = (Sign)b.getState(); - String[] lines = sign.getLines(); - for(int i = 0;i<4;i++){ - sign.setLine(i, lines[i]); - } - - sign.update(true); - } - e.setCancelled(true); - } -} diff --git a/src/nl/armeagle/TradeCraft/TradeCraftConfigurationFile.java b/src/nl/armeagle/TradeCraft/TradeCraftConfigurationFile.java deleted file mode 100644 index 589ab2b..0000000 --- a/src/nl/armeagle/TradeCraft/TradeCraftConfigurationFile.java +++ /dev/null @@ -1,199 +0,0 @@ -package nl.armeagle.TradeCraft; - -import java.io.BufferedReader; -import java.io.File; -import java.io.FileReader; -import java.io.IOException; -import java.util.Arrays; -import java.util.HashMap; -import java.util.Iterator; -import java.util.Map; -import java.util.Map.Entry; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -import org.bukkit.configuration.MemorySection; - -import nl.armeagle.Configuration.StatefulYamlConfiguration; - -/** - * The name of this class is a bit misleading. This class stores all the items and their default - * trade rates that can be used in the game. The actual configuration of the plugin itself - * is handled by the TradeCraftProperties class. - */ -class TradeCraftConfigurationFile { - private static final String fileName = TradeCraft.pluginName + ".txt"; - private static final String configName = "items"; - - private static final Pattern commentPattern = Pattern.compile("^\\s*#.*$"); - private static final Pattern infoPattern = Pattern.compile( - "^\\s*([^,]+)\\s*," + // name - "\\s*(\\d+(?:;\\d+)?)\\s*" + // id[;data] (optional ;data) - "(?:,\\s*(\\d+)\\s*:\\s*(\\d+))?\\s*" + // buyAmount:buyValue - "(?:,\\s*(\\d+)\\s*:\\s*(\\d+))?\\s*$"); // sellAmount:sellValue - - private HashMap mapItemNames = new HashMap(); - - private final TradeCraft plugin; - // create an index from an TradeCraftItem (id;data) to the TradeCraftConfigurationInfo entry. This for configured name lookup based on id;data - private final Map TCitemInfoIndex = new HashMap(); - - TradeCraftConfigurationFile(TradeCraft plugin) { - this.plugin = plugin; - } - - public StatefulYamlConfiguration getConfig() { - return this.plugin.getConfig(TradeCraftConfigurationFile.configName); - } - - void load() { - StatefulYamlConfiguration config = (StatefulYamlConfiguration) this.getConfig(); - - // if file exists, load the config to it once and then rename the old config - File file = new File(plugin.getDataFolder().getAbsolutePath(), fileName); - if ( file.exists() ) { - try { - FileReader reader = new FileReader(file); - BufferedReader configurationFile = new BufferedReader(reader); - - int lineNumber = 0; - String line; - - while ((line = configurationFile.readLine()) != null) { - lineNumber += 1; - - if (line.trim().equals("")) { - continue; - } - - Matcher commentMatcher = commentPattern.matcher(line); - - if (commentMatcher.matches()) { - continue; - } - - Matcher infoMatcher = infoPattern.matcher(line); - - if (!infoMatcher.matches()) { - plugin.log.warning( - "Failed to parse line number " + lineNumber + - " in " + file.getAbsolutePath() + - ": " + line); - continue; - } - - TradeCraftConfigurationInfo info = new TradeCraftConfigurationInfo(); - info.name = infoMatcher.group(1); - - // try to split ID and Data, separated by a semicolon mark - Matcher IdSplitData = TradeCraft.itemPatternIdSplitData.matcher(infoMatcher.group(2)); - - if (!IdSplitData.matches()) { - plugin.log.info( - "Failed to parse line number " + lineNumber + - " in " + file.getAbsolutePath() + - ": " + line); - continue; - } - - int id = Integer.parseInt(IdSplitData.group(1)); - if ( IdSplitData.group(2) != null ) { - short data = Short.parseShort(IdSplitData.group(2)); - info.type = new TradeCraftItem(id, data); - } else { - info.type = new TradeCraftItem(id); - } - - if (infoMatcher.group(3) != null) { - info.sellAmount = info.buyAmount = Integer.parseInt(infoMatcher.group(3)); - info.sellValue = info.buyValue = Integer.parseInt(infoMatcher.group(4)); - } - - if (infoMatcher.group(5) != null) { - info.sellAmount = Integer.parseInt(infoMatcher.group(5)); - info.sellValue = Integer.parseInt(infoMatcher.group(6)); - } - -// config.set(info.name.toUpperCase(), info.toMemoryConfiguration()); - Iterator> iter = info.toMap().entrySet().iterator(); - while (iter.hasNext()) { - Map.Entry pairs = (Map.Entry)iter.next(); - if (!pairs.getKey().equals("name")) { - config.set(info.name +"."+ pairs.getKey(), pairs.getValue()); - } - } - -// TCitemInfoIndex.put(info.type, info.name); - } - configurationFile.close(); - reader.close(); - config.save(); - if (file.renameTo(new File(file.getAbsolutePath() + ".converted.to."+ TradeCraftConfigurationFile.configName +".yml"))) { - plugin.log.info("Converted old config to new style and renamed the old config file"); - } else { - plugin.log.severe("FAILED to convert old config to new style"); - } - - } catch (IOException e) { - plugin.log.severe("Error reading " + file.getAbsolutePath()); - } - } else { - try { - config.load(); - } catch (IOException e) { - plugin.log.severe("Error loading plugin config file"); - } - } - - Iterator> iter = config.getValues(false).entrySet().iterator(); - while (iter.hasNext()) { - // store map of lowercase item names to key names in the configuration - Map.Entry entry = (Map.Entry)iter.next(); - this.mapItemNames.put(entry.getKey().toLowerCase(), entry.getKey()); - // store map of item types to key names - MemorySection section = (MemorySection) entry.getValue(); - TradeCraftItem tcItem = new TradeCraftItem(section.getInt("itemTypeId", 266), section.getInt("itemTypeData", 0)); - TCitemInfoIndex.put(tcItem, entry.getKey()); - } - } - - public String[] getNames() { - String[] names = this.getConfig().getKeys(false).toArray(new String[0]); - Arrays.sort(names); - return names; - } - - public boolean isConfigured(String name) { - return this.mapItemNames.containsKey(name.toLowerCase()); - } - - public TradeCraftConfigurationInfo get(String name) { - - // @todo, config code doesn't seem to like serialized objects, cannot seem to find the TradeCraftConfigurationInfo class - // though, the otherwise resulting ==: classpath lines aren't very user friendly anyway. -// return (TradeCraftConfigurationInfo) plugin.getConfig().get(name.toUpperCase()); - String itemName = this.mapItemNames.get(name.toLowerCase()); - if (null != itemName) { - MemorySection item = ((MemorySection)this.getConfig().get(itemName)); - if (null != item) { - Map itemData = item.getValues(false); - if (null != itemData) { - return new TradeCraftConfigurationInfo(itemData, name); - } - } - } - return null; - } - public TradeCraftConfigurationInfo get(int id) { - return this.get(new TradeCraftItem(id)); - } - public TradeCraftConfigurationInfo get(int id, short data) { - return this.get(new TradeCraftItem(id, data)); - } - public TradeCraftConfigurationInfo get(TradeCraftItem item) { - if (!TCitemInfoIndex.containsKey(item)) { - return null; - } - return this.get(TCitemInfoIndex.get(item)); - } -} \ No newline at end of file diff --git a/src/nl/armeagle/TradeCraft/TradeCraftConfigurationInfo.java b/src/nl/armeagle/TradeCraft/TradeCraftConfigurationInfo.java deleted file mode 100644 index 7b62336..0000000 --- a/src/nl/armeagle/TradeCraft/TradeCraftConfigurationInfo.java +++ /dev/null @@ -1,55 +0,0 @@ -package nl.armeagle.TradeCraft; - -import java.util.LinkedHashMap; -import java.util.Map; -import org.bukkit.configuration.serialization.ConfigurationSerializable; -import org.bukkit.configuration.serialization.SerializableAs; - -@SerializableAs("nl.armeagle.TradeCraft.TradeCraftConfigurationInfo") -class TradeCraftConfigurationInfo implements ConfigurationSerializable { - public String name; - public TradeCraftItem type; - public int buyAmount; - public int buyValue; - public int sellAmount; - public int sellValue; - - TradeCraftConfigurationInfo() { - } - - TradeCraftConfigurationInfo(Map map, String name) { - this.name = name; - this.type = new TradeCraftItem((Integer) map.get("itemTypeId"), (Integer) map.get("itemTypeData")); - this.buyAmount = (Integer) map.get("buyAmount"); - this.buyValue = (Integer) map.get("buyValue"); - this.sellAmount = (Integer) map.get("sellAmount"); - this.sellValue = (Integer) map.get("sellValue"); - } - TradeCraftConfigurationInfo(Map map) { - this.name = (String) map.get("name"); - this.type = new TradeCraftItem((Integer) map.get("itemTypeId"), (Integer) map.get("itemTypeData")); - this.buyAmount = (Integer) map.get("buyAmount"); - this.buyValue = (Integer) map.get("buyValue"); - this.sellAmount = (Integer) map.get("sellAmount"); - this.sellValue = (Integer) map.get("sellValue"); - } - - public Map toMap() { - LinkedHashMap map = new LinkedHashMap(); - map.put("name", this.name); - map.put("itemTypeId", this.type.id); - map.put("itemTypeData", this.type.data); - map.put("buyAmount", this.buyAmount); - map.put("buyValue", this.buyValue); - map.put("sellAmount", this.sellAmount); - map.put("sellValue", this.sellValue); - return map; - } - @Override - public Map serialize() { - return this.toMap(); - } - public static TradeCraftConfigurationInfo deserialize(Map map) { - return new TradeCraftConfigurationInfo(map); - } -} \ No newline at end of file diff --git a/src/nl/armeagle/TradeCraft/TradeCraftItem.java b/src/nl/armeagle/TradeCraft/TradeCraftItem.java deleted file mode 100644 index 9bb32f5..0000000 --- a/src/nl/armeagle/TradeCraft/TradeCraftItem.java +++ /dev/null @@ -1,63 +0,0 @@ -package nl.armeagle.TradeCraft; - -/** - * - * @author ArmEagle - * Store item type(ID) and optionally the data bit - */ -public class TradeCraftItem implements Comparable { - public int id; - public short data; - - TradeCraftItem(int id) { - this(id, (short)0); - } - TradeCraftItem(int id, int data) { - this(id, new Integer(data).shortValue()); - } - TradeCraftItem(int id, short data) { - this.id = id; - this.data = data; - } - - /** - * @param compare - * @throws NullPointerException if compare is null - * @return default < 0 > compare values - */ - @Override public int compareTo(TradeCraftItem compare) { - if ( this == compare ) { - return 0; - } - if ( this.id < compare.id ) { - return -1; - } else if ( this.id > compare.id ) { - return 1; - } else { - if ( this.data < compare.data ) { - return -1; - } else if ( this.data > compare.data ) { - return 1; - } else { - return 0; - } - } - } - @Override public boolean equals(Object compare) { - return (compare == null ? false : (compare instanceof TradeCraftItem? this.compareTo((TradeCraftItem)compare) == 0 : false)); - } - @Override public int hashCode() { - return this.id * 32768 + this.data; - } - - @Override public String toString() { - return "TradeCraftItem("+ this.id +";"+ this.data +")"; - } - public String toShortString() { - if ( this.data == 0 ) { - return String.valueOf(this.id); - } else { - return this.id +";"+ this.data; - } - } -} diff --git a/src/nl/armeagle/TradeCraft/TradeCraftPermissions.java b/src/nl/armeagle/TradeCraft/TradeCraftPermissions.java deleted file mode 100644 index 2471728..0000000 --- a/src/nl/armeagle/TradeCraft/TradeCraftPermissions.java +++ /dev/null @@ -1,68 +0,0 @@ -package nl.armeagle.TradeCraft; - -import org.bukkit.command.CommandSender; -import org.bukkit.entity.Player; -import org.bukkit.plugin.Plugin; - -public class TradeCraftPermissions { - TradeCraft plugin; - - TradeCraftPermissions(TradeCraft plugin) { - this.plugin = plugin; - } - - public boolean canBuy(Player p) { - return p.hasPermission("TradeCraft.canBuy"); - } - - public boolean canSell(Player p) { - return p.hasPermission("TradeCraft.canSell"); - } - - public boolean canMakeInfShops(Player p) { - return p.hasPermission("TradeCraft.canMakeInfShops"); - } - - public boolean canMakePlayerShops(Player p) { - return p.hasPermission("TradeCraft.canMakePlayerShops"); - } - - public boolean canDestroyShops(Player p) { - return p.hasPermission("TradeCraft.canDestroyShops"); - } - - public boolean canSetCurrency(Player p) { - return p.hasPermission("TradeCraft.canSetCurrency"); - } - - public boolean canReload(Player p) { - return p.hasPermission("TradeCraft.canReload"); - } - - public boolean canQueryOtherShops(Player p) { - return p.hasPermission("TradeCraft.canQueryOtherShops"); - } - - public boolean canQueryPlayer(Player p) { - return p.hasPermission("TradeCraft.canQueryPlayer"); - } - - public void debug(CommandSender sender, String n){ - Player p = plugin.getServer().getPlayer(n); - if(p == null){ - plugin.getServer().broadcastMessage("/tc canPlayer used with a name of player who is not online."); - return; - } - String name = p.getName(); - sender.sendMessage("" + name + " has:"); - sender.sendMessage("canBuy " + canBuy(p)); - sender.sendMessage("canSell " + canSell(p)); - sender.sendMessage("canMakeInf " + canMakeInfShops(p)); - sender.sendMessage("canMakePersonal " + canMakePlayerShops(p)); - sender.sendMessage("canDestroy " + canDestroyShops(p)); - sender.sendMessage("canSetCurrency " + canSetCurrency(p)); - sender.sendMessage("canReload " + canReload(p)); - sender.sendMessage("canQueryOtherShops " + canQueryOtherShops(p)); - } - -} diff --git a/src/nl/armeagle/TradeCraft/TradeCraftPropertiesFile.java b/src/nl/armeagle/TradeCraft/TradeCraftPropertiesFile.java deleted file mode 100644 index 028704d..0000000 --- a/src/nl/armeagle/TradeCraft/TradeCraftPropertiesFile.java +++ /dev/null @@ -1,153 +0,0 @@ -package nl.armeagle.TradeCraft; - -import java.io.File; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.util.logging.Level; - -import org.bukkit.ChatColor; -import org.bukkit.configuration.InvalidConfigurationException; -import org.bukkit.configuration.file.YamlConfiguration; - -/** - * This class, handles the actual configuration of the plugin. The TradeCraftConfiguration class - * actually handles all the items that can be used by the shops in the game. - */ -public class TradeCraftPropertiesFile { - private static final String fileName = TradeCraft.pluginName + ".properties"; - private static final String filePath = "plugins" + File.separator + TradeCraft.pluginName; - public static final String defaultLanguage = "en"; - - private TradeCraft plugin; - private final YamlConfiguration properties; - - public TradeCraftPropertiesFile(TradeCraft plugin) { - this.plugin = plugin; - // make folder in the plugins dir if it doesn't exist yet - File path = new File(filePath); - if ( !path.exists() ) { - path.mkdirs(); - } - path = null; - - // if file does not exist in this directory, copy it from the jar - File file = new File(filePath + File.separator + fileName); - if ( !file.exists() ) { - this.plugin.log.info(filePath + File.separator + fileName +" does not exist, creating..."); - InputStream input = this.getClass().getResourceAsStream("/" + fileName); - if ( input != null ) { - FileOutputStream output = null; - - try { - output = new FileOutputStream(file); - byte[] buf = new byte[8192]; - int length = 0; - while ((length = input.read(buf)) > 0) { - output.write(buf, 0, length); - } - } catch (IOException e) { - e.printStackTrace(); - } finally { - try { - if (input != null) { - input.close(); - } - } catch (IOException e) {} - - try { - if (output != null) { - output.close(); - } - } catch (IOException e) {} - } - } - } - - properties = new YamlConfiguration(); - try { - properties.load(file); - } catch (InvalidConfigurationException e) { - plugin.log(Level.SEVERE, "Failed to load file: %s", file.toURI()); - } catch (IOException e) { - plugin.log(Level.SEVERE, "Failed to read file: %s", file.toURI()); - } - } - - protected void save() { - File file = new File(filePath + File.separator + fileName); - try { - properties.save(file); - } catch (IOException e) { - this.plugin.log(Level.SEVERE, "Error saving to file: %s", file.toURI()); - } - } - - public TradeCraftItem getCurrencyType(){ - int id = properties.getInt("currency-id",266); - short data = (short)properties.getInt("currency-data",0); - return new TradeCraftItem(id, data); - } - public void setCurrencyType(TradeCraftItem item) { - properties.set("currency-id", item.id); - properties.set("currency-data", item.data); - this.save(); - } - public boolean getNormalStackSizeUsed(){ - return properties.getBoolean("normal-stack-size", true); - } - - public boolean getInfiniteShopsEnabled() { - return properties.getBoolean("infinite-shops-enabled", true); - } - - public boolean getPlayerOwnedShopsEnabled() { - return properties.getBoolean("player-owned-shops-enabled", true); - } - - public boolean getRepairShopsEnabled() { - return properties.getBoolean("repair-shops-enabled", false); - } - - public int getRepairCost() { - return properties.getInt("repair-cost", 0); - } - - public boolean getEnableDebugMessages() { - return properties.getBoolean("enable-debug-messages", false); - } - - public String getLanguage() { - return properties.getString("language", TradeCraftPropertiesFile.defaultLanguage); - } - - public boolean autoUpdateLanguageFiles() { - return properties.getBoolean("auto-update-language-files", true); - } - - public boolean logShopUse() { - return properties.getBoolean("log-shop-use", false); - } - - public boolean showShopLocation() { - return properties.getBoolean("show-shop-location", false); - } - - public int getPlayerWorldShopLimit() { - return properties.getInt("player-world-shop-limit", 5); - } - public int getPlayerTotalShopLimit() { - return properties.getInt("player-total-shop-limit", 10); - } - - public ChatColor getMessageTypeColor(TradeCraft.MessageTypes mtype) { - switch (mtype) { - case WITHDRAW: - return ChatColor.YELLOW; - case DEPOSIT: - return ChatColor.GRAY; - default: - return ChatColor.WHITE; - } - } -} From 495f23aa2a9c0f7f7e680121c031c9c082183b9e Mon Sep 17 00:00:00 2001 From: armeagle Date: Sat, 11 Aug 2012 18:19:16 +0200 Subject: [PATCH 095/100] 1.4.1 - Moved all files into a source subdirectory - Removed support for old Permissions plugin, now only supporting Bukkit's built-in system. - Cleanup up tabs for whitespaces --- .../StatefulYamlConfiguration.java | 79 --- nl/armeagle/TradeCraft/TradeCraft.java | 656 ------------------ .../TradeCraft/TradeCraftBlockListener.java | 224 ------ .../TradeCraftConfigurationFile.java | 199 ------ .../TradeCraftConfigurationInfo.java | 55 -- nl/armeagle/TradeCraft/TradeCraftItem.java | 63 -- .../TradeCraft/TradeCraftPermissions.java | 121 ---- .../TradeCraft/TradeCraftPropertiesFile.java | 153 ---- README.txt => src/README.txt | 0 TODO.txt => src/TODO.txt | 0 TradeCraft.en.lang => src/TradeCraft.en.lang | 0 TradeCraft.ge.lang => src/TradeCraft.ge.lang | 0 .../TradeCraft.properties | 0 TradeCraft.sv.lang => src/TradeCraft.sv.lang | 0 TradeCraft.txt => src/TradeCraft.txt | 0 items.yml => src/items.yml | 0 .../StatefulYamlConfiguration.java | 79 +++ src/nl/armeagle/TradeCraft/TradeCraft.java | 651 +++++++++++++++++ .../TradeCraft/TradeCraftBlockListener.java | 224 ++++++ .../armeagle/TradeCraft/TradeCraftChest.java | 26 +- .../TradeCraftConfigurationFile.java | 199 ++++++ .../TradeCraftConfigurationInfo.java | 55 ++ .../TradeCraft/TradeCraftDataFile.java | 146 ++-- .../TradeCraft/TradeCraftDataInfo.java | 12 +- .../TradeCraft/TradeCraftExchangeRate.java | 18 +- .../TradeCraft/TradeCraftInfiniteShop.java | 4 +- .../armeagle/TradeCraft/TradeCraftItem.java | 63 ++ .../TradeCraft/TradeCraftItemShop.java | 294 ++++---- .../TradeCraft/TradeCraftLocalization.java | 28 +- .../TradeCraft/TradeCraftPermissions.java | 68 ++ .../TradeCraft/TradeCraftPlayerListener.java | 52 +- .../TradeCraft/TradeCraftPlayerOwnedShop.java | 10 +- .../TradeCraft/TradeCraftPropertiesFile.java | 153 ++++ .../TradeCraft/TradeCraftRepairShop.java | 22 +- .../armeagle/TradeCraft/TradeCraftShop.java | 10 +- plugin.yml => src/plugin.yml | 0 36 files changed, 1803 insertions(+), 1861 deletions(-) delete mode 100644 nl/armeagle/Configuration/StatefulYamlConfiguration.java delete mode 100644 nl/armeagle/TradeCraft/TradeCraft.java delete mode 100644 nl/armeagle/TradeCraft/TradeCraftBlockListener.java delete mode 100644 nl/armeagle/TradeCraft/TradeCraftConfigurationFile.java delete mode 100644 nl/armeagle/TradeCraft/TradeCraftConfigurationInfo.java delete mode 100644 nl/armeagle/TradeCraft/TradeCraftItem.java delete mode 100644 nl/armeagle/TradeCraft/TradeCraftPermissions.java delete mode 100644 nl/armeagle/TradeCraft/TradeCraftPropertiesFile.java rename README.txt => src/README.txt (100%) rename TODO.txt => src/TODO.txt (100%) rename TradeCraft.en.lang => src/TradeCraft.en.lang (100%) rename TradeCraft.ge.lang => src/TradeCraft.ge.lang (100%) rename TradeCraft.properties => src/TradeCraft.properties (100%) rename TradeCraft.sv.lang => src/TradeCraft.sv.lang (100%) rename TradeCraft.txt => src/TradeCraft.txt (100%) rename items.yml => src/items.yml (100%) create mode 100644 src/nl/armeagle/Configuration/StatefulYamlConfiguration.java create mode 100644 src/nl/armeagle/TradeCraft/TradeCraft.java create mode 100644 src/nl/armeagle/TradeCraft/TradeCraftBlockListener.java rename {nl => src/nl}/armeagle/TradeCraft/TradeCraftChest.java (75%) create mode 100644 src/nl/armeagle/TradeCraft/TradeCraftConfigurationFile.java create mode 100644 src/nl/armeagle/TradeCraft/TradeCraftConfigurationInfo.java rename {nl => src/nl}/armeagle/TradeCraft/TradeCraftDataFile.java (73%) rename {nl => src/nl}/armeagle/TradeCraft/TradeCraftDataInfo.java (61%) rename {nl => src/nl}/armeagle/TradeCraft/TradeCraftExchangeRate.java (52%) rename {nl => src/nl}/armeagle/TradeCraft/TradeCraftInfiniteShop.java (93%) create mode 100644 src/nl/armeagle/TradeCraft/TradeCraftItem.java rename {nl => src/nl}/armeagle/TradeCraft/TradeCraftItemShop.java (50%) rename {nl => src/nl}/armeagle/TradeCraft/TradeCraftLocalization.java (54%) create mode 100644 src/nl/armeagle/TradeCraft/TradeCraftPermissions.java rename {nl => src/nl}/armeagle/TradeCraft/TradeCraftPlayerListener.java (54%) rename {nl => src/nl}/armeagle/TradeCraft/TradeCraftPlayerOwnedShop.java (94%) create mode 100644 src/nl/armeagle/TradeCraft/TradeCraftPropertiesFile.java rename {nl => src/nl}/armeagle/TradeCraft/TradeCraftRepairShop.java (75%) rename {nl => src/nl}/armeagle/TradeCraft/TradeCraftShop.java (72%) rename plugin.yml => src/plugin.yml (100%) diff --git a/nl/armeagle/Configuration/StatefulYamlConfiguration.java b/nl/armeagle/Configuration/StatefulYamlConfiguration.java deleted file mode 100644 index cc72966..0000000 --- a/nl/armeagle/Configuration/StatefulYamlConfiguration.java +++ /dev/null @@ -1,79 +0,0 @@ -package nl.armeagle.Configuration; - -import java.io.File; -import java.io.FileNotFoundException; -import java.io.IOException; -import java.io.InputStream; -import java.util.logging.Level; -import java.util.logging.Logger; - -import org.bukkit.Bukkit; -import org.bukkit.configuration.InvalidConfigurationException; -import org.bukkit.configuration.file.YamlConfiguration; -import org.bukkit.plugin.java.JavaPlugin; -import org.yaml.snakeyaml.error.YAMLException; - -public class StatefulYamlConfiguration extends YamlConfiguration { - protected File file; - - public StatefulYamlConfiguration(File file) { - this.file = file; - } - - public void load() throws IOException { - this.load(false); - } - public void load(boolean forceDefaults) throws IOException { - boolean notLoaded = false; - if (this.file == null) { - throw new IllegalArgumentException("File cannot be null"); - } - - YamlConfiguration baseConfig = new YamlConfiguration(); - - try { - baseConfig.load(this.file); - } catch (FileNotFoundException fnf) { - notLoaded = true; - } catch (IOException ex) { - Bukkit.getLogger().log(Level.SEVERE, "Cannot load " + this.file, ex); - } catch (InvalidConfigurationException ex) { - if (ex.getCause() instanceof YAMLException) { - Bukkit.getLogger().severe("Config file " + this.file + " isn't valid! " + ex.getCause()); - } else if ((ex.getCause() == null) || (ex.getCause() instanceof ClassCastException)) { - Bukkit.getLogger().severe("Config file " + this.file + " isn't valid!"); - } else { - Bukkit.getLogger().log(Level.SEVERE, "Cannot load " + this.file + ": " + ex.getCause().getClass(), ex); - } - } - - if (notLoaded || forceDefaults) { - InputStream defaultInput = this.getClass().getResourceAsStream("/" + file.getName()); - if ( null != defaultInput ) { - YamlConfiguration defaultConfig = YamlConfiguration.loadConfiguration(defaultInput); - - try { - this.loadFromString(defaultConfig.saveToString()); - } catch (InvalidConfigurationException e) { - Logger.getLogger(JavaPlugin.class.getName()).log(Level.SEVERE, "Invalid default configuation"); - } - this.save(); - } - } else if (! notLoaded) { - try { - this.loadFromString(baseConfig.saveToString()); - } catch (InvalidConfigurationException e) { - Logger.getLogger(JavaPlugin.class.getName()).log(Level.SEVERE, "Invalid configuation"); - } - } - - } - - public void save() throws IOException { - super.save(this.file); - } - - public File getFile() { - return this.file; - } -} diff --git a/nl/armeagle/TradeCraft/TradeCraft.java b/nl/armeagle/TradeCraft/TradeCraft.java deleted file mode 100644 index 8f00413..0000000 --- a/nl/armeagle/TradeCraft/TradeCraft.java +++ /dev/null @@ -1,656 +0,0 @@ -package nl.armeagle.TradeCraft; - -import java.io.*; - -import java.text.SimpleDateFormat; -import java.util.ArrayList; -import java.util.Date; -import java.util.HashMap; -import java.util.IllegalFormatException; -import java.util.Map; -import java.util.logging.Level; -import java.util.logging.Logger; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -import nl.armeagle.Configuration.StatefulYamlConfiguration; - -import org.bukkit.ChatColor; -import org.bukkit.Material; -import org.bukkit.Server; -import org.bukkit.block.Block; -import org.bukkit.block.BlockFace; -import org.bukkit.block.Chest; -import org.bukkit.block.Sign; -import org.bukkit.command.Command; -import org.bukkit.command.CommandSender; -import org.bukkit.command.ConsoleCommandSender; -import org.bukkit.entity.Player; -import org.bukkit.inventory.ItemStack; -import org.bukkit.material.MaterialData; -import org.bukkit.plugin.PluginDescriptionFile; -import org.bukkit.plugin.PluginManager; -import org.bukkit.plugin.java.JavaPlugin; - -import com.nijikokun.bukkit.Permissions.Permissions; - -public class TradeCraft extends JavaPlugin { - public static enum MessageTypes {WITHDRAW, DEPOSIT}; - - // The plugin name. - static final String pluginName = "TradeCraft"; - - public static final Pattern itemPatternIdSplitData = Pattern.compile("^(\\d+)(?:;(\\d+))?$"); - - private static enum Commands { - tcsetcurrency, - tcgetcurrency, - tcshops, - tcpshops, - tcreload, - tcplayerperms, - tchelp, - tc - }; - - // Stuff used to interact with the server. - final Logger log = Logger.getLogger("Minecraft"); - final Server server = this.getServer(); - - protected BufferedWriter usageLog = null; - // Objects used by the plugin. - static TradeCraftItem currency; - static TradeCraftPropertiesFile properties; - TradeCraftConfigurationFile configuration; - public TradeCraftLocalization localization; - TradeCraftDataFile data; - - private HashMap configs = new HashMap(); - - private final TradeCraftBlockListener blockListener = new TradeCraftBlockListener(this); - private final TradeCraftPlayerListener playerListener = new TradeCraftPlayerListener(this); - public TradeCraftPermissions permissions = new TradeCraftPermissions(this); - public Permissions permissionsPlugin = null; - public boolean permEnabled = false; - - // prevent the script from registering the event listeners multiple times (by dis-/enable) - public static boolean hasRegisteredEventListeners = false; - - public void onDisable() { - this.disable(); - } - private void disable() { - if ( this.usageLog != null ) { - try { - this.usageLog.close(); - } catch (IOException e) { - this.log(Level.WARNING, "Failed to close shop usage log file"); - } - } - properties = null; - configuration = null; - this.localization = null; - data.save(); - data = null; - } - - public void onEnable() { - this.enable(); - } - private void enable() { - properties = new TradeCraftPropertiesFile(this); - configuration = new TradeCraftConfigurationFile(this); - data = new TradeCraftDataFile(this); - this.localization = new TradeCraftLocalization(this); - - if ( TradeCraft.properties.logShopUse() ) { - File usageLogFile = new File(this.getDataFolder(), "shopUsage.log"); - try { - if ( !usageLogFile.exists() ) { - usageLogFile.createNewFile(); - } - if ( usageLogFile.canWrite() ) { - this.usageLog = new BufferedWriter(new FileWriter(usageLogFile, true)); - this.log(Level.INFO, "Writing shop usage to log file: "+ usageLogFile.toString()); - } else { - this.log(Level.WARNING, "Error opening shop usage log file: "+ usageLogFile.toString()); - } - } catch (IOException e) { - this.log(Level.WARNING, "Failed to open shop usage log file: "+ usageLogFile.toString()); - } - } - - configuration.load(); - data.load(); - currency = properties.getCurrencyType(); - permissions.setupPermissions(); - - if ( !TradeCraft.hasRegisteredEventListeners ) { - PluginManager pm = this.getServer().getPluginManager(); - pm.registerEvents(playerListener, this); - pm.registerEvents(blockListener, this); - TradeCraft.hasRegisteredEventListeners = true; - } - - PluginDescriptionFile pdfFile = this.getDescription(); - this.log.info(pdfFile.getName() + " version " + pdfFile.getVersion() + " is enabled!"); - - } - - @Override - public boolean onCommand(CommandSender sender, Command cmd, String label, String[] args) { - try { - TradeCraft.Commands command = TradeCraft.Commands.valueOf(cmd.getName()); - if (sender instanceof Player) { - Player p = (Player) sender; - switch (command) { - case tcsetcurrency: - if ( args.length == 1 && this.permissions.canSetCurrency(p) ) { - TradeCraftItem testCurrency = null; - // try to split ID and Data, separated by a semicolon mark - Matcher IdSplitData = TradeCraft.itemPatternIdSplitData.matcher(args[0]); - - if ( !IdSplitData.matches() ) { - // try to match the parameter to item names from the configuration - TradeCraftConfigurationInfo setCurr = this.configuration.get(args[0]); - if ( setCurr == null ) { - this.sendMessage(p, TradeCraftLocalization.get("IS_NO_VALID_CURRENCY_USE_INSTEAD"), - args[0]); - return false; - } else { - currency = setCurr.type; - } - } else { - try { - int cid = Integer.parseInt(IdSplitData.group(1)); - if ( IdSplitData.group(2) != null ) { - testCurrency = new TradeCraftItem(cid, Short.parseShort(IdSplitData.group(2))); - } else { - testCurrency = new TradeCraftItem(cid); - } - } catch ( NumberFormatException e ) { - this.sendMessage(p, TradeCraftLocalization.get("INVALID_CURRENCY"), - args[0]); - return false; - } - if ( this.configuration.get(testCurrency) != null ) { - currency = testCurrency; - } else { - this.sendMessage(p, TradeCraftLocalization.get("INVALID_CURRENCY"), - args[0]); - return false; - } - } - - TradeCraft.properties.setCurrencyType(currency); - this.sendMessage(p, TradeCraftLocalization.get("CURRENCY_IS_SET_TO_A_IDDATA"), - this.getCurrencyName(), - currency.toShortString()); - return true; - } - return true; - case tcgetcurrency: - this.sendMessage(p, TradeCraftLocalization.get("CURRENCY_IS_A_IDDATA"), - this.getCurrencyName(), - currency.toShortString()); - return true; - case tcshops: - // lookup own shows - displayShops(p.getName(), p, false); - return true; - case tcpshops: - // Check whether another parameter is passed, if so check whether - // the player can get information about other player's shops. - if ( this.permissions.canQueryOtherShops(p) ) { - if ( args.length == 1 ) { - // lookup other player's shops - displayShops(args[0], p, true); - } else { - this.sendMessage(p, cmd.getUsage()); - } - } - return true; - case tcreload: - if ( this.permissions.canReload(p)) { - this.sendMessage(p, TradeCraftLocalization.get("RESTARTING_PLUGIN"), TradeCraft.pluginName); - this.disable(); - this.enable(); - this.sendMessage(p, TradeCraftLocalization.get("RESTARTING_PLUGIN_DONE"), TradeCraft.pluginName); - } - return true; - case tcplayerperms: - if ( this.permissions.canQueryPlayer(p)) { - if ( args.length == 1 ) { - permissions.debug(sender, args[0]); - } else { - sender.sendMessage(cmd.getUsage()); - } - } - return true; - default: - displayCommandHelpText(p); - return true; - } - } else if ( sender instanceof ConsoleCommandSender ) { - switch (command) { - case tcplayerperms: - if ( args.length == 1 ) { - permissions.debug(sender, args[0]); - } else { - sender.sendMessage(cmd.getUsage()); - } - return true; - case tcpshops: - // Check whether another parameter is passed, if so check whether - // the player can get information about other player's shops. - if ( args.length == 1 ) { - // lookup other player's shops - displayShops(args[0], sender, true); - } else { - sender.sendMessage(cmd.getUsage()); - } - return true; - case tcreload: - sender.sendMessage(String.format(TradeCraftLocalization.get("RESTARTING_PLUGIN"), - TradeCraft.pluginName)); - this.disable(); - this.enable(); - sender.sendMessage(String.format(TradeCraftLocalization.get("RESTARTING_PLUGIN_DONE"), - TradeCraft.pluginName)); - return true; - default: - displayCommandHelpText(null); - return true; - } - } - } catch (IllegalArgumentException e) { - return false; - } - return false; - } - - void displayShops(String infoPlayerName, CommandSender displayTo, boolean otherQuery) { - Map list = data.shopsOwned(infoPlayerName); - if (list.size() == 0) { - if ( otherQuery ) { - // elevated player looking for other player's shops - displayTo.sendMessage(String.format(TradeCraftLocalization.get("A_DOES_NOT_OWN_ANY_SHOPS"), - infoPlayerName)); - } else { - displayTo.sendMessage(TradeCraftLocalization.get("YOU_DONT_OWN_ANY_SHOPS")); - } - return; - } - - if ( otherQuery ) { - displayTo.sendMessage(String.format(TradeCraftLocalization.get("SHOPS_OF_A"), - infoPlayerName)); - } else { - displayTo.sendMessage(TradeCraftLocalization.get("YOUR_SHOPS")); - } - for (Map.Entry entry : list.entrySet()) { - TradeCraftDataInfo info = entry.getValue(); - String message = ""; - if (TradeCraft.properties.showShopLocation()) { - String location = entry.getKey().replaceFirst(",", "(") +")"; - message += ChatColor.GRAY + location +" "+ ChatColor.WHITE; - } - message += TradeCraftLocalization.get("ITEM") +": "+ this.configuration.get(info.itemType).name +"("+ info.itemType.toShortString() +")" - +" "+ TradeCraftLocalization.get("AMOUNT") +": "+ info.itemAmount +" " - + this.getCurrencyName() +": "+ info.currencyAmount; - - displayTo.sendMessage(message); - } - - } - - void sendMessage(Player player, TradeCraft.MessageTypes messageType, String format, Object... args) { - try { - String message = String.format(format, args); - player.sendMessage(TradeCraft.properties.getMessageTypeColor(messageType) + message); - } catch ( IllegalFormatException e ) { - player.sendMessage(TradeCraftLocalization.get("ERROR_IN_FORMAT_STRING") +" "+ format); - } - } - - void sendMessage(Player player, String format, Object... args) { - try { - String message = String.format(format, args); - player.sendMessage(message); - } catch ( IllegalFormatException e ) { - player.sendMessage(TradeCraftLocalization.get("ERROR_IN_FORMAT_STRING") +" "+ format); - } - } - - void trace(Player player, String format, Object... args) { - if (properties.getEnableDebugMessages()) { - if (null != player) { - sendMessage(player, format, args); - } else { - this.log(Level.INFO, format, args); - } - } - } - /* - * When a block behind a shop sign is destroyed, the sign would be destroyed too. - * Check all side faces of this block, for a sign attached to this block. Then - * pass that sign block to getShopFromSignBlock. - * - * This should only be used for checking whether a normal block can be destroyed, for there not being any - * signs attached to it, or this block being a chest or sign itself. - * Since one block can - */ - ArrayList getShopsFromBlock(Player player, Block block) { - ArrayList shops = new ArrayList(); - // use other function(s) directly if applicable - if ( block.getType() == Material.CHEST || block.getType() == Material.WALL_SIGN ) { - TradeCraftShop shop = getShopFromSignOrChestBlock(player, block); - if ( shop != null ) { - shops.add(shop); - } - } else { - // go through all 4 faces of this block to check for a sign attached to this block - final BlockFace[] sides = {BlockFace.NORTH, BlockFace.EAST, BlockFace.SOUTH, BlockFace.WEST}; - for ( int index_sides = 0; index_sides < sides.length; index_sides++ ) { - BlockFace side = sides[index_sides]; - // get the block on that side - Block sideBlock = block.getRelative(side); - // check for it being a wall sign - if ( sideBlock.getType() == Material.WALL_SIGN ) { - // get the sign (extending MaterialData) for clean attached face checking - org.bukkit.material.Sign materialSign = new org.bukkit.material.Sign(Material.WALL_SIGN, sideBlock.getData()); - /* Now, for easy comparison, we'll compare to the direction the sign is facing. If that's - * the same as the current face (of the 4 we're looping through), then this sign is attached - * to the block that is being destroyed. - */ - if ( materialSign.getFacing() == side ) { - TradeCraftShop shop = this.getShopFromSignBlock(player, sideBlock); - if ( shop != null ) { - shops.add(shop); - } - } - - } - } - } - return shops; - } - TradeCraftShop getShopFromSignOrChestBlock(Player player, Block block) { - if (block.getType() == Material.CHEST) { - block = block.getWorld().getBlockAt( - block.getX(), - block.getY() + 1, - block.getZ() - ); - } - - return getShopFromSignBlock(player, block); - } - - TradeCraftShop getShopFromSignBlock(Player player, Block block) { - if (block.getType() != Material.WALL_SIGN) { - return null; - } - - int x = block.getX(); - int y = block.getY(); - int z = block.getZ(); - - trace(player, "You clicked a sign at %d, %d, %d in world: %s.", x, y, z, block.getWorld().getName()); - - Sign sign = (Sign) block.getWorld().getBlockAt(x, y, z).getState(); - - // The sign at this location can be null if it was just destroyed. - if (sign == null) { - trace(player, "The sign is no longer there."); - - return null; - } - - String itemName = getItemName(sign.getLines()); - - if (itemName == null) { - trace(player, "There is no item name on the sign."); - - return null; - } - - trace(player, "The item name on the sign is %s.", itemName); - - Block blockBelowSign = block.getRelative(0, -1, 0); - - if (blockBelowSign.getType() != Material.CHEST) { - trace(player, "There is no chest beneath the sign."); - return null; - } - - Chest chest = (Chest) blockBelowSign.getState(); - - if (itemName.toLowerCase().equals("repair")) { - if (!properties.getRepairShopsEnabled()) { - trace(player, "Repair shops are not enabled."); - return null; - } - - if (player == null || !player.isOp()) { - trace(player, "You can't use repair shops."); - return null; - } - - trace(player, "This is a repair shop."); - return new TradeCraftRepairShop(this, sign, chest); - } - - if (!configuration.isConfigured(itemName)) { - trace(player, - "The item name %s is not configured in the config file.", - itemName); - return null; - } - - // TODO change to use chest getOwner - //String ownerName = getOwnerName(sign.getLine(3)); - String ownerName = data.getOwnerOfSign(sign); - - if (ownerName == null) { - trace(player, "There is no owner name on the sign."); - - if (!properties.getInfiniteShopsEnabled()) { - trace(player, "Ininite shops are not enabled."); - return null; - } - - trace(player, "This is an infinite shop."); - try { - return new TradeCraftInfiniteShop(this, sign, chest); - } catch (Exception e) { - trace(player, e.getMessage()); - } - return null; - } - - trace(player, "The owner name on the sign is %s.", ownerName); - - if (!properties.getPlayerOwnedShopsEnabled()) { - trace(player, "Player-owned shops are not enabled."); - return null; - } - - trace(player, "This is a player-owned shop."); - return new TradeCraftPlayerOwnedShop(this, sign, chest, ownerName); - } - - String getItemName(String[] signLines) { - return getSpecialText(signLines, "[", "]"); - } - - String getOwnerName(String signLine) { - return getSpecialTextOnLine(signLine, "-", "-"); - } - - private String getSpecialText(String[] signLines, String prefix, String suffix) { - for (int i = 0; i < 4; i++) { - String text = getSpecialTextOnLine(signLines[i], prefix, suffix); - - if (text != null) { - return text; - } - } - - return null; - } - - private String getSpecialTextOnLine(String signLine, String prefix, String suffix) { - if (signLine == null) { - return null; - } - - signLine = signLine.trim(); - - if (signLine.startsWith(prefix) && signLine.endsWith(suffix) - && signLine.length() > 2) { - - String text = signLine.substring(1, signLine.length() - 1); - text = text.trim(); - if (text.equals("")) { - return null; - } - - return text; - } - - return null; - } - - TradeCraftExchangeRate getExchangeRate(String[] signLines, int lineNumber) { - if ( lineNumber < 0 || lineNumber >= signLines.length ) { - return null; - } - return getExchangeRate(signLines[lineNumber]); - } - TradeCraftExchangeRate getExchangeRate(String signLine) { - return new TradeCraftExchangeRate(signLine); } - - static int getMaxStackSize(int itemType) { - if ( TradeCraft.properties.getNormalStackSizeUsed() ) { - return Material.getMaterial(itemType).getMaxStackSize(); - } else { - return 64; - } - } - - /** - * Get a CamelCased string based on the current currency. - * - * @return a string representing the currency. - */ - public String getCurrencyName() { - // Try to get the name from the configuration file first - TradeCraftConfigurationInfo configInfo = this.configuration.get(TradeCraft.currency); - if ( configInfo != null ) { - return configInfo.name; - } else { - - ItemStack currencyStack = new ItemStack(TradeCraft.currency.id, 1, TradeCraft.currency.data); // weird that there's no Material.getMaterial(id, short/byte) - MaterialData currencyData = currencyStack.getType().getNewData((byte)TradeCraft.currency.data); - String currencyString; - if ( currencyData == null ) { - currencyString = currencyStack.getType().name(); - } else { - currencyString = currencyData.toString(); - } - - //String baseName = stack.getType().name(); - String[] words = currencyString.replace("null ", "").split("\\(")[0].split("[ _]{1}"); - String name = ""; - for ( int word_ind = 0; word_ind < words.length; word_ind++ ) { - String word = words[word_ind]; - if ( word_ind > 0 ) { - name = name.concat(" "); - } - name = name.concat(word.substring(0, 1).toUpperCase()).concat(word.substring(1).toLowerCase()); - } - return name; - } - } - - private void displayCommandHelpText(Player player) { - if ( player != null ) { - this.sendMessage(player, TradeCraftLocalization.get("POSSIBLE_COMMANDS_FOR_THE_PLUGIN"), - TradeCraft.pluginName); - this.sendMessage(player, "/tc[help]"+ ChatColor.GRAY +" "+ TradeCraftLocalization.get("TC_HELP_THIS_TEXT")); - this.sendMessage(player, "/tcshops"+ ChatColor.GRAY +" "+ TradeCraftLocalization.get("TC_SHOPS")); - if ( this.permissions.canQueryOtherShops(player) ) { - this.sendMessage(player, "/tcpshops [player]"+ ChatColor.GRAY +" "+ TradeCraftLocalization.get("TC_PSHOPS")); - } - this.sendMessage(player, "/tcgetcurrency"+ ChatColor.GRAY +" "+ TradeCraftLocalization.get("TC_CURRENCY_GET_CURRENCY")); - if ( this.permissions.canSetCurrency(player) ) { - this.sendMessage(player, "/tcsetcurrency [id[;data]]"+ ChatColor.GRAY +" "+ TradeCraftLocalization.get("TC_CURRENCY_OPT_PARAM_GETSET_CURRENCY")); - } - if ( this.permissions.canReload(player) ) { - this.sendMessage(player, "/tcreload"+ ChatColor.GRAY +" "+ TradeCraftLocalization.get("TC_RELOAD")); - } - if ( this.permissions.canQueryPlayer(player) ) { - this.sendMessage(player, "/tcplayerperms"+ ChatColor.GRAY +" "+ TradeCraftLocalization.get("TC_CAN_PLAYER")); - } - } else { - // console command help - this.log(Level.INFO, TradeCraftLocalization.get("POSSIBLE_COMMANDS_FOR_THE_PLUGIN"), - TradeCraft.pluginName); - this.log(Level.INFO, "tc[help]: "+ TradeCraftLocalization.get("TC_HELP_THIS_TEXT")); - this.log(Level.INFO, "tcplayerperms playername: "+ TradeCraftLocalization.get("TC_CAN_PLAYER")); - this.log(Level.INFO, "tcpshops [player]: "+ TradeCraftLocalization.get("TC_PSHOPS")); - this.log(Level.INFO, "tcreload: "+ TradeCraftLocalization.get("TC_RELOAD")); - } - } - - public void saveConfig() { - Logger.getLogger(JavaPlugin.class.getName()).log(Level.INFO, "saving config to: "+ this.getConfig("config").getFile().getAbsolutePath()); - try { - this.getConfig("config").save(); - } catch (IOException ex) { - Logger.getLogger(JavaPlugin.class.getName()).log(Level.SEVERE, "Could not save config to " + this.getConfig("config").getFile().getName(), ex); - } - } - public StatefulYamlConfiguration getConfig() { - return this.getConfig("config"); - } - public StatefulYamlConfiguration getConfig(String name) { - if (name.indexOf(".") < 0) { - name += ".yml"; - } - if (this.configs.containsKey(name)) { - return this.configs.get(name); - } else { - File configFile = new File(this.getDataFolder(), name); - StatefulYamlConfiguration config = new StatefulYamlConfiguration(configFile); - this.configs.put(name, config); - return config; - } - } - - public void log(Level level, String format, Object... args) { - this.log.log(level, TradeCraft.pluginName +": "+ String.format(format, args)); - } - - public void useLog(Player player, TradeCraftShop shop, String format, Object... args) { - if ( TradeCraft.properties.logShopUse() ) { - if ( this.usageLog != null ) { - try { - SimpleDateFormat formatter = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss"); - this.usageLog.write(String.format("%s %s \t%s %s\n", - formatter.format(new Date()), - shop.toString(), - player.getDisplayName(), - String.format(format, args))); - this.usageLog.flush(); - } catch (IOException e) { - this.log(Level.WARNING, "Failed to write to shop use log file"); - } - } else { - this.log(Level.INFO, "not written to log file"); - } - } - } - -} \ No newline at end of file diff --git a/nl/armeagle/TradeCraft/TradeCraftBlockListener.java b/nl/armeagle/TradeCraft/TradeCraftBlockListener.java deleted file mode 100644 index 9075fe3..0000000 --- a/nl/armeagle/TradeCraft/TradeCraftBlockListener.java +++ /dev/null @@ -1,224 +0,0 @@ -package nl.armeagle.TradeCraft; - -import java.util.ArrayList; -import java.util.List; - -import org.bukkit.Material; -import org.bukkit.block.Block; -import org.bukkit.block.Sign; -import org.bukkit.entity.Player; -import org.bukkit.event.block.BlockBreakEvent; -import org.bukkit.event.block.BlockPistonExtendEvent; -import org.bukkit.event.block.BlockPistonRetractEvent; -import org.bukkit.event.EventHandler; -import org.bukkit.event.EventPriority; -import org.bukkit.event.Listener; -import org.bukkit.event.block.SignChangeEvent; - -public class TradeCraftBlockListener implements Listener{ - - private TradeCraft plugin; - - TradeCraftBlockListener(TradeCraft plugin){ - this.plugin = plugin; - } - - public void debug(String str){ - plugin.getServer().broadcastMessage(str); - } - - @EventHandler - public void onNormalBlockBreak(BlockBreakEvent e) { - this.onBlockBreak(e, EventPriority.NORMAL); - } - - @EventHandler(priority = EventPriority.MONITOR) - public void onMonitorBlockBreak(BlockBreakEvent e) { - this.onBlockBreak(e, EventPriority.MONITOR); - } - - private void onBlockBreak(BlockBreakEvent e, EventPriority p) { - if ( !this.plugin.isEnabled() ) { - return; - } - - Player player = e.getPlayer(); - Block block = e.getBlock(); - ArrayList shops = plugin.getShopsFromBlock(player, block); - - if (shops.size() == 0) { - return; - } - - // Go through all shops in the list and check whether the player can destroy them all first. - // Only if that is the case proceed to destroy. - if (EventPriority.NORMAL == p) { - for ( TradeCraftShop shop : shops ) { - if (!shop.playerCanDestroy(player) && !plugin.permissions.canDestroyShops(player) || - shop.shopCanBeWithdrawnFrom() ) { - // cannot destroy this shop, so cancel destruction, use distinct error messages - if ( shop.shopCanBeWithdrawnFrom() ) { - plugin.sendMessage(player, TradeCraftLocalization.get("ALL_ITEMS_MUST_BE_WITHDRAWN")); - } else { - if ( block.getType() == Material.WALL_SIGN ) { - plugin.sendMessage(player, TradeCraftLocalization.get("YOU_CANT_DESTROY_THIS_SIGN")); - } else if ( block.getType() == Material.CHEST ) { - plugin.sendMessage(player, TradeCraftLocalization.get("YOU_CANT_DESTROY_THIS_CHEST")); - } else { - plugin.sendMessage(player, TradeCraftLocalization.get("YOU_CANT_DESTROY_THIS_BLOCK_ATTACHED")); - } - } - stopDestruction(block,e); - return; - } - } - } - - if (EventPriority.MONITOR == p && ! e.isCancelled()) { - // player can destroy all shops, so proceed - for ( TradeCraftShop shop : shops ) { - plugin.data.deleteShop(shop); - } - } - } - - @EventHandler(priority = EventPriority.MONITOR) - public void onMonitorSignChange(SignChangeEvent e) { - this.onSignChange(e, EventPriority.MONITOR); - } - - @EventHandler - public void onNormalSignChange(SignChangeEvent e) { - this.onSignChange(e, EventPriority.NORMAL); - } - - public void onSignChange(SignChangeEvent event, EventPriority priority) { - if ( !this.plugin.isEnabled() ) { - return; - } - - // Check whether this block is (still) a sign. Can be revoked already in case the sign was temporary, - // for example when the plugin SimpleSignEdit is being used. - if ( event.getBlock().getType() != Material.SIGN_POST && - event.getBlock().getType() != Material.WALL_SIGN ) { - return; - } - - Sign sign = (Sign) event.getBlock().getState(); - - Player player = event.getPlayer(); - String ownerName = player.getName(); - - String itemName = plugin.getItemName(event.getLines()); - - if (itemName == null) { - plugin.trace(player, "sign change, no item name, ignore"); - return; - } - // Check whether this is an existing item. Try to prevent as little normal sign placement as possible. - // Only treat signs with "[name]" as item line where "name" is found in the configuration (.txt) file. - TradeCraftConfigurationInfo itemInfo = plugin.configuration.get(itemName); - if ( itemInfo == null ) { - plugin.trace(player, "sign change, %s is not a valid item name, ignore this sign", itemName); - return; - } - - TradeCraftExchangeRate buyRate = new TradeCraftExchangeRate(event.getLine(1)); - TradeCraftExchangeRate sellRate = new TradeCraftExchangeRate(event.getLine(2)); - // no buy rate means this is an infinite shop - if ( !buyRate.isValid() && !sellRate.isValid() ) { - if (plugin.permissions.canMakeInfShops(player)){ - plugin.trace(player, "sign change, infinite chest of %s", itemName); - return; - } - - if (EventPriority.NORMAL == priority) { - plugin.sendMessage(player, TradeCraftLocalization.get("YOU_CANT_CREATE_INF_SHOPS")); - event.setCancelled(true); - return; - } - } - // there is a buy rate, so this is a player owned shop - - if (EventPriority.NORMAL == priority && !plugin.permissions.canMakePlayerShops(player)){ - plugin.sendMessage(player, TradeCraftLocalization.get("YOU_DONT_HAVE_PERM_CREATE_PLAYER_SHOP")); - event.setCancelled(true); - return; - } - - // check whether the player doesn't have too many shops - if (EventPriority.NORMAL == priority) { - int totalShopLimit = TradeCraft.properties.getPlayerTotalShopLimit(); - int worldShopLimit = TradeCraft.properties.getPlayerWorldShopLimit(); - if (plugin.data.getPlayerShopCount(player) >= totalShopLimit) { - plugin.sendMessage(player, TradeCraftLocalization.get("TOTAL_SHOP_LIMIT_X"), totalShopLimit); - event.setCancelled(true); - return; - } else if (plugin.data.getPlayerShopCount(player, player.getWorld()) >= worldShopLimit) { - plugin.sendMessage(player, TradeCraftLocalization.get("WORLD_SHOP_LIMIT_X"), worldShopLimit); - event.setCancelled(true); - return; - } - - } - - if (EventPriority.MONITOR == priority && !event.isCancelled()) { - plugin.trace(player, "Setting owner of sign to: %s", ownerName); - // set the player name on the last line - event.setLine(3, "-"+ ownerName.substring(0, Math.min(ownerName.length(), 15)) +"-"); - plugin.data.createNewSign(ownerName, itemInfo, sign); - } - - /* - if ( this.plugin.properties.getStrictPlayerShopOwnerNameRequired() ) { - if (player.getName().equalsIgnoreCase(ownerName)) { - plugin.data.setOwnerOfSign(player.getName(), sign); - return; - } - } else { - if (player.getName().startsWith(ownerName)) { - plugin.data.setOwnerOfSign(player.getName(), sign); - return; - } - } - */ - } - - // prevent pistons from pushing away (the block behind) a sign. Block all retract and extend events that would move a block behind a sign, also for shop owners - @EventHandler - public void onNormalBlockPistonRetract(BlockPistonRetractEvent e) { - if (e.isSticky()) { - Block block = e.getRetractLocation().getBlock(); - ArrayList shops = plugin.getShopsFromBlock(null, block); - - if (shops.size() != 0) { - e.setCancelled(true); - } - } - } - @EventHandler - public void onNormalBlockPistonExtend(BlockPistonExtendEvent e) { - List blocks = e.getBlocks(); - for (Block block:blocks) { - ArrayList shops = plugin.getShopsFromBlock(null, block); - - if (shops.size() != 0) { - e.setCancelled(true); - return; - } - } - } - - public void stopDestruction(Block b, BlockBreakEvent e){ - if(b.getState() instanceof Sign){ - Sign sign = (Sign)b.getState(); - String[] lines = sign.getLines(); - for(int i = 0;i<4;i++){ - sign.setLine(i, lines[i]); - } - - sign.update(true); - } - e.setCancelled(true); - } -} diff --git a/nl/armeagle/TradeCraft/TradeCraftConfigurationFile.java b/nl/armeagle/TradeCraft/TradeCraftConfigurationFile.java deleted file mode 100644 index 94dff0b..0000000 --- a/nl/armeagle/TradeCraft/TradeCraftConfigurationFile.java +++ /dev/null @@ -1,199 +0,0 @@ -package nl.armeagle.TradeCraft; - -import java.io.BufferedReader; -import java.io.File; -import java.io.FileReader; -import java.io.IOException; -import java.util.Arrays; -import java.util.HashMap; -import java.util.Iterator; -import java.util.Map; -import java.util.Map.Entry; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -import org.bukkit.configuration.MemorySection; - -import nl.armeagle.Configuration.StatefulYamlConfiguration; - -/** - * The name of this class is a bit misleading. This class stores all the items and their default - * trade rates that can be used in the game. The actual configuration of the plugin itself - * is handled by the TradeCraftProperties class. - */ -class TradeCraftConfigurationFile { - private static final String fileName = TradeCraft.pluginName + ".txt"; - private static final String configName = "items"; - - private static final Pattern commentPattern = Pattern.compile("^\\s*#.*$"); - private static final Pattern infoPattern = Pattern.compile( - "^\\s*([^,]+)\\s*," + // name - "\\s*(\\d+(?:;\\d+)?)\\s*" + // id[;data] (optional ;data) - "(?:,\\s*(\\d+)\\s*:\\s*(\\d+))?\\s*" + // buyAmount:buyValue - "(?:,\\s*(\\d+)\\s*:\\s*(\\d+))?\\s*$"); // sellAmount:sellValue - - private HashMap mapItemNames = new HashMap(); - - private final TradeCraft plugin; - // create an index from an TradeCraftItem (id;data) to the TradeCraftConfigurationInfo entry. This for configured name lookup based on id;data - private final Map TCitemInfoIndex = new HashMap(); - - TradeCraftConfigurationFile(TradeCraft plugin) { - this.plugin = plugin; - } - - public StatefulYamlConfiguration getConfig() { - return this.plugin.getConfig(TradeCraftConfigurationFile.configName); - } - - void load() { - StatefulYamlConfiguration config = (StatefulYamlConfiguration) this.getConfig(); - - // if file exists, load the config to it once and then rename the old config - File file = new File(plugin.getDataFolder().getAbsolutePath(), fileName); - if ( file.exists() ) { - try { - FileReader reader = new FileReader(file); - BufferedReader configurationFile = new BufferedReader(reader); - - int lineNumber = 0; - String line; - - while ((line = configurationFile.readLine()) != null) { - lineNumber += 1; - - if (line.trim().equals("")) { - continue; - } - - Matcher commentMatcher = commentPattern.matcher(line); - - if (commentMatcher.matches()) { - continue; - } - - Matcher infoMatcher = infoPattern.matcher(line); - - if (!infoMatcher.matches()) { - plugin.log.warning( - "Failed to parse line number " + lineNumber + - " in " + file.getAbsolutePath() + - ": " + line); - continue; - } - - TradeCraftConfigurationInfo info = new TradeCraftConfigurationInfo(); - info.name = infoMatcher.group(1); - - // try to split ID and Data, separated by a semicolon mark - Matcher IdSplitData = TradeCraft.itemPatternIdSplitData.matcher(infoMatcher.group(2)); - - if (!IdSplitData.matches()) { - plugin.log.info( - "Failed to parse line number " + lineNumber + - " in " + file.getAbsolutePath() + - ": " + line); - continue; - } - - int id = Integer.parseInt(IdSplitData.group(1)); - if ( IdSplitData.group(2) != null ) { - short data = Short.parseShort(IdSplitData.group(2)); - info.type = new TradeCraftItem(id, data); - } else { - info.type = new TradeCraftItem(id); - } - - if (infoMatcher.group(3) != null) { - info.sellAmount = info.buyAmount = Integer.parseInt(infoMatcher.group(3)); - info.sellValue = info.buyValue = Integer.parseInt(infoMatcher.group(4)); - } - - if (infoMatcher.group(5) != null) { - info.sellAmount = Integer.parseInt(infoMatcher.group(5)); - info.sellValue = Integer.parseInt(infoMatcher.group(6)); - } - -// config.set(info.name.toUpperCase(), info.toMemoryConfiguration()); - Iterator> iter = info.toMap().entrySet().iterator(); - while (iter.hasNext()) { - Map.Entry pairs = (Map.Entry)iter.next(); - if (!pairs.getKey().equals("name")) { - config.set(info.name +"."+ pairs.getKey(), pairs.getValue()); - } - } - -// TCitemInfoIndex.put(info.type, info.name); - } - configurationFile.close(); - reader.close(); - config.save(); - if (file.renameTo(new File(file.getAbsolutePath() + ".converted.to."+ TradeCraftConfigurationFile.configName +".yml"))) { - plugin.log.info("Converted old config to new style and renamed the old config file"); - } else { - plugin.log.severe("FAILED to convert old config to new style"); - } - - } catch (IOException e) { - plugin.log.severe("Error reading " + file.getAbsolutePath()); - } - } else { - try { - config.load(); - } catch (IOException e) { - plugin.log.severe("Error loading plugin config file"); - } - } - - Iterator> iter = config.getValues(false).entrySet().iterator(); - while (iter.hasNext()) { - // store map of lowercase item names to key names in the configuration - Map.Entry entry = (Map.Entry)iter.next(); - this.mapItemNames.put(entry.getKey().toLowerCase(), entry.getKey()); - // store map of item types to key names - MemorySection section = (MemorySection) entry.getValue(); - TradeCraftItem tcItem = new TradeCraftItem(section.getInt("itemTypeId", 266), section.getInt("itemTypeData", 0)); - TCitemInfoIndex.put(tcItem, entry.getKey()); - } - } - - public String[] getNames() { - String[] names = this.getConfig().getKeys(false).toArray(new String[0]); - Arrays.sort(names); - return names; - } - - public boolean isConfigured(String name) { - return this.mapItemNames.containsKey(name.toLowerCase()); - } - - public TradeCraftConfigurationInfo get(String name) { - - // @todo, config code doesn't seem to like serialized objects, cannot seem to find the TradeCraftConfigurationInfo class - // though, the otherwise resulting ==: classpath lines aren't very user friendly anyway. -// return (TradeCraftConfigurationInfo) plugin.getConfig().get(name.toUpperCase()); - String itemName = this.mapItemNames.get(name.toLowerCase()); - if (null != itemName) { - MemorySection item = ((MemorySection)this.getConfig().get(itemName)); - if (null != item) { - Map itemData = item.getValues(false); - if (null != itemData) { - return new TradeCraftConfigurationInfo(itemData, name); - } - } - } - return null; - } - public TradeCraftConfigurationInfo get(int id) { - return this.get(new TradeCraftItem(id)); - } - public TradeCraftConfigurationInfo get(int id, short data) { - return this.get(new TradeCraftItem(id, data)); - } - public TradeCraftConfigurationInfo get(TradeCraftItem item) { - if (!TCitemInfoIndex.containsKey(item)) { - return null; - } - return this.get(TCitemInfoIndex.get(item)); - } -} \ No newline at end of file diff --git a/nl/armeagle/TradeCraft/TradeCraftConfigurationInfo.java b/nl/armeagle/TradeCraft/TradeCraftConfigurationInfo.java deleted file mode 100644 index 671a524..0000000 --- a/nl/armeagle/TradeCraft/TradeCraftConfigurationInfo.java +++ /dev/null @@ -1,55 +0,0 @@ -package nl.armeagle.TradeCraft; - -import java.util.LinkedHashMap; -import java.util.Map; -import org.bukkit.configuration.serialization.ConfigurationSerializable; -import org.bukkit.configuration.serialization.SerializableAs; - -@SerializableAs("nl.armeagle.TradeCraft.TradeCraftConfigurationInfo") -class TradeCraftConfigurationInfo implements ConfigurationSerializable { - public String name; - public TradeCraftItem type; - public int buyAmount; - public int buyValue; - public int sellAmount; - public int sellValue; - - TradeCraftConfigurationInfo() { - } - - TradeCraftConfigurationInfo(Map map, String name) { - this.name = name; - this.type = new TradeCraftItem((Integer) map.get("itemTypeId"), (Integer) map.get("itemTypeData")); - this.buyAmount = (Integer) map.get("buyAmount"); - this.buyValue = (Integer) map.get("buyValue"); - this.sellAmount = (Integer) map.get("sellAmount"); - this.sellValue = (Integer) map.get("sellValue"); - } - TradeCraftConfigurationInfo(Map map) { - this.name = (String) map.get("name"); - this.type = new TradeCraftItem((Integer) map.get("itemTypeId"), (Integer) map.get("itemTypeData")); - this.buyAmount = (Integer) map.get("buyAmount"); - this.buyValue = (Integer) map.get("buyValue"); - this.sellAmount = (Integer) map.get("sellAmount"); - this.sellValue = (Integer) map.get("sellValue"); - } - - public Map toMap() { - LinkedHashMap map = new LinkedHashMap(); - map.put("name", this.name); - map.put("itemTypeId", this.type.id); - map.put("itemTypeData", this.type.data); - map.put("buyAmount", this.buyAmount); - map.put("buyValue", this.buyValue); - map.put("sellAmount", this.sellAmount); - map.put("sellValue", this.sellValue); - return map; - } - @Override - public Map serialize() { - return this.toMap(); - } - public static TradeCraftConfigurationInfo deserialize(Map map) { - return new TradeCraftConfigurationInfo(map); - } -} \ No newline at end of file diff --git a/nl/armeagle/TradeCraft/TradeCraftItem.java b/nl/armeagle/TradeCraft/TradeCraftItem.java deleted file mode 100644 index d8155fa..0000000 --- a/nl/armeagle/TradeCraft/TradeCraftItem.java +++ /dev/null @@ -1,63 +0,0 @@ -package nl.armeagle.TradeCraft; - -/** - * - * @author ArmEagle - * Store item type(ID) and optionally the data bit - */ -public class TradeCraftItem implements Comparable { - public int id; - public short data; - - TradeCraftItem(int id) { - this(id, (short)0); - } - TradeCraftItem(int id, int data) { - this(id, new Integer(data).shortValue()); - } - TradeCraftItem(int id, short data) { - this.id = id; - this.data = data; - } - - /** - * @param compare - * @throws NullPointerException if compare is null - * @return default < 0 > compare values - */ - @Override public int compareTo(TradeCraftItem compare) { - if ( this == compare ) { - return 0; - } - if ( this.id < compare.id ) { - return -1; - } else if ( this.id > compare.id ) { - return 1; - } else { - if ( this.data < compare.data ) { - return -1; - } else if ( this.data > compare.data ) { - return 1; - } else { - return 0; - } - } - } - @Override public boolean equals(Object compare) { - return (compare == null ? false : (compare instanceof TradeCraftItem? this.compareTo((TradeCraftItem)compare) == 0 : false)); - } - @Override public int hashCode() { - return this.id * 32768 + this.data; - } - - @Override public String toString() { - return "TradeCraftItem("+ this.id +";"+ this.data +")"; - } - public String toShortString() { - if ( this.data == 0 ) { - return String.valueOf(this.id); - } else { - return this.id +";"+ this.data; - } - } -} diff --git a/nl/armeagle/TradeCraft/TradeCraftPermissions.java b/nl/armeagle/TradeCraft/TradeCraftPermissions.java deleted file mode 100644 index ddf29f8..0000000 --- a/nl/armeagle/TradeCraft/TradeCraftPermissions.java +++ /dev/null @@ -1,121 +0,0 @@ -package nl.armeagle.TradeCraft; - -import org.bukkit.command.CommandSender; -import org.bukkit.entity.Player; -import org.bukkit.plugin.Plugin; - -import com.nijiko.permissions.PermissionHandler; -import com.nijikokun.bukkit.Permissions.Permissions; - -public class TradeCraftPermissions { - - PermissionHandler permHandler = null; - TradeCraft plugin; - - TradeCraftPermissions(TradeCraft plugin) { - this.plugin = plugin; - } - - public void setupPermissions() { - Plugin test = plugin.getServer().getPluginManager().getPlugin("Permissions"); - - if (permHandler == null) { - if (test != null) { - this.permHandler = ((Permissions)test).getHandler(); - plugin.permEnabled = true; - plugin.log.info("[TradeCraft] has recognized Permissions"); - } - } - } - - public boolean canBuy(Player p) { - if ( plugin.permEnabled ) { - return permHandler.has(p, "TradeCraft.canBuy"); - } else { - return p.hasPermission("TradeCraft.canBuy"); - } - } - - public boolean canSell(Player p) { - if ( plugin.permEnabled ) { - return permHandler.has(p, "TradeCraft.canSell"); - } else { - return p.hasPermission("TradeCraft.canSell"); - } - } - - public boolean canMakeInfShops(Player p) { - if ( plugin.permEnabled ) { - return permHandler.has(p, "TradeCraft.canMakeInfShops"); - } else { - return p.hasPermission("TradeCraft.canMakeInfShops"); - } - } - - public boolean canMakePlayerShops(Player p) { - if ( plugin.permEnabled ) { - return permHandler.has(p, "TradeCraft.canMakePlayerShops"); - } else { - return p.hasPermission("TradeCraft.canMakePlayerShops"); - } - } - - public boolean canDestroyShops(Player p) { - if ( plugin.permEnabled ) { - return permHandler.has(p, "TradeCraft.canDestroyShops"); - } else { - return p.hasPermission("TradeCraft.canDestroyShops"); - } - } - - public boolean canSetCurrency(Player p) { - if ( plugin.permEnabled ) { - return permHandler.has(p, "TradeCraft.canSetCurrency"); - } else { - return p.hasPermission("TradeCraft.canSetCurrency"); - } - } - - public boolean canReload(Player p) { - if ( plugin.permEnabled ) { - return permHandler.has(p, "TradeCraft.canReload"); - } else { - return p.hasPermission("TradeCraft.canReload"); - } - } - - public boolean canQueryOtherShops(Player p) { - if ( plugin.permEnabled ) { - return permHandler.has(p, "TradeCraft.canQueryOtherShops"); - } else { - return p.hasPermission("TradeCraft.canQueryOtherShops"); - } - } - - public boolean canQueryPlayer(Player p) { - if ( plugin.permEnabled ) { - return permHandler.has(p, "TradeCraft.canQueryPlayer"); - } else { - return p.hasPermission("TradeCraft.canQueryPlayer"); - } - } - - public void debug(CommandSender sender, String n){ - Player p = plugin.getServer().getPlayer(n); - if(p == null){ - plugin.getServer().broadcastMessage("/tc canPlayer used with a name of player who is not online."); - return; - } - String name = p.getName(); - sender.sendMessage("" + name + " has:"); - sender.sendMessage("canBuy " + canBuy(p)); - sender.sendMessage("canSell " + canSell(p)); - sender.sendMessage("canMakeInf " + canMakeInfShops(p)); - sender.sendMessage("canMakePersonal " + canMakePlayerShops(p)); - sender.sendMessage("canDestroy " + canDestroyShops(p)); - sender.sendMessage("canSetCurrency " + canSetCurrency(p)); - sender.sendMessage("canReload " + canReload(p)); - sender.sendMessage("canQueryOtherShops " + canQueryOtherShops(p)); - } - -} diff --git a/nl/armeagle/TradeCraft/TradeCraftPropertiesFile.java b/nl/armeagle/TradeCraft/TradeCraftPropertiesFile.java deleted file mode 100644 index de95254..0000000 --- a/nl/armeagle/TradeCraft/TradeCraftPropertiesFile.java +++ /dev/null @@ -1,153 +0,0 @@ -package nl.armeagle.TradeCraft; - -import java.io.File; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.util.logging.Level; - -import org.bukkit.ChatColor; -import org.bukkit.configuration.InvalidConfigurationException; -import org.bukkit.configuration.file.YamlConfiguration; - -/** - * This class, handles the actual configuration of the plugin. The TradeCraftConfiguration class - * actually handles all the items that can be used by the shops in the game. - */ -public class TradeCraftPropertiesFile { - private static final String fileName = TradeCraft.pluginName + ".properties"; - private static final String filePath = "plugins" + File.separator + TradeCraft.pluginName; - public static final String defaultLanguage = "en"; - - private TradeCraft plugin; - private final YamlConfiguration properties; - - public TradeCraftPropertiesFile(TradeCraft plugin) { - this.plugin = plugin; - // make folder in the plugins dir if it doesn't exist yet - File path = new File(filePath); - if ( !path.exists() ) { - path.mkdirs(); - } - path = null; - - // if file does not exist in this directory, copy it from the jar - File file = new File(filePath + File.separator + fileName); - if ( !file.exists() ) { - this.plugin.log.info(filePath + File.separator + fileName +" does not exist, creating..."); - InputStream input = this.getClass().getResourceAsStream("/" + fileName); - if ( input != null ) { - FileOutputStream output = null; - - try { - output = new FileOutputStream(file); - byte[] buf = new byte[8192]; - int length = 0; - while ((length = input.read(buf)) > 0) { - output.write(buf, 0, length); - } - } catch (IOException e) { - e.printStackTrace(); - } finally { - try { - if (input != null) { - input.close(); - } - } catch (IOException e) {} - - try { - if (output != null) { - output.close(); - } - } catch (IOException e) {} - } - } - } - - properties = new YamlConfiguration(); - try { - properties.load(file); - } catch (InvalidConfigurationException e) { - plugin.log(Level.SEVERE, "Failed to load file: %s", file.toURI()); - } catch (IOException e) { - plugin.log(Level.SEVERE, "Failed to read file: %s", file.toURI()); - } - } - - protected void save() { - File file = new File(filePath + File.separator + fileName); - try { - properties.save(file); - } catch (IOException e) { - this.plugin.log(Level.SEVERE, "Error saving to file: %s", file.toURI()); - } - } - - public TradeCraftItem getCurrencyType(){ - int id = properties.getInt("currency-id",266); - short data = (short)properties.getInt("currency-data",0); - return new TradeCraftItem(id, data); - } - public void setCurrencyType(TradeCraftItem item) { - properties.set("currency-id", item.id); - properties.set("currency-data", item.data); - this.save(); - } - public boolean getNormalStackSizeUsed(){ - return properties.getBoolean("normal-stack-size", true); - } - - public boolean getInfiniteShopsEnabled() { - return properties.getBoolean("infinite-shops-enabled", true); - } - - public boolean getPlayerOwnedShopsEnabled() { - return properties.getBoolean("player-owned-shops-enabled", true); - } - - public boolean getRepairShopsEnabled() { - return properties.getBoolean("repair-shops-enabled", false); - } - - public int getRepairCost() { - return properties.getInt("repair-cost", 0); - } - - public boolean getEnableDebugMessages() { - return properties.getBoolean("enable-debug-messages", false); - } - - public String getLanguage() { - return properties.getString("language", TradeCraftPropertiesFile.defaultLanguage); - } - - public boolean autoUpdateLanguageFiles() { - return properties.getBoolean("auto-update-language-files", true); - } - - public boolean logShopUse() { - return properties.getBoolean("log-shop-use", false); - } - - public boolean showShopLocation() { - return properties.getBoolean("show-shop-location", false); - } - - public int getPlayerWorldShopLimit() { - return properties.getInt("player-world-shop-limit", 5); - } - public int getPlayerTotalShopLimit() { - return properties.getInt("player-total-shop-limit", 10); - } - - public ChatColor getMessageTypeColor(TradeCraft.MessageTypes mtype) { - switch (mtype) { - case WITHDRAW: - return ChatColor.YELLOW; - case DEPOSIT: - return ChatColor.GRAY; - default: - return ChatColor.WHITE; - } - } -} diff --git a/README.txt b/src/README.txt similarity index 100% rename from README.txt rename to src/README.txt diff --git a/TODO.txt b/src/TODO.txt similarity index 100% rename from TODO.txt rename to src/TODO.txt diff --git a/TradeCraft.en.lang b/src/TradeCraft.en.lang similarity index 100% rename from TradeCraft.en.lang rename to src/TradeCraft.en.lang diff --git a/TradeCraft.ge.lang b/src/TradeCraft.ge.lang similarity index 100% rename from TradeCraft.ge.lang rename to src/TradeCraft.ge.lang diff --git a/TradeCraft.properties b/src/TradeCraft.properties similarity index 100% rename from TradeCraft.properties rename to src/TradeCraft.properties diff --git a/TradeCraft.sv.lang b/src/TradeCraft.sv.lang similarity index 100% rename from TradeCraft.sv.lang rename to src/TradeCraft.sv.lang diff --git a/TradeCraft.txt b/src/TradeCraft.txt similarity index 100% rename from TradeCraft.txt rename to src/TradeCraft.txt diff --git a/items.yml b/src/items.yml similarity index 100% rename from items.yml rename to src/items.yml diff --git a/src/nl/armeagle/Configuration/StatefulYamlConfiguration.java b/src/nl/armeagle/Configuration/StatefulYamlConfiguration.java new file mode 100644 index 0000000..4e38c35 --- /dev/null +++ b/src/nl/armeagle/Configuration/StatefulYamlConfiguration.java @@ -0,0 +1,79 @@ +package nl.armeagle.Configuration; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStream; +import java.util.logging.Level; +import java.util.logging.Logger; + +import org.bukkit.Bukkit; +import org.bukkit.configuration.InvalidConfigurationException; +import org.bukkit.configuration.file.YamlConfiguration; +import org.bukkit.plugin.java.JavaPlugin; +import org.yaml.snakeyaml.error.YAMLException; + +public class StatefulYamlConfiguration extends YamlConfiguration { + protected File file; + + public StatefulYamlConfiguration(File file) { + this.file = file; + } + + public void load() throws IOException { + this.load(false); + } + public void load(boolean forceDefaults) throws IOException { + boolean notLoaded = false; + if (this.file == null) { + throw new IllegalArgumentException("File cannot be null"); + } + + YamlConfiguration baseConfig = new YamlConfiguration(); + + try { + baseConfig.load(this.file); + } catch (FileNotFoundException fnf) { + notLoaded = true; + } catch (IOException ex) { + Bukkit.getLogger().log(Level.SEVERE, "Cannot load " + this.file, ex); + } catch (InvalidConfigurationException ex) { + if (ex.getCause() instanceof YAMLException) { + Bukkit.getLogger().severe("Config file " + this.file + " isn't valid! " + ex.getCause()); + } else if ((ex.getCause() == null) || (ex.getCause() instanceof ClassCastException)) { + Bukkit.getLogger().severe("Config file " + this.file + " isn't valid!"); + } else { + Bukkit.getLogger().log(Level.SEVERE, "Cannot load " + this.file + ": " + ex.getCause().getClass(), ex); + } + } + + if (notLoaded || forceDefaults) { + InputStream defaultInput = this.getClass().getResourceAsStream("/" + file.getName()); + if ( null != defaultInput ) { + YamlConfiguration defaultConfig = YamlConfiguration.loadConfiguration(defaultInput); + + try { + this.loadFromString(defaultConfig.saveToString()); + } catch (InvalidConfigurationException e) { + Logger.getLogger(JavaPlugin.class.getName()).log(Level.SEVERE, "Invalid default configuation"); + } + this.save(); + } + } else if (! notLoaded) { + try { + this.loadFromString(baseConfig.saveToString()); + } catch (InvalidConfigurationException e) { + Logger.getLogger(JavaPlugin.class.getName()).log(Level.SEVERE, "Invalid configuation"); + } + } + + } + + public void save() throws IOException { + super.save(this.file); + } + + public File getFile() { + return this.file; + } +} diff --git a/src/nl/armeagle/TradeCraft/TradeCraft.java b/src/nl/armeagle/TradeCraft/TradeCraft.java new file mode 100644 index 0000000..7e00048 --- /dev/null +++ b/src/nl/armeagle/TradeCraft/TradeCraft.java @@ -0,0 +1,651 @@ +package nl.armeagle.TradeCraft; + +import java.io.*; + +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Date; +import java.util.HashMap; +import java.util.IllegalFormatException; +import java.util.Map; +import java.util.logging.Level; +import java.util.logging.Logger; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import nl.armeagle.Configuration.StatefulYamlConfiguration; + +import org.bukkit.ChatColor; +import org.bukkit.Material; +import org.bukkit.Server; +import org.bukkit.block.Block; +import org.bukkit.block.BlockFace; +import org.bukkit.block.Chest; +import org.bukkit.block.Sign; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.command.ConsoleCommandSender; +import org.bukkit.entity.Player; +import org.bukkit.inventory.ItemStack; +import org.bukkit.material.MaterialData; +import org.bukkit.plugin.PluginDescriptionFile; +import org.bukkit.plugin.PluginManager; +import org.bukkit.plugin.java.JavaPlugin; + +public class TradeCraft extends JavaPlugin { + public static enum MessageTypes {WITHDRAW, DEPOSIT}; + + // The plugin name. + static final String pluginName = "TradeCraft"; + + public static final Pattern itemPatternIdSplitData = Pattern.compile("^(\\d+)(?:;(\\d+))?$"); + + private static enum Commands { + tcsetcurrency, + tcgetcurrency, + tcshops, + tcpshops, + tcreload, + tcplayerperms, + tchelp, + tc + }; + + // Stuff used to interact with the server. + final Logger log = Logger.getLogger("Minecraft"); + final Server server = this.getServer(); + + protected BufferedWriter usageLog = null; + // Objects used by the plugin. + static TradeCraftItem currency; + static TradeCraftPropertiesFile properties; + TradeCraftConfigurationFile configuration; + public TradeCraftLocalization localization; + TradeCraftDataFile data; + + private HashMap configs = new HashMap(); + + private final TradeCraftBlockListener blockListener = new TradeCraftBlockListener(this); + private final TradeCraftPlayerListener playerListener = new TradeCraftPlayerListener(this); + public TradeCraftPermissions permissions = new TradeCraftPermissions(this); + + // prevent the script from registering the event listeners multiple times (by dis-/enable) + public static boolean hasRegisteredEventListeners = false; + + public void onDisable() { + this.disable(); + } + private void disable() { + if ( this.usageLog != null ) { + try { + this.usageLog.close(); + } catch (IOException e) { + this.log(Level.WARNING, "Failed to close shop usage log file"); + } + } + properties = null; + configuration = null; + this.localization = null; + data.save(); + data = null; + } + + public void onEnable() { + this.enable(); + } + private void enable() { + properties = new TradeCraftPropertiesFile(this); + configuration = new TradeCraftConfigurationFile(this); + data = new TradeCraftDataFile(this); + this.localization = new TradeCraftLocalization(this); + + if ( TradeCraft.properties.logShopUse() ) { + File usageLogFile = new File(this.getDataFolder(), "shopUsage.log"); + try { + if ( !usageLogFile.exists() ) { + usageLogFile.createNewFile(); + } + if ( usageLogFile.canWrite() ) { + this.usageLog = new BufferedWriter(new FileWriter(usageLogFile, true)); + this.log(Level.INFO, "Writing shop usage to log file: "+ usageLogFile.toString()); + } else { + this.log(Level.WARNING, "Error opening shop usage log file: "+ usageLogFile.toString()); + } + } catch (IOException e) { + this.log(Level.WARNING, "Failed to open shop usage log file: "+ usageLogFile.toString()); + } + } + + configuration.load(); + data.load(); + currency = properties.getCurrencyType(); + + if ( !TradeCraft.hasRegisteredEventListeners ) { + PluginManager pm = this.getServer().getPluginManager(); + pm.registerEvents(playerListener, this); + pm.registerEvents(blockListener, this); + TradeCraft.hasRegisteredEventListeners = true; + } + + PluginDescriptionFile pdfFile = this.getDescription(); + this.log.info(pdfFile.getName() + " version " + pdfFile.getVersion() + " is enabled!"); + + } + + @Override + public boolean onCommand(CommandSender sender, Command cmd, String label, String[] args) { + try { + TradeCraft.Commands command = TradeCraft.Commands.valueOf(cmd.getName()); + if (sender instanceof Player) { + Player p = (Player) sender; + switch (command) { + case tcsetcurrency: + if ( args.length == 1 && this.permissions.canSetCurrency(p) ) { + TradeCraftItem testCurrency = null; + // try to split ID and Data, separated by a semicolon mark + Matcher IdSplitData = TradeCraft.itemPatternIdSplitData.matcher(args[0]); + + if ( !IdSplitData.matches() ) { + // try to match the parameter to item names from the configuration + TradeCraftConfigurationInfo setCurr = this.configuration.get(args[0]); + if ( setCurr == null ) { + this.sendMessage(p, TradeCraftLocalization.get("IS_NO_VALID_CURRENCY_USE_INSTEAD"), + args[0]); + return false; + } else { + currency = setCurr.type; + } + } else { + try { + int cid = Integer.parseInt(IdSplitData.group(1)); + if ( IdSplitData.group(2) != null ) { + testCurrency = new TradeCraftItem(cid, Short.parseShort(IdSplitData.group(2))); + } else { + testCurrency = new TradeCraftItem(cid); + } + } catch ( NumberFormatException e ) { + this.sendMessage(p, TradeCraftLocalization.get("INVALID_CURRENCY"), + args[0]); + return false; + } + if ( this.configuration.get(testCurrency) != null ) { + currency = testCurrency; + } else { + this.sendMessage(p, TradeCraftLocalization.get("INVALID_CURRENCY"), + args[0]); + return false; + } + } + + TradeCraft.properties.setCurrencyType(currency); + this.sendMessage(p, TradeCraftLocalization.get("CURRENCY_IS_SET_TO_A_IDDATA"), + this.getCurrencyName(), + currency.toShortString()); + return true; + } + return true; + case tcgetcurrency: + this.sendMessage(p, TradeCraftLocalization.get("CURRENCY_IS_A_IDDATA"), + this.getCurrencyName(), + currency.toShortString()); + return true; + case tcshops: + // lookup own shows + displayShops(p.getName(), p, false); + return true; + case tcpshops: + // Check whether another parameter is passed, if so check whether + // the player can get information about other player's shops. + if ( this.permissions.canQueryOtherShops(p) ) { + if ( args.length == 1 ) { + // lookup other player's shops + displayShops(args[0], p, true); + } else { + this.sendMessage(p, cmd.getUsage()); + } + } + return true; + case tcreload: + if ( this.permissions.canReload(p)) { + this.sendMessage(p, TradeCraftLocalization.get("RESTARTING_PLUGIN"), TradeCraft.pluginName); + this.disable(); + this.enable(); + this.sendMessage(p, TradeCraftLocalization.get("RESTARTING_PLUGIN_DONE"), TradeCraft.pluginName); + } + return true; + case tcplayerperms: + if ( this.permissions.canQueryPlayer(p)) { + if ( args.length == 1 ) { + permissions.debug(sender, args[0]); + } else { + sender.sendMessage(cmd.getUsage()); + } + } + return true; + default: + displayCommandHelpText(p); + return true; + } + } else if ( sender instanceof ConsoleCommandSender ) { + switch (command) { + case tcplayerperms: + if ( args.length == 1 ) { + permissions.debug(sender, args[0]); + } else { + sender.sendMessage(cmd.getUsage()); + } + return true; + case tcpshops: + // Check whether another parameter is passed, if so check whether + // the player can get information about other player's shops. + if ( args.length == 1 ) { + // lookup other player's shops + displayShops(args[0], sender, true); + } else { + sender.sendMessage(cmd.getUsage()); + } + return true; + case tcreload: + sender.sendMessage(String.format(TradeCraftLocalization.get("RESTARTING_PLUGIN"), + TradeCraft.pluginName)); + this.disable(); + this.enable(); + sender.sendMessage(String.format(TradeCraftLocalization.get("RESTARTING_PLUGIN_DONE"), + TradeCraft.pluginName)); + return true; + default: + displayCommandHelpText(null); + return true; + } + } + } catch (IllegalArgumentException e) { + return false; + } + return false; + } + + void displayShops(String infoPlayerName, CommandSender displayTo, boolean otherQuery) { + Map list = data.shopsOwned(infoPlayerName); + if (list.size() == 0) { + if ( otherQuery ) { + // elevated player looking for other player's shops + displayTo.sendMessage(String.format(TradeCraftLocalization.get("A_DOES_NOT_OWN_ANY_SHOPS"), + infoPlayerName)); + } else { + displayTo.sendMessage(TradeCraftLocalization.get("YOU_DONT_OWN_ANY_SHOPS")); + } + return; + } + + if ( otherQuery ) { + displayTo.sendMessage(String.format(TradeCraftLocalization.get("SHOPS_OF_A"), + infoPlayerName)); + } else { + displayTo.sendMessage(TradeCraftLocalization.get("YOUR_SHOPS")); + } + for (Map.Entry entry : list.entrySet()) { + TradeCraftDataInfo info = entry.getValue(); + String message = ""; + if (TradeCraft.properties.showShopLocation()) { + String location = entry.getKey().replaceFirst(",", "(") +")"; + message += ChatColor.GRAY + location +" "+ ChatColor.WHITE; + } + message += TradeCraftLocalization.get("ITEM") +": "+ this.configuration.get(info.itemType).name +"("+ info.itemType.toShortString() +")" + +" "+ TradeCraftLocalization.get("AMOUNT") +": "+ info.itemAmount +" " + + this.getCurrencyName() +": "+ info.currencyAmount; + + displayTo.sendMessage(message); + } + + } + + void sendMessage(Player player, TradeCraft.MessageTypes messageType, String format, Object... args) { + try { + String message = String.format(format, args); + player.sendMessage(TradeCraft.properties.getMessageTypeColor(messageType) + message); + } catch ( IllegalFormatException e ) { + player.sendMessage(TradeCraftLocalization.get("ERROR_IN_FORMAT_STRING") +" "+ format); + } + } + + void sendMessage(Player player, String format, Object... args) { + try { + String message = String.format(format, args); + player.sendMessage(message); + } catch ( IllegalFormatException e ) { + player.sendMessage(TradeCraftLocalization.get("ERROR_IN_FORMAT_STRING") +" "+ format); + } + } + + void trace(Player player, String format, Object... args) { + if (properties.getEnableDebugMessages()) { + if (null != player) { + sendMessage(player, format, args); + } else { + this.log(Level.INFO, format, args); + } + } + } + /* + * When a block behind a shop sign is destroyed, the sign would be destroyed too. + * Check all side faces of this block, for a sign attached to this block. Then + * pass that sign block to getShopFromSignBlock. + * + * This should only be used for checking whether a normal block can be destroyed, for there not being any + * signs attached to it, or this block being a chest or sign itself. + * Since one block can + */ + ArrayList getShopsFromBlock(Player player, Block block) { + ArrayList shops = new ArrayList(); + // use other function(s) directly if applicable + if ( block.getType() == Material.CHEST || block.getType() == Material.WALL_SIGN ) { + TradeCraftShop shop = getShopFromSignOrChestBlock(player, block); + if ( shop != null ) { + shops.add(shop); + } + } else { + // go through all 4 faces of this block to check for a sign attached to this block + final BlockFace[] sides = {BlockFace.NORTH, BlockFace.EAST, BlockFace.SOUTH, BlockFace.WEST}; + for ( int index_sides = 0; index_sides < sides.length; index_sides++ ) { + BlockFace side = sides[index_sides]; + // get the block on that side + Block sideBlock = block.getRelative(side); + // check for it being a wall sign + if ( sideBlock.getType() == Material.WALL_SIGN ) { + // get the sign (extending MaterialData) for clean attached face checking + org.bukkit.material.Sign materialSign = new org.bukkit.material.Sign(Material.WALL_SIGN, sideBlock.getData()); + /* Now, for easy comparison, we'll compare to the direction the sign is facing. If that's + * the same as the current face (of the 4 we're looping through), then this sign is attached + * to the block that is being destroyed. + */ + if ( materialSign.getFacing() == side ) { + TradeCraftShop shop = this.getShopFromSignBlock(player, sideBlock); + if ( shop != null ) { + shops.add(shop); + } + } + + } + } + } + return shops; + } + TradeCraftShop getShopFromSignOrChestBlock(Player player, Block block) { + if (block.getType() == Material.CHEST) { + block = block.getWorld().getBlockAt( + block.getX(), + block.getY() + 1, + block.getZ() + ); + } + + return getShopFromSignBlock(player, block); + } + + TradeCraftShop getShopFromSignBlock(Player player, Block block) { + if (block.getType() != Material.WALL_SIGN) { + return null; + } + + int x = block.getX(); + int y = block.getY(); + int z = block.getZ(); + + trace(player, "You clicked a sign at %d, %d, %d in world: %s.", x, y, z, block.getWorld().getName()); + + Sign sign = (Sign) block.getWorld().getBlockAt(x, y, z).getState(); + + // The sign at this location can be null if it was just destroyed. + if (sign == null) { + trace(player, "The sign is no longer there."); + + return null; + } + + String itemName = getItemName(sign.getLines()); + + if (itemName == null) { + trace(player, "There is no item name on the sign."); + + return null; + } + + trace(player, "The item name on the sign is %s.", itemName); + + Block blockBelowSign = block.getRelative(0, -1, 0); + + if (blockBelowSign.getType() != Material.CHEST) { + trace(player, "There is no chest beneath the sign."); + return null; + } + + Chest chest = (Chest) blockBelowSign.getState(); + + if (itemName.toLowerCase().equals("repair")) { + if (!properties.getRepairShopsEnabled()) { + trace(player, "Repair shops are not enabled."); + return null; + } + + if (player == null || !player.isOp()) { + trace(player, "You can't use repair shops."); + return null; + } + + trace(player, "This is a repair shop."); + return new TradeCraftRepairShop(this, sign, chest); + } + + if (!configuration.isConfigured(itemName)) { + trace(player, + "The item name %s is not configured in the config file.", + itemName); + return null; + } + + // TODO change to use chest getOwner + //String ownerName = getOwnerName(sign.getLine(3)); + String ownerName = data.getOwnerOfSign(sign); + + if (ownerName == null) { + trace(player, "There is no owner name on the sign."); + + if (!properties.getInfiniteShopsEnabled()) { + trace(player, "Ininite shops are not enabled."); + return null; + } + + trace(player, "This is an infinite shop."); + try { + return new TradeCraftInfiniteShop(this, sign, chest); + } catch (Exception e) { + trace(player, e.getMessage()); + } + return null; + } + + trace(player, "The owner name on the sign is %s.", ownerName); + + if (!properties.getPlayerOwnedShopsEnabled()) { + trace(player, "Player-owned shops are not enabled."); + return null; + } + + trace(player, "This is a player-owned shop."); + return new TradeCraftPlayerOwnedShop(this, sign, chest, ownerName); + } + + String getItemName(String[] signLines) { + return getSpecialText(signLines, "[", "]"); + } + + String getOwnerName(String signLine) { + return getSpecialTextOnLine(signLine, "-", "-"); + } + + private String getSpecialText(String[] signLines, String prefix, String suffix) { + for (int i = 0; i < 4; i++) { + String text = getSpecialTextOnLine(signLines[i], prefix, suffix); + + if (text != null) { + return text; + } + } + + return null; + } + + private String getSpecialTextOnLine(String signLine, String prefix, String suffix) { + if (signLine == null) { + return null; + } + + signLine = signLine.trim(); + + if (signLine.startsWith(prefix) && signLine.endsWith(suffix) + && signLine.length() > 2) { + + String text = signLine.substring(1, signLine.length() - 1); + text = text.trim(); + if (text.equals("")) { + return null; + } + + return text; + } + + return null; + } + + TradeCraftExchangeRate getExchangeRate(String[] signLines, int lineNumber) { + if ( lineNumber < 0 || lineNumber >= signLines.length ) { + return null; + } + return getExchangeRate(signLines[lineNumber]); + } + TradeCraftExchangeRate getExchangeRate(String signLine) { + return new TradeCraftExchangeRate(signLine); } + + static int getMaxStackSize(int itemType) { + if ( TradeCraft.properties.getNormalStackSizeUsed() ) { + return Material.getMaterial(itemType).getMaxStackSize(); + } else { + return 64; + } + } + + /** + * Get a CamelCased string based on the current currency. + * + * @return a string representing the currency. + */ + public String getCurrencyName() { + // Try to get the name from the configuration file first + TradeCraftConfigurationInfo configInfo = this.configuration.get(TradeCraft.currency); + if ( configInfo != null ) { + return configInfo.name; + } else { + + ItemStack currencyStack = new ItemStack(TradeCraft.currency.id, 1, TradeCraft.currency.data); // weird that there's no Material.getMaterial(id, short/byte) + MaterialData currencyData = currencyStack.getType().getNewData((byte)TradeCraft.currency.data); + String currencyString; + if ( currencyData == null ) { + currencyString = currencyStack.getType().name(); + } else { + currencyString = currencyData.toString(); + } + + //String baseName = stack.getType().name(); + String[] words = currencyString.replace("null ", "").split("\\(")[0].split("[ _]{1}"); + String name = ""; + for ( int word_ind = 0; word_ind < words.length; word_ind++ ) { + String word = words[word_ind]; + if ( word_ind > 0 ) { + name = name.concat(" "); + } + name = name.concat(word.substring(0, 1).toUpperCase()).concat(word.substring(1).toLowerCase()); + } + return name; + } + } + + private void displayCommandHelpText(Player player) { + if ( player != null ) { + this.sendMessage(player, TradeCraftLocalization.get("POSSIBLE_COMMANDS_FOR_THE_PLUGIN"), + TradeCraft.pluginName); + this.sendMessage(player, "/tc[help]"+ ChatColor.GRAY +" "+ TradeCraftLocalization.get("TC_HELP_THIS_TEXT")); + this.sendMessage(player, "/tcshops"+ ChatColor.GRAY +" "+ TradeCraftLocalization.get("TC_SHOPS")); + if ( this.permissions.canQueryOtherShops(player) ) { + this.sendMessage(player, "/tcpshops [player]"+ ChatColor.GRAY +" "+ TradeCraftLocalization.get("TC_PSHOPS")); + } + this.sendMessage(player, "/tcgetcurrency"+ ChatColor.GRAY +" "+ TradeCraftLocalization.get("TC_CURRENCY_GET_CURRENCY")); + if ( this.permissions.canSetCurrency(player) ) { + this.sendMessage(player, "/tcsetcurrency [id[;data]]"+ ChatColor.GRAY +" "+ TradeCraftLocalization.get("TC_CURRENCY_OPT_PARAM_GETSET_CURRENCY")); + } + if ( this.permissions.canReload(player) ) { + this.sendMessage(player, "/tcreload"+ ChatColor.GRAY +" "+ TradeCraftLocalization.get("TC_RELOAD")); + } + if ( this.permissions.canQueryPlayer(player) ) { + this.sendMessage(player, "/tcplayerperms"+ ChatColor.GRAY +" "+ TradeCraftLocalization.get("TC_CAN_PLAYER")); + } + } else { + // console command help + this.log(Level.INFO, TradeCraftLocalization.get("POSSIBLE_COMMANDS_FOR_THE_PLUGIN"), + TradeCraft.pluginName); + this.log(Level.INFO, "tc[help]: "+ TradeCraftLocalization.get("TC_HELP_THIS_TEXT")); + this.log(Level.INFO, "tcplayerperms playername: "+ TradeCraftLocalization.get("TC_CAN_PLAYER")); + this.log(Level.INFO, "tcpshops [player]: "+ TradeCraftLocalization.get("TC_PSHOPS")); + this.log(Level.INFO, "tcreload: "+ TradeCraftLocalization.get("TC_RELOAD")); + } + } + + public void saveConfig() { + Logger.getLogger(JavaPlugin.class.getName()).log(Level.INFO, "saving config to: "+ this.getConfig("config").getFile().getAbsolutePath()); + try { + this.getConfig("config").save(); + } catch (IOException ex) { + Logger.getLogger(JavaPlugin.class.getName()).log(Level.SEVERE, "Could not save config to " + this.getConfig("config").getFile().getName(), ex); + } + } + public StatefulYamlConfiguration getConfig() { + return this.getConfig("config"); + } + public StatefulYamlConfiguration getConfig(String name) { + if (name.indexOf(".") < 0) { + name += ".yml"; + } + if (this.configs.containsKey(name)) { + return this.configs.get(name); + } else { + File configFile = new File(this.getDataFolder(), name); + StatefulYamlConfiguration config = new StatefulYamlConfiguration(configFile); + this.configs.put(name, config); + return config; + } + } + + public void log(Level level, String format, Object... args) { + this.log.log(level, TradeCraft.pluginName +": "+ String.format(format, args)); + } + + public void useLog(Player player, TradeCraftShop shop, String format, Object... args) { + if ( TradeCraft.properties.logShopUse() ) { + if ( this.usageLog != null ) { + try { + SimpleDateFormat formatter = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss"); + this.usageLog.write(String.format("%s %s \t%s %s\n", + formatter.format(new Date()), + shop.toString(), + player.getDisplayName(), + String.format(format, args))); + this.usageLog.flush(); + } catch (IOException e) { + this.log(Level.WARNING, "Failed to write to shop use log file"); + } + } else { + this.log(Level.INFO, "not written to log file"); + } + } + } + +} \ No newline at end of file diff --git a/src/nl/armeagle/TradeCraft/TradeCraftBlockListener.java b/src/nl/armeagle/TradeCraft/TradeCraftBlockListener.java new file mode 100644 index 0000000..10d8e3b --- /dev/null +++ b/src/nl/armeagle/TradeCraft/TradeCraftBlockListener.java @@ -0,0 +1,224 @@ +package nl.armeagle.TradeCraft; + +import java.util.ArrayList; +import java.util.List; + +import org.bukkit.Material; +import org.bukkit.block.Block; +import org.bukkit.block.Sign; +import org.bukkit.entity.Player; +import org.bukkit.event.block.BlockBreakEvent; +import org.bukkit.event.block.BlockPistonExtendEvent; +import org.bukkit.event.block.BlockPistonRetractEvent; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.Listener; +import org.bukkit.event.block.SignChangeEvent; + +public class TradeCraftBlockListener implements Listener{ + + private TradeCraft plugin; + + TradeCraftBlockListener(TradeCraft plugin){ + this.plugin = plugin; + } + + public void debug(String str){ + plugin.getServer().broadcastMessage(str); + } + + @EventHandler + public void onNormalBlockBreak(BlockBreakEvent e) { + this.onBlockBreak(e, EventPriority.NORMAL); + } + + @EventHandler(priority = EventPriority.MONITOR) + public void onMonitorBlockBreak(BlockBreakEvent e) { + this.onBlockBreak(e, EventPriority.MONITOR); + } + + private void onBlockBreak(BlockBreakEvent e, EventPriority p) { + if ( !this.plugin.isEnabled() ) { + return; + } + + Player player = e.getPlayer(); + Block block = e.getBlock(); + ArrayList shops = plugin.getShopsFromBlock(player, block); + + if (shops.size() == 0) { + return; + } + + // Go through all shops in the list and check whether the player can destroy them all first. + // Only if that is the case proceed to destroy. + if (EventPriority.NORMAL == p) { + for ( TradeCraftShop shop : shops ) { + if (!shop.playerCanDestroy(player) && !plugin.permissions.canDestroyShops(player) || + shop.shopCanBeWithdrawnFrom() ) { + // cannot destroy this shop, so cancel destruction, use distinct error messages + if ( shop.shopCanBeWithdrawnFrom() ) { + plugin.sendMessage(player, TradeCraftLocalization.get("ALL_ITEMS_MUST_BE_WITHDRAWN")); + } else { + if ( block.getType() == Material.WALL_SIGN ) { + plugin.sendMessage(player, TradeCraftLocalization.get("YOU_CANT_DESTROY_THIS_SIGN")); + } else if ( block.getType() == Material.CHEST ) { + plugin.sendMessage(player, TradeCraftLocalization.get("YOU_CANT_DESTROY_THIS_CHEST")); + } else { + plugin.sendMessage(player, TradeCraftLocalization.get("YOU_CANT_DESTROY_THIS_BLOCK_ATTACHED")); + } + } + stopDestruction(block,e); + return; + } + } + } + + if (EventPriority.MONITOR == p && ! e.isCancelled()) { + // player can destroy all shops, so proceed + for ( TradeCraftShop shop : shops ) { + plugin.data.deleteShop(shop); + } + } + } + + @EventHandler(priority = EventPriority.MONITOR) + public void onMonitorSignChange(SignChangeEvent e) { + this.onSignChange(e, EventPriority.MONITOR); + } + + @EventHandler + public void onNormalSignChange(SignChangeEvent e) { + this.onSignChange(e, EventPriority.NORMAL); + } + + public void onSignChange(SignChangeEvent event, EventPriority priority) { + if ( !this.plugin.isEnabled() ) { + return; + } + + // Check whether this block is (still) a sign. Can be revoked already in case the sign was temporary, + // for example when the plugin SimpleSignEdit is being used. + if ( event.getBlock().getType() != Material.SIGN_POST && + event.getBlock().getType() != Material.WALL_SIGN ) { + return; + } + + Sign sign = (Sign) event.getBlock().getState(); + + Player player = event.getPlayer(); + String ownerName = player.getName(); + + String itemName = plugin.getItemName(event.getLines()); + + if (itemName == null) { + plugin.trace(player, "sign change, no item name, ignore"); + return; + } + // Check whether this is an existing item. Try to prevent as little normal sign placement as possible. + // Only treat signs with "[name]" as item line where "name" is found in the configuration (.txt) file. + TradeCraftConfigurationInfo itemInfo = plugin.configuration.get(itemName); + if ( itemInfo == null ) { + plugin.trace(player, "sign change, %s is not a valid item name, ignore this sign", itemName); + return; + } + + TradeCraftExchangeRate buyRate = new TradeCraftExchangeRate(event.getLine(1)); + TradeCraftExchangeRate sellRate = new TradeCraftExchangeRate(event.getLine(2)); + // no buy rate means this is an infinite shop + if ( !buyRate.isValid() && !sellRate.isValid() ) { + if (plugin.permissions.canMakeInfShops(player)){ + plugin.trace(player, "sign change, infinite chest of %s", itemName); + return; + } + + if (EventPriority.NORMAL == priority) { + plugin.sendMessage(player, TradeCraftLocalization.get("YOU_CANT_CREATE_INF_SHOPS")); + event.setCancelled(true); + return; + } + } + // there is a buy rate, so this is a player owned shop + + if (EventPriority.NORMAL == priority && !plugin.permissions.canMakePlayerShops(player)){ + plugin.sendMessage(player, TradeCraftLocalization.get("YOU_DONT_HAVE_PERM_CREATE_PLAYER_SHOP")); + event.setCancelled(true); + return; + } + + // check whether the player doesn't have too many shops + if (EventPriority.NORMAL == priority) { + int totalShopLimit = TradeCraft.properties.getPlayerTotalShopLimit(); + int worldShopLimit = TradeCraft.properties.getPlayerWorldShopLimit(); + if (plugin.data.getPlayerShopCount(player) >= totalShopLimit) { + plugin.sendMessage(player, TradeCraftLocalization.get("TOTAL_SHOP_LIMIT_X"), totalShopLimit); + event.setCancelled(true); + return; + } else if (plugin.data.getPlayerShopCount(player, player.getWorld()) >= worldShopLimit) { + plugin.sendMessage(player, TradeCraftLocalization.get("WORLD_SHOP_LIMIT_X"), worldShopLimit); + event.setCancelled(true); + return; + } + + } + + if (EventPriority.MONITOR == priority && !event.isCancelled()) { + plugin.trace(player, "Setting owner of sign to: %s", ownerName); + // set the player name on the last line + event.setLine(3, "-"+ ownerName.substring(0, Math.min(ownerName.length(), 15)) +"-"); + plugin.data.createNewSign(ownerName, itemInfo, sign); + } + + /* + if ( this.plugin.properties.getStrictPlayerShopOwnerNameRequired() ) { + if (player.getName().equalsIgnoreCase(ownerName)) { + plugin.data.setOwnerOfSign(player.getName(), sign); + return; + } + } else { + if (player.getName().startsWith(ownerName)) { + plugin.data.setOwnerOfSign(player.getName(), sign); + return; + } + } + */ + } + + // prevent pistons from pushing away (the block behind) a sign. Block all retract and extend events that would move a block behind a sign, also for shop owners + @EventHandler + public void onNormalBlockPistonRetract(BlockPistonRetractEvent e) { + if (e.isSticky()) { + Block block = e.getRetractLocation().getBlock(); + ArrayList shops = plugin.getShopsFromBlock(null, block); + + if (shops.size() != 0) { + e.setCancelled(true); + } + } + } + @EventHandler + public void onNormalBlockPistonExtend(BlockPistonExtendEvent e) { + List blocks = e.getBlocks(); + for (Block block:blocks) { + ArrayList shops = plugin.getShopsFromBlock(null, block); + + if (shops.size() != 0) { + e.setCancelled(true); + return; + } + } + } + + public void stopDestruction(Block b, BlockBreakEvent e){ + if(b.getState() instanceof Sign){ + Sign sign = (Sign)b.getState(); + String[] lines = sign.getLines(); + for(int i = 0;i<4;i++){ + sign.setLine(i, lines[i]); + } + + sign.update(true); + } + e.setCancelled(true); + } +} diff --git a/nl/armeagle/TradeCraft/TradeCraftChest.java b/src/nl/armeagle/TradeCraft/TradeCraftChest.java similarity index 75% rename from nl/armeagle/TradeCraft/TradeCraftChest.java rename to src/nl/armeagle/TradeCraft/TradeCraftChest.java index d089909..0f8655f 100644 --- a/nl/armeagle/TradeCraft/TradeCraftChest.java +++ b/src/nl/armeagle/TradeCraft/TradeCraftChest.java @@ -17,16 +17,16 @@ public TradeCraftChest(Chest c) { chest = c.getInventory(); for (ItemStack item : chest.getContents()) { - if(item != null){ - short itemData = (item.getData() == null ? (short)0 : item.getDurability()); - if(type.id != 0 && (type.id != item.getTypeId() || type.data != itemData) ){ - diffFlag = true; - return; - } - type.id = item.getTypeId(); - type.data = itemData; - total += item.getAmount(); - } + if(item != null){ + short itemData = (item.getData() == null ? (short)0 : item.getDurability()); + if(type.id != 0 && (type.id != item.getTypeId() || type.data != itemData) ){ + diffFlag = true; + return; + } + type.id = item.getTypeId(); + type.data = itemData; + total += item.getAmount(); + } } } @@ -66,7 +66,7 @@ public int getAmountOfCurrencyInChest() { int amount = 0; for (ItemStack item : ((Inventory)chest).getContents()) { if (item != null) { - short itemData = (item.getData() == null ? (short)0 : item.getDurability()); + short itemData = (item.getData() == null ? (short)0 : item.getDurability()); if (item.getTypeId() == TradeCraft.currency.id && itemData == TradeCraft.currency.data) { amount += item.getAmount(); } @@ -79,7 +79,7 @@ public List getNonCurrencyItems() { List items = new ArrayList(); for (ItemStack item : chest.getContents()) { if (item != null) { - short itemData = (item.getData() == null ? (short)0 : item.getDurability()); + short itemData = (item.getData() == null ? (short)0 : item.getDurability()); if (item.getTypeId() != TradeCraft.currency.id || itemData != TradeCraft.currency.data) { items.add(item); } @@ -89,6 +89,6 @@ public List getNonCurrencyItems() { } public int getSize() { - return (this.chest == null ? 0 : this.chest.getSize()); + return (this.chest == null ? 0 : this.chest.getSize()); } } diff --git a/src/nl/armeagle/TradeCraft/TradeCraftConfigurationFile.java b/src/nl/armeagle/TradeCraft/TradeCraftConfigurationFile.java new file mode 100644 index 0000000..589ab2b --- /dev/null +++ b/src/nl/armeagle/TradeCraft/TradeCraftConfigurationFile.java @@ -0,0 +1,199 @@ +package nl.armeagle.TradeCraft; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileReader; +import java.io.IOException; +import java.util.Arrays; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; +import java.util.Map.Entry; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import org.bukkit.configuration.MemorySection; + +import nl.armeagle.Configuration.StatefulYamlConfiguration; + +/** + * The name of this class is a bit misleading. This class stores all the items and their default + * trade rates that can be used in the game. The actual configuration of the plugin itself + * is handled by the TradeCraftProperties class. + */ +class TradeCraftConfigurationFile { + private static final String fileName = TradeCraft.pluginName + ".txt"; + private static final String configName = "items"; + + private static final Pattern commentPattern = Pattern.compile("^\\s*#.*$"); + private static final Pattern infoPattern = Pattern.compile( + "^\\s*([^,]+)\\s*," + // name + "\\s*(\\d+(?:;\\d+)?)\\s*" + // id[;data] (optional ;data) + "(?:,\\s*(\\d+)\\s*:\\s*(\\d+))?\\s*" + // buyAmount:buyValue + "(?:,\\s*(\\d+)\\s*:\\s*(\\d+))?\\s*$"); // sellAmount:sellValue + + private HashMap mapItemNames = new HashMap(); + + private final TradeCraft plugin; + // create an index from an TradeCraftItem (id;data) to the TradeCraftConfigurationInfo entry. This for configured name lookup based on id;data + private final Map TCitemInfoIndex = new HashMap(); + + TradeCraftConfigurationFile(TradeCraft plugin) { + this.plugin = plugin; + } + + public StatefulYamlConfiguration getConfig() { + return this.plugin.getConfig(TradeCraftConfigurationFile.configName); + } + + void load() { + StatefulYamlConfiguration config = (StatefulYamlConfiguration) this.getConfig(); + + // if file exists, load the config to it once and then rename the old config + File file = new File(plugin.getDataFolder().getAbsolutePath(), fileName); + if ( file.exists() ) { + try { + FileReader reader = new FileReader(file); + BufferedReader configurationFile = new BufferedReader(reader); + + int lineNumber = 0; + String line; + + while ((line = configurationFile.readLine()) != null) { + lineNumber += 1; + + if (line.trim().equals("")) { + continue; + } + + Matcher commentMatcher = commentPattern.matcher(line); + + if (commentMatcher.matches()) { + continue; + } + + Matcher infoMatcher = infoPattern.matcher(line); + + if (!infoMatcher.matches()) { + plugin.log.warning( + "Failed to parse line number " + lineNumber + + " in " + file.getAbsolutePath() + + ": " + line); + continue; + } + + TradeCraftConfigurationInfo info = new TradeCraftConfigurationInfo(); + info.name = infoMatcher.group(1); + + // try to split ID and Data, separated by a semicolon mark + Matcher IdSplitData = TradeCraft.itemPatternIdSplitData.matcher(infoMatcher.group(2)); + + if (!IdSplitData.matches()) { + plugin.log.info( + "Failed to parse line number " + lineNumber + + " in " + file.getAbsolutePath() + + ": " + line); + continue; + } + + int id = Integer.parseInt(IdSplitData.group(1)); + if ( IdSplitData.group(2) != null ) { + short data = Short.parseShort(IdSplitData.group(2)); + info.type = new TradeCraftItem(id, data); + } else { + info.type = new TradeCraftItem(id); + } + + if (infoMatcher.group(3) != null) { + info.sellAmount = info.buyAmount = Integer.parseInt(infoMatcher.group(3)); + info.sellValue = info.buyValue = Integer.parseInt(infoMatcher.group(4)); + } + + if (infoMatcher.group(5) != null) { + info.sellAmount = Integer.parseInt(infoMatcher.group(5)); + info.sellValue = Integer.parseInt(infoMatcher.group(6)); + } + +// config.set(info.name.toUpperCase(), info.toMemoryConfiguration()); + Iterator> iter = info.toMap().entrySet().iterator(); + while (iter.hasNext()) { + Map.Entry pairs = (Map.Entry)iter.next(); + if (!pairs.getKey().equals("name")) { + config.set(info.name +"."+ pairs.getKey(), pairs.getValue()); + } + } + +// TCitemInfoIndex.put(info.type, info.name); + } + configurationFile.close(); + reader.close(); + config.save(); + if (file.renameTo(new File(file.getAbsolutePath() + ".converted.to."+ TradeCraftConfigurationFile.configName +".yml"))) { + plugin.log.info("Converted old config to new style and renamed the old config file"); + } else { + plugin.log.severe("FAILED to convert old config to new style"); + } + + } catch (IOException e) { + plugin.log.severe("Error reading " + file.getAbsolutePath()); + } + } else { + try { + config.load(); + } catch (IOException e) { + plugin.log.severe("Error loading plugin config file"); + } + } + + Iterator> iter = config.getValues(false).entrySet().iterator(); + while (iter.hasNext()) { + // store map of lowercase item names to key names in the configuration + Map.Entry entry = (Map.Entry)iter.next(); + this.mapItemNames.put(entry.getKey().toLowerCase(), entry.getKey()); + // store map of item types to key names + MemorySection section = (MemorySection) entry.getValue(); + TradeCraftItem tcItem = new TradeCraftItem(section.getInt("itemTypeId", 266), section.getInt("itemTypeData", 0)); + TCitemInfoIndex.put(tcItem, entry.getKey()); + } + } + + public String[] getNames() { + String[] names = this.getConfig().getKeys(false).toArray(new String[0]); + Arrays.sort(names); + return names; + } + + public boolean isConfigured(String name) { + return this.mapItemNames.containsKey(name.toLowerCase()); + } + + public TradeCraftConfigurationInfo get(String name) { + + // @todo, config code doesn't seem to like serialized objects, cannot seem to find the TradeCraftConfigurationInfo class + // though, the otherwise resulting ==: classpath lines aren't very user friendly anyway. +// return (TradeCraftConfigurationInfo) plugin.getConfig().get(name.toUpperCase()); + String itemName = this.mapItemNames.get(name.toLowerCase()); + if (null != itemName) { + MemorySection item = ((MemorySection)this.getConfig().get(itemName)); + if (null != item) { + Map itemData = item.getValues(false); + if (null != itemData) { + return new TradeCraftConfigurationInfo(itemData, name); + } + } + } + return null; + } + public TradeCraftConfigurationInfo get(int id) { + return this.get(new TradeCraftItem(id)); + } + public TradeCraftConfigurationInfo get(int id, short data) { + return this.get(new TradeCraftItem(id, data)); + } + public TradeCraftConfigurationInfo get(TradeCraftItem item) { + if (!TCitemInfoIndex.containsKey(item)) { + return null; + } + return this.get(TCitemInfoIndex.get(item)); + } +} \ No newline at end of file diff --git a/src/nl/armeagle/TradeCraft/TradeCraftConfigurationInfo.java b/src/nl/armeagle/TradeCraft/TradeCraftConfigurationInfo.java new file mode 100644 index 0000000..7b62336 --- /dev/null +++ b/src/nl/armeagle/TradeCraft/TradeCraftConfigurationInfo.java @@ -0,0 +1,55 @@ +package nl.armeagle.TradeCraft; + +import java.util.LinkedHashMap; +import java.util.Map; +import org.bukkit.configuration.serialization.ConfigurationSerializable; +import org.bukkit.configuration.serialization.SerializableAs; + +@SerializableAs("nl.armeagle.TradeCraft.TradeCraftConfigurationInfo") +class TradeCraftConfigurationInfo implements ConfigurationSerializable { + public String name; + public TradeCraftItem type; + public int buyAmount; + public int buyValue; + public int sellAmount; + public int sellValue; + + TradeCraftConfigurationInfo() { + } + + TradeCraftConfigurationInfo(Map map, String name) { + this.name = name; + this.type = new TradeCraftItem((Integer) map.get("itemTypeId"), (Integer) map.get("itemTypeData")); + this.buyAmount = (Integer) map.get("buyAmount"); + this.buyValue = (Integer) map.get("buyValue"); + this.sellAmount = (Integer) map.get("sellAmount"); + this.sellValue = (Integer) map.get("sellValue"); + } + TradeCraftConfigurationInfo(Map map) { + this.name = (String) map.get("name"); + this.type = new TradeCraftItem((Integer) map.get("itemTypeId"), (Integer) map.get("itemTypeData")); + this.buyAmount = (Integer) map.get("buyAmount"); + this.buyValue = (Integer) map.get("buyValue"); + this.sellAmount = (Integer) map.get("sellAmount"); + this.sellValue = (Integer) map.get("sellValue"); + } + + public Map toMap() { + LinkedHashMap map = new LinkedHashMap(); + map.put("name", this.name); + map.put("itemTypeId", this.type.id); + map.put("itemTypeData", this.type.data); + map.put("buyAmount", this.buyAmount); + map.put("buyValue", this.buyValue); + map.put("sellAmount", this.sellAmount); + map.put("sellValue", this.sellValue); + return map; + } + @Override + public Map serialize() { + return this.toMap(); + } + public static TradeCraftConfigurationInfo deserialize(Map map) { + return new TradeCraftConfigurationInfo(map); + } +} \ No newline at end of file diff --git a/nl/armeagle/TradeCraft/TradeCraftDataFile.java b/src/nl/armeagle/TradeCraft/TradeCraftDataFile.java similarity index 73% rename from nl/armeagle/TradeCraft/TradeCraftDataFile.java rename to src/nl/armeagle/TradeCraft/TradeCraftDataFile.java index 2e4cb06..29f7dbd 100644 --- a/nl/armeagle/TradeCraft/TradeCraftDataFile.java +++ b/src/nl/armeagle/TradeCraft/TradeCraftDataFile.java @@ -17,13 +17,13 @@ import org.bukkit.entity.Player; class TradeCraftDataFile { - /* - * As of version 1.0.5 there is support for multiple worlds. - * Newly created shops will add the world name to the information stored. - * Old shops will be converted when first interacted with. - */ + /* + * As of version 1.0.5 there is support for multiple worlds. + * Newly created shops will add the world name to the information stored. + * Old shops will be converted when first interacted with. + */ - private static final String fileName = "plugins" + File.separator + TradeCraft.pluginName+ File.separator + TradeCraft.pluginName + ".data"; + private static final String fileName = "plugins" + File.separator + TradeCraft.pluginName+ File.separator + TradeCraft.pluginName + ".data"; private static final Pattern infoPatternNoWorld = Pattern.compile( "^\\s*([^,]+)\\s*," + // ownerName "\\s*(-?\\d+)\\s*,\\s*(-?\\d+)\\s*,\\s*(-?\\d+)\\s*," + // x,y,z @@ -50,7 +50,7 @@ class TradeCraftDataFile { public void load() { try { - dFile.createNewFile(); + dFile.createNewFile(); data.clear(); BufferedReader reader = new BufferedReader(new FileReader(fileName)); @@ -82,15 +82,15 @@ public void load() { itemAmount = Integer.parseInt(infoMatcher2.group(6)); currencyAmount = Integer.parseInt(infoMatcher2.group(7)); } else { - // support for multiple worlds - Matcher infoMatcher3 = infoPatternWorld.matcher(line); - if ( !infoMatcher3.matches()) { - plugin.log.warning( + // support for multiple worlds + Matcher infoMatcher3 = infoPatternWorld.matcher(line); + if ( !infoMatcher3.matches()) { + plugin.log.warning( "Failed to parse line number " + lineNumber + " in " + fileName + ": " + line); continue; - } + } ownerName = infoMatcher3.group(1); worldName = infoMatcher3.group(2); @@ -116,12 +116,12 @@ public void load() { if ( IdSplitData.matches() ) { int itemId = Integer.parseInt(IdSplitData.group(1)); if ( IdSplitData.group(2) != null ) { - info.itemType = new TradeCraftItem(itemId, Short.parseShort(IdSplitData.group(2))); + info.itemType = new TradeCraftItem(itemId, Short.parseShort(IdSplitData.group(2))); } else { - info.itemType = new TradeCraftItem(itemId); + info.itemType = new TradeCraftItem(itemId); } } else { - plugin.log.warning( + plugin.log.warning( "Failed to parse line number " + lineNumber + " in " + fileName + ": " + line); @@ -138,13 +138,13 @@ public void load() { } public void save() { - if ( ! this.wasLoaded ) { - this.plugin.log.severe("TradeCraft: failed to load data file when plugin was enabled, will not save to prevent loss of items."); - // The failure should have been such that no interaction with shops would have been possible, so no items should have been lost since the plugin was - // loaded till this save point. TODO, make sure that no items are lost, when save is actually called after motations, even though that situation - // should never possibly occur. - return; - } + if ( ! this.wasLoaded ) { + this.plugin.log.severe("TradeCraft: failed to load data file when plugin was enabled, will not save to prevent loss of items."); + // The failure should have been such that no interaction with shops would have been possible, so no items should have been lost since the plugin was + // loaded till this save point. TODO, make sure that no items are lost, when save is actually called after motations, even though that situation + // should never possibly occur. + return; + } try { BufferedWriter writer = new BufferedWriter(new FileWriter(fileName)); @@ -170,24 +170,24 @@ public void save() { } public void deleteShop(TradeCraftShop shop){ - Location l = shop.sign.getBlock().getLocation(); - String key = getKey(shop.sign.getWorld().getName(), l.getBlockX(),l.getBlockY(),l.getBlockZ()); - if(data.containsKey(key)){ - data.remove(key); - save(); - } + Location l = shop.sign.getBlock().getLocation(); + String key = getKey(shop.sign.getWorld().getName(), l.getBlockX(),l.getBlockY(),l.getBlockZ()); + if(data.containsKey(key)){ + data.remove(key); + save(); + } } public Map shopsOwned(String playerName){ - Map list = new HashMap(); - for (String key : data.keySet()) { - TradeCraftDataInfo info = data.get(key); - if(info.ownerName.equalsIgnoreCase(playerName)){ - list.put(key, info); - } - } - - return list; + Map list = new HashMap(); + for (String key : data.keySet()) { + TradeCraftDataInfo info = data.get(key); + if(info.ownerName.equalsIgnoreCase(playerName)){ + list.put(key, info); + } + } + + return list; } public void setOwnerOfSign(String ownerName, Sign sign) { @@ -241,7 +241,7 @@ public void depositItems(String ownerName, Sign sign, TradeCraftItem itemType, i } public void depositCurrency(String ownerName, Sign sign, int currencyAmount) { - TradeCraftDataInfo info; + TradeCraftDataInfo info; String key = getKeyFromSign(sign); if (data.containsKey(key)) { @@ -301,30 +301,30 @@ private String getKeyFromSign(Sign sign) { String keyWithWorld = getKey(sign.getWorld().getName(), sign.getX(), sign.getY(), sign.getZ()); // convert old style keys (without world name) to new style and return the new key if ( !data.containsKey(keyWithWorld) ) { - // try the old style key, without the world part - String keyWithoutWorld = getKey(null, sign.getX(), sign.getY(), sign.getZ()); - if ( data.containsKey(keyWithoutWorld) ) { - TradeCraftDataInfo shopInfo = data.get(keyWithoutWorld); - data.remove(keyWithoutWorld); - data.put(keyWithWorld, shopInfo); - } + // try the old style key, without the world part + String keyWithoutWorld = getKey(null, sign.getX(), sign.getY(), sign.getZ()); + if ( data.containsKey(keyWithoutWorld) ) { + TradeCraftDataInfo shopInfo = data.get(keyWithoutWorld); + data.remove(keyWithoutWorld); + data.put(keyWithWorld, shopInfo); + } } return keyWithWorld; } private String getKey(String world, int x, int y, int z) { - // support for multiple words now, optionally accepting a world passed on. - if ( world == null ) { - return x + "," + y + "," + z; - } else { - return world + "," + x + "," + y + "," + z; - } + // support for multiple words now, optionally accepting a world passed on. + if ( world == null ) { + return x + "," + y + "," + z; + } else { + return world + "," + x + "," + y + "," + z; + } } - public void createNewSign(String ownerName, TradeCraftConfigurationInfo itemInfo, Sign sign) { - TradeCraftDataInfo info; - String key = getKeyFromSign(sign); - + public void createNewSign(String ownerName, TradeCraftConfigurationInfo itemInfo, Sign sign) { + TradeCraftDataInfo info; + String key = getKeyFromSign(sign); + if (data.containsKey(key)) { info = data.get(key); } else { @@ -339,22 +339,22 @@ public void createNewSign(String ownerName, TradeCraftConfigurationInfo itemInfo data.put(key, info); save(); - } - - public int getPlayerShopCount(Player player, World world) { - Map list = this.shopsOwned(player.getName()); - int shopsOwnedCount = 0; - - for (String key : list.keySet()) { - TradeCraftDataInfo info = data.get(key); - if (info.worldName.equalsIgnoreCase(world.getName())) { - shopsOwnedCount++; - } - } - - return shopsOwnedCount; - } - public int getPlayerShopCount(Player player) { - return this.shopsOwned(player.getName()).size(); - } + } + + public int getPlayerShopCount(Player player, World world) { + Map list = this.shopsOwned(player.getName()); + int shopsOwnedCount = 0; + + for (String key : list.keySet()) { + TradeCraftDataInfo info = data.get(key); + if (info.worldName.equalsIgnoreCase(world.getName())) { + shopsOwnedCount++; + } + } + + return shopsOwnedCount; + } + public int getPlayerShopCount(Player player) { + return this.shopsOwned(player.getName()).size(); + } } \ No newline at end of file diff --git a/nl/armeagle/TradeCraft/TradeCraftDataInfo.java b/src/nl/armeagle/TradeCraft/TradeCraftDataInfo.java similarity index 61% rename from nl/armeagle/TradeCraft/TradeCraftDataInfo.java rename to src/nl/armeagle/TradeCraft/TradeCraftDataInfo.java index 18e50ea..e9dce65 100644 --- a/nl/armeagle/TradeCraft/TradeCraftDataInfo.java +++ b/src/nl/armeagle/TradeCraft/TradeCraftDataInfo.java @@ -8,10 +8,10 @@ class TradeCraftDataInfo { public int currencyAmount; public TradeCraftDataInfo() { - this.ownerName = null; - this.worldName = null; - this.itemType = null; - this.itemAmount = 0; - this.currencyAmount = 0; - } + this.ownerName = null; + this.worldName = null; + this.itemType = null; + this.itemAmount = 0; + this.currencyAmount = 0; + } } \ No newline at end of file diff --git a/nl/armeagle/TradeCraft/TradeCraftExchangeRate.java b/src/nl/armeagle/TradeCraft/TradeCraftExchangeRate.java similarity index 52% rename from nl/armeagle/TradeCraft/TradeCraftExchangeRate.java rename to src/nl/armeagle/TradeCraft/TradeCraftExchangeRate.java index a63d6e7..5fd3ae0 100644 --- a/nl/armeagle/TradeCraft/TradeCraftExchangeRate.java +++ b/src/nl/armeagle/TradeCraft/TradeCraftExchangeRate.java @@ -10,18 +10,18 @@ public class TradeCraftExchangeRate { private static final Pattern ratePattern = Pattern.compile("(\\d+)\\D+(\\d+)\\s*"); TradeCraftExchangeRate(String signLine) { - Matcher matcher = ratePattern.matcher(signLine); + Matcher matcher = ratePattern.matcher(signLine); - if (matcher.find()) { - this.amount = Integer.parseInt(matcher.group(1)); - this.value = Integer.parseInt(matcher.group(2)); - } else { - this.amount = 0; - this.value = 0; - } + if (matcher.find()) { + this.amount = Integer.parseInt(matcher.group(1)); + this.value = Integer.parseInt(matcher.group(2)); + } else { + this.amount = 0; + this.value = 0; + } } public boolean isValid() { - return this.amount != 0; + return this.amount != 0; } } diff --git a/nl/armeagle/TradeCraft/TradeCraftInfiniteShop.java b/src/nl/armeagle/TradeCraft/TradeCraftInfiniteShop.java similarity index 93% rename from nl/armeagle/TradeCraft/TradeCraftInfiniteShop.java rename to src/nl/armeagle/TradeCraft/TradeCraftInfiniteShop.java index b717bf2..94786d5 100644 --- a/nl/armeagle/TradeCraft/TradeCraftInfiniteShop.java +++ b/src/nl/armeagle/TradeCraft/TradeCraftInfiniteShop.java @@ -13,12 +13,12 @@ public TradeCraftInfiniteShop(TradeCraft plugin, Sign sign, Chest chest) throws String itemName = plugin.getItemName(sign.getLines()); configurationInfo = plugin.configuration.get(itemName); if (null == configurationInfo) { - throw new Exception("Invalid item name on sign: "+ itemName); + throw new Exception("Invalid item name on sign: "+ itemName); } } public boolean playerCanDestroy(Player player) { - return plugin.permissions.canDestroyShops(player); + return plugin.permissions.canDestroyShops(player); } public boolean shopCanBeWithdrawnFrom() { diff --git a/src/nl/armeagle/TradeCraft/TradeCraftItem.java b/src/nl/armeagle/TradeCraft/TradeCraftItem.java new file mode 100644 index 0000000..9bb32f5 --- /dev/null +++ b/src/nl/armeagle/TradeCraft/TradeCraftItem.java @@ -0,0 +1,63 @@ +package nl.armeagle.TradeCraft; + +/** + * + * @author ArmEagle + * Store item type(ID) and optionally the data bit + */ +public class TradeCraftItem implements Comparable { + public int id; + public short data; + + TradeCraftItem(int id) { + this(id, (short)0); + } + TradeCraftItem(int id, int data) { + this(id, new Integer(data).shortValue()); + } + TradeCraftItem(int id, short data) { + this.id = id; + this.data = data; + } + + /** + * @param compare + * @throws NullPointerException if compare is null + * @return default < 0 > compare values + */ + @Override public int compareTo(TradeCraftItem compare) { + if ( this == compare ) { + return 0; + } + if ( this.id < compare.id ) { + return -1; + } else if ( this.id > compare.id ) { + return 1; + } else { + if ( this.data < compare.data ) { + return -1; + } else if ( this.data > compare.data ) { + return 1; + } else { + return 0; + } + } + } + @Override public boolean equals(Object compare) { + return (compare == null ? false : (compare instanceof TradeCraftItem? this.compareTo((TradeCraftItem)compare) == 0 : false)); + } + @Override public int hashCode() { + return this.id * 32768 + this.data; + } + + @Override public String toString() { + return "TradeCraftItem("+ this.id +";"+ this.data +")"; + } + public String toShortString() { + if ( this.data == 0 ) { + return String.valueOf(this.id); + } else { + return this.id +";"+ this.data; + } + } +} diff --git a/nl/armeagle/TradeCraft/TradeCraftItemShop.java b/src/nl/armeagle/TradeCraft/TradeCraftItemShop.java similarity index 50% rename from nl/armeagle/TradeCraft/TradeCraftItemShop.java rename to src/nl/armeagle/TradeCraft/TradeCraftItemShop.java index 09b5fde..ad65463 100644 --- a/nl/armeagle/TradeCraft/TradeCraftItemShop.java +++ b/src/nl/armeagle/TradeCraft/TradeCraftItemShop.java @@ -27,61 +27,61 @@ private void handleOwnerClick(Player player) { if (getChestItemCount() == 0) { int currencyAmount = withdrawCurrency(); if (currencyAmount > 0) { - // limit amount of currency dropped into the chest to the max amount it can hold - int maxCurrencyChestCanHold = chest.getSize() * TradeCraft.getMaxStackSize(TradeCraft.currency.id); - if ( currencyAmount > maxCurrencyChestCanHold ) { - populateChest(TradeCraft.currency, maxCurrencyChestCanHold); // fill to the max - depositCurrency(currencyAmount - maxCurrencyChestCanHold); // return remaining money back to shop data - plugin.sendMessage(player, TradeCraft.MessageTypes.WITHDRAW, - TradeCraftLocalization.get("WITHDREW_X_CURRENCY_SHOP_STILL_HOLDS_Y_CURRENCY"), - maxCurrencyChestCanHold, - plugin.getCurrencyName(), - currencyAmount - maxCurrencyChestCanHold); - plugin.useLog(player, this, - TradeCraftLocalization.get("WITHDREW_X_CURRENCY_SHOP_STILL_HOLDS_Y_CURRENCY"), - maxCurrencyChestCanHold, - plugin.getCurrencyName(), - currencyAmount - maxCurrencyChestCanHold); - } else { - populateChest(TradeCraft.currency, currencyAmount); - plugin.sendMessage(player, TradeCraft.MessageTypes.WITHDRAW, - TradeCraftLocalization.get("WITHDREW_X_A"), - currencyAmount, - plugin.getCurrencyName()); - plugin.useLog(player, this, - TradeCraftLocalization.get("WITHDREW_X_A"), - currencyAmount, - plugin.getCurrencyName()); - } + // limit amount of currency dropped into the chest to the max amount it can hold + int maxCurrencyChestCanHold = chest.getSize() * TradeCraft.getMaxStackSize(TradeCraft.currency.id); + if ( currencyAmount > maxCurrencyChestCanHold ) { + populateChest(TradeCraft.currency, maxCurrencyChestCanHold); // fill to the max + depositCurrency(currencyAmount - maxCurrencyChestCanHold); // return remaining money back to shop data + plugin.sendMessage(player, TradeCraft.MessageTypes.WITHDRAW, + TradeCraftLocalization.get("WITHDREW_X_CURRENCY_SHOP_STILL_HOLDS_Y_CURRENCY"), + maxCurrencyChestCanHold, + plugin.getCurrencyName(), + currencyAmount - maxCurrencyChestCanHold); + plugin.useLog(player, this, + TradeCraftLocalization.get("WITHDREW_X_CURRENCY_SHOP_STILL_HOLDS_Y_CURRENCY"), + maxCurrencyChestCanHold, + plugin.getCurrencyName(), + currencyAmount - maxCurrencyChestCanHold); + } else { + populateChest(TradeCraft.currency, currencyAmount); + plugin.sendMessage(player, TradeCraft.MessageTypes.WITHDRAW, + TradeCraftLocalization.get("WITHDREW_X_A"), + currencyAmount, + plugin.getCurrencyName()); + plugin.useLog(player, this, + TradeCraftLocalization.get("WITHDREW_X_A"), + currencyAmount, + plugin.getCurrencyName()); + } } else { - // limit amount of items dropped into the chest + // limit amount of items dropped into the chest int itemAmount = withdrawItems(); if (itemAmount > 0) { - int maxItemsChestCanHold = chest.getSize() * TradeCraft.getMaxStackSize(getItemType().id); - if ( itemAmount > maxItemsChestCanHold ) { - populateChest(getItemType(), maxItemsChestCanHold); - depositItems(itemAmount - maxItemsChestCanHold); - plugin.sendMessage(player, TradeCraft.MessageTypes.WITHDRAW, - TradeCraftLocalization.get("WITHDREW_X_A_SHOP_STILL_HOLDS_Y_A"), - maxItemsChestCanHold, - getItemName(), - itemAmount - maxItemsChestCanHold); - plugin.useLog(player, this, - TradeCraftLocalization.get("WITHDREW_X_A_SHOP_STILL_HOLDS_Y_A"), - maxItemsChestCanHold, - getItemName(), - itemAmount - maxItemsChestCanHold); - } else { - populateChest(getItemType(), itemAmount); - plugin.sendMessage(player, TradeCraft.MessageTypes.WITHDRAW, - TradeCraftLocalization.get("WITHDREW_X_A"), - itemAmount, - getItemName()); - plugin.useLog(player, this, - TradeCraftLocalization.get("WITHDREW_X_A"), - itemAmount, - getItemName()); - } + int maxItemsChestCanHold = chest.getSize() * TradeCraft.getMaxStackSize(getItemType().id); + if ( itemAmount > maxItemsChestCanHold ) { + populateChest(getItemType(), maxItemsChestCanHold); + depositItems(itemAmount - maxItemsChestCanHold); + plugin.sendMessage(player, TradeCraft.MessageTypes.WITHDRAW, + TradeCraftLocalization.get("WITHDREW_X_A_SHOP_STILL_HOLDS_Y_A"), + maxItemsChestCanHold, + getItemName(), + itemAmount - maxItemsChestCanHold); + plugin.useLog(player, this, + TradeCraftLocalization.get("WITHDREW_X_A_SHOP_STILL_HOLDS_Y_A"), + maxItemsChestCanHold, + getItemName(), + itemAmount - maxItemsChestCanHold); + } else { + populateChest(getItemType(), itemAmount); + plugin.sendMessage(player, TradeCraft.MessageTypes.WITHDRAW, + TradeCraftLocalization.get("WITHDREW_X_A"), + itemAmount, + getItemName()); + plugin.useLog(player, this, + TradeCraftLocalization.get("WITHDREW_X_A"), + itemAmount, + getItemName()); + } } else { plugin.sendMessage(player, TradeCraftLocalization.get("THERE_IS_NOTHING_TO_WITHDRAW")); } @@ -89,61 +89,61 @@ private void handleOwnerClick(Player player) { } else if ( getChestItemType().compareTo(TradeCraft.currency) == 0 ) { depositCurrency(getChestItemCount()); plugin.sendMessage(player, TradeCraft.MessageTypes.DEPOSIT, - TradeCraftLocalization.get("DEPOSITED_X_A"), - getChestItemCount(), - plugin.getCurrencyName()); + TradeCraftLocalization.get("DEPOSITED_X_A"), + getChestItemCount(), + plugin.getCurrencyName()); plugin.useLog(player, this, - TradeCraftLocalization.get("DEPOSITED_X_A"), - getChestItemCount(), - plugin.getCurrencyName()); + TradeCraftLocalization.get("DEPOSITED_X_A"), + getChestItemCount(), + plugin.getCurrencyName()); populateChest(new TradeCraftItem(0), 0); int itemAmount = withdrawItems(); if (itemAmount > 0) { - int maxItemsChestCanHold = chest.getSize() * TradeCraft.getMaxStackSize(getChestItemType().id); - if ( itemAmount > maxItemsChestCanHold ) { - populateChest(getItemType(), maxItemsChestCanHold); - depositItems(itemAmount - maxItemsChestCanHold); + int maxItemsChestCanHold = chest.getSize() * TradeCraft.getMaxStackSize(getChestItemType().id); + if ( itemAmount > maxItemsChestCanHold ) { + populateChest(getItemType(), maxItemsChestCanHold); + depositItems(itemAmount - maxItemsChestCanHold); plugin.sendMessage(player, TradeCraft.MessageTypes.WITHDRAW, - TradeCraftLocalization.get("WITHDREW_X_A_SHOP_STILL_HOLDS_Y_A"), - maxItemsChestCanHold, - getItemName(), - itemAmount - maxItemsChestCanHold); + TradeCraftLocalization.get("WITHDREW_X_A_SHOP_STILL_HOLDS_Y_A"), + maxItemsChestCanHold, + getItemName(), + itemAmount - maxItemsChestCanHold); plugin.useLog(player, this, - TradeCraftLocalization.get("WITHDREW_X_A_SHOP_STILL_HOLDS_Y_A"), - maxItemsChestCanHold, - getItemName(), - itemAmount - maxItemsChestCanHold); - } else { + TradeCraftLocalization.get("WITHDREW_X_A_SHOP_STILL_HOLDS_Y_A"), + maxItemsChestCanHold, + getItemName(), + itemAmount - maxItemsChestCanHold); + } else { populateChest(getItemType(), itemAmount); plugin.sendMessage(player, TradeCraft.MessageTypes.WITHDRAW, - TradeCraftLocalization.get("WITHDREW_X_A"), - itemAmount, - getItemName()); + TradeCraftLocalization.get("WITHDREW_X_A"), + itemAmount, + getItemName()); plugin.useLog(player, this, - TradeCraftLocalization.get("WITHDREW_X_A"), - itemAmount, - getItemName()); - } + TradeCraftLocalization.get("WITHDREW_X_A"), + itemAmount, + getItemName()); + } } } else if ( getChestItemType().compareTo(getItemType()) == 0 ) { depositItems(getChestItemCount()); populateChest(new TradeCraftItem(0), 0); plugin.sendMessage(player, TradeCraft.MessageTypes.DEPOSIT, - TradeCraftLocalization.get("DEPOSITED_X_A"), - getChestItemCount(), - getItemName()); + TradeCraftLocalization.get("DEPOSITED_X_A"), + getChestItemCount(), + getItemName()); plugin.useLog(player, this, - TradeCraftLocalization.get("DEPOSITED_X_A"), - getChestItemCount(), - getItemName()); + TradeCraftLocalization.get("DEPOSITED_X_A"), + getChestItemCount(), + getItemName()); } else { plugin.sendMessage(player, TradeCraftLocalization.get("YOU_CANT_DEPOSIT_THAT_HERE")); } } private void handlePatronClick(Player player) { - - + + boolean playerCanBuy= (plugin.permissions.canBuy(player)); boolean playerCanSell = plugin.permissions.canSell(player); @@ -156,45 +156,45 @@ private void handlePatronClick(Player player) { if (getChestItemCount() == 0) { if (playerCanBuy && playerCanBuy()) { - if ( this instanceof TradeCraftInfiniteShop ) { - plugin.sendMessage(player, - TradeCraftLocalization.get("YOU_CAN_BUY_Y_A_FOR_X_B"), - getBuyAmount(), - getItemName(), - getBuyValue(), - plugin.getCurrencyName()); - } else { - plugin.sendMessage(player, - TradeCraftLocalization.get("YOU_CAN_BUY_X_A_FOR_Y_B_UP_TO_Z"), - getBuyAmount(), - getItemName(), - getBuyValue(), - plugin.getCurrencyName(), - this.getItemsInShop()); - } + if ( this instanceof TradeCraftInfiniteShop ) { + plugin.sendMessage(player, + TradeCraftLocalization.get("YOU_CAN_BUY_Y_A_FOR_X_B"), + getBuyAmount(), + getItemName(), + getBuyValue(), + plugin.getCurrencyName()); + } else { + plugin.sendMessage(player, + TradeCraftLocalization.get("YOU_CAN_BUY_X_A_FOR_Y_B_UP_TO_Z"), + getBuyAmount(), + getItemName(), + getBuyValue(), + plugin.getCurrencyName(), + this.getItemsInShop()); + } } if (playerCanSell && playerCanSell()) { - if ( this instanceof TradeCraftInfiniteShop ) { - plugin.sendMessage(player, - TradeCraftLocalization.get("YOU_CAN_SELL_X_A_FOR_Y_B"), - getSellAmount(), - getItemName(), - getSellValue(), - plugin.getCurrencyName()); - } else { - plugin.sendMessage(player, - TradeCraftLocalization.get("YOU_CAN_SELL_X_A_FOR_Y_B_UP_TO_Z"), - getSellAmount(), - getItemName(), - getSellValue(), - plugin.getCurrencyName(), - this.getCurrencyInShop()); - } + if ( this instanceof TradeCraftInfiniteShop ) { + plugin.sendMessage(player, + TradeCraftLocalization.get("YOU_CAN_SELL_X_A_FOR_Y_B"), + getSellAmount(), + getItemName(), + getSellValue(), + plugin.getCurrencyName()); + } else { + plugin.sendMessage(player, + TradeCraftLocalization.get("YOU_CAN_SELL_X_A_FOR_Y_B_UP_TO_Z"), + getSellAmount(), + getItemName(), + getSellValue(), + plugin.getCurrencyName(), + this.getCurrencyInShop()); + } } if ( this instanceof TradeCraftInfiniteShop ) { - plugin.sendMessage(player, TradeCraftLocalization.get("THIS_IS_AN_INFINITE_SHOP")); + plugin.sendMessage(player, TradeCraftLocalization.get("THIS_IS_AN_INFINITE_SHOP")); } return; } @@ -225,10 +225,10 @@ private void playerWantsToBuy(Player player) { int currencyPlayerWantsToSpend = getChestItemCount(); int amountPlayerWantsToBuy = ((currencyPlayerWantsToSpend - (currencyPlayerWantsToSpend % getBuyValue()) ) / getBuyValue()) * getBuyAmount(); - if ( getBuyAmount() > this.chest.getSize() * TradeCraft.getMaxStackSize(getItemType().id) ) { - plugin.sendMessage(player, TradeCraftLocalization.get("THIS_SHOP_ALWAYS_RETURNS_TOO_MUCH")); - return; - } + if ( getBuyAmount() > this.chest.getSize() * TradeCraft.getMaxStackSize(getItemType().id) ) { + plugin.sendMessage(player, TradeCraftLocalization.get("THIS_SHOP_ALWAYS_RETURNS_TOO_MUCH")); + return; + } if (amountPlayerWantsToBuy == 0) { plugin.sendMessage(player, @@ -240,26 +240,26 @@ private void playerWantsToBuy(Player player) { } if (amountPlayerWantsToBuy > getItemsInShop()) { - if ( getItemsInShop() == 0 ) { - plugin.sendMessage(player, - TradeCraftLocalization.get("CANT_BUY_SHOP_HAS_NO_A_LEFT"), - getItemName()); - } else { - plugin.sendMessage(player, - TradeCraftLocalization.get("CANNOT_BUY_SHOP_ONLY_HAS_X_A"), - getItemsInShop(), - getItemName()); - } - return; + if ( getItemsInShop() == 0 ) { + plugin.sendMessage(player, + TradeCraftLocalization.get("CANT_BUY_SHOP_HAS_NO_A_LEFT"), + getItemName()); + } else { + plugin.sendMessage(player, + TradeCraftLocalization.get("CANNOT_BUY_SHOP_ONLY_HAS_X_A"), + getItemsInShop(), + getItemName()); + } + return; } int requiredCurrencyForThatAmount = amountPlayerWantsToBuy * getBuyValue() / getBuyAmount(); if ( Math.ceil( (currencyPlayerWantsToSpend - requiredCurrencyForThatAmount) / TradeCraft.getMaxStackSize(TradeCraft.currency.id)) - + Math.ceil( amountPlayerWantsToBuy / TradeCraft.getMaxStackSize(getItemType().id)) - > this.chest.getSize() ) { - plugin.sendMessage(player, TradeCraftLocalization.get("THIS_SHOP_WOULD_RETURN_TOO_MANY_BUY_LESS")); - return; + + Math.ceil( amountPlayerWantsToBuy / TradeCraft.getMaxStackSize(getItemType().id)) + > this.chest.getSize() ) { + plugin.sendMessage(player, TradeCraftLocalization.get("THIS_SHOP_WOULD_RETURN_TOO_MANY_BUY_LESS")); + return; } updateItemAndCurrencyAmounts(-amountPlayerWantsToBuy, requiredCurrencyForThatAmount); @@ -270,13 +270,13 @@ private void playerWantsToBuy(Player player) { chest.update(); plugin.sendMessage(player, - TradeCraftLocalization.get("YOU_BOUGHT_X_A_FOR_Y_B"), + TradeCraftLocalization.get("YOU_BOUGHT_X_A_FOR_Y_B"), amountPlayerWantsToBuy, getItemName(), requiredCurrencyForThatAmount, plugin.getCurrencyName()); plugin.useLog(player, this, - TradeCraftLocalization.get("BOUGHT_X_A_FOR_Y_B"), + TradeCraftLocalization.get("BOUGHT_X_A_FOR_Y_B"), amountPlayerWantsToBuy, getItemName(), requiredCurrencyForThatAmount, @@ -292,10 +292,10 @@ private void playerWantsToSell(Player player) { int amountPlayerWantsToSell = getChestItemCount(); int currencyPlayerShouldReceive = ((amountPlayerWantsToSell - (amountPlayerWantsToSell % getSellAmount())) / getSellAmount()) * getSellValue(); // prevent too much currency (more than can fit in a chest) to be given to the customer - if ( getSellValue() > this.chest.getSize() * TradeCraft.getMaxStackSize(TradeCraft.currency.id) ) { - plugin.sendMessage(player, TradeCraftLocalization.get("THIS_SHOP_ALWAYS_RETURNS_TOO_MUCH_CURRENCY"), this.plugin.getCurrencyName()); - return; - } + if ( getSellValue() > this.chest.getSize() * TradeCraft.getMaxStackSize(TradeCraft.currency.id) ) { + plugin.sendMessage(player, TradeCraftLocalization.get("THIS_SHOP_ALWAYS_RETURNS_TOO_MUCH_CURRENCY"), this.plugin.getCurrencyName()); + return; + } if (currencyPlayerShouldReceive == 0) { plugin.sendMessage(player, @@ -318,10 +318,10 @@ private void playerWantsToSell(Player player) { // prevent too much items+currency stacks to end up in the chest if ( Math.ceil( (amountPlayerWantsToSell - amountThatCanBeSold)/ TradeCraft.getMaxStackSize(getItemType().id) ) - + Math.ceil( currencyPlayerShouldReceive / TradeCraft.getMaxStackSize(TradeCraft.currency.id) ) + + Math.ceil( currencyPlayerShouldReceive / TradeCraft.getMaxStackSize(TradeCraft.currency.id) ) > chest.getSize() ) { - plugin.sendMessage(player, TradeCraftLocalization.get("THIS_SHOP_WOULD_RETURN_TOO_MUCH_CURRENCY_SELL_LESS"), this.plugin.getCurrencyName()); - return; + plugin.sendMessage(player, TradeCraftLocalization.get("THIS_SHOP_WOULD_RETURN_TOO_MUCH_CURRENCY_SELL_LESS"), this.plugin.getCurrencyName()); + return; } updateItemAndCurrencyAmounts(amountThatCanBeSold, -currencyPlayerShouldReceive); diff --git a/nl/armeagle/TradeCraft/TradeCraftLocalization.java b/src/nl/armeagle/TradeCraft/TradeCraftLocalization.java similarity index 54% rename from nl/armeagle/TradeCraft/TradeCraftLocalization.java rename to src/nl/armeagle/TradeCraft/TradeCraftLocalization.java index ffd2044..a85f769 100644 --- a/nl/armeagle/TradeCraft/TradeCraftLocalization.java +++ b/src/nl/armeagle/TradeCraft/TradeCraftLocalization.java @@ -14,39 +14,39 @@ * such other language file to be used. */ public class TradeCraftLocalization { - private static final String filePreName = TradeCraft.pluginName + ".%1$s.lang"; + private static final String filePreName = TradeCraft.pluginName + ".%1$s.lang"; private static StatefulYamlConfiguration localization; - public TradeCraftLocalization(TradeCraft plugin) { - String filename = String.format(TradeCraftLocalization.filePreName, TradeCraft.properties.getLanguage()); + public TradeCraftLocalization(TradeCraft plugin) { + String filename = String.format(TradeCraftLocalization.filePreName, TradeCraft.properties.getLanguage()); TradeCraftLocalization.localization = plugin.getConfig(filename); try { - TradeCraftLocalization.localization.load(); + TradeCraftLocalization.localization.load(); } catch (IOException e) { - plugin.log(Level.SEVERE, "Failed to read file: %s", filename); + plugin.log(Level.SEVERE, "Failed to read file: %s", filename); } String defaultFilename = String.format(TradeCraftLocalization.filePreName, "en"); if (TradeCraftLocalization.localization.getKeys(false).isEmpty()) { - plugin.log(Level.INFO, "Language file %s does not exist or is empty, defaulting to %s", filename, defaultFilename); + plugin.log(Level.INFO, "Language file %s does not exist or is empty, defaulting to %s", filename, defaultFilename); } else { - return; + return; } TradeCraftLocalization.localization = plugin.getConfig(defaultFilename); try { - TradeCraftLocalization.localization.load(); + TradeCraftLocalization.localization.load(); } catch (IOException e) { - plugin.log(Level.SEVERE, "Failed to read file: %s", defaultFilename); + plugin.log(Level.SEVERE, "Failed to read file: %s", defaultFilename); } if (TradeCraftLocalization.localization.getKeys(false).isEmpty()) { - plugin.log(Level.SEVERE, "Default language file %s is also empty", defaultFilename); + plugin.log(Level.SEVERE, "Default language file %s is also empty", defaultFilename); } - } + } - public static String get(String key) { - return TradeCraftLocalization.localization.getString(key, "key error \""+ key +"\""); - } + public static String get(String key) { + return TradeCraftLocalization.localization.getString(key, "key error \""+ key +"\""); + } } diff --git a/src/nl/armeagle/TradeCraft/TradeCraftPermissions.java b/src/nl/armeagle/TradeCraft/TradeCraftPermissions.java new file mode 100644 index 0000000..2471728 --- /dev/null +++ b/src/nl/armeagle/TradeCraft/TradeCraftPermissions.java @@ -0,0 +1,68 @@ +package nl.armeagle.TradeCraft; + +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; +import org.bukkit.plugin.Plugin; + +public class TradeCraftPermissions { + TradeCraft plugin; + + TradeCraftPermissions(TradeCraft plugin) { + this.plugin = plugin; + } + + public boolean canBuy(Player p) { + return p.hasPermission("TradeCraft.canBuy"); + } + + public boolean canSell(Player p) { + return p.hasPermission("TradeCraft.canSell"); + } + + public boolean canMakeInfShops(Player p) { + return p.hasPermission("TradeCraft.canMakeInfShops"); + } + + public boolean canMakePlayerShops(Player p) { + return p.hasPermission("TradeCraft.canMakePlayerShops"); + } + + public boolean canDestroyShops(Player p) { + return p.hasPermission("TradeCraft.canDestroyShops"); + } + + public boolean canSetCurrency(Player p) { + return p.hasPermission("TradeCraft.canSetCurrency"); + } + + public boolean canReload(Player p) { + return p.hasPermission("TradeCraft.canReload"); + } + + public boolean canQueryOtherShops(Player p) { + return p.hasPermission("TradeCraft.canQueryOtherShops"); + } + + public boolean canQueryPlayer(Player p) { + return p.hasPermission("TradeCraft.canQueryPlayer"); + } + + public void debug(CommandSender sender, String n){ + Player p = plugin.getServer().getPlayer(n); + if(p == null){ + plugin.getServer().broadcastMessage("/tc canPlayer used with a name of player who is not online."); + return; + } + String name = p.getName(); + sender.sendMessage("" + name + " has:"); + sender.sendMessage("canBuy " + canBuy(p)); + sender.sendMessage("canSell " + canSell(p)); + sender.sendMessage("canMakeInf " + canMakeInfShops(p)); + sender.sendMessage("canMakePersonal " + canMakePlayerShops(p)); + sender.sendMessage("canDestroy " + canDestroyShops(p)); + sender.sendMessage("canSetCurrency " + canSetCurrency(p)); + sender.sendMessage("canReload " + canReload(p)); + sender.sendMessage("canQueryOtherShops " + canQueryOtherShops(p)); + } + +} diff --git a/nl/armeagle/TradeCraft/TradeCraftPlayerListener.java b/src/nl/armeagle/TradeCraft/TradeCraftPlayerListener.java similarity index 54% rename from nl/armeagle/TradeCraft/TradeCraftPlayerListener.java rename to src/nl/armeagle/TradeCraft/TradeCraftPlayerListener.java index 8ba5d7d..9bc6bef 100644 --- a/nl/armeagle/TradeCraft/TradeCraftPlayerListener.java +++ b/src/nl/armeagle/TradeCraft/TradeCraftPlayerListener.java @@ -10,34 +10,34 @@ public class TradeCraftPlayerListener implements Listener{ - private TradeCraft plugin; - - TradeCraftPlayerListener(TradeCraft plugin){ - this.plugin = plugin; - } - - @EventHandler - public void onPlayerInteract(PlayerInteractEvent e) { - if ( !this.plugin.isEnabled() ) { - return; - } - if ( e.getAction() == Action.RIGHT_CLICK_BLOCK ) { - Block blockClicked = e.getClickedBlock(); - Player player = e.getPlayer(); - - TradeCraftShop shop = plugin.getShopFromSignBlock(player, blockClicked); - - if (shop == null) { - return; - } + private TradeCraft plugin; + + TradeCraftPlayerListener(TradeCraft plugin){ + this.plugin = plugin; + } + + @EventHandler + public void onPlayerInteract(PlayerInteractEvent e) { + if ( !this.plugin.isEnabled() ) { + return; + } + if ( e.getAction() == Action.RIGHT_CLICK_BLOCK ) { + Block blockClicked = e.getClickedBlock(); + Player player = e.getPlayer(); + + TradeCraftShop shop = plugin.getShopFromSignBlock(player, blockClicked); + + if (shop == null) { + return; + } - shop.handleRightClick(player); - e.setCancelled(true); - } - } - + shop.handleRightClick(player); + e.setCancelled(true); + } + } + @SuppressWarnings("unused") - private void displayItems(Player player) { + private void displayItems(Player player) { String[] names = plugin.configuration.getNames(); StringBuilder sb = new StringBuilder(); for (String name : names) { diff --git a/nl/armeagle/TradeCraft/TradeCraftPlayerOwnedShop.java b/src/nl/armeagle/TradeCraft/TradeCraftPlayerOwnedShop.java similarity index 94% rename from nl/armeagle/TradeCraft/TradeCraftPlayerOwnedShop.java rename to src/nl/armeagle/TradeCraft/TradeCraftPlayerOwnedShop.java index e453e64..69ecd5e 100644 --- a/nl/armeagle/TradeCraft/TradeCraftPlayerOwnedShop.java +++ b/src/nl/armeagle/TradeCraft/TradeCraftPlayerOwnedShop.java @@ -30,11 +30,11 @@ public boolean shopCanBeWithdrawnFrom() { } public boolean isOwnedByPlayer(Player player) { - if ( ownerName == null ) { - return false; - } else { - return player.getName().equals(ownerName); - } + if ( ownerName == null ) { + return false; + } else { + return player.getName().equals(ownerName); + } } public TradeCraftItem getItemType() { diff --git a/src/nl/armeagle/TradeCraft/TradeCraftPropertiesFile.java b/src/nl/armeagle/TradeCraft/TradeCraftPropertiesFile.java new file mode 100644 index 0000000..028704d --- /dev/null +++ b/src/nl/armeagle/TradeCraft/TradeCraftPropertiesFile.java @@ -0,0 +1,153 @@ +package nl.armeagle.TradeCraft; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.util.logging.Level; + +import org.bukkit.ChatColor; +import org.bukkit.configuration.InvalidConfigurationException; +import org.bukkit.configuration.file.YamlConfiguration; + +/** + * This class, handles the actual configuration of the plugin. The TradeCraftConfiguration class + * actually handles all the items that can be used by the shops in the game. + */ +public class TradeCraftPropertiesFile { + private static final String fileName = TradeCraft.pluginName + ".properties"; + private static final String filePath = "plugins" + File.separator + TradeCraft.pluginName; + public static final String defaultLanguage = "en"; + + private TradeCraft plugin; + private final YamlConfiguration properties; + + public TradeCraftPropertiesFile(TradeCraft plugin) { + this.plugin = plugin; + // make folder in the plugins dir if it doesn't exist yet + File path = new File(filePath); + if ( !path.exists() ) { + path.mkdirs(); + } + path = null; + + // if file does not exist in this directory, copy it from the jar + File file = new File(filePath + File.separator + fileName); + if ( !file.exists() ) { + this.plugin.log.info(filePath + File.separator + fileName +" does not exist, creating..."); + InputStream input = this.getClass().getResourceAsStream("/" + fileName); + if ( input != null ) { + FileOutputStream output = null; + + try { + output = new FileOutputStream(file); + byte[] buf = new byte[8192]; + int length = 0; + while ((length = input.read(buf)) > 0) { + output.write(buf, 0, length); + } + } catch (IOException e) { + e.printStackTrace(); + } finally { + try { + if (input != null) { + input.close(); + } + } catch (IOException e) {} + + try { + if (output != null) { + output.close(); + } + } catch (IOException e) {} + } + } + } + + properties = new YamlConfiguration(); + try { + properties.load(file); + } catch (InvalidConfigurationException e) { + plugin.log(Level.SEVERE, "Failed to load file: %s", file.toURI()); + } catch (IOException e) { + plugin.log(Level.SEVERE, "Failed to read file: %s", file.toURI()); + } + } + + protected void save() { + File file = new File(filePath + File.separator + fileName); + try { + properties.save(file); + } catch (IOException e) { + this.plugin.log(Level.SEVERE, "Error saving to file: %s", file.toURI()); + } + } + + public TradeCraftItem getCurrencyType(){ + int id = properties.getInt("currency-id",266); + short data = (short)properties.getInt("currency-data",0); + return new TradeCraftItem(id, data); + } + public void setCurrencyType(TradeCraftItem item) { + properties.set("currency-id", item.id); + properties.set("currency-data", item.data); + this.save(); + } + public boolean getNormalStackSizeUsed(){ + return properties.getBoolean("normal-stack-size", true); + } + + public boolean getInfiniteShopsEnabled() { + return properties.getBoolean("infinite-shops-enabled", true); + } + + public boolean getPlayerOwnedShopsEnabled() { + return properties.getBoolean("player-owned-shops-enabled", true); + } + + public boolean getRepairShopsEnabled() { + return properties.getBoolean("repair-shops-enabled", false); + } + + public int getRepairCost() { + return properties.getInt("repair-cost", 0); + } + + public boolean getEnableDebugMessages() { + return properties.getBoolean("enable-debug-messages", false); + } + + public String getLanguage() { + return properties.getString("language", TradeCraftPropertiesFile.defaultLanguage); + } + + public boolean autoUpdateLanguageFiles() { + return properties.getBoolean("auto-update-language-files", true); + } + + public boolean logShopUse() { + return properties.getBoolean("log-shop-use", false); + } + + public boolean showShopLocation() { + return properties.getBoolean("show-shop-location", false); + } + + public int getPlayerWorldShopLimit() { + return properties.getInt("player-world-shop-limit", 5); + } + public int getPlayerTotalShopLimit() { + return properties.getInt("player-total-shop-limit", 10); + } + + public ChatColor getMessageTypeColor(TradeCraft.MessageTypes mtype) { + switch (mtype) { + case WITHDRAW: + return ChatColor.YELLOW; + case DEPOSIT: + return ChatColor.GRAY; + default: + return ChatColor.WHITE; + } + } +} diff --git a/nl/armeagle/TradeCraft/TradeCraftRepairShop.java b/src/nl/armeagle/TradeCraft/TradeCraftRepairShop.java similarity index 75% rename from nl/armeagle/TradeCraft/TradeCraftRepairShop.java rename to src/nl/armeagle/TradeCraft/TradeCraftRepairShop.java index f0d4b6b..438f769 100644 --- a/nl/armeagle/TradeCraft/TradeCraftRepairShop.java +++ b/src/nl/armeagle/TradeCraft/TradeCraftRepairShop.java @@ -20,8 +20,8 @@ public void handleRightClick(Player player) { if (currencyAmount == 0 && items.size() == 0) { plugin.sendMessage(player, TradeCraftLocalization.get("IT_COSTS_X_A_TO_REPAIR_AN_ITEM"), - repairCost, - plugin.getCurrencyName()); + repairCost, + plugin.getCurrencyName()); return; } @@ -29,26 +29,26 @@ public void handleRightClick(Player player) { if (items.size() == 0) { plugin.sendMessage(player, TradeCraftLocalization.get("WITH_THIS_MUCH_A_YOU_CAN_REPAIR_Y_ITEMS"), - plugin.getCurrencyName(), - currencyAmount / repairCost); + plugin.getCurrencyName(), + currencyAmount / repairCost); return; } if (currencyAmount < actualCost) { if (currencyAmount > 0) { plugin.sendMessage(player, TradeCraftLocalization.get("THAT_IS_NOT_ENOUGH_A"), - plugin.getCurrencyName()); + plugin.getCurrencyName()); } plugin.sendMessage(player, TradeCraftLocalization.get("YOU_NEED_X_A_TO_REPAIR_ALL_THIS"), - actualCost, - plugin.getCurrencyName()); + actualCost, + plugin.getCurrencyName()); return; } chest.clear(); for (ItemStack item : items) { - short itemData = (item.getData() == null ? (short)0 : item.getDurability()); + short itemData = (item.getData() == null ? (short)0 : item.getDurability()); chest.add(new TradeCraftItem(item.getTypeId(), itemData), 1); } @@ -58,9 +58,9 @@ public void handleRightClick(Player player) { chest.update(); plugin.sendMessage(player, TradeCraftLocalization.get("YOU_REPAIRED_X_ITEMS_FOR_Y_B"), - items.size(), - actualCost, - plugin.getCurrencyName()); + items.size(), + actualCost, + plugin.getCurrencyName()); } public boolean playerCanDestroy(Player player) { diff --git a/nl/armeagle/TradeCraft/TradeCraftShop.java b/src/nl/armeagle/TradeCraft/TradeCraftShop.java similarity index 72% rename from nl/armeagle/TradeCraft/TradeCraftShop.java rename to src/nl/armeagle/TradeCraft/TradeCraftShop.java index b66a611..f537991 100644 --- a/nl/armeagle/TradeCraft/TradeCraftShop.java +++ b/src/nl/armeagle/TradeCraft/TradeCraftShop.java @@ -22,10 +22,10 @@ public TradeCraftShop(TradeCraft plugin, Sign sign, Chest chest) { public abstract boolean shopCanBeWithdrawnFrom(); public String toString() { - return String.format("Shop(%s:%d,%d,%d)", - this.sign.getWorld().getName(), - this.sign.getX(), - this.sign.getY(), - this.sign.getZ()); + return String.format("Shop(%s:%d,%d,%d)", + this.sign.getWorld().getName(), + this.sign.getX(), + this.sign.getY(), + this.sign.getZ()); } } diff --git a/plugin.yml b/src/plugin.yml similarity index 100% rename from plugin.yml rename to src/plugin.yml From a45eafc8d43611fee436c5dc7c299fc774cdbc95 Mon Sep 17 00:00:00 2001 From: armeagle Date: Sat, 11 Aug 2012 18:21:36 +0200 Subject: [PATCH 096/100] Remove old files --- .classpath | 8 -------- .project | 17 ----------------- TradeCraft.jar | Bin 64185 -> 0 bytes 3 files changed, 25 deletions(-) delete mode 100644 .classpath delete mode 100644 .project delete mode 100644 TradeCraft.jar diff --git a/.classpath b/.classpath deleted file mode 100644 index 131551b..0000000 --- a/.classpath +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - diff --git a/.project b/.project deleted file mode 100644 index 80cf29a..0000000 --- a/.project +++ /dev/null @@ -1,17 +0,0 @@ - - - TradeCraft - - - - - - org.eclipse.jdt.core.javabuilder - - - - - - org.eclipse.jdt.core.javanature - - diff --git a/TradeCraft.jar b/TradeCraft.jar deleted file mode 100644 index 7f521b2afaf263e2a0e5b1099b8a2d395e41a283..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 64185 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 SSw{ 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

3kMZK8f{o}_(<231;llxbUb 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 Vpl|*H008dWr{is&d*{Et{XZ?AI`{wp From 3144561f4251101f10566cd873837c601605998e Mon Sep 17 00:00:00 2001 From: armeagle Date: Fri, 23 Nov 2012 21:59:28 +0100 Subject: [PATCH 097/100] Fixed: When standing too close, a RIGHT_CLICK_AIR event can be fired instead of RIGHT_CLICK_BLOCK. Scan ahead 2 blocks in those situations instead and try to find a block that way. Version upped to 1.4.1. --- src/plugin.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugin.yml b/src/plugin.yml index 2c5f311..7528caa 100644 --- a/src/plugin.yml +++ b/src/plugin.yml @@ -1,5 +1,5 @@ name: TradeCraft -version: AE-1.4 +version: AE-1.4.1 description: Setup shops with a combination of chest and sign. author: ArmEagle main: nl.armeagle.TradeCraft.TradeCraft From b1b744f2141ae67e5e9d2ef5c29724c2da9d5db9 Mon Sep 17 00:00:00 2001 From: armeagle Date: Sun, 2 Dec 2012 12:02:58 +0100 Subject: [PATCH 098/100] Forgot to commit a file with last: Fixed: When standing too close, a RIGHT_CLICK_AIR event can be fired instead of RIGHT_CLICK_BLOCK. Scan ahead 2 blocks in those situations instead and try to find a block that way. Version upped to 1.4.1. --- .../TradeCraft/TradeCraftPlayerListener.java | 28 +++++++++++++------ 1 file changed, 20 insertions(+), 8 deletions(-) diff --git a/src/nl/armeagle/TradeCraft/TradeCraftPlayerListener.java b/src/nl/armeagle/TradeCraft/TradeCraftPlayerListener.java index 9bc6bef..ad39122 100644 --- a/src/nl/armeagle/TradeCraft/TradeCraftPlayerListener.java +++ b/src/nl/armeagle/TradeCraft/TradeCraftPlayerListener.java @@ -11,22 +11,34 @@ public class TradeCraftPlayerListener implements Listener{ private TradeCraft plugin; - + TradeCraftPlayerListener(TradeCraft plugin){ this.plugin = plugin; } - + @EventHandler public void onPlayerInteract(PlayerInteractEvent e) { if ( !this.plugin.isEnabled() ) { return; } + + Player player = e.getPlayer(); + if ( player == null ) { + return; + } + + Block blockClicked = null; if ( e.getAction() == Action.RIGHT_CLICK_BLOCK ) { - Block blockClicked = e.getClickedBlock(); - Player player = e.getPlayer(); - + blockClicked = e.getClickedBlock(); + } else if ( e.getAction() == Action.RIGHT_CLICK_AIR ) { + // If player is holding a solid block and in the way of placing the block, a click air event will be fired instead. + // Look for up to two blocks away. + blockClicked = player.getTargetBlock(null, 2); + } + + if ( blockClicked != null ) { TradeCraftShop shop = plugin.getShopFromSignBlock(player, blockClicked); - + if (shop == null) { return; } @@ -35,11 +47,11 @@ public void onPlayerInteract(PlayerInteractEvent e) { e.setCancelled(true); } } - + @SuppressWarnings("unused") private void displayItems(Player player) { String[] names = plugin.configuration.getNames(); - StringBuilder sb = new StringBuilder(); + StringBuilder sb = new StringBuilder(); for (String name : names) { if (sb.length() + name.length() > 60) { plugin.sendMessage(player, sb.toString()); From 0d848f47ffbb56691b62e35f07efd17c74b1207f Mon Sep 17 00:00:00 2001 From: armeagle Date: Sun, 30 Dec 2012 18:53:42 +0100 Subject: [PATCH 099/100] Fix division by zero when getting stack size of AIR (which was 64 before and now 0). --- src/nl/armeagle/TradeCraft/TradeCraft.java | 55 +++++++++++----------- 1 file changed, 28 insertions(+), 27 deletions(-) diff --git a/src/nl/armeagle/TradeCraft/TradeCraft.java b/src/nl/armeagle/TradeCraft/TradeCraft.java index 7e00048..ccc40ae 100644 --- a/src/nl/armeagle/TradeCraft/TradeCraft.java +++ b/src/nl/armeagle/TradeCraft/TradeCraft.java @@ -34,7 +34,7 @@ public class TradeCraft extends JavaPlugin { public static enum MessageTypes {WITHDRAW, DEPOSIT}; - + // The plugin name. static final String pluginName = "TradeCraft"; @@ -63,12 +63,12 @@ private static enum Commands { public TradeCraftLocalization localization; TradeCraftDataFile data; - private HashMap configs = new HashMap(); + private HashMap configs = new HashMap(); private final TradeCraftBlockListener blockListener = new TradeCraftBlockListener(this); private final TradeCraftPlayerListener playerListener = new TradeCraftPlayerListener(this); public TradeCraftPermissions permissions = new TradeCraftPermissions(this); - + // prevent the script from registering the event listeners multiple times (by dis-/enable) public static boolean hasRegisteredEventListeners = false; @@ -98,7 +98,7 @@ private void enable() { configuration = new TradeCraftConfigurationFile(this); data = new TradeCraftDataFile(this); this.localization = new TradeCraftLocalization(this); - + if ( TradeCraft.properties.logShopUse() ) { File usageLogFile = new File(this.getDataFolder(), "shopUsage.log"); try { @@ -115,7 +115,7 @@ private void enable() { this.log(Level.WARNING, "Failed to open shop usage log file: "+ usageLogFile.toString()); } } - + configuration.load(); data.load(); currency = properties.getCurrencyType(); @@ -129,7 +129,7 @@ private void enable() { PluginDescriptionFile pdfFile = this.getDescription(); this.log.info(pdfFile.getName() + " version " + pdfFile.getVersion() + " is enabled!"); - + } @Override @@ -144,7 +144,7 @@ public boolean onCommand(CommandSender sender, Command cmd, String label, String TradeCraftItem testCurrency = null; // try to split ID and Data, separated by a semicolon mark Matcher IdSplitData = TradeCraft.itemPatternIdSplitData.matcher(args[0]); - + if ( !IdSplitData.matches() ) { // try to match the parameter to item names from the configuration TradeCraftConfigurationInfo setCurr = this.configuration.get(args[0]); @@ -176,7 +176,7 @@ public boolean onCommand(CommandSender sender, Command cmd, String label, String return false; } } - + TradeCraft.properties.setCurrencyType(currency); this.sendMessage(p, TradeCraftLocalization.get("CURRENCY_IS_SET_TO_A_IDDATA"), this.getCurrencyName(), @@ -228,7 +228,7 @@ public boolean onCommand(CommandSender sender, Command cmd, String label, String } } else if ( sender instanceof ConsoleCommandSender ) { switch (command) { - case tcplayerperms: + case tcplayerperms: if ( args.length == 1 ) { permissions.debug(sender, args[0]); } else { @@ -253,7 +253,7 @@ public boolean onCommand(CommandSender sender, Command cmd, String label, String sender.sendMessage(String.format(TradeCraftLocalization.get("RESTARTING_PLUGIN_DONE"), TradeCraft.pluginName)); return true; - default: + default: displayCommandHelpText(null); return true; } @@ -276,7 +276,7 @@ void displayShops(String infoPlayerName, CommandSender displayTo, boolean otherQ } return; } - + if ( otherQuery ) { displayTo.sendMessage(String.format(TradeCraftLocalization.get("SHOPS_OF_A"), infoPlayerName)); @@ -293,7 +293,7 @@ void displayShops(String infoPlayerName, CommandSender displayTo, boolean otherQ message += TradeCraftLocalization.get("ITEM") +": "+ this.configuration.get(info.itemType).name +"("+ info.itemType.toShortString() +")" +" "+ TradeCraftLocalization.get("AMOUNT") +": "+ info.itemAmount +" " + this.getCurrencyName() +": "+ info.currencyAmount; - + displayTo.sendMessage(message); } @@ -307,7 +307,7 @@ void sendMessage(Player player, TradeCraft.MessageTypes messageType, String form player.sendMessage(TradeCraftLocalization.get("ERROR_IN_FORMAT_STRING") +" "+ format); } } - + void sendMessage(Player player, String format, Object... args) { try { String message = String.format(format, args); @@ -330,10 +330,10 @@ void trace(Player player, String format, Object... args) { * When a block behind a shop sign is destroyed, the sign would be destroyed too. * Check all side faces of this block, for a sign attached to this block. Then * pass that sign block to getShopFromSignBlock. - * + * * This should only be used for checking whether a normal block can be destroyed, for there not being any * signs attached to it, or this block being a chest or sign itself. - * Since one block can + * Since one block can */ ArrayList getShopsFromBlock(Player player, Block block) { ArrayList shops = new ArrayList(); @@ -356,7 +356,7 @@ ArrayList getShopsFromBlock(Player player, Block block) { org.bukkit.material.Sign materialSign = new org.bukkit.material.Sign(Material.WALL_SIGN, sideBlock.getData()); /* Now, for easy comparison, we'll compare to the direction the sign is facing. If that's * the same as the current face (of the 4 we're looping through), then this sign is attached - * to the block that is being destroyed. + * to the block that is being destroyed. */ if ( materialSign.getFacing() == side ) { TradeCraftShop shop = this.getShopFromSignBlock(player, sideBlock); @@ -364,7 +364,7 @@ ArrayList getShopsFromBlock(Player player, Block block) { shops.add(shop); } } - + } } } @@ -528,15 +528,16 @@ TradeCraftExchangeRate getExchangeRate(String signLine) { static int getMaxStackSize(int itemType) { if ( TradeCraft.properties.getNormalStackSizeUsed() ) { - return Material.getMaterial(itemType).getMaxStackSize(); + int stackSize = Material.getMaterial(itemType).getMaxStackSize(); + return (stackSize == 0 ? 64 : Material.getMaterial(itemType).getMaxStackSize()); } else { return 64; } } - + /** * Get a CamelCased string based on the current currency. - * + * * @return a string representing the currency. */ public String getCurrencyName() { @@ -545,16 +546,16 @@ public String getCurrencyName() { if ( configInfo != null ) { return configInfo.name; } else { - + ItemStack currencyStack = new ItemStack(TradeCraft.currency.id, 1, TradeCraft.currency.data); // weird that there's no Material.getMaterial(id, short/byte) MaterialData currencyData = currencyStack.getType().getNewData((byte)TradeCraft.currency.data); - String currencyString; + String currencyString; if ( currencyData == null ) { currencyString = currencyStack.getType().name(); } else { currencyString = currencyData.toString(); } - + //String baseName = stack.getType().name(); String[] words = currencyString.replace("null ", "").split("\\(")[0].split("[ _]{1}"); String name = ""; @@ -598,7 +599,7 @@ private void displayCommandHelpText(Player player) { this.log(Level.INFO, "tcreload: "+ TradeCraftLocalization.get("TC_RELOAD")); } } - + public void saveConfig() { Logger.getLogger(JavaPlugin.class.getName()).log(Level.INFO, "saving config to: "+ this.getConfig("config").getFile().getAbsolutePath()); try { @@ -623,16 +624,16 @@ public StatefulYamlConfiguration getConfig(String name) { return config; } } - + public void log(Level level, String format, Object... args) { this.log.log(level, TradeCraft.pluginName +": "+ String.format(format, args)); } - + public void useLog(Player player, TradeCraftShop shop, String format, Object... args) { if ( TradeCraft.properties.logShopUse() ) { if ( this.usageLog != null ) { try { - SimpleDateFormat formatter = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss"); + SimpleDateFormat formatter = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss"); this.usageLog.write(String.format("%s %s \t%s %s\n", formatter.format(new Date()), shop.toString(), From 37bd9391a90f9fc4990331efeeeb87045c0dde7c Mon Sep 17 00:00:00 2001 From: armeagle Date: Sun, 30 Dec 2012 18:53:56 +0100 Subject: [PATCH 100/100] Changed version to 1.4.2 --- src/plugin.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugin.yml b/src/plugin.yml index 7528caa..bd45a39 100644 --- a/src/plugin.yml +++ b/src/plugin.yml @@ -1,5 +1,5 @@ name: TradeCraft -version: AE-1.4.1 +version: AE-1.4.2 description: Setup shops with a combination of chest and sign. author: ArmEagle main: nl.armeagle.TradeCraft.TradeCraft