package net.TheDgtl.Tombstone; /** * Tombstone - A tombstone plugin for Bukkit * Copyright (C) 2011 Steven "Drakia" Scott <Drakia@Gmail.com> * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ import java.io.BufferedWriter; import java.io.File; import java.io.FileWriter; import java.io.IOException; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Date; import java.util.HashMap; import java.util.Iterator; import java.util.Scanner; import java.util.concurrent.ConcurrentLinkedQueue; import java.util.logging.Logger; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.bukkit.Location; import org.bukkit.Material; import org.bukkit.World; import org.bukkit.block.Block; import org.bukkit.block.BlockFace; import org.bukkit.block.BlockState; import org.bukkit.block.Chest; import org.bukkit.block.Sign; import org.bukkit.command.Command; import org.bukkit.command.CommandSender; import org.bukkit.configuration.file.FileConfiguration; import org.bukkit.entity.Creeper; import org.bukkit.entity.Entity; import org.bukkit.entity.Fireball; import org.bukkit.entity.Ghast; import org.bukkit.entity.Giant; import org.bukkit.entity.PigZombie; import org.bukkit.entity.Player; import org.bukkit.entity.Skeleton; import org.bukkit.entity.Slime; import org.bukkit.entity.Spider; import org.bukkit.entity.TNTPrimed; import org.bukkit.entity.Wolf; import org.bukkit.entity.Zombie; import org.bukkit.event.Event.Result; import org.bukkit.event.EventHandler; import org.bukkit.event.EventPriority; import org.bukkit.event.Listener; import org.bukkit.event.block.Action; import org.bukkit.event.block.BlockBreakEvent; import org.bukkit.event.block.BlockPlaceEvent; import org.bukkit.event.entity.EntityDamageByEntityEvent; import org.bukkit.event.entity.EntityDamageEvent; import org.bukkit.event.entity.EntityDamageEvent.DamageCause; import org.bukkit.event.entity.EntityDeathEvent; import org.bukkit.event.player.PlayerInteractEvent; import org.bukkit.event.server.PluginDisableEvent; import org.bukkit.event.server.PluginEnableEvent; import org.bukkit.inventory.ItemStack; import org.bukkit.plugin.Plugin; import org.bukkit.plugin.PluginDescriptionFile; import org.bukkit.plugin.PluginManager; import org.bukkit.plugin.java.JavaPlugin; import com.griefcraft.lwc.LWC; import com.griefcraft.lwc.LWCPlugin; import com.griefcraft.model.Protection; import com.nijikokun.bukkit.Permissions.Permissions; public class Tombstone extends JavaPlugin { public static Logger log; PluginManager pm; private Permissions permissions = null; private LWCPlugin lwcPlugin = null; private ConcurrentLinkedQueue<TombBlock> tombList = new ConcurrentLinkedQueue<TombBlock>(); private HashMap<Location, TombBlock> tombBlockList = new HashMap<Location, TombBlock>(); private HashMap<String, ArrayList<TombBlock>> playerTombList = new HashMap<String, ArrayList<TombBlock>>(); private HashMap<String, EntityDamageEvent> deathCause = new HashMap<String, EntityDamageEvent>(); private FileConfiguration newConfig; private Tombstone plugin; /** * Configuration options - Defaults */ private int lwcTime = 3600; private int removeTime = 18000; private boolean lwcEnable = true; private boolean lwcRemove = false; private boolean lwcPublic = false; private boolean tombRemove = false; private boolean tombSign = true; private boolean pMessage = true; private boolean saveTombList = true; private boolean destroyQuickLoot = false; private boolean noDestroy = false; private boolean noInterfere = true; private boolean logEvents = false; private boolean skipBuildCheck = false; String signTemplate[] = new String[4]; public void onEnable() { PluginDescriptionFile pdfFile = getDescription(); log = Logger.getLogger("Minecraft"); newConfig = this.getConfig(); log.info(pdfFile.getName() + " v." + pdfFile.getVersion() + " is enabled."); pm = getServer().getPluginManager(); pm.registerEvents(new eListener(), this); pm.registerEvents(new pListener(), this); pm.registerEvents(new bListener(), this); pm.registerEvents(new sListener(), this); permissions = (Permissions)checkPlugin("Permissions"); lwcPlugin = (LWCPlugin)checkPlugin("LWC"); plugin = this; loadConfig(); loadSignTemplate(); for (World w : getServer().getWorlds()) loadTombList(w.getName()); // Start removal timer. Run every 5 seconds (20 ticks per second) if (lwcRemove || tombRemove) getServer().getScheduler().scheduleSyncRepeatingTask(this, new TombThread(), 0L, 100L); } public void loadConfig() { reloadConfig(); newConfig = this.getConfig(); // Copy default values if required newConfig.options().copyDefaults(true); lwcEnable = newConfig.getBoolean("lwcEnable", lwcEnable); lwcTime = newConfig.getInt("lwcTimeout", lwcTime); lwcRemove = newConfig.getBoolean("lwcRemove", lwcRemove); lwcPublic = newConfig.getBoolean("lwcPublic", lwcPublic); tombSign = newConfig.getBoolean("tombSign", tombSign); removeTime = newConfig.getInt("removeTime", removeTime); tombRemove = newConfig.getBoolean("tombRemove", tombRemove); pMessage = newConfig.getBoolean("playerMessage", pMessage); saveTombList = newConfig.getBoolean("saveTombList", saveTombList); destroyQuickLoot = newConfig.getBoolean("destroyQuickLoot", destroyQuickLoot); noDestroy = newConfig.getBoolean("noDestroy", noDestroy); noInterfere = newConfig.getBoolean("noInterfere", noInterfere); logEvents = newConfig.getBoolean("logEvents", logEvents); skipBuildCheck = newConfig.getBoolean("skipBuildCheck", skipBuildCheck); saveConfig(); } public void loadSignTemplate() { signTemplate[0] = "{name}"; signTemplate[1] = "Killed By"; signTemplate[2] = "{cause}"; signTemplate[3] = "{time}"; try { File fh = new File(this.getDataFolder(), "sign.tpl"); // Create default if (!fh.exists()) { BufferedWriter bw = new BufferedWriter(new FileWriter(fh)); for (int i = 0; i < 4; i++) { bw.append(signTemplate[i]); bw.newLine(); } bw.close(); // Load sign template } else { Scanner scanner = new Scanner(fh); for (int i = 0; i < 4; i++) { signTemplate[i] = scanner.nextLine().trim(); } scanner.close(); } } catch (Exception e) {} } public void loadTombList(String world) { if (!saveTombList) return; try { File fh = new File(this.getDataFolder(), "tombList-" + world + ".db"); if (!fh.exists()) return; Scanner scanner = new Scanner(fh); while (scanner.hasNextLine()) { String line = scanner.nextLine().trim(); String[] split = line.split(":"); //block:lblock:sign:time:name:lwc Block block = readBlock(split[0]); Block lBlock = readBlock(split[1]); Block sign = readBlock(split[2]); String owner = split[3]; long time = Long.valueOf(split[4]); boolean lwc = Boolean.valueOf(split[5]); if (block == null || owner == null) { log.info("[Tombstone] Invalid tombstone in database " + fh.getName()); continue; } TombBlock tBlock = new TombBlock(block, lBlock, sign, owner, time, lwc); tombList.offer(tBlock); // Used for quick tombStone lookup tombBlockList.put(block.getLocation(), tBlock); if (lBlock != null) tombBlockList.put(lBlock.getLocation(), tBlock); if (sign != null) tombBlockList.put(sign.getLocation(), tBlock); ArrayList<TombBlock> pList = playerTombList.get(owner); if (pList == null) { pList = new ArrayList<TombBlock>(); playerTombList.put(owner, pList); } pList.add(tBlock); } scanner.close(); } catch (IOException e) { Tombstone.log.info("[Tombstone] Error loading tombstone list: " + e); } } public void saveTombList(String world) { if (!saveTombList) return; try { File fh = new File(this.getDataFolder(), "tombList-" + world + ".db"); BufferedWriter bw = new BufferedWriter(new FileWriter(fh)); for (Iterator<TombBlock> iter = tombList.iterator(); iter.hasNext();) { TombBlock tBlock = iter.next(); // Skip not this world if (!tBlock.getBlock().getWorld().getName().equalsIgnoreCase(world)) continue; bw.append(printBlock(tBlock.getBlock())); bw.append(":"); bw.append(printBlock(tBlock.getLBlock())); bw.append(":"); bw.append(printBlock(tBlock.getSign())); bw.append(":"); bw.append(tBlock.getOwner()); bw.append(":"); bw.append(String.valueOf(tBlock.getTime())); bw.append(":"); bw.append(String.valueOf(tBlock.getLwcEnabled())); bw.newLine(); } bw.close(); } catch (IOException e) { Tombstone.log.info("[Tombstone] Error saving tombstone list: " + e); } } private String printBlock(Block b) { if (b == null) return ""; return b.getWorld().getName() + "," + b.getX() + "," + b.getY() + "," + b.getZ(); } private Block readBlock(String b) { if (b.length() == 0) return null; String[] split = b.split(","); //world,x,y,z World world = getServer().getWorld(split[0]); if (world == null) return null; return world.getBlockAt(Integer.valueOf(split[1]), Integer.valueOf(split[2]), Integer.valueOf(split[3])); } public void onDisable() { for (World w : getServer().getWorlds()) saveTombList(w.getName()); } /* * Check if a plugin is loaded/enabled already. Returns the plugin if so, null otherwise */ private Plugin checkPlugin(String p) { Plugin plugin = pm.getPlugin(p); return checkPlugin(plugin); } private Plugin checkPlugin(Plugin plugin) { if (plugin != null && plugin.isEnabled()) { log.info("[Tombstone] Using " + plugin.getDescription().getName() + " (v" + plugin.getDescription().getVersion() + ")"); return plugin; } return null; } private Boolean activateLWC(Player player, TombBlock tBlock) { if (!lwcEnable) return false; if (lwcPlugin == null) return false; LWC lwc = lwcPlugin.getLWC(); // Register the chest + sign as private Block block = tBlock.getBlock(); Block sign = tBlock.getSign(); lwc.getPhysicalDatabase().registerProtection(block.getTypeId(), Protection.Type.PRIVATE, block.getWorld().getName(), player.getName(), "", block.getX(), block.getY(), block.getZ()); if (sign != null) lwc.getPhysicalDatabase().registerProtection(sign.getTypeId(), Protection.Type.PRIVATE, block.getWorld().getName(), player.getName(), "", sign.getX(), sign.getY(), sign.getZ()); tBlock.setLwcEnabled(true); return true; } private void deactivateLWC(TombBlock tBlock, boolean force) { if (!lwcEnable) return; if (lwcPlugin == null) return; LWC lwc = lwcPlugin.getLWC(); // Remove the protection on the chest Block _block = tBlock.getBlock(); Protection protection = lwc.findProtection(_block); if (protection != null) { protection.remove(); //Set to public instead of removing completely if (lwcPublic && !force) lwc.getPhysicalDatabase().registerProtection(_block.getTypeId(), Protection.Type.PUBLIC, _block.getWorld().getName(), tBlock.getOwner(), "", _block.getX(), _block.getY(), _block.getZ()); } // Remove the protection on the sign _block = tBlock.getSign(); if (_block != null) { protection = lwc.findProtection(_block); if (protection != null) { protection.remove(); // Set to public instead of removing completely if (lwcPublic && !force) lwc.getPhysicalDatabase().registerProtection(_block.getTypeId(), Protection.Type.PUBLIC, _block.getWorld().getName(), tBlock.getOwner(), "", _block.getX(), _block.getY(), _block.getZ()); } } tBlock.setLwcEnabled(false); } private void removeTomb(TombBlock tBlock, boolean removeList) { if (tBlock == null) return; tombBlockList.remove(tBlock.getBlock().getLocation()); if (tBlock.getLBlock() != null) tombBlockList.remove(tBlock.getLBlock().getLocation()); if (tBlock.getSign() != null) tombBlockList.remove(tBlock.getSign().getLocation()); // Remove just this tomb from tombList ArrayList<TombBlock> tList = playerTombList.get(tBlock.getOwner()); if (tList != null) { tList.remove(tBlock); if (tList.size() == 0) { playerTombList.remove(tBlock.getOwner()); } } if (removeList) tombList.remove(tBlock); if (tBlock.getBlock() != null) saveTombList(tBlock.getBlock().getWorld().getName()); } /* * Check whether the player has the given permissions. */ public boolean hasPerm(Player player, String perm) { if (permissions != null) { return permissions.getHandler().has(player, perm); } else { return player.hasPermission(perm); } } public void sendMessage(Player p, String msg) { if (!pMessage) return; p.sendMessage("[Tombstone] " + msg); } @Override public boolean onCommand(CommandSender sender, Command command, String label, String[] args) { if (!(sender instanceof Player)) return false; Player p = (Player)sender; String cmd = command.getName(); if (cmd.equalsIgnoreCase("tomblist")) { if (!hasPerm(p, "tombstone.cmd.tomblist")) { sendMessage(p, "Permission Denied"); return true; } ArrayList<TombBlock> pList = playerTombList.get(p.getName()); if (pList == null) { sendMessage(p, "You have no tombstones."); return true; } sendMessage(p, "Tombstone List:"); int i = 0; for (TombBlock tomb : pList) { i++; if (tomb.getBlock() == null) continue; int X = tomb.getBlock().getX(); int Y = tomb.getBlock().getY(); int Z = tomb.getBlock().getZ(); sendMessage(p, " " + i + " - World: " + tomb.getBlock().getWorld().getName() + " @(" + X + "," + Y + "," + Z + ")"); } return true; } else if (cmd.equalsIgnoreCase("tombfind")) { if (!hasPerm(p, "tombstone.cmd.tombfind")) { sendMessage(p, "Permission Denied"); return true; } if (args.length != 1) return false; ArrayList<TombBlock> pList = playerTombList.get(p.getName()); if (pList == null) { sendMessage(p, "You have no tombstones."); return true; } int slot = 0; try { slot = Integer.parseInt(args[0]); } catch (Exception e) { sendMessage(p, "Invalid Tombstone"); return true; } slot -= 1; if (slot < 0 || slot >= pList.size()) { sendMessage(p, "Invalid Tombstone"); return true; } TombBlock tBlock = pList.get(slot); double degrees = (getYawTo(tBlock.getBlock().getLocation(), p.getLocation()) + 270) % 360; p.setCompassTarget(tBlock.getBlock().getLocation()); sendMessage(p, "Your compass is pointing at your tombstone"); sendMessage(p, "Your tombstone #" + args[0] + " is to the " + getDirection(degrees)); return true; } else if (cmd.equalsIgnoreCase("tombreset")) { if (!hasPerm(p, "tombstone.cmd.tombreset")) { sendMessage(p, "Permission Denied"); return true; } p.setCompassTarget(p.getWorld().getSpawnLocation()); return true; } return false; } /** * Gets the Yaw from one location to another in relation to North. */ public double getYawTo(Location from, Location to) { final int distX = to.getBlockX() - from.getBlockX(); final int distZ = to.getBlockZ() - from.getBlockZ(); double degrees = Math.toDegrees(Math.atan2(-distX, distZ)); degrees += 180; return degrees; } /** * Converts a rotation to a cardinal direction name. * Author: sk89q - Original function from CommandBook plugin * @param rot * @return */ private static String getDirection(double rot) { if (0 <= rot && rot < 22.5) { return "North"; } else if (22.5 <= rot && rot < 67.5) { return "Northeast"; } else if (67.5 <= rot && rot < 112.5) { return "East"; } else if (112.5 <= rot && rot < 157.5) { return "Southeast"; } else if (157.5 <= rot && rot < 202.5) { return "South"; } else if (202.5 <= rot && rot < 247.5) { return "Southwest"; } else if (247.5 <= rot && rot < 292.5) { return "West"; } else if (292.5 <= rot && rot < 337.5) { return "Northwest"; } else if (337.5 <= rot && rot < 360.0) { return "North"; } else { return null; } } /** * * Print a message to terminal if logEvents is enabled * @param msg * @return * */ private void logEvent(String msg) { if (!logEvents) return; log.info("[Tombstone] " + msg); } @SuppressWarnings("unused") private class bListener implements Listener { @EventHandler public void onBlockBreak(BlockBreakEvent event) { Block b = event.getBlock(); Player p = event.getPlayer(); if (b.getType() != Material.CHEST && b.getType() != Material.SIGN_POST) return; TombBlock tBlock = tombBlockList.get(b.getLocation()); if (tBlock == null) return; if (noDestroy && !hasPerm(p, "tombstone.admin")) { logEvent(p.getName() + " tried to destroy tombstone at " + b.getLocation()); sendMessage(p, "Tombstone unable to be destroyed"); event.setCancelled(true); return; } if (lwcPlugin != null && lwcEnable && tBlock.getLwcEnabled()) { if (tBlock.getOwner().equals(p.getName()) || hasPerm(p, "tombstone.admin")) { logEvent("Deactivating LWC"); deactivateLWC(tBlock, true); } else { event.setCancelled(true); return; } } logEvent(p.getName() + " destroyed tombstone at " + b.getLocation()); removeTomb(tBlock, true); } } @SuppressWarnings("unused") private class pListener implements Listener { // To ignore the stupid updateInventory deprecation @SuppressWarnings("deprecation") @EventHandler(priority = EventPriority.HIGHEST) public void onPlayerInteract(PlayerInteractEvent event) { if (event.getAction() != Action.RIGHT_CLICK_BLOCK) return; Block b = event.getClickedBlock(); if (b.getType() != Material.SIGN_POST && b.getType() != Material.CHEST) return; // We'll do quickloot on rightclick of chest if we're going to destroy it anyways if (b.getType() == Material.CHEST && (!destroyQuickLoot || !noDestroy)) return; if (!hasPerm(event.getPlayer(), "tombstone.quickloot")) return; TombBlock tBlock = tombBlockList.get(b.getLocation()); if (tBlock == null || !(tBlock.getBlock().getState() instanceof Chest)) return; if (!tBlock.getOwner().equals(event.getPlayer().getName())) return; Chest sChest = (Chest)tBlock.getBlock().getState(); Chest lChest = (tBlock.getLBlock() != null) ? (Chest)tBlock.getLBlock().getState() : null; ItemStack[] items = sChest.getInventory().getContents(); boolean overflow = false; for (int cSlot = 0; cSlot < items.length; cSlot++) { ItemStack item = items[cSlot]; if (item == null) continue; if (item.getType() == Material.AIR) continue; int slot = event.getPlayer().getInventory().firstEmpty(); if (slot == -1) { overflow = true; break; } event.getPlayer().getInventory().setItem(slot, item); sChest.getInventory().clear(cSlot); } if (lChest != null) { items = lChest.getInventory().getContents(); for (int cSlot = 0; cSlot < items.length; cSlot++) { ItemStack item = items[cSlot]; if (item == null) continue; if (item.getType() == Material.AIR) continue; int slot = event.getPlayer().getInventory().firstEmpty(); if (slot == -1) { overflow = true; break; } event.getPlayer().getInventory().setItem(slot, item); lChest.getInventory().clear(cSlot); } } if (!overflow) { // We're quicklooting, so no need to resume this interaction event.setUseInteractedBlock(Result.DENY); event.setUseItemInHand(Result.DENY); event.setCancelled(true); // Deactivate LWC deactivateLWC(tBlock, true); removeTomb(tBlock, true); if (destroyQuickLoot) { if (tBlock.getSign() != null) tBlock.getSign().setType(Material.AIR); tBlock.getBlock().setType(Material.AIR); if (tBlock.getLBlock() != null) tBlock.getLBlock().setType(Material.AIR); } } // Manually update inventory for the time being. event.getPlayer().updateInventory(); sendMessage(event.getPlayer(), "Tombstone quicklooted!"); logEvent(event.getPlayer() + " quicklooted tombstone at " + tBlock.getBlock().getLocation()); } } @SuppressWarnings("unused") private class eListener implements Listener { @EventHandler(priority = EventPriority.HIGHEST) public void onEntityDamage(EntityDamageEvent event) { if (event.isCancelled()) return; if (!(event.getEntity() instanceof Player))return; Player player = (Player)event.getEntity(); // Add them to the list if they're about to die if (player.getHealth() - event.getDamage() <= 0) { deathCause.put(player.getName(), event); } } @EventHandler(priority = EventPriority.HIGHEST) public void onEntityDeath(EntityDeathEvent event ) { if (!(event.getEntity() instanceof Player)) return; Player p = (Player)event.getEntity(); String name = p.getName(); if (!hasPerm(p, "tombstone.use")) return; logEvent(name + " died."); if (event.getDrops().size() == 0) { sendMessage(p, "Inventory Empty."); logEvent(name + " inventory empty."); return; } // Check if this is a void death EntityDamageEvent dmg = deathCause.get(name); if (dmg != null && dmg.getCause() == DamageCause.VOID) { sendMessage(p, "Your items were lost in the void."); logEvent(name + " died in the void."); return; } // Get the current player location. Location loc = p.getLocation(); Block block = p.getWorld().getBlockAt(loc.getBlockX(), loc.getBlockY(), loc.getBlockZ()); // Check if we're allowed to build here. Will stop things like a tomb in spawn if (!skipBuildCheck && !canBuild(block, p)) { sendMessage(p, "Your tombstone can't spawn here. Inventory dropped."); logEvent(name + " Building disallowed at death location."); return; } // If we run into something we don't want to destroy, go one up. if ( block.getType() == Material.STEP || block.getType() == Material.TORCH || block.getType() == Material.REDSTONE_WIRE || block.getType() == Material.RAILS || block.getType() == Material.STONE_PLATE || block.getType() == Material.WOOD_PLATE || block.getType() == Material.REDSTONE_TORCH_ON || block.getType() == Material.REDSTONE_TORCH_OFF || block.getType() == Material.CAKE_BLOCK) { block = p.getWorld().getBlockAt(loc.getBlockX(), loc.getBlockY() + 1, loc.getBlockZ()); } // Check if the player has a chest. int pChestCount = 0; int pSignCount = 0; for (ItemStack item : event.getDrops()) { if (item == null) continue; if (item.getType() == Material.CHEST) pChestCount += item.getAmount(); if (item.getType() == Material.SIGN) pSignCount += item.getAmount(); } if (pChestCount == 0 && !hasPerm(p, "tombstone.freechest")) { sendMessage(p, "No chest found in inventory. Inventory dropped"); logEvent(name + " No chest in inventory."); return; } // Check if we can replace the block. block = findPlace(block); if ( block == null ) { sendMessage(p, "Could not find room for chest. Inventory dropped"); logEvent(name + " Could not find room for chest."); return; } // Check if there is a nearby chest if (noInterfere && checkInterfere(block)) { sendMessage(p, "There is a chest or sign interfering with your tombstone. Inventory dropped"); logEvent(name + " Chest/Sign interfered with tombstone creation."); return; } int removeChestCount = 1; int removeSign = 0; // Do the check for a large chest block here so we can check for interference Block lBlock = findLarge(block); // Set the current block to a chest, init some variables for later use. block.setType(Material.CHEST); // We're running into issues with 1.3 where we can't cast to a Chest :( BlockState state = block.getState(); if (!(state instanceof Chest)) { sendMessage(p, "Could not access chest. Inventory dropped."); logEvent(name + " Could not access chest."); return; } Chest sChest = (Chest)state; Chest lChest = null; int slot = 0; int maxSlot = sChest.getInventory().getSize(); // Check if they need a large chest. if (event.getDrops().size() > maxSlot) { // If they are allowed spawn a large chest to catch their entire inventory. if (lBlock != null && hasPerm(p, "tombstone.large")) { removeChestCount = 2; // Check if the player has enough chests if (pChestCount >= removeChestCount || hasPerm(p, "tombstone.freechest")) { lBlock.setType(Material.CHEST); lChest = (Chest)lBlock.getState(); maxSlot = maxSlot * 2; } else { removeChestCount = 1; } } } // Don't remove any chests if they get a free one. if (hasPerm(p, "tombstone.freechest")) removeChestCount = 0; // Check if we have signs enabled, if the player can use signs, and if the player has a sign or gets a free sign Block sBlock = null; if (tombSign && hasPerm(p, "tombstone.sign") && (pSignCount > 0 || hasPerm(p, "tombstone.freesign"))) { // Find a place to put the sign, then place the sign. sBlock = sChest.getWorld().getBlockAt(sChest.getX(), sChest.getY() + 1, sChest.getZ()); if (canReplace(sBlock.getType())) { createSign(sBlock, p); removeSign = 1; } else if (lChest != null) { sBlock = lChest.getWorld().getBlockAt(lChest.getX(), lChest.getY() + 1, lChest.getZ()); if (canReplace(sBlock.getType())) { createSign(sBlock, p); removeSign = 1; } } } // Don't remove a sign if they get a free one if (hasPerm(p, "tombstone.freesign")) removeSign = 0; // Create a TombBlock for this tombstone TombBlock tBlock = new TombBlock(sChest.getBlock(), (lChest != null) ? lChest.getBlock() : null, sBlock, p.getName(), (System.currentTimeMillis() / 1000)); // Protect the chest/sign if LWC is installed. Boolean prot = false; if (hasPerm(p, "tombstone.lwc")) prot = activateLWC(p, tBlock); tBlock.setLwcEnabled(prot); // Add tombstone to list tombList.offer(tBlock); // Add tombstone blocks to tombBlockList tombBlockList.put(tBlock.getBlock().getLocation(), tBlock); if (tBlock.getLBlock() != null) tombBlockList.put(tBlock.getLBlock().getLocation(), tBlock); if (tBlock.getSign() != null) tombBlockList.put(tBlock.getSign().getLocation(), tBlock); // Add tombstone to player lookup list ArrayList<TombBlock> pList = playerTombList.get(name); if (pList == null) { pList = new ArrayList<TombBlock>(); playerTombList.put(name, pList); } pList.add(tBlock); saveTombList(p.getWorld().getName()); // Next get the players inventory using the getDrops() method. for (Iterator<ItemStack> iter = event.getDrops().listIterator(); iter.hasNext();) { ItemStack item = iter.next(); if (item == null) continue; // Take the chest(s) if (removeChestCount > 0 && item.getType() == Material.CHEST) { if (item.getAmount() >= removeChestCount) { item.setAmount(item.getAmount() - removeChestCount); removeChestCount = 0; } else { removeChestCount -= item.getAmount(); item.setAmount(0); } if (item.getAmount() == 0) { iter.remove(); continue; } } // Take a sign if (removeSign > 0 && item.getType() == Material.SIGN){ item.setAmount(item.getAmount() - 1); removeSign = 0; if (item.getAmount() == 0) { iter.remove(); continue; } } // Add items to chest if not full. if (slot < maxSlot) { if (slot >= sChest.getInventory().getSize()) { if (lChest == null) continue; lChest.getInventory().setItem(slot % sChest.getInventory().getSize(), item); } else { sChest.getInventory().setItem(slot, item); } iter.remove(); slot++; } else if (removeChestCount == 0) break; } // Tell the player how many items went into chest. String msg = "Inventory stored in chest. "; if (event.getDrops().size() > 0) msg += event.getDrops().size() + " items wouldn't fit in chest."; sendMessage(p, msg); logEvent(name + " " + msg); if (prot) { sendMessage(p, "Chest protected with LWC. " + lwcTime + "s before chest is unprotected."); logEvent(name + " Chest protected with LWC. " + lwcTime + "s before chest is unprotected."); } if (tombRemove) { sendMessage(p, "Chest will be automatically removed in " + removeTime + "s"); logEvent(name + " Chest will be automatically removed in " + removeTime + "s"); } } private void createSign(Block signBlock, Player p) { // Variables used in template HashMap<String, String> vars = new HashMap<String, String>(); vars.put("date", new SimpleDateFormat("yyyy-MM-dd").format(new Date())); vars.put("time", new SimpleDateFormat("hh:mm a").format(new Date())); // Name and cause have to be checked for length. String name = p.getName(); String cause = "Unknown"; EntityDamageEvent dmg = deathCause.get(name); if (dmg != null) { deathCause.remove(name); cause = getCause(dmg); } if (name.length() > 15) name = name.substring(0, 15); if (cause.length() > 15) cause = cause.substring(0, 15); vars.put("name", name); vars.put("cause", cause); signBlock.setType(Material.SIGN_POST); final Sign sign = (Sign)signBlock.getState(); for (int i = 0; i < 4; i++) { sign.setLine(i, parseVars(signTemplate[i], vars)); } getServer().getScheduler().scheduleSyncDelayedTask(plugin, new Runnable() { public void run() { sign.update(); } }); } private String getCause(EntityDamageEvent dmg) { switch (dmg.getCause()) { case ENTITY_ATTACK: { EntityDamageByEntityEvent event = (EntityDamageByEntityEvent)dmg; Entity e = event.getDamager(); if (e == null) { return "a Dispenser"; } else if (e instanceof Player) { return ((Player) e).getDisplayName(); } else if (e instanceof PigZombie) { return "a Pig Zombie"; } else if (e instanceof Giant) { return "a Giant"; } else if (e instanceof Zombie) { return "a Zombie"; } else if (e instanceof Skeleton) { return "a Skeleton"; } else if (e instanceof Spider) { return "a Spider"; } else if (e instanceof Creeper) { return "a Creeper"; } else if (e instanceof Ghast) { return "a Ghast"; } else if (e instanceof Slime) { return "a Slime"; } else if (e instanceof Wolf) { return "a Wolf"; } else { return "a Monster"; } } case CONTACT: return "a Cactus"; case SUFFOCATION: return "Suffocation"; case FALL: return "a Fall"; case FIRE: return "a Fire"; case FIRE_TICK: return "Burning"; case LAVA: return "Lava"; case DROWNING: return "Drowning"; case BLOCK_EXPLOSION: return "an Explosion"; case ENTITY_EXPLOSION: { try { EntityDamageByEntityEvent event = (EntityDamageByEntityEvent)dmg; Entity e = event.getDamager(); if (e instanceof TNTPrimed) return "a TNT Explosion"; else if (e instanceof Fireball) return "a Ghast"; else return "a Creeper"; } catch (Exception e) { return "an Explosion"; } } case VOID: return "the Void"; case LIGHTNING: return "Lightning"; default: return "Unknown"; } } private String parseVars(String format, HashMap<String, String> vars) { Pattern pattern = Pattern.compile("\\{(.*?)\\}"); Matcher matcher = pattern.matcher(format); StringBuffer sb = new StringBuffer(); while (matcher.find()) { String var = vars.get(matcher.group(1)); if (var == null) var = ""; matcher.appendReplacement(sb, Matcher.quoteReplacement(var)); } matcher.appendTail(sb); return sb.toString(); } boolean canBuild(Block b, Player p) { // Check spawn distance int spawnSize = p.getServer().getSpawnRadius(); Location spawn = p.getWorld().getSpawnLocation(); if (spawnSize > 0) { int distanceFromSpawn = (int) Math.max(Math.abs(p.getLocation().getBlockX() - spawn.getBlockX()), Math.abs(p.getLocation().getBlockZ() - spawn.getBlockZ())); if (distanceFromSpawn <= spawnSize) return false; } // Check if another plugin stops us from building here BlockPlaceEvent event = new BlockPlaceEvent(b, b.getState(), b.getRelative(BlockFace.DOWN), p.getItemInHand(), p, true); pm.callEvent(event); if (event.isCancelled()) return false; return true; } /** * Find a block near the base block to place the tombstone * @param base * @return */ Block findPlace(Block base) { if (canReplace(base.getType())) return base; int x = base.getX(); int y = base.getY(); int z = base.getZ(); World w = base.getWorld(); for (int i = x - 1; i < x + 1; i++) { for (int j = z - 1; j < z + 1; j++) { Block b = w.getBlockAt(i, y, j); if (canReplace(b.getType())) return b; } } return null; } Block findLarge(Block base) { // Check all 4 sides for air. Block exp; exp = base.getWorld().getBlockAt(base.getX() - 1, base.getY(), base.getZ()); if (canReplace(exp.getType()) && (!noInterfere || !checkInterfere(exp))) return exp; exp = base.getWorld().getBlockAt(base.getX(), base.getY(), base.getZ() - 1); if (canReplace(exp.getType()) && (!noInterfere || !checkInterfere(exp))) return exp; exp = base.getWorld().getBlockAt(base.getX() + 1, base.getY(), base.getZ()); if (canReplace(exp.getType()) && (!noInterfere || !checkInterfere(exp))) return exp; exp = base.getWorld().getBlockAt(base.getX(), base.getY(), base.getZ() + 1); if (canReplace(exp.getType()) && (!noInterfere || !checkInterfere(exp))) return exp; return null; } boolean checkInterfere(Block base) { // Check all 4 sides for an interfering block int baseX = base.getX(); int baseY = base.getY(); int baseZ = base.getZ(); Block exp; exp = base.getWorld().getBlockAt(baseX - 1, baseY, baseZ); if (willInterfere(exp.getType())) return true; exp = base.getWorld().getBlockAt(baseX, baseY, baseZ - 1); if (willInterfere(exp.getType())) return true; exp = base.getWorld().getBlockAt(baseX + 1, baseY, baseZ); if (willInterfere(exp.getType())) return true; exp = base.getWorld().getBlockAt(baseX, baseY, baseZ + 1); if (willInterfere(exp.getType())) return true; return false; } boolean willInterfere(Material mat) { if (mat == Material.CHEST || mat == Material.SIGN_POST || mat == Material.WALL_SIGN) return true; return false; } boolean canReplace(Material mat) { return (mat == Material.AIR || mat == Material.SAPLING || mat == Material.WATER || mat == Material.STATIONARY_WATER || mat == Material.LAVA || mat == Material.STATIONARY_LAVA || mat == Material.YELLOW_FLOWER || mat == Material.RED_ROSE || mat == Material.BROWN_MUSHROOM || mat == Material.RED_MUSHROOM || mat == Material.FIRE || mat == Material.CROPS || mat == Material.SNOW || mat == Material.SUGAR_CANE); } } @SuppressWarnings("unused") private class sListener implements Listener { @EventHandler(priority = EventPriority.HIGHEST) public void onPluginEnable(PluginEnableEvent event) { if (lwcPlugin == null) { if (event.getPlugin().getDescription().getName().equalsIgnoreCase("LWC")) { lwcPlugin = (LWCPlugin)checkPlugin(event.getPlugin()); } } if (permissions == null) { if (event.getPlugin().getDescription().getName().equalsIgnoreCase("Permissions")) { permissions = (Permissions)checkPlugin(event.getPlugin()); } } } @EventHandler(priority = EventPriority.HIGHEST) public void onPluginDisable(PluginDisableEvent event) { if (event.getPlugin() == lwcPlugin) { log.info("[Tombstone] LWC plugin lost."); lwcPlugin = null; } if (event.getPlugin() == permissions) { log.info("[Tombstone] Permissions plugin lost."); permissions = null; } } } private class TombThread extends Thread { public void run() { long cTime = System.currentTimeMillis() / 1000; for (Iterator<TombBlock> iter = tombList.iterator(); iter.hasNext();) { TombBlock tBlock = iter.next(); if (lwcRemove && tBlock.getLwcEnabled() && lwcPlugin != null) { if (cTime > (tBlock.getTime() + lwcTime)) { // Remove the protection on the block deactivateLWC(tBlock, false); tBlock.setLwcEnabled(false); Player p = getServer().getPlayer(tBlock.getOwner()); if (p != null) sendMessage(p, "LWC Protection disabled on your tombstone!"); } } // Remove block, drop items on ground (One last free-for-all) if (tombRemove && cTime > (tBlock.getTime() + removeTime)) { tBlock.getBlock().getChunk().load(); if (tBlock.getLwcEnabled()) { deactivateLWC(tBlock, true); } if (tBlock.getSign() != null) tBlock.getSign().setType(Material.AIR); tBlock.getBlock().setType(Material.AIR); if (tBlock.getLBlock() != null) tBlock.getLBlock().setType(Material.AIR); // Remove from tombList iter.remove(); removeTomb(tBlock, false); Player p = getServer().getPlayer(tBlock.getOwner()); if (p != null) sendMessage(p, "Your tombstone has been destroyed!"); Block b = tBlock.getBlock(); logEvent("[Tombstone] Removed tombstone @ (" + b.getX() + ", " + b.getY() + ", " + b.getZ() + ")"); } } } } private class TombBlock { private Block block; private Block lBlock; private Block sign; private long time; private String owner; private boolean lwcEnabled = false; TombBlock(Block block, Block lBlock, Block sign, String owner, long time) { this.block = block; this.lBlock = lBlock; this.sign = sign; this.owner = owner; this.time = time; } TombBlock(Block block, Block lBlock, Block sign, String owner, long time, boolean lwc) { this.block = block; this.lBlock = lBlock; this.sign = sign; this.owner = owner; this.time = time; this.lwcEnabled = lwc; } long getTime() { return time; } Block getBlock() { return block; } Block getLBlock() { return lBlock; } Block getSign() { return sign; } String getOwner() { return owner; } boolean getLwcEnabled() { return lwcEnabled; } void setLwcEnabled(boolean val) { lwcEnabled = val; } } }