diff --git a/pom.xml b/pom.xml index f8e3766..8f97fd6 100644 --- a/pom.xml +++ b/pom.xml @@ -1,4 +1,3 @@ - 4.0.0 @@ -28,24 +27,22 @@ http://ci.kitteh.org/job/LogBlock - - - kittehReleases - Kitteh Releases - http://repo.kitteh.org/content/repositories/releases - - - kittehSnapshots - Kitteh Snapshots - http://repo.kitteh.org/content/repositories/snapshots - - + + + md_5-releases + http://repo.md-5.net/content/repositories/releases/ + + + md_5-snapshots + http://repo.md-5.net/content/repositories/snapshots/ + + org.bukkit bukkit - 1.6.1-R0.1-SNAPSHOT + 1.7.2-R0.3 ${project.groupId} @@ -57,7 +54,7 @@ com.sk89q worldedit - 5.5 + 6.0.0-SNAPSHOT diff --git a/src/main/java/de/diddiz/LogBlock/Actor.java b/src/main/java/de/diddiz/LogBlock/Actor.java new file mode 100644 index 0000000..69987c7 --- /dev/null +++ b/src/main/java/de/diddiz/LogBlock/Actor.java @@ -0,0 +1,99 @@ +package de.diddiz.LogBlock; + +import static de.diddiz.util.BukkitUtils.entityName; + +import java.sql.ResultSet; +import java.sql.SQLException; + +import org.bukkit.entity.Entity; +import org.bukkit.entity.EntityType; +import org.bukkit.entity.Player; +import org.bukkit.projectiles.BlockProjectileSource; +import org.bukkit.projectiles.ProjectileSource; + +public class Actor { + + @Override + public int hashCode() { + int hash = 5; + hash = 79 * hash + (this.UUID != null ? this.UUID.hashCode() : 0); + return hash; + } + + @Override + public boolean equals(Object obj) { + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + final Actor other = (Actor) obj; + return ((this.UUID == null && other.UUID == null) || this.UUID.equals(other.UUID)); + } + + final String name; + final String UUID; + + public Actor(String name, String UUID) { + this.name = name; + this.UUID = UUID; + + } + + public Actor(String name, java.util.UUID UUID) { + this.name = name; + this.UUID = UUID.toString(); + + } + + public Actor(String name) { + this(name, generateUUID(name)); + } + + public Actor(ResultSet rs) throws SQLException { + this(rs.getString("playername"),rs.getString("UUID")); + } + + public String getName() { + return name; + } + + public String getUUID() { + return UUID; + } + + public static Actor actorFromEntity(Entity entity) { + if (entity instanceof Player) { + return new Actor(entityName(entity),entity.getUniqueId()); + } else { + return new Actor(entityName(entity)); + } + } + + public static Actor actorFromEntity(EntityType entity) { + return new Actor(entity.getName()); + } + + public static Actor actorFromProjectileSource(ProjectileSource psource) { + if (psource instanceof Entity) return actorFromEntity((Entity) psource); + if (psource instanceof BlockProjectileSource) return new Actor(((BlockProjectileSource) psource).getBlock().getType().toString()); + else return new Actor(psource.toString()); + + } + + public static boolean isValidUUID(String uuid) { + try { + java.util.UUID.fromString(uuid); + return true; + } catch (IllegalArgumentException e) { + return false; + } + } + + public static String generateUUID(String name) { + return "log_" + name; + + } + +} diff --git a/src/main/java/de/diddiz/LogBlock/BlockChange.java b/src/main/java/de/diddiz/LogBlock/BlockChange.java index 3e839c4..493e52d 100644 --- a/src/main/java/de/diddiz/LogBlock/BlockChange.java +++ b/src/main/java/de/diddiz/LogBlock/BlockChange.java @@ -8,40 +8,44 @@ import de.diddiz.util.BukkitUtils; import org.bukkit.Location; import de.diddiz.LogBlock.config.Config; +import static de.diddiz.util.LoggingUtil.checkText; import org.bukkit.Material; public class BlockChange implements LookupCacheElement { public final long id, date; public final Location loc; + public final Actor actor; public final String playerName; public final int replaced, type; public final byte data; public final String signtext; public final ChestAccess ca; - public BlockChange(long date, Location loc, String playerName, int replaced, int type, byte data, String signtext, ChestAccess ca) { + public BlockChange(long date, Location loc, Actor actor, int replaced, int type, byte data, String signtext, ChestAccess ca) { id = 0; this.date = date; this.loc = loc; - this.playerName = playerName; + this.actor = actor; this.replaced = replaced; this.type = type; this.data = data; - this.signtext = signtext; + this.signtext = checkText(signtext); this.ca = ca; + this.playerName = actor == null ? null : actor.getName(); } public BlockChange(ResultSet rs, QueryParams p) throws SQLException { id = p.needId ? rs.getInt("id") : 0; date = p.needDate ? rs.getTimestamp("date").getTime() : 0; loc = p.needCoords ? new Location(p.world, rs.getInt("x"), rs.getInt("y"), rs.getInt("z")) : null; + actor = p.needPlayer ? new Actor(rs) : null; playerName = p.needPlayer ? rs.getString("playername") : null; replaced = p.needType ? rs.getInt("replaced") : 0; type = p.needType ? rs.getInt("type") : 0; data = p.needData ? rs.getByte("data") : (byte)0; signtext = p.needSignText ? rs.getString("signtext") : null; - ca = p.needChestAccess && rs.getShort("itemtype") != 0 && rs.getShort("itemamount") != 0 ? new ChestAccess(rs.getShort("itemtype"), rs.getShort("itemamount"), rs.getByte("itemdata")) : null; + ca = p.needChestAccess && rs.getShort("itemtype") != 0 && rs.getShort("itemamount") != 0 ? new ChestAccess(rs.getShort("itemtype"), rs.getShort("itemamount"), rs.getShort("itemdata")) : null; } @Override @@ -49,8 +53,8 @@ public class BlockChange implements LookupCacheElement final StringBuilder msg = new StringBuilder(); if (date > 0) msg.append(Config.formatter.format(date)).append(" "); - if (playerName != null) - msg.append(playerName).append(" "); + if (actor != null) + msg.append(actor.getName()).append(" "); if (signtext != null) { final String action = type == 0 ? "destroyed " : "created "; if (!signtext.contains("\0")) @@ -64,9 +68,9 @@ public class BlockChange implements LookupCacheElement if (ca.itemType == 0 || ca.itemAmount == 0) msg.append("looked inside ").append(materialName(type)); else if (ca.itemAmount < 0) - msg.append("took ").append(-ca.itemAmount).append("x ").append(materialName(ca.itemType, ca.itemData)); + msg.append("took ").append(-ca.itemAmount).append("x ").append(materialName(ca.itemType, ca.itemData)).append(" from ").append(materialName(type)); else - msg.append("put in ").append(ca.itemAmount).append("x ").append(materialName(ca.itemType, ca.itemData)); + msg.append("put ").append(ca.itemAmount).append("x ").append(materialName(ca.itemType, ca.itemData)).append(" into ").append(materialName(type)); } else if (BukkitUtils.getContainerBlocks().contains(Material.getMaterial(type))) msg.append("opened ").append(materialName(type)); else if (type == 64 || type == 71) diff --git a/src/main/java/de/diddiz/LogBlock/ChatMessage.java b/src/main/java/de/diddiz/LogBlock/ChatMessage.java index 00272d5..ec5920e 100644 --- a/src/main/java/de/diddiz/LogBlock/ChatMessage.java +++ b/src/main/java/de/diddiz/LogBlock/ChatMessage.java @@ -1,5 +1,6 @@ package de.diddiz.LogBlock; +import static de.diddiz.util.LoggingUtil.checkText; import java.sql.ResultSet; import java.sql.SQLException; import org.bukkit.Location; @@ -8,17 +9,20 @@ public class ChatMessage implements LookupCacheElement { final long id, date; final String playerName, message; + final Actor player; - public ChatMessage(String playerName, String message) { + public ChatMessage(Actor player, String message) { id = 0; date = System.currentTimeMillis() / 1000; - this.playerName = playerName; - this.message = message; + this.player = player; + this.message = checkText(message); + this.playerName = player == null ? null : player.getName(); } public ChatMessage(ResultSet rs, QueryParams p) throws SQLException { id = p.needId ? rs.getInt("id") : 0; date = p.needDate ? rs.getTimestamp("date").getTime() : 0; + player = p.needPlayer ? new Actor(rs) : null; playerName = p.needPlayer ? rs.getString("playername") : null; message = p.needMessage ? rs.getString("message") : null; } @@ -30,6 +34,6 @@ public class ChatMessage implements LookupCacheElement @Override public String getMessage() { - return (playerName != null ? "<" + playerName + "> " : "") + (message != null ? message : ""); + return (player != null ? "<" + player.getName() + "> " : "") + (message != null ? message : ""); } } diff --git a/src/main/java/de/diddiz/LogBlock/ChestAccess.java b/src/main/java/de/diddiz/LogBlock/ChestAccess.java index a59080b..f31b735 100644 --- a/src/main/java/de/diddiz/LogBlock/ChestAccess.java +++ b/src/main/java/de/diddiz/LogBlock/ChestAccess.java @@ -2,10 +2,9 @@ package de.diddiz.LogBlock; public class ChestAccess { - final short itemType, itemAmount; - final byte itemData; + final short itemType, itemAmount, itemData; - public ChestAccess(short itemType, short itemAmount, byte itemData) { + public ChestAccess(short itemType, short itemAmount, short itemData) { this.itemType = itemType; this.itemAmount = itemAmount; this.itemData = itemData >= 0 ? itemData : 0; diff --git a/src/main/java/de/diddiz/LogBlock/CommandsHandler.java b/src/main/java/de/diddiz/LogBlock/CommandsHandler.java index a8aadb2..c6636c3 100755 --- a/src/main/java/de/diddiz/LogBlock/CommandsHandler.java +++ b/src/main/java/de/diddiz/LogBlock/CommandsHandler.java @@ -306,6 +306,8 @@ public class CommandsHandler implements CommandExecutor } } catch (final IllegalArgumentException ex) { sender.sendMessage(ChatColor.RED + ex.getMessage()); + } catch (final ArrayIndexOutOfBoundsException ex) { + sender.sendMessage(ChatColor.RED + "Not enough arguments given"); } catch (final Exception ex) { sender.sendMessage(ChatColor.RED + "Error, check server.log"); getLogger().log(Level.WARNING, "Exception in commands handler: ", ex); @@ -404,7 +406,7 @@ public class CommandsHandler implements CommandExecutor params.needPlayer = true; if (params.types.isEmpty() || Block.inList(params.types, 63) || Block.inList(params.types, 68)) params.needSignText = true; - if (params.bct == BlockChangeType.CHESTACCESS || params.types.isEmpty() || Block.inList(params.types, 23) || Block.inList(params.types, 54) || Block.inList(params.types, 61) || Block.inList(params.types, 62)) + if (params.bct == BlockChangeType.CHESTACCESS || params.bct == BlockChangeType.ALL) params.needChestAccess = true; } conn = logblock.getConnection(); @@ -464,7 +466,7 @@ public class CommandsHandler implements CommandExecutor params.needPlayer = true; if (params.types.isEmpty() || Block.inList(params.types, 63) || Block.inList(params.types, 68)) params.needSignText = true; - if (params.types.isEmpty() || Block.inList(params.types, 23) || Block.inList(params.types, 54) || Block.inList(params.types, 61) || Block.inList(params.types, 62)) + if (params.bct == BlockChangeType.CHESTACCESS || params.bct == BlockChangeType.ALL) params.needChestAccess = true; } conn = logblock.getConnection(); @@ -536,9 +538,8 @@ public class CommandsHandler implements CommandExecutor public void run() { try { params.needCoords = true; - if (params.bct == BlockChangeType.CHESTACCESS || params.types.isEmpty() || Block.inList(params.types, 23) || Block.inList(params.types, 54) || Block.inList(params.types, 61) || Block.inList(params.types, 62)) { + if (params.bct == BlockChangeType.CHESTACCESS || params.bct == BlockChangeType.ALL) params.needChestAccess = true; - } params.limit = 1; params.sum = SummarizationMode.NONE; conn = logblock.getConnection(); @@ -550,8 +551,8 @@ public class CommandsHandler implements CommandExecutor rs = state.executeQuery(params.getQuery()); if (rs.next()) { final Player player = (Player)sender; - final int y = rs.getInt(2); - final Location loc = new Location(params.world, rs.getInt(1) + 0.5, y, rs.getInt(3) + 0.5, player.getLocation().getYaw(), 90); + final int y = rs.getInt("y"); + final Location loc = new Location(params.world, rs.getInt("x") + 0.5, y, rs.getInt("z") + 0.5, player.getLocation().getYaw(), 90); // Teleport the player sync because omg thread safety logblock.getServer().getScheduler().scheduleSyncDelayedTask(logblock, new Runnable() { @@ -606,7 +607,7 @@ public class CommandsHandler implements CommandExecutor final WorldEditor editor = new WorldEditor(logblock, params.world); while (rs.next()) - editor.queueEdit(rs.getInt("x"), rs.getInt("y"), rs.getInt("z"), rs.getInt("replaced"), rs.getInt("type"), rs.getByte("data"), rs.getString("signtext"), rs.getShort("itemtype"), rs.getShort("itemamount"), rs.getByte("itemdata")); + editor.queueEdit(rs.getInt("x"), rs.getInt("y"), rs.getInt("z"), rs.getInt("replaced"), rs.getInt("type"), rs.getByte("data"), rs.getString("signtext"), rs.getShort("itemtype"), rs.getShort("itemamount"), rs.getShort("itemdata")); final int changes = editor.getSize(); if (changes > 10000) { editor.setSender(sender); @@ -671,7 +672,7 @@ public class CommandsHandler implements CommandExecutor sender.sendMessage(ChatColor.DARK_AQUA + "Searching " + params.getTitle() + ":"); final WorldEditor editor = new WorldEditor(logblock, params.world); while (rs.next()) - editor.queueEdit(rs.getInt("x"), rs.getInt("y"), rs.getInt("z"), rs.getInt("type"), rs.getInt("replaced"), rs.getByte("data"), rs.getString("signtext"), rs.getShort("itemtype"), (short)-rs.getShort("itemamount"), rs.getByte("itemdata")); + editor.queueEdit(rs.getInt("x"), rs.getInt("y"), rs.getInt("z"), rs.getInt("type"), rs.getInt("replaced"), rs.getByte("data"), rs.getString("signtext"), rs.getShort("itemtype"), (short)-rs.getShort("itemamount"), rs.getShort("itemdata")); final int changes = editor.getSize(); if (!params.silent) sender.sendMessage(ChatColor.GREEN.toString() + changes + " blocks found."); diff --git a/src/main/java/de/diddiz/LogBlock/Consumer.java b/src/main/java/de/diddiz/LogBlock/Consumer.java index 57b7dd0..61ccc95 100644 --- a/src/main/java/de/diddiz/LogBlock/Consumer.java +++ b/src/main/java/de/diddiz/LogBlock/Consumer.java @@ -26,13 +26,14 @@ import java.util.logging.Level; import static de.diddiz.LogBlock.config.Config.*; import static de.diddiz.util.BukkitUtils.*; import static org.bukkit.Bukkit.getLogger; +import org.bukkit.projectiles.ProjectileSource; public class Consumer extends TimerTask { private final Queue queue = new LinkedBlockingQueue(); - private final Set failedPlayers = new HashSet(); + private final Set failedPlayers = new HashSet(); private final LogBlock logblock; - private final Map playerIds = new HashMap(); + private final Map playerIds = new HashMap(); private final Lock lock = new ReentrantLock(); Consumer(LogBlock logblock) { @@ -46,8 +47,8 @@ public class Consumer extends TimerTask /** * Logs any block change. Don't try to combine broken and placed blocks. Queue two block changes or use the queueBLockReplace methods. */ - public void queueBlock(String playerName, Location loc, int typeBefore, int typeAfter, byte data) { - queueBlock(playerName, loc, typeBefore, typeAfter, data, null, null); + public void queueBlock(Actor actor, Location loc, int typeBefore, int typeAfter, byte data) { + queueBlock(actor, loc, typeBefore, typeAfter, data, null, null); } /** @@ -56,15 +57,15 @@ public class Consumer extends TimerTask * @param before * Blockstate of the block before actually being destroyed. */ - public void queueBlockBreak(String playerName, BlockState before) { - queueBlockBreak(playerName, new Location(before.getWorld(), before.getX(), before.getY(), before.getZ()), before.getTypeId(), before.getRawData()); + public void queueBlockBreak(Actor actor, BlockState before) { + queueBlockBreak(actor, new Location(before.getWorld(), before.getX(), before.getY(), before.getZ()), before.getTypeId(), before.getRawData()); } /** * Logs a block break. The block type afterwards is assumed to be o (air). */ - public void queueBlockBreak(String playerName, Location loc, int typeBefore, byte dataBefore) { - queueBlock(playerName, loc, typeBefore, 0, dataBefore); + public void queueBlockBreak(Actor actor, Location loc, int typeBefore, byte dataBefore) { + queueBlock(actor, loc, typeBefore, 0, dataBefore); } /** @@ -73,15 +74,15 @@ public class Consumer extends TimerTask * @param after * Blockstate of the block after actually being placed. */ - public void queueBlockPlace(String playerName, BlockState after) { - queueBlockPlace(playerName, new Location(after.getWorld(), after.getX(), after.getY(), after.getZ()), after.getBlock().getTypeId(), after.getBlock().getData()); + public void queueBlockPlace(Actor actor, BlockState after) { + queueBlockPlace(actor, new Location(after.getWorld(), after.getX(), after.getY(), after.getZ()), after.getBlock().getTypeId(), after.getBlock().getData()); } /** * Logs a block place. The block type before is assumed to be o (air). */ - public void queueBlockPlace(String playerName, Location loc, int type, byte data) { - queueBlock(playerName, loc, 0, type, data); + public void queueBlockPlace(Actor actor, Location loc, int type, byte data) { + queueBlock(actor, loc, 0, type, data); } /** @@ -90,93 +91,109 @@ public class Consumer extends TimerTask * @param after * Blockstate of the block after actually being placed. */ - public void queueBlockReplace(String playerName, BlockState before, BlockState after) { - queueBlockReplace(playerName, new Location(before.getWorld(), before.getX(), before.getY(), before.getZ()), before.getTypeId(), before.getRawData(), after.getTypeId(), after.getRawData()); + public void queueBlockReplace(Actor actor, BlockState before, BlockState after) { + queueBlockReplace(actor, new Location(before.getWorld(), before.getX(), before.getY(), before.getZ()), before.getTypeId(), before.getRawData(), after.getTypeId(), after.getRawData()); } /** * @param before * Blockstate of the block before actually being destroyed. */ - public void queueBlockReplace(String playerName, BlockState before, int typeAfter, byte dataAfter) { - queueBlockReplace(playerName, new Location(before.getWorld(), before.getX(), before.getY(), before.getZ()), before.getTypeId(), before.getRawData(), typeAfter, dataAfter); + public void queueBlockReplace(Actor actor, BlockState before, int typeAfter, byte dataAfter) { + queueBlockReplace(actor, new Location(before.getWorld(), before.getX(), before.getY(), before.getZ()), before.getTypeId(), before.getRawData(), typeAfter, dataAfter); } /** * @param after * Blockstate of the block after actually being placed. */ - public void queueBlockReplace(String playerName, int typeBefore, byte dataBefore, BlockState after) { - queueBlockReplace(playerName, new Location(after.getWorld(), after.getX(), after.getY(), after.getZ()), typeBefore, dataBefore, after.getTypeId(), after.getRawData()); + public void queueBlockReplace(Actor actor, int typeBefore, byte dataBefore, BlockState after) { + queueBlockReplace(actor, new Location(after.getWorld(), after.getX(), after.getY(), after.getZ()), typeBefore, dataBefore, after.getTypeId(), after.getRawData()); } - public void queueBlockReplace(String playerName, Location loc, int typeBefore, byte dataBefore, int typeAfter, byte dataAfter) { + public void queueBlockReplace(Actor actor, Location loc, int typeBefore, byte dataBefore, int typeAfter, byte dataAfter) { if (dataBefore == 0 && (typeBefore != typeAfter)) - queueBlock(playerName, loc, typeBefore, typeAfter, dataAfter); + queueBlock(actor, loc, typeBefore, typeAfter, dataAfter); else { - queueBlockBreak(playerName, loc, typeBefore, dataBefore); - queueBlockPlace(playerName, loc, typeAfter, dataAfter); + queueBlockBreak(actor, loc, typeBefore, dataBefore); + queueBlockPlace(actor, loc, typeAfter, dataAfter); } } /** * @param container - * The respective container. Must be an instance of Chest, Dispencer or Furnace. + * The respective container. Must be an instance of an InventoryHolder. */ - public void queueChestAccess(String playerName, BlockState container, short itemType, short itemAmount, byte itemData) { + public void queueChestAccess(Actor actor, BlockState container, short itemType, short itemAmount, short itemData) { if (!(container instanceof InventoryHolder)) return; - queueChestAccess(playerName, new Location(container.getWorld(), container.getX(), container.getY(), container.getZ()), container.getTypeId(), itemType, itemAmount, itemData); + queueChestAccess(actor, new Location(container.getWorld(), container.getX(), container.getY(), container.getZ()), container.getTypeId(), itemType, itemAmount, itemData); } /** * @param type - * Type id of the container. Must be 63 or 68. + * Type id of the container. */ - public void queueChestAccess(String playerName, Location loc, int type, short itemType, short itemAmount, byte itemData) { - queueBlock(playerName, loc, type, type, (byte)0, null, new ChestAccess(itemType, itemAmount, itemData)); + public void queueChestAccess(Actor actor, Location loc, int type, short itemType, short itemAmount, short itemData) { + queueBlock(actor, loc, type, type, (byte)0, null, new ChestAccess(itemType, itemAmount, itemData)); } /** * Logs a container block break. The block type before is assumed to be o (air). All content is assumed to be taken. * * @param container - * Must be instanceof InventoryHolder + * Must be an instance of InventoryHolder */ - public void queueContainerBreak(String playerName, BlockState container) { + public void queueContainerBreak(Actor actor, BlockState container) { if (!(container instanceof InventoryHolder)) return; - queueContainerBreak(playerName, new Location(container.getWorld(), container.getX(), container.getY(), container.getZ()), container.getTypeId(), container.getRawData(), ((InventoryHolder)container).getInventory()); + queueContainerBreak(actor, new Location(container.getWorld(), container.getX(), container.getY(), container.getZ()), container.getTypeId(), container.getRawData(), ((InventoryHolder)container).getInventory()); } /** * Logs a container block break. The block type before is assumed to be o (air). All content is assumed to be taken. */ - public void queueContainerBreak(String playerName, Location loc, int type, byte data, Inventory inv) { + public void queueContainerBreak(Actor actor, Location loc, int type, byte data, Inventory inv) { final ItemStack[] items = compressInventory(inv.getContents()); for (final ItemStack item : items) - queueChestAccess(playerName, loc, type, (short)item.getTypeId(), (short)(item.getAmount() * -1), rawData(item)); - queueBlockBreak(playerName, loc, type, data); + queueChestAccess(actor, loc, type, (short)item.getTypeId(), (short)(item.getAmount() * -1), rawData(item)); + queueBlockBreak(actor, loc, type, data); } /** * @param killer - * Can' be null + * Can't be null * @param victim - * Can' be null + * Can't be null */ public void queueKill(Entity killer, Entity victim) { if (killer == null || victim == null) return; int weapon = 0; - - if(killer instanceof Projectile) - killer = ((Projectile)killer).getShooter(); - + Actor killerActor = Actor.actorFromEntity(killer); + // If it's a projectile kill we want to manually assign the weapon, so check for player before converting a projectile to its source if (killer instanceof Player && ((Player)killer).getItemInHand() != null) weapon = ((Player)killer).getItemInHand().getTypeId(); - - queueKill(victim.getLocation(), entityName(killer), entityName(victim), weapon); + if(killer instanceof Projectile) { + ProjectileSource ps = ((Projectile)killer).getShooter(); + killerActor = Actor.actorFromProjectileSource(ps); + weapon = itemIDfromProjectileEntity(killer); + } + + queueKill(victim.getLocation(), killerActor, Actor.actorFromEntity(victim), weapon); + } + + /** + * This form should only be used when the killer is not an entity e.g. for fall or suffocation damage + * @param killer + * Can't be null + * @param victim + * Can't be null + */ + public void queueKill(Actor killer, Entity victim) { + if (killer == null || victim == null) + return; + queueKill(victim.getLocation(), killer, Actor.actorFromEntity(victim), 0); } /** @@ -191,24 +208,24 @@ public class Consumer extends TimerTask * @deprecated Use {@link #queueKill(Location,String,String,int)} instead */ @Deprecated - public void queueKill(World world, String killerName, String victimName, int weapon) { - queueKill(new Location(world, 0, 0, 0), killerName, victimName, weapon); + public void queueKill(World world, Actor killer, Actor victim, int weapon) { + queueKill(new Location(world, 0, 0, 0), killer, victim, weapon); } /** * @param location * Location of the victim. - * @param killerName - * Name of the killer. Can be null. - * @param victimName - * Name of the victim. Can't be null. + * @param killer + * Killer Actor. Can be null. + * @param victim + * Victim Actor. Can't be null. * @param weapon * Item id of the weapon. 0 for no weapon. */ - public void queueKill(Location location, String killerName, String victimName, int weapon) { - if (victimName == null || !isLogged(location.getWorld())) + public void queueKill(Location location, Actor killer, Actor victim, int weapon) { + if (victim == null || !isLogged(location.getWorld())) return; - queue.add(new KillRow(location, killerName == null ? null : killerName.replaceAll("[^a-zA-Z0-9_]", ""), victimName.replaceAll("[^a-zA-Z0-9_]", ""), weapon)); + queue.add(new KillRow(location, killer == null ? null : killer, victim, weapon)); } /** @@ -217,14 +234,14 @@ public class Consumer extends TimerTask * @param lines * The four lines on the sign. */ - public void queueSignBreak(String playerName, Location loc, int type, byte data, String[] lines) { + public void queueSignBreak(Actor actor, Location loc, int type, byte data, String[] lines) { if (type != 63 && type != 68 || lines == null || lines.length != 4) return; - queueBlock(playerName, loc, type, 0, data, lines[0] + "\0" + lines[1] + "\0" + lines[2] + "\0" + lines[3], null); + queueBlock(actor, loc, type, 0, data, lines[0] + "\0" + lines[1] + "\0" + lines[2] + "\0" + lines[3], null); } - public void queueSignBreak(String playerName, Sign sign) { - queueSignBreak(playerName, new Location(sign.getWorld(), sign.getX(), sign.getY(), sign.getZ()), sign.getTypeId(), sign.getRawData(), sign.getLines()); + public void queueSignBreak(Actor actor, Sign sign) { + queueSignBreak(actor, new Location(sign.getWorld(), sign.getX(), sign.getY(), sign.getZ()), sign.getTypeId(), sign.getRawData(), sign.getLines()); } /** @@ -233,17 +250,17 @@ public class Consumer extends TimerTask * @param lines * The four lines on the sign. */ - public void queueSignPlace(String playerName, Location loc, int type, byte data, String[] lines) { + public void queueSignPlace(Actor actor, Location loc, int type, byte data, String[] lines) { if (type != 63 && type != 68 || lines == null || lines.length != 4) return; - queueBlock(playerName, loc, 0, type, data, lines[0] + "\0" + lines[1] + "\0" + lines[2] + "\0" + lines[3], null); + queueBlock(actor, loc, 0, type, data, lines[0] + "\0" + lines[1] + "\0" + lines[2] + "\0" + lines[3], null); } - public void queueSignPlace(String playerName, Sign sign) { - queueSignPlace(playerName, new Location(sign.getWorld(), sign.getX(), sign.getY(), sign.getZ()), sign.getTypeId(), sign.getRawData(), sign.getLines()); + public void queueSignPlace(Actor actor, Sign sign) { + queueSignPlace(actor, new Location(sign.getWorld(), sign.getX(), sign.getY(), sign.getZ()), sign.getTypeId(), sign.getRawData(), sign.getLines()); } - public void queueChat(String player, String message) { + public void queueChat(Actor player, String message) { for (String ignored : Config.ignoredChat) { if (message.startsWith(ignored)) { return; @@ -261,7 +278,7 @@ public class Consumer extends TimerTask } @Override - public void run() { + public synchronized void run() { if (queue.isEmpty() || !lock.tryLock()) return; final Connection conn = logblock.getConnection(); @@ -282,12 +299,12 @@ public class Consumer extends TimerTask final Row r = queue.poll(); if (r == null) continue; - for (final String player : r.getPlayers()) { - if (!playerIds.containsKey(player)) { - if (!addPlayer(state, player)) { - if (!failedPlayers.contains(player)) { - failedPlayers.add(player); - getLogger().warning("[Consumer] Failed to add player " + player); + for (final Actor actor : r.getActors()) { + if (!playerIds.containsKey(actor)) { + if (!addPlayer(state, actor)) { + if (!failedPlayers.contains(actor)) { + failedPlayers.add(actor); + getLogger().warning("[Consumer] Failed to add player " + actor.getName()); } continue process; } @@ -295,6 +312,24 @@ public class Consumer extends TimerTask } if (r instanceof PreparedStatementRow) { PreparedStatementRow PSRow = (PreparedStatementRow) r; + if (r instanceof MergeableRow) { + int batchCount=count; + // if we've reached our row target but not exceeded our time target, allow merging of up to 50% of our row limit more rows + if (count > forceToProcessAtLeast) batchCount = forceToProcessAtLeast / 2; + while(!queue.isEmpty()) { + MergeableRow mRow = (MergeableRow) PSRow; + Row s = queue.peek(); + if (s == null) break; + if (!(s instanceof MergeableRow)) break; + MergeableRow mRow2 = (MergeableRow) s; + if (mRow.canMerge(mRow2)) { + PSRow = mRow.merge((MergeableRow) queue.poll()); + count++; + batchCount++; + if (batchCount > forceToProcessAtLeast) break; + } else break; + } + } PSRow.setConnection(conn); try { PSRow.executeStatements(); @@ -332,7 +367,7 @@ public class Consumer extends TimerTask public void writeToFile() throws FileNotFoundException { final long time = System.currentTimeMillis(); - final Set insertedPlayers = new HashSet(); + final Set insertedPlayers = new HashSet(); int counter = 0; new File("plugins/LogBlock/import/").mkdirs(); PrintWriter writer = new PrintWriter(new File("plugins/LogBlock/import/queue-" + time + "-0.sql")); @@ -340,10 +375,11 @@ public class Consumer extends TimerTask final Row r = queue.poll(); if (r == null) continue; - for (final String player : r.getPlayers()) - if (!playerIds.containsKey(player) && !insertedPlayers.contains(player)) { - writer.println("INSERT IGNORE INTO `lb-players` (playername) VALUES ('" + player + "');"); - insertedPlayers.add(player); + for (final Actor actor : r.getActors()) + if (!playerIds.containsKey(actor) && !insertedPlayers.contains(actor)) { + // Odd query contruction is to work around innodb auto increment behaviour - bug #492 + writer.println("INSERT IGNORE INTO `lb-players` (playername,UUID) SELECT '" + actor.getName() + "','" + actor.getUUID() + "' FROM `lb-players` WHERE NOT EXISTS (SELECT NULL FROM `lb-players` WHERE UUID = '" + actor.getUUID() + "') LIMIT 1;"); + insertedPlayers.add(actor); } for (final String insert : r.getInserts()) writer.println(insert); @@ -370,25 +406,28 @@ public class Consumer extends TimerTask return true; } - private boolean addPlayer(Statement state, String playerName) throws SQLException { - state.execute("INSERT IGNORE INTO `lb-players` (playername) VALUES ('" + playerName + "')"); - final ResultSet rs = state.executeQuery("SELECT playerid FROM `lb-players` WHERE playername = '" + playerName + "'"); + private boolean addPlayer(Statement state, Actor actor) throws SQLException { + // Odd query contruction is to work around innodb auto increment behaviour - bug #492 + String name = actor.getName(); + String uuid = actor.getUUID(); + state.execute("INSERT IGNORE INTO `lb-players` (playername,UUID) SELECT '" + name + "','" + uuid + "' FROM `lb-players` WHERE NOT EXISTS (SELECT NULL FROM `lb-players` WHERE UUID = '" + uuid + "') LIMIT 1;"); + final ResultSet rs = state.executeQuery("SELECT playerid FROM `lb-players` WHERE UUID = '" + uuid + "'"); if (rs.next()) - playerIds.put(playerName, rs.getInt(1)); + playerIds.put(actor, rs.getInt(1)); rs.close(); - return playerIds.containsKey(playerName); + return playerIds.containsKey(actor); } - private void queueBlock(String playerName, Location loc, int typeBefore, int typeAfter, byte data, String signtext, ChestAccess ca) { + private void queueBlock(Actor actor, Location loc, int typeBefore, int typeAfter, byte data, String signtext, ChestAccess ca) { if (Config.fireCustomEvents) { // Create and call the event - BlockChangePreLogEvent event = new BlockChangePreLogEvent(playerName, loc, typeBefore, typeAfter, data, signtext, ca); + BlockChangePreLogEvent event = new BlockChangePreLogEvent(actor, loc, typeBefore, typeAfter, data, signtext, ca); logblock.getServer().getPluginManager().callEvent(event); if (event.isCancelled()) return; // Update variables - playerName = event.getOwner(); + actor = event.getOwnerActor(); loc = event.getLocation(); typeBefore = event.getTypeBefore(); typeAfter = event.getTypeAfter(); @@ -397,31 +436,37 @@ public class Consumer extends TimerTask ca = event.getChestAccess(); } // Do this last so LogBlock still has final say in what is being added - if (playerName == null || loc == null || typeBefore < 0 || typeAfter < 0 || (Config.safetyIdCheck && (typeBefore > 255 || typeAfter > 255)) || hiddenPlayers.contains(playerName.toLowerCase()) || !isLogged(loc.getWorld()) || typeBefore != typeAfter && hiddenBlocks.contains(typeBefore) && hiddenBlocks.contains(typeAfter)) return; - queue.add(new BlockRow(loc, playerName.replaceAll("[^a-zA-Z0-9_]", ""), typeBefore, typeAfter, data, signtext, ca)); + if (actor == null || loc == null || typeBefore < 0 || typeAfter < 0 || (Config.safetyIdCheck && (typeBefore > 255 || typeAfter > 255)) || hiddenPlayers.contains(actor.getName().toLowerCase()) || !isLogged(loc.getWorld()) || typeBefore != typeAfter && hiddenBlocks.contains(typeBefore) && hiddenBlocks.contains(typeAfter)) return; + queue.add(new BlockRow(loc, actor, typeBefore, typeAfter, data, signtext, ca)); } - private String playerID(String playerName) { - if (playerName == null) + private String playerID(Actor actor) { + if (actor == null) return "NULL"; - final Integer id = playerIds.get(playerName); + final Integer id = playerIds.get(actor); if (id != null) return id.toString(); - return "(SELECT playerid FROM `lb-players` WHERE playername = '" + playerName + "')"; + return "(SELECT playerid FROM `lb-players` WHERE UUID = '" + actor.getUUID() + "')"; } - private Integer playerIDAsInt(String playerName) { - if (playerName == null) { + private Integer playerIDAsInt(Actor actor) { + if (actor == null) { return null; } - return playerIds.get(playerName); + return playerIds.get(actor); } private static interface Row { String[] getInserts(); + /** + * + * @deprecated - Names are not guaranteed to be unique. Use {@link #getActors() } + */ String[] getPlayers(); + + Actor[] getActors(); } private interface PreparedStatementRow extends Row @@ -429,22 +474,28 @@ public class Consumer extends TimerTask abstract void setConnection(Connection connection); abstract void executeStatements() throws SQLException; - } - private class BlockRow extends BlockChange implements PreparedStatementRow + private interface MergeableRow extends PreparedStatementRow + { + abstract boolean isUnique(); + abstract boolean canMerge(MergeableRow row); + abstract MergeableRow merge(MergeableRow second); + } + + private class BlockRow extends BlockChange implements MergeableRow { private Connection connection; - public BlockRow(Location loc, String playerName, int replaced, int type, byte data, String signtext, ChestAccess ca) { - super(System.currentTimeMillis() / 1000, loc, playerName, replaced, type, data, signtext, ca); + public BlockRow(Location loc, Actor actor, int replaced, int type, byte data, String signtext, ChestAccess ca) { + super(System.currentTimeMillis() / 1000, loc, actor, replaced, type, data, signtext, ca); } @Override public String[] getInserts() { final String table = getWorldConfig(loc.getWorld()).table; final String[] inserts = new String[ca != null || signtext != null ? 2 : 1]; - inserts[0] = "INSERT INTO `" + table + "` (date, playerid, replaced, type, data, x, y, z) VALUES (FROM_UNIXTIME(" + date + "), " + playerID(playerName) + ", " + replaced + ", " + type + ", " + data + ", '" + loc.getBlockX() + "', " + loc.getBlockY() + ", '" + loc.getBlockZ() + "');"; + inserts[0] = "INSERT INTO `" + table + "` (date, playerid, replaced, type, data, x, y, z) VALUES (FROM_UNIXTIME(" + date + "), " + playerID(actor) + ", " + replaced + ", " + type + ", " + data + ", '" + loc.getBlockX() + "', " + loc.getBlockY() + ", '" + loc.getBlockZ() + "');"; if (signtext != null) { inserts[1] = "INSERT INTO `" + table + "-sign` (id, signtext) values (LAST_INSERT_ID(), '" + signtext.replace("\\", "\\\\").replace("'", "\\'") + "');"; } @@ -455,7 +506,12 @@ public class Consumer extends TimerTask @Override public String[] getPlayers() { - return new String[]{playerName}; + return new String[]{actor.getName()}; + } + + @Override + public Actor[] getActors() { + return new Actor[]{actor}; } @Override @@ -470,7 +526,7 @@ public class Consumer extends TimerTask PreparedStatement ps1 = null; PreparedStatement ps = null; try { - ps1 = connection.prepareStatement("INSERT INTO `" + table + "` (date, playerid, replaced, type, data, x, y, z) VALUES(FROM_UNIXTIME(?), " + playerID(playerName) + ", ?, ?, ?, ?, ?, ?)", Statement.RETURN_GENERATED_KEYS); + ps1 = connection.prepareStatement("INSERT INTO `" + table + "` (date, playerid, replaced, type, data, x, y, z) VALUES(FROM_UNIXTIME(?), " + playerID(actor) + ", ?, ?, ?, ?, ?, ?)", Statement.RETURN_GENERATED_KEYS); ps1.setLong(1, date ); ps1.setInt(2, replaced); ps1.setInt(3, type); @@ -499,7 +555,15 @@ public class Consumer extends TimerTask ps.executeUpdate(); } } - // we intentionally do not catch SQLException, it is thrown to the caller + catch (final SQLException ex) { + if (ps1 != null) { + getLogger().log(Level.SEVERE, "[Consumer] Troublesome query: " + ps1.toString()); + } + if (ps != null) { + getLogger().log(Level.SEVERE, "[Consumer] Troublesome query: " + ps.toString()); + } + throw ex; + } finally { // individual try/catch here, though ugly, prevents resource leaks if( ps1 != null ) { @@ -524,16 +588,128 @@ public class Consumer extends TimerTask } } } + + @Override + public boolean isUnique() { + return !(signtext == null && ca == null && playerIds.containsKey(actor)); + } + + @Override + public boolean canMerge(MergeableRow row) { + return !this.isUnique() && !row.isUnique() && row instanceof BlockRow && getWorldConfig(loc.getWorld()).table.equals(getWorldConfig(((BlockRow) row).loc.getWorld()).table); + } + + @Override + public MergeableRow merge(MergeableRow singleRow) { + return new MultiBlockChangeRow(this,(BlockRow) singleRow); + } + } + + private class MultiBlockChangeRow implements MergeableRow{ + private List rows = new ArrayList(); + private Connection connection; + private Set players = new HashSet(); + private Set actors = new HashSet(); + private String table; + + MultiBlockChangeRow (BlockRow first, BlockRow second) { + if (first.isUnique() || second.isUnique()) throw new IllegalArgumentException("Can't merge a unique row"); + rows.add(first); + rows.add(second); + actors.addAll(Arrays.asList(first.getActors())); + actors.addAll(Arrays.asList(second.getActors())); + players.addAll(Arrays.asList(first.getPlayers())); + players.addAll(Arrays.asList(second.getPlayers())); + table = getWorldConfig(first.loc.getWorld()).table; + } + + @Override + public void setConnection(Connection connection) { + this.connection = connection; + } + + @Override + public void executeStatements() throws SQLException { + PreparedStatement ps = null; + try { + ps = connection.prepareStatement("INSERT INTO `" + table + "` (date, playerid, replaced, type, data, x, y, z) VALUES(FROM_UNIXTIME(?), ?, ?, ?, ?, ?, ?, ?)"); + for (BlockRow row : rows) { + ps.setLong(1, row.date ); + ps.setInt(2, playerIds.get(row.actor)); + ps.setInt(3, row.replaced); + ps.setInt(4, row.type); + ps.setInt(5, row.data); + ps.setInt(6, row.loc.getBlockX()); + ps.setInt(7, row.loc.getBlockY()); + ps.setInt(8, row.loc.getBlockZ()); + ps.addBatch(); + } + ps.executeBatch(); + } catch (final SQLException ex) { + if (ps != null) { + getLogger().log(Level.SEVERE, "[Consumer] Troublesome query: " + ps.toString()); + } + throw ex; + } finally { + // individual try/catch here, though ugly, prevents resource leaks + if( ps != null ) { + try { + ps.close(); + } + catch(SQLException e) { + e.printStackTrace(); + } + } + } + } + + @Override + public boolean isUnique() { + return true; + } + + @Override + public boolean canMerge(MergeableRow row) { + return !row.isUnique() && row instanceof BlockRow && table.equals(getWorldConfig(((BlockRow) row).loc.getWorld()).table); + } + + @Override + public MergeableRow merge(MergeableRow second) { + if (second.isUnique()) throw new IllegalArgumentException("Can't merge a unique row"); + rows.add((BlockRow) second); + actors.addAll(Arrays.asList(second.getActors())); + players.addAll(Arrays.asList(second.getPlayers())); + return this; + } + + @Override + public String[] getInserts() { + List l = new ArrayList(); + for (BlockRow row : rows) { + l.addAll(Arrays.asList(row.getInserts())); + } + return (String[]) l.toArray(); + } + + @Override + public String[] getPlayers() { + return (String[]) players.toArray(); + } + + @Override + public Actor[] getActors() { + return (Actor[]) actors.toArray(); + } } private class KillRow implements Row { final long date; - final String killer, victim; + final Actor killer, victim; final int weapon; final Location loc; - KillRow(Location loc, String attacker, String defender, int weapon) { + KillRow(Location loc, Actor attacker, Actor defender, int weapon) { date = System.currentTimeMillis() / 1000; this.loc = loc; killer = attacker; @@ -543,12 +719,17 @@ public class Consumer extends TimerTask @Override public String[] getInserts() { - return new String[]{"INSERT INTO `" + getWorldConfig(loc.getWorld()).table + "-kills` (date, killer, victim, weapon, x, y, z) VALUES (FROM_UNIXTIME(" + date + "), " + playerID(killer) + ", " + playerID(victim) + ", " + weapon + ", " + loc.getBlockX() + ", " + loc.getBlockY() + ", " + loc.getBlockZ() + ");"}; + return new String[]{"INSERT INTO `" + getWorldConfig(loc.getWorld()).table + "-kills` (date, killer, victim, weapon, x, y, z) VALUES (FROM_UNIXTIME(" + date + "), " + playerID(killer) + ", " + playerID(victim) + ", " + weapon + ", " + loc.getBlockX() + ", " + (loc.getBlockY() < 0 ? 0 : loc.getBlockY()) + ", " + loc.getBlockZ() + ");"}; } @Override public String[] getPlayers() { - return new String[]{killer, victim}; + return new String[]{killer.getName(), victim.getName()}; + } + + @Override + public Actor[] getActors() { + return new Actor[]{killer,victim}; } } @@ -556,18 +737,23 @@ public class Consumer extends TimerTask { private Connection connection; - ChatRow(String player, String message) { + ChatRow(Actor player, String message) { super(player, message); } @Override public String[] getInserts() { - return new String[]{"INSERT INTO `lb-chat` (date, playerid, message) VALUES (FROM_UNIXTIME(" + date + "), " + playerID(playerName) + ", '" + message.replace("\\", "\\\\").replace("'", "\\'") + "');"}; + return new String[]{"INSERT INTO `lb-chat` (date, playerid, message) VALUES (FROM_UNIXTIME(" + date + "), " + playerID(player) + ", '" + message.replace("\\", "\\\\").replace("'", "\\'") + "');"}; } @Override public String[] getPlayers() { - return new String[]{playerName}; + return new String[]{player.getName()}; + } + + @Override + public Actor[] getActors() { + return new Actor[]{player}; } @Override @@ -581,9 +767,9 @@ public class Consumer extends TimerTask Integer id; String sql = "INSERT INTO `lb-chat` (date, playerid, message) VALUES (FROM_UNIXTIME(?), "; - if ((id = playerIDAsInt(playerName)) == null) { + if ((id = playerIDAsInt(player)) == null) { noID = true; - sql += playerID(playerName) + ", "; + sql += playerID(player) + ", "; } else { sql += "?, "; } @@ -618,45 +804,59 @@ public class Consumer extends TimerTask private class PlayerJoinRow implements Row { - private final String playerName; + private final Actor player; private final long lastLogin; private final String ip; PlayerJoinRow(Player player) { - playerName = player.getName(); + this.player = Actor.actorFromEntity(player); lastLogin = System.currentTimeMillis() / 1000; ip = player.getAddress().toString().replace("'", "\\'"); } @Override public String[] getInserts() { - return new String[]{"UPDATE `lb-players` SET lastlogin = FROM_UNIXTIME(" + lastLogin + "), firstlogin = IF(firstlogin = 0, FROM_UNIXTIME(" + lastLogin + "), firstlogin), ip = '" + ip + "' WHERE " + (playerIds.containsKey(playerName) ? "playerid = " + playerIds.get(playerName) : "playerName = '" + playerName + "'") + ";"}; + if (logPlayerInfo) + return new String[]{"UPDATE `lb-players` SET lastlogin = FROM_UNIXTIME(" + lastLogin + "), firstlogin = IF(firstlogin = 0, FROM_UNIXTIME(" + lastLogin + "), firstlogin), ip = '" + ip + "', playername = '" + player.getName() + "' WHERE UUID = '" + player.getUUID() + "';"}; + return new String[]{"UPDATE `lb-players` SET playername = '" + player.getName() + "' WHERE UUID = '" + player.getUUID() + "';"}; } @Override public String[] getPlayers() { - return new String[]{playerName}; + return new String[]{player.getName()}; + } + + @Override + public Actor[] getActors() { + return new Actor[]{player}; } } private class PlayerLeaveRow implements Row - { - private final String playerName; + {; private final long leaveTime; + private final Actor actor; PlayerLeaveRow(Player player) { - playerName = player.getName(); leaveTime = System.currentTimeMillis() / 1000; + actor = Actor.actorFromEntity(player); } @Override public String[] getInserts() { - return new String[]{"UPDATE `lb-players` SET onlinetime = onlinetime + TIMESTAMPDIFF(SECOND, lastlogin, FROM_UNIXTIME('" + leaveTime + "')) WHERE lastlogin > 0 && " + (playerIds.containsKey(playerName) ? "playerid = " + playerIds.get(playerName) : "playerName = '" + playerName + "'") + ";"}; + if (logPlayerInfo) + return new String[]{"UPDATE `lb-players` SET onlinetime = onlinetime + TIMESTAMPDIFF(SECOND, lastlogin, FROM_UNIXTIME('" + leaveTime + "')), playername = '" + actor.getName() + "' WHERE lastlogin > 0 && UUID = '" + actor.getUUID() + "';"}; + return new String[]{"UPDATE `lb-players` SET playername = '" + actor.getName() + "' WHERE UUID = '" + actor.getUUID() + "';"}; } @Override public String[] getPlayers() { - return new String[]{playerName}; + return new String[]{actor.getName()}; + } + + @Override + public Actor[] getActors() { + return new Actor[]{actor}; } } } diff --git a/src/main/java/de/diddiz/LogBlock/LogBlock.java b/src/main/java/de/diddiz/LogBlock/LogBlock.java index 0e31eff..860701e 100644 --- a/src/main/java/de/diddiz/LogBlock/LogBlock.java +++ b/src/main/java/de/diddiz/LogBlock/LogBlock.java @@ -1,9 +1,30 @@ package de.diddiz.LogBlock; import de.diddiz.LogBlock.config.Config; -import de.diddiz.LogBlock.listeners.*; +import de.diddiz.LogBlock.listeners.BanListener; +import de.diddiz.LogBlock.listeners.BlockBreakLogging; +import de.diddiz.LogBlock.listeners.BlockBurnLogging; +import de.diddiz.LogBlock.listeners.BlockPlaceLogging; +import de.diddiz.LogBlock.listeners.BlockSpreadLogging; +import de.diddiz.LogBlock.listeners.ChatLogging; +import de.diddiz.LogBlock.listeners.ChestAccessLogging; +import de.diddiz.LogBlock.listeners.CreatureInteractLogging; +import de.diddiz.LogBlock.listeners.EndermenLogging; +import de.diddiz.LogBlock.listeners.ExplosionLogging; +import de.diddiz.LogBlock.listeners.FluidFlowLogging; +import de.diddiz.LogBlock.listeners.InteractLogging; +import de.diddiz.LogBlock.listeners.KillLogging; +import de.diddiz.LogBlock.listeners.LeavesDecayLogging; +import de.diddiz.LogBlock.listeners.LockedChestDecayLogging; +import de.diddiz.LogBlock.listeners.PlayerInfoLogging; +import de.diddiz.LogBlock.listeners.SignChangeLogging; +import de.diddiz.LogBlock.listeners.SnowFadeLogging; +import de.diddiz.LogBlock.listeners.SnowFormLogging; +import de.diddiz.LogBlock.listeners.StructureGrowLogging; +import de.diddiz.LogBlock.listeners.ToolListener; +import de.diddiz.LogBlock.listeners.WitherLogging; import de.diddiz.util.MySQLConnectionPool; -import de.diddiz.worldedit.LogBlockEditSessionFactory; +import de.diddiz.worldedit.WorldEditLoggingHook; import org.bukkit.ChatColor; import org.bukkit.command.Command; import org.bukkit.command.CommandSender; @@ -24,6 +45,7 @@ import java.util.Timer; import java.util.logging.Level; import static de.diddiz.LogBlock.config.Config.*; +import static de.diddiz.util.MaterialName.materialName; import static org.bukkit.Bukkit.getPluginManager; public class LogBlock extends JavaPlugin @@ -65,6 +87,13 @@ public class LogBlock extends JavaPlugin noDb = true; return; } + final Statement st = conn.createStatement(); + final ResultSet rs = st.executeQuery("SHOW CHARACTER SET where charset='utf8mb4';"); + if (rs.next()) { + Config.mb4=true; + // Allegedly JDBC driver since 2010 hasn't needed this. I did. + st.executeQuery("SET NAMES utf8mb4;"); + } conn.close(); if (updater.update()) load(this); @@ -81,6 +110,7 @@ public class LogBlock extends JavaPlugin @Override public void onEnable() { + materialName(0); // Force static code to run final PluginManager pm = getPluginManager(); if (errorAtLoading) { pm.disablePlugin(this); @@ -89,27 +119,32 @@ public class LogBlock extends JavaPlugin if (noDb) return; if (pm.getPlugin("WorldEdit") != null) { - LogBlockEditSessionFactory.initialize(this); + if(Integer.parseInt(pm.getPlugin("WorldEdit").getDescription().getVersion().substring(0, 1)) > 5) { + new WorldEditLoggingHook(this).hook(); + } else { + getLogger().warning("Failed to hook into WorldEdit. Your WorldEdit version seems to be outdated, please make sure WorldEdit is at least version 6."); + } } commandsHandler = new CommandsHandler(this); getCommand("lb").setExecutor(commandsHandler); if (enableAutoClearLog && autoClearLogDelay > 0) - getServer().getScheduler().scheduleAsyncRepeatingTask(this, new AutoClearLog(this), 6000, autoClearLogDelay * 60 * 20); - getServer().getScheduler().scheduleAsyncDelayedTask(this, new DumpedLogImporter(this)); + getServer().getScheduler().runTaskTimerAsynchronously(this, new AutoClearLog(this), 6000, autoClearLogDelay * 60 * 20); + getServer().getScheduler().runTaskAsynchronously(this, new DumpedLogImporter(this)); registerEvents(); if (useBukkitScheduler) { - if (getServer().getScheduler().scheduleAsyncRepeatingTask(this, consumer, delayBetweenRuns * 20, delayBetweenRuns * 20) > 0) + if (getServer().getScheduler().runTaskTimerAsynchronously(this, consumer, delayBetweenRuns < 20 ? 20 : delayBetweenRuns, delayBetweenRuns).getTaskId() > 0) getLogger().info("Scheduled consumer with bukkit scheduler."); else { getLogger().warning("Failed to schedule consumer with bukkit scheduler. Now trying schedule with timer."); timer = new Timer(); - timer.scheduleAtFixedRate(consumer, delayBetweenRuns * 1000, delayBetweenRuns * 1000); + timer.schedule(consumer, delayBetweenRuns < 20 ? 1000 : delayBetweenRuns * 50, delayBetweenRuns * 50); } } else { timer = new Timer(); - timer.scheduleAtFixedRate(consumer, delayBetweenRuns * 1000, delayBetweenRuns * 1000); + timer.schedule(consumer, delayBetweenRuns < 20 ? 1000 : delayBetweenRuns * 50, delayBetweenRuns * 50); getLogger().info("Scheduled consumer with timer."); } + getServer().getScheduler().runTaskAsynchronously(this, new Updater.PlayerCountChecker(this)); for (final Tool tool : toolsByType.values()) if (pm.getPermission("logblock.tools." + tool.name) == null) { final Permission perm = new Permission("logblock.tools." + tool.name, tool.permissionDefault); @@ -126,6 +161,7 @@ public class LogBlock extends JavaPlugin private void registerEvents() { final PluginManager pm = getPluginManager(); pm.registerEvents(new ToolListener(this), this); + pm.registerEvents(new PlayerInfoLogging(this), this); if (askRollbackAfterBan) pm.registerEvents(new BanListener(this), this); if (isLogging(Logging.BLOCKPLACE)) @@ -166,8 +202,8 @@ public class LogBlock extends JavaPlugin pm.registerEvents(new StructureGrowLogging(this), this); if (isLogging(Logging.GRASSGROWTH) || isLogging(Logging.MYCELIUMSPREAD) || isLogging(Logging.VINEGROWTH) || isLogging(Logging.MUSHROOMSPREAD)) pm.registerEvents(new BlockSpreadLogging(this), this); - if (logPlayerInfo) - pm.registerEvents(new PlayerInfoLogging(this), this); + if (isLogging(Logging.LOCKEDCHESTDECAY)) + pm.registerEvents(new LockedChestDecayLogging(this), this); } @Override @@ -179,9 +215,10 @@ public class LogBlock extends JavaPlugin if (logPlayerInfo && getServer().getOnlinePlayers() != null) for (final Player player : getServer().getOnlinePlayers()) consumer.queueLeave(player); + getLogger().info("Waiting for consumer ..."); + consumer.run(); if (consumer.getQueueSize() > 0) { - getLogger().info("Waiting for consumer ..."); - int tries = 10; + int tries = 9; while (consumer.getQueueSize() > 0) { getLogger().info("Remaining queue size: " + consumer.getQueueSize()); if (tries > 0) diff --git a/src/main/java/de/diddiz/LogBlock/Logging.java b/src/main/java/de/diddiz/LogBlock/Logging.java index de0c817..80e2f6d 100644 --- a/src/main/java/de/diddiz/LogBlock/Logging.java +++ b/src/main/java/de/diddiz/LogBlock/Logging.java @@ -9,7 +9,7 @@ public enum Logging PRESUREPLATEINTERACT, TRIPWIREINTERACT, CREATURECROPTRAMPLE, CROPTRAMPLE, NATURALSTRUCTUREGROW, GRASSGROWTH, MYCELIUMSPREAD, VINEGROWTH, MUSHROOMSPREAD, WITHER(true), WITHER_SKULL(true), BONEMEALSTRUCTUREGROW, - WORLDEDIT, TNTMINECARTEXPLOSION(true); + WORLDEDIT, TNTMINECARTEXPLOSION(true), LOCKEDCHESTDECAY; public static final int length = Logging.values().length; private final boolean defaultEnabled; diff --git a/src/main/java/de/diddiz/LogBlock/QueryParams.java b/src/main/java/de/diddiz/LogBlock/QueryParams.java old mode 100755 new mode 100644 index 964aa3e..0c2cb29 --- a/src/main/java/de/diddiz/LogBlock/QueryParams.java +++ b/src/main/java/de/diddiz/LogBlock/QueryParams.java @@ -20,11 +20,12 @@ import static de.diddiz.LogBlock.config.Config.*; import static de.diddiz.util.BukkitUtils.friendlyWorldname; import static de.diddiz.util.BukkitUtils.getBlockEquivalents; import static de.diddiz.util.MaterialName.materialName; +import static de.diddiz.util.MaterialName.typeFromName; import static de.diddiz.util.Utils.*; public final class QueryParams implements Cloneable { - private static final Set keywords = new HashSet(Arrays.asList("player".hashCode(), "area".hashCode(), "selection".hashCode(), "sel".hashCode(), "block".hashCode(), "type".hashCode(), "sum".hashCode(), "destroyed".hashCode(), "created".hashCode(), "chestaccess".hashCode(), "all".hashCode(), "time".hashCode(), "since".hashCode(), "before".hashCode(), "limit".hashCode(), "world".hashCode(), "asc".hashCode(), "desc".hashCode(), "last".hashCode(), "coords".hashCode(), "silent".hashCode(), "chat".hashCode(), "search".hashCode(), "match".hashCode(), "loc".hashCode(), "location".hashCode(), "kills".hashCode(), "killer".hashCode(), "victim".hashCode())); + private static final Set keywords = new HashSet(Arrays.asList("player".hashCode(), "area".hashCode(), "selection".hashCode(), "sel".hashCode(), "block".hashCode(), "type".hashCode(), "sum".hashCode(), "destroyed".hashCode(), "created".hashCode(), "chestaccess".hashCode(), "all".hashCode(), "time".hashCode(), "since".hashCode(), "before".hashCode(), "limit".hashCode(), "world".hashCode(), "asc".hashCode(), "desc".hashCode(), "last".hashCode(), "coords".hashCode(), "silent".hashCode(), "chat".hashCode(), "search".hashCode(), "match".hashCode(), "loc".hashCode(), "location".hashCode(), "kills".hashCode(), "killer".hashCode(), "victim".hashCode(), "both".hashCode())); public BlockChangeType bct = BlockChangeType.BOTH; public int limit = -1, before = 0, since = 0, radius = -1; public Location loc = null; @@ -32,7 +33,7 @@ public final class QueryParams implements Cloneable public List players = new ArrayList(); public List killers = new ArrayList(); public List victims = new ArrayList(); - public boolean excludePlayersMode = false, excludeKillersMode = false, excludeVictimsMode = false, prepareToolQuery = false, silent = false; + public boolean excludePlayersMode = false, excludeKillersMode = false, excludeVictimsMode = false, excludeBlocksMode = false, prepareToolQuery = false, silent = false; public RegionContainer sel = null; public SummarizationMode sum = SummarizationMode.NONE; public List types = new ArrayList(); @@ -69,7 +70,7 @@ public final class QueryParams implements Cloneable if (needDate) select += "date, "; if (needPlayer) - select += "playername, "; + select += "playername, UUID,"; if (needMessage) select += "message, "; select = select.substring(0, select.length() - 2); @@ -110,7 +111,7 @@ public final class QueryParams implements Cloneable return select + " " + from + getWhere() + "ORDER BY date " + order + ", id " + order + " " + getLimit(); } else if (sum == SummarizationMode.PLAYERS) - return "SELECT playername, SUM(kills) AS kills, SUM(killed) AS killed FROM ((SELECT killer AS playerid, count(*) AS kills, 0 as killed FROM `" + getTable() + "-kills` INNER JOIN `lb-players` as killers ON (killer=killers.playerid) INNER JOIN `lb-players` as victims ON (victim=victims.playerid) " + getWhere(BlockChangeType.KILLS) + "GROUP BY killer) UNION (SELECT victim AS playerid, 0 as kills, count(*) AS killed FROM `" + getTable() + "-kills` INNER JOIN `lb-players` as killers ON (killer=killers.playerid) INNER JOIN `lb-players` as victims ON (victim=victims.playerid) " + getWhere(BlockChangeType.KILLS) + "GROUP BY victim)) AS t INNER JOIN `lb-players` USING (playerid) GROUP BY playerid ORDER BY SUM(kills) + SUM(killed) " + order + " " + getLimit(); + return "SELECT playername, UUID, SUM(kills) AS kills, SUM(killed) AS killed FROM ((SELECT killer AS playerid, count(*) AS kills, 0 as killed FROM `" + getTable() + "-kills` INNER JOIN `lb-players` as killers ON (killer=killers.playerid) INNER JOIN `lb-players` as victims ON (victim=victims.playerid) " + getWhere(BlockChangeType.KILLS) + "GROUP BY killer) UNION (SELECT victim AS playerid, 0 as kills, count(*) AS killed FROM `" + getTable() + "-kills` INNER JOIN `lb-players` as killers ON (killer=killers.playerid) INNER JOIN `lb-players` as victims ON (victim=victims.playerid) " + getWhere(BlockChangeType.KILLS) + "GROUP BY victim)) AS t INNER JOIN `lb-players` USING (playerid) GROUP BY playerid ORDER BY SUM(kills) + SUM(killed) " + order + " " + getLimit(); } if (sum == SummarizationMode.NONE) { String select = "SELECT "; @@ -126,7 +127,7 @@ public final class QueryParams implements Cloneable if (needData) select += "data, "; if (needPlayer) - select += "playername, "; + select += "playername, UUID, "; if (needCoords) select += "x, y, z, "; if (needSignText) @@ -141,12 +142,17 @@ public final class QueryParams implements Cloneable if (needSignText) from += "LEFT JOIN `" + getTable() + "-sign` USING (id) "; if (needChestAccess) - from += "LEFT JOIN `" + getTable() + "-chest` USING (id) "; + // If BlockChangeType is CHESTACCESS, we can use more efficient query + if (bct == BlockChangeType.CHESTACCESS) { + from += "RIGHT JOIN `" + getTable() + "-chest` USING (id) "; + } else { + from += "LEFT JOIN `" + getTable() + "-chest` USING (id) "; + } return select + " " + from + getWhere() + "ORDER BY date " + order + ", id " + order + " " + getLimit(); } else if (sum == SummarizationMode.TYPES) return "SELECT type, SUM(created) AS created, SUM(destroyed) AS destroyed FROM ((SELECT type, count(*) AS created, 0 AS destroyed FROM `" + getTable() + "` INNER JOIN `lb-players` USING (playerid) " + getWhere(BlockChangeType.CREATED) + "GROUP BY type) UNION (SELECT replaced AS type, 0 AS created, count(*) AS destroyed FROM `" + getTable() + "` INNER JOIN `lb-players` USING (playerid) " + getWhere(BlockChangeType.DESTROYED) + "GROUP BY replaced)) AS t GROUP BY type ORDER BY SUM(created) + SUM(destroyed) " + order + " " + getLimit(); else - return "SELECT playername, SUM(created) AS created, SUM(destroyed) AS destroyed FROM ((SELECT playerid, count(*) AS created, 0 AS destroyed FROM `" + getTable() + "` " + getWhere(BlockChangeType.CREATED) + "GROUP BY playerid) UNION (SELECT playerid, 0 AS created, count(*) AS destroyed FROM `" + getTable() + "` " + getWhere(BlockChangeType.DESTROYED) + "GROUP BY playerid)) AS t INNER JOIN `lb-players` USING (playerid) GROUP BY playerid ORDER BY SUM(created) + SUM(destroyed) " + order + " " + getLimit(); + return "SELECT playername, UUID, SUM(created) AS created, SUM(destroyed) AS destroyed FROM ((SELECT playerid, count(*) AS created, 0 AS destroyed FROM `" + getTable() + "` " + getWhere(BlockChangeType.CREATED) + "GROUP BY playerid) UNION (SELECT playerid, 0 AS created, count(*) AS destroyed FROM `" + getTable() + "` " + getWhere(BlockChangeType.DESTROYED) + "GROUP BY playerid)) AS t INNER JOIN `lb-players` USING (playerid) GROUP BY playerid ORDER BY SUM(created) + SUM(destroyed) " + order + " " + getLimit(); } public String getTable() { @@ -163,6 +169,8 @@ public final class QueryParams implements Cloneable title.append("kills "); else { if (!types.isEmpty()) { + if (excludeBlocksMode) + title.append("all blocks except "); final String[] blocknames = new String[types.size()]; for (int i = 0; i < types.size(); i++) blocknames[i] = materialName(types.get(i).getBlock()); @@ -198,7 +206,7 @@ public final class QueryParams implements Cloneable title.append("more than ").append(before * -1).append(" minutes ago "); if (loc != null) { if (radius > 0) - title.append("within ").append(radius).append(" blocks of ").append(prepareToolQuery ? "clicked block" : "you").append(" "); + title.append("within ").append(radius).append(" blocks of ").append(prepareToolQuery ? "clicked block" : "location").append(" "); else if (radius == 0) title.append("at ").append(loc.getBlockX()).append(":").append(loc.getBlockY()).append(":").append(loc.getBlockZ()).append(" "); } else if (sel != null) @@ -299,6 +307,8 @@ public final class QueryParams implements Cloneable switch (blockChangeType) { case ALL: if (!types.isEmpty()) { + if (excludeBlocksMode) + where.append("NOT "); where.append('('); for (final Block block : types) { where.append("((type = ").append(block.getBlock()).append(" OR replaced = ").append(block.getBlock()); @@ -315,6 +325,8 @@ public final class QueryParams implements Cloneable break; case BOTH: if (!types.isEmpty()) { + if (excludeBlocksMode) + where.append("NOT "); where.append('('); for (final Block block : types) { where.append("((type = ").append(block.getBlock()).append(" OR replaced = ").append(block.getBlock()); @@ -332,6 +344,8 @@ public final class QueryParams implements Cloneable break; case CREATED: if (!types.isEmpty()) { + if (excludeBlocksMode) + where.append("NOT "); where.append('('); for (final Block block : types) { where.append("((type = ").append(block.getBlock()); @@ -344,12 +358,13 @@ public final class QueryParams implements Cloneable } where.delete(where.length() - 4, where.length()); where.append(") AND "); - } else - where.append("type != 0 AND "); - where.append("type != replaced AND "); + } + where.append("type != 0 AND type != replaced AND "); break; case DESTROYED: if (!types.isEmpty()) { + if (excludeBlocksMode) + where.append("NOT "); where.append('('); for (final Block block : types) { where.append("((replaced = ").append(block.getBlock()); @@ -362,13 +377,13 @@ public final class QueryParams implements Cloneable } where.delete(where.length() - 4, where.length()); where.append(") AND "); - } else - where.append("replaced != 0 AND "); - where.append("type != replaced AND "); + } + where.append("replaced != 0 AND type != replaced AND "); break; case CHESTACCESS: - where.append("(type = 23 OR type = 54 OR type = 61 OR type = 62) AND type = replaced AND "); if (!types.isEmpty()) { + if (excludeBlocksMode) + where.append("NOT "); where.append('('); for (final Block block : types) { where.append("((itemtype = ").append(block.getBlock()); @@ -488,6 +503,7 @@ public final class QueryParams implements Cloneable players.add(matches.size() == 1 ? matches.get(0).getName() : playerName.replaceAll("[^a-zA-Z0-9_]", "")); } } + needPlayer = true; } else if (param.equals("killer")) { if (values.length < 1) throw new IllegalArgumentException("No or wrong count of arguments for '" + param + "'"); @@ -504,6 +520,7 @@ public final class QueryParams implements Cloneable killers.add(matches.size() == 1 ? matches.get(0).getName() : killerName.replaceAll("[^a-zA-Z0-9_]", "")); } } + needKiller = true; } else if (param.equals("victim")) { if (values.length < 1) throw new IllegalArgumentException("No or wrong count of arguments for '" + param + "'"); @@ -520,6 +537,7 @@ public final class QueryParams implements Cloneable victims.add(matches.size() == 1 ? matches.get(0).getName() : victimName.replaceAll("[^a-zA-Z0-9_]", "")); } } + needVictim = true; } else if (param.equals("weapon")) { if (values.length < 1) throw new IllegalArgumentException("No or wrong count of arguments for '" + param + "'"); @@ -535,10 +553,15 @@ public final class QueryParams implements Cloneable throw new IllegalArgumentException("No material matching: '" + weaponName + "'"); types.add(new Block(mat.getId(), -1)); } + needWeapon = true; } else if (param.equals("block") || param.equals("type")) { if (values.length < 1) throw new IllegalArgumentException("No or wrong count of arguments for '" + param + "'"); - for (final String blockName : values) { + for (String blockName : values) { + if (blockName.startsWith("!")) { + excludeBlocksMode = true; + blockName = blockName.substring(1); + } if (blockName.contains(":")) { String[] blockNameSplit = blockName.split(":"); if (blockNameSplit.length > 2) @@ -557,23 +580,21 @@ public final class QueryParams implements Cloneable types.add(new Block(mat.getId(), data)); } else { final Material mat = Material.matchMaterial(blockName); - if (mat == null) - throw new IllegalArgumentException("No material matching: '" + blockName + "'"); - types.add(new Block(mat.getId(), -1)); + types.add(new Block(typeFromName(blockName), -1)); } } } else if (param.equals("area")) { - if (player == null && !prepareToolQuery) - throw new IllegalArgumentException("You have to ba a player to use area"); + if (player == null && !prepareToolQuery && loc == null) + throw new IllegalArgumentException("You have to be a player to use area, or specify a location first"); if (values.length == 0) { radius = defaultDist; - if (!prepareToolQuery) + if (!prepareToolQuery && loc == null) loc = player.getLocation(); } else { if (!isInt(values[0])) throw new IllegalArgumentException("Not a number: '" + values[0] + "'"); radius = Integer.parseInt(values[0]); - if (!prepareToolQuery) + if (!prepareToolQuery && loc == null) loc = player.getLocation(); } } else if (param.equals("selection") || param.equals("sel")) { @@ -608,6 +629,8 @@ public final class QueryParams implements Cloneable bct = BlockChangeType.CREATED; else if (param.equals("destroyed")) bct = BlockChangeType.DESTROYED; + else if (param.equals("both")) + bct = BlockChangeType.BOTH; else if (param.equals("chestaccess")) bct = BlockChangeType.CHESTACCESS; else if (param.equals("chat")) @@ -655,8 +678,12 @@ public final class QueryParams implements Cloneable throw new IllegalArgumentException("Not a valid argument: '" + param + "'"); i += values.length; } - if (bct == BlockChangeType.KILLS && !getWorldConfig(world).isLogging(Logging.KILL)) - throw new IllegalArgumentException("Kill logging not enabled for world '" + world.getName() + "'"); + if (bct == BlockChangeType.KILLS) { + if (world == null) + throw new IllegalArgumentException("No world specified"); + if (!getWorldConfig(world).isLogging(Logging.KILL)) + throw new IllegalArgumentException("Kill logging not enabled for world '" + world.getName() + "'"); + } if (types.size() > 0) for (final Set equivalent : getBlockEquivalents()) { boolean found = false; diff --git a/src/main/java/de/diddiz/LogBlock/SummedBlockChanges.java b/src/main/java/de/diddiz/LogBlock/SummedBlockChanges.java index 089c2db..5e7cd61 100644 --- a/src/main/java/de/diddiz/LogBlock/SummedBlockChanges.java +++ b/src/main/java/de/diddiz/LogBlock/SummedBlockChanges.java @@ -12,11 +12,14 @@ public class SummedBlockChanges implements LookupCacheElement private final String group; private final int created, destroyed; private final float spaceFactor; + private final Actor actor; public SummedBlockChanges(ResultSet rs, QueryParams p, float spaceFactor) throws SQLException { - group = p.sum == SummarizationMode.PLAYERS ? rs.getString(1) : materialName(rs.getInt(1)); - created = rs.getInt(2); - destroyed = rs.getInt(3); + // Actor currently useless here as we don't yet output UUID in results anywhere + actor = p.sum == SummarizationMode.PLAYERS ? new Actor(rs) : null; + group = actor == null ? materialName(rs.getInt("type")) : actor.getName(); + created = rs.getInt("created"); + destroyed = rs.getInt("destroyed"); this.spaceFactor = spaceFactor; } diff --git a/src/main/java/de/diddiz/LogBlock/SummedKills.java b/src/main/java/de/diddiz/LogBlock/SummedKills.java old mode 100755 new mode 100644 index 3dba238..5e3d702 --- a/src/main/java/de/diddiz/LogBlock/SummedKills.java +++ b/src/main/java/de/diddiz/LogBlock/SummedKills.java @@ -1,22 +1,20 @@ package de.diddiz.LogBlock; -import static de.diddiz.util.MaterialName.materialName; import static de.diddiz.util.Utils.spaces; import java.sql.ResultSet; import java.sql.SQLException; import org.bukkit.Location; -import de.diddiz.LogBlock.QueryParams.SummarizationMode; public class SummedKills implements LookupCacheElement { - private final String playerName; + private final Actor player; private final int kills, killed; private final float spaceFactor; public SummedKills(ResultSet rs, QueryParams p, float spaceFactor) throws SQLException { - playerName = rs.getString(1); - kills = rs.getInt(2); - killed = rs.getInt(3); + player = new Actor(rs); + kills = rs.getInt("kills"); + killed = rs.getInt("killed"); this.spaceFactor = spaceFactor; } @@ -27,6 +25,6 @@ public class SummedKills implements LookupCacheElement @Override public String getMessage() { - return kills + spaces((int)((6 - String.valueOf(kills).length()) / spaceFactor)) + killed + spaces((int)((7 - String.valueOf(killed).length()) / spaceFactor)) + playerName; + return kills + spaces((int)((6 - String.valueOf(kills).length()) / spaceFactor)) + killed + spaces((int)((7 - String.valueOf(killed).length()) / spaceFactor)) + player.getName(); } } diff --git a/src/main/java/de/diddiz/LogBlock/Updater.java b/src/main/java/de/diddiz/LogBlock/Updater.java index 84c6759..ea4aa18 100644 --- a/src/main/java/de/diddiz/LogBlock/Updater.java +++ b/src/main/java/de/diddiz/LogBlock/Updater.java @@ -1,9 +1,11 @@ package de.diddiz.LogBlock; -import static de.diddiz.LogBlock.config.Config.getLoggedWorlds; -import static de.diddiz.LogBlock.config.Config.isLogging; -import static de.diddiz.util.BukkitUtils.friendlyWorldname; -import static org.bukkit.Bukkit.getLogger; +import de.diddiz.LogBlock.config.Config; +import de.diddiz.LogBlock.config.WorldConfig; +import org.bukkit.Bukkit; +import org.bukkit.configuration.ConfigurationSection; +import org.bukkit.configuration.file.YamlConfiguration; + import java.io.File; import java.io.IOException; import java.sql.Connection; @@ -12,14 +14,24 @@ import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; import java.util.logging.Level; -import org.bukkit.Bukkit; -import org.bukkit.configuration.ConfigurationSection; -import org.bukkit.configuration.file.YamlConfiguration; -import de.diddiz.LogBlock.config.WorldConfig; +import java.util.ArrayList; +import java.util.List; +import java.util.Map.Entry; + +import static de.diddiz.LogBlock.config.Config.getLoggedWorlds; +import static de.diddiz.LogBlock.config.Config.isLogging; +import static de.diddiz.util.BukkitUtils.friendlyWorldname; +import de.diddiz.util.UUIDFetcher; +import java.util.HashMap; +import java.util.Map; +import java.util.UUID; +import java.util.logging.Logger; +import static org.bukkit.Bukkit.getLogger; class Updater { private final LogBlock logblock; + final int UUID_CONVERT_BATCH_SIZE = 100; Updater(LogBlock logblock) { this.logblock = logblock; @@ -166,7 +178,7 @@ class Updater final Statement st = conn.createStatement(); for (final WorldConfig wcfg : getLoggedWorlds()) if (wcfg.isLogging(Logging.KILL)) - st.execute("ALTER TABLE `" + wcfg.table + "-kills` ADD (x SMALLINT NOT NULL DEFAULT 0, y TINYINT UNSIGNED NOT NULL DEFAULT 0, z SMALLINT NOT NULL DEFAULT 0)"); + st.execute("ALTER TABLE `" + wcfg.table + "-kills` ADD (x MEDIUMINT NOT NULL DEFAULT 0, y SMALLINT NOT NULL DEFAULT 0, z MEDIUMINT NOT NULL DEFAULT 0)"); st.close(); conn.close(); } catch (final SQLException ex) { @@ -197,6 +209,176 @@ class Updater } config.set("version", "1.52"); } + if (config.getString("version").compareTo("1.81") < 0) { + getLogger().info("Updating tables to 1.81 ..."); + final Connection conn = logblock.getConnection(); + try { + conn.setAutoCommit(true); + final Statement st = conn.createStatement(); + for (final WorldConfig wcfg : getLoggedWorlds()) { + if (wcfg.isLogging(Logging.CHESTACCESS)) { + st.execute("ALTER TABLE `"+wcfg.table+"-chest` CHANGE itemdata itemdata SMALLINT NOT NULL"); + getLogger().info("Table "+wcfg.table+"-chest modified"); + } + } + st.close(); + conn.close(); + } catch (final SQLException ex) { + Bukkit.getLogger().log(Level.SEVERE, "[Updater] Error: ", ex); + return false; + } + config.set("version", "1.81"); + } + + if (config.getString("version").compareTo("1.90") < 0) { + getLogger().info("Updating tables to 1.9 ..."); + getLogger().info("Importing UUIDs for large databases may take some time"); + final Connection conn = logblock.getConnection(); + try { + conn.setAutoCommit(true); + final Statement st = conn.createStatement(); + st.execute("ALTER TABLE `lb-players` ADD `UUID` VARCHAR(36) NOT NULL"); + } catch (final SQLException ex) { + // Error 1060 is MySQL error "column already exists". We want to continue with import if we get that error + if (ex.getErrorCode() != 1060) { + Bukkit.getLogger().log(Level.SEVERE, "[Updater] Error: ", ex); + return false; + } + } + try { + String unimportedPrefix="noimport_"; + ResultSet rs; + conn.setAutoCommit(true); + final Statement st = conn.createStatement(); + if (config.getBoolean("logging.logPlayerInfo")) { + // Start by assuming anything with no onlinetime is not a player + st.execute("UPDATE `lb-players` SET UUID = CONCAT ('log_',playername) WHERE onlinetime=0 AND LENGTH(UUID) = 0"); + } else { + // If we can't assume that, we must assume anything we can't look up is not a player + unimportedPrefix = "log_"; + } + // Tell people how many are needing converted + rs = st.executeQuery("SELECT COUNT(playername) FROM `lb-players` WHERE LENGTH(UUID)=0"); + rs.next(); + String total = Integer.toString(rs.getInt(1)); + getLogger().info(total + " players to convert"); + int done = 0; + + conn.setAutoCommit(false); + Map players = new HashMap(); + List names = new ArrayList(UUID_CONVERT_BATCH_SIZE); + Map response; + rs = st.executeQuery("SELECT playerid,playername FROM `lb-players` WHERE LENGTH(UUID)=0 LIMIT " + Integer.toString(UUID_CONVERT_BATCH_SIZE)); + while (rs.next()) { + do { + players.put(rs.getString(2),rs.getInt(1)); + names.add(rs.getString(2)); + } while (rs.next()); + if (names.size()>0) { + String theUUID; + response = UUIDFetcher.getUUIDs(names); + for (Map.Entry entry : players.entrySet()) { + if (response.get(entry.getKey()) == null) { + theUUID = unimportedPrefix + entry.getKey(); + getLogger().warning(entry.getKey() + " not found - giving UUID of " + theUUID); + } else { + theUUID = response.get(entry.getKey()).toString(); + } + String thePID = entry.getValue().toString(); + st.execute("UPDATE `lb-players` SET UUID = '" + theUUID + "' WHERE playerid = " + thePID); + done++; + } + conn.commit(); + players.clear(); + names.clear(); + getLogger().info("Processed " + Integer.toString(done) + " out of " + total); + rs = st.executeQuery("SELECT playerid,playername FROM `lb-players` WHERE LENGTH(UUID)=0 LIMIT " + Integer.toString(UUID_CONVERT_BATCH_SIZE)); + } + } + st.close(); + conn.close(); + + } catch (final SQLException ex) { + Bukkit.getLogger().log(Level.SEVERE, "[Updater] Error: ", ex); + return false; + } catch (Exception ex) { + Bukkit.getLogger().log(Level.SEVERE, "[UUID importer]", ex); + return false; + } + config.set("version", "1.90"); + } + // Ensure charset for free-text fields is UTF-8, or UTF8-mb4 if possible + // As this may be an expensive operation and the database default may already be this, check on a table-by-table basis before converting + if (config.getString("version").compareTo("1.92") < 0) { + getLogger().info("Updating tables to 1.92 ..."); + String charset = "utf8"; + if ( Config.mb4) charset="utf8mb4"; + final Connection conn = logblock.getConnection(); + try { + conn.setAutoCommit(true); + final Statement st = conn.createStatement(); + if (isLogging(Logging.CHAT)) { + final ResultSet rs = st.executeQuery("SHOW FULL COLUMNS FROM `lb-chat` WHERE field = 'message'"); + if (rs.next() && !rs.getString("Collation").substring(0,4).equalsIgnoreCase(charset)) { + st.execute("ALTER TABLE `lb-chat` CONVERT TO CHARSET " + charset); + getLogger().info("Table lb-chat modified"); + } else { + getLogger().info("Table lb-chat already fine, skipping it"); + } + } + for (final WorldConfig wcfg : getLoggedWorlds()) { + if (wcfg.isLogging(Logging.SIGNTEXT)) { + final ResultSet rs = st.executeQuery("SHOW FULL COLUMNS FROM `"+wcfg.table+"-sign` WHERE field = 'signtext'"); + if (rs.next() && !rs.getString("Collation").substring(0,4).equalsIgnoreCase(charset)) { + st.execute("ALTER TABLE `"+wcfg.table+"-sign` CONVERT TO CHARSET " + charset); + getLogger().info("Table "+wcfg.table+"-sign modified"); + } else { + getLogger().info("Table "+wcfg.table+"-sign already fine, skipping it"); + } + } + } + st.close(); + conn.close(); + } catch (final SQLException ex) { + Bukkit.getLogger().log(Level.SEVERE, "[Updater] Error: ", ex); + return false; + } + config.set("version", "1.92"); + } + if (config.getString("version").compareTo("1.93") < 0) { + getLogger().info("Updating tables to 1.93 ..."); + final Connection conn = logblock.getConnection(); + try { + conn.setAutoCommit(true); + final Statement st = conn.createStatement(); + // Need to wrap both these next two inside individual try/catch statements in case index does not exist + try { + st.execute("DROP INDEX UUID ON `lb-players`"); + } catch (final SQLException ex) { + if (ex.getErrorCode() != 1091) { + Bukkit.getLogger().log(Level.SEVERE, "[Updater] Error: ", ex); + return false; + } + } + try { + st.execute("DROP INDEX playername ON `lb-players`"); + } catch (final SQLException ex) { + if (ex.getErrorCode() != 1091) { + Bukkit.getLogger().log(Level.SEVERE, "[Updater] Error: ", ex); + return false; + } + } + st.execute("CREATE INDEX UUID ON `lb-players` (UUID);"); + st.execute("CREATE INDEX playername ON `lb-players` (playername);"); + st.close(); + conn.close(); + } catch (final SQLException ex) { + Bukkit.getLogger().log(Level.SEVERE, "[Updater] Error: ", ex); + return false; + } + config.set("version", "1.93"); + } + logblock.saveConfig(); return true; } @@ -208,15 +390,19 @@ class Updater final Statement state = conn.createStatement(); final DatabaseMetaData dbm = conn.getMetaData(); conn.setAutoCommit(true); - createTable(dbm, state, "lb-players", "(playerid SMALLINT UNSIGNED NOT NULL AUTO_INCREMENT, playername varchar(32) NOT NULL, firstlogin DATETIME NOT NULL, lastlogin DATETIME NOT NULL, onlinetime INT UNSIGNED NOT NULL, ip varchar(255) NOT NULL, PRIMARY KEY (playerid), UNIQUE (playername))"); + createTable(dbm, state, "lb-players", "(playerid INT UNSIGNED NOT NULL AUTO_INCREMENT, UUID varchar(36) NOT NULL, playername varchar(32) NOT NULL, firstlogin DATETIME NOT NULL, lastlogin DATETIME NOT NULL, onlinetime INT UNSIGNED NOT NULL, ip varchar(255) NOT NULL, PRIMARY KEY (playerid), INDEX (UUID))"); + // Players table must not be empty or inserts won't work - bug #492 + final ResultSet rs = state.executeQuery("SELECT NULL FROM `lb-players` LIMIT 1;"); + if (!rs.next()) + state.execute("INSERT IGNORE INTO `lb-players` (UUID,playername) VALUES ('log_dummy_record','dummy_record')"); if (isLogging(Logging.CHAT)) - createTable(dbm, state, "lb-chat", "(id INT UNSIGNED NOT NULL AUTO_INCREMENT, date DATETIME NOT NULL, playerid SMALLINT UNSIGNED NOT NULL, message VARCHAR(255) NOT NULL, PRIMARY KEY (id), KEY playerid (playerid), FULLTEXT message (message)) ENGINE=MyISAM"); + createTable(dbm, state, "lb-chat", "(id INT UNSIGNED NOT NULL AUTO_INCREMENT, date DATETIME NOT NULL, playerid INT UNSIGNED NOT NULL, message VARCHAR(255) NOT NULL, PRIMARY KEY (id), KEY playerid (playerid), FULLTEXT message (message)) ENGINE=MyISAM DEFAULT CHARSET utf8"); for (final WorldConfig wcfg : getLoggedWorlds()) { - createTable(dbm, state, wcfg.table, "(id INT UNSIGNED NOT NULL AUTO_INCREMENT, date DATETIME NOT NULL, playerid SMALLINT UNSIGNED NOT NULL, replaced TINYINT UNSIGNED NOT NULL, type TINYINT UNSIGNED NOT NULL, data TINYINT UNSIGNED NOT NULL, x MEDIUMINT NOT NULL, y SMALLINT UNSIGNED NOT NULL, z MEDIUMINT NOT NULL, PRIMARY KEY (id), KEY coords (x, z, y), KEY date (date), KEY playerid (playerid))"); - createTable(dbm, state, wcfg.table + "-sign", "(id INT UNSIGNED NOT NULL, signtext VARCHAR(255) NOT NULL, PRIMARY KEY (id))"); - createTable(dbm, state, wcfg.table + "-chest", "(id INT UNSIGNED NOT NULL, itemtype SMALLINT UNSIGNED NOT NULL, itemamount SMALLINT NOT NULL, itemdata TINYINT UNSIGNED NOT NULL, PRIMARY KEY (id))"); + createTable(dbm, state, wcfg.table, "(id INT UNSIGNED NOT NULL AUTO_INCREMENT, date DATETIME NOT NULL, playerid INT UNSIGNED NOT NULL, replaced TINYINT UNSIGNED NOT NULL, type TINYINT UNSIGNED NOT NULL, data TINYINT UNSIGNED NOT NULL, x MEDIUMINT NOT NULL, y SMALLINT UNSIGNED NOT NULL, z MEDIUMINT NOT NULL, PRIMARY KEY (id), KEY coords (x, z, y), KEY date (date), KEY playerid (playerid))"); + createTable(dbm, state, wcfg.table + "-sign", "(id INT UNSIGNED NOT NULL, signtext VARCHAR(255) NOT NULL, PRIMARY KEY (id)) DEFAULT CHARSET utf8"); + createTable(dbm, state, wcfg.table + "-chest", "(id INT UNSIGNED NOT NULL, itemtype SMALLINT UNSIGNED NOT NULL, itemamount SMALLINT NOT NULL, itemdata SMALLINT NOT NULL, PRIMARY KEY (id))"); if (wcfg.isLogging(Logging.KILL)) - createTable(dbm, state, wcfg.table + "-kills", "(id INT UNSIGNED NOT NULL AUTO_INCREMENT, date DATETIME NOT NULL, killer SMALLINT UNSIGNED, victim SMALLINT UNSIGNED NOT NULL, weapon SMALLINT UNSIGNED NOT NULL, x SMALLINT NOT NULL, y TINYINT UNSIGNED NOT NULL, z SMALLINT NOT NULL, PRIMARY KEY (id))"); + createTable(dbm, state, wcfg.table + "-kills", "(id INT UNSIGNED NOT NULL AUTO_INCREMENT, date DATETIME NOT NULL, killer INT UNSIGNED, victim INT UNSIGNED NOT NULL, weapon SMALLINT UNSIGNED NOT NULL, x MEDIUMINT NOT NULL, y SMALLINT NOT NULL, z MEDIUMINT NOT NULL, PRIMARY KEY (id))"); } state.close(); conn.close(); @@ -230,4 +416,32 @@ class Updater throw new SQLException("Table " + table + " not found and failed to create"); } } + + public static class PlayerCountChecker implements Runnable { + + private LogBlock logblock; + + public PlayerCountChecker(LogBlock logblock) { + this.logblock = logblock; + } + + @Override + public void run() { + final Connection conn = logblock.getConnection(); + try { + conn.setAutoCommit(true); + final Statement st = conn.createStatement(); + ResultSet rs = st.executeQuery("SELECT auto_increment FROM information_schema.columns AS col join information_schema.tables AS tab ON (col.table_schema=tab.table_schema AND col.table_name=tab.table_name) WHERE col.table_name = 'lb-players' AND col.column_name = 'playerid' AND col.data_type = 'smallint' AND col.table_schema = DATABASE() AND auto_increment > 65000;"); + if (rs.next()) { + for (int i = 0; i < 6; i++) { + logblock.getLogger().warning("Your server reached 65000 players. You should soon update your database table schema - see FAQ: https://github.com/LogBlock/LogBlock/wiki/FAQ#logblock-your-server-reached-65000-players-"); + } + } + st.close(); + conn.close(); + } catch (final SQLException ex) { + logblock.getLogger().log(Level.SEVERE, "[Updater] Error: ", ex); + } + } + } } diff --git a/src/main/java/de/diddiz/LogBlock/WorldEditor.java b/src/main/java/de/diddiz/LogBlock/WorldEditor.java index 4654224..fb5553f 100644 --- a/src/main/java/de/diddiz/LogBlock/WorldEditor.java +++ b/src/main/java/de/diddiz/LogBlock/WorldEditor.java @@ -10,6 +10,7 @@ import org.bukkit.block.Sign; import org.bukkit.command.CommandSender; import org.bukkit.inventory.InventoryHolder; import org.bukkit.inventory.ItemStack; +import org.bukkit.Material; import org.bukkit.material.Bed; import org.bukkit.material.PistonBaseMaterial; import org.bukkit.material.PistonExtensionMaterial; @@ -26,6 +27,7 @@ import java.util.logging.Level; import static de.diddiz.LogBlock.config.Config.dontRollback; import static de.diddiz.LogBlock.config.Config.replaceAnyway; import static de.diddiz.util.BukkitUtils.equalTypes; +import static de.diddiz.util.BukkitUtils.getContainerBlocks; import static de.diddiz.util.BukkitUtils.modifyContainer; import static de.diddiz.util.MaterialName.materialName; import static org.bukkit.Bukkit.getLogger; @@ -71,7 +73,7 @@ public class WorldEditor implements Runnable this.sender = sender; } - public void queueEdit(int x, int y, int z, int replaced, int type, byte data, String signtext, short itemType, short itemAmount, byte itemData) { + public void queueEdit(int x, int y, int z, int replaced, int type, byte data, String signtext, short itemType, short itemAmount, short itemData) { edits.add(new Edit(0, new Location(world, x, y, z), null, replaced, type, data, signtext, new ChestAccess(itemType, itemAmount, itemData))); } @@ -145,8 +147,8 @@ public class WorldEditor implements Runnable private class Edit extends BlockChange { - public Edit(long time, Location loc, String playerName, int replaced, int type, byte data, String signtext, ChestAccess ca) { - super(time, loc, playerName, replaced, type, data, signtext, ca); + public Edit(long time, Location loc, Actor actor, int replaced, int type, byte data, String signtext, ChestAccess ca) { + super(time, loc, actor, replaced, type, data, signtext, ca); } PerformResult perform() throws WorldEditorException { @@ -162,21 +164,24 @@ public class WorldEditor implements Runnable if (type == 0) { if (!block.setTypeId(0)) throw new WorldEditorException(block.getTypeId(), 0, block.getLocation()); - } else if (ca != null && (type == 23 || type == 54 || type == 61 || type == 62)) { - int leftover; - try { - leftover = modifyContainer(state, new ItemStack(ca.itemType, -ca.itemAmount, (short)0, ca.itemData)); - if (leftover > 0) - for (final BlockFace face : new BlockFace[]{BlockFace.NORTH, BlockFace.SOUTH, BlockFace.EAST, BlockFace.WEST}) - if (block.getRelative(face).getTypeId() == 54) - leftover = modifyContainer(block.getRelative(face).getState(), new ItemStack(ca.itemType, ca.itemAmount < 0 ? leftover : -leftover, (short)0, ca.itemData)); - } catch (final Exception ex) { - throw new WorldEditorException(ex.getMessage(), block.getLocation()); + } else if (ca != null) { + if (getContainerBlocks().contains(Material.getMaterial(type))) { + int leftover; + try { + leftover = modifyContainer(state, new ItemStack(ca.itemType, -ca.itemAmount, ca.itemData)); + // Special-case blocks which might be double chests + if (leftover > 0 && (type == 54 || type == 146)) + for (final BlockFace face : new BlockFace[]{BlockFace.NORTH, BlockFace.SOUTH, BlockFace.EAST, BlockFace.WEST}) + if (block.getRelative(face).getTypeId() == type) + leftover = modifyContainer(block.getRelative(face).getState(), new ItemStack(ca.itemType, ca.itemAmount < 0 ? leftover : -leftover, ca.itemData)); + } catch (final Exception ex) { + throw new WorldEditorException(ex.getMessage(), block.getLocation()); + } + if (!state.update()) + throw new WorldEditorException("Failed to update inventory of " + materialName(block.getTypeId()), block.getLocation()); + if (leftover > 0 && ca.itemAmount < 0) + throw new WorldEditorException("Not enough space left in " + materialName(block.getTypeId()), block.getLocation()); } - if (!state.update()) - throw new WorldEditorException("Failed to update inventory of " + materialName(block.getTypeId()), block.getLocation()); - if (leftover > 0 && ca.itemAmount < 0) - throw new WorldEditorException("Not enough space left in " + materialName(block.getTypeId()), block.getLocation()); } else return PerformResult.NO_ACTION; return PerformResult.SUCCESS; diff --git a/src/main/java/de/diddiz/LogBlock/config/Config.java b/src/main/java/de/diddiz/LogBlock/config/Config.java index e93c861..27bca7d 100644 --- a/src/main/java/de/diddiz/LogBlock/config/Config.java +++ b/src/main/java/de/diddiz/LogBlock/config/Config.java @@ -46,6 +46,9 @@ public class Config public static Set ignoredChat; public static SimpleDateFormat formatter; public static boolean safetyIdCheck; + public static boolean logEnvironmentalKills; + // Not loaded from config - checked at runtime + public static boolean mb4 = false; public static enum LogKillsLevel { @@ -82,6 +85,7 @@ public class Config def.put("clearlog.autoClearLogDelay", "6h"); def.put("logging.logCreeperExplosionsAsPlayerWhoTriggeredThese", false); def.put("logging.logKillsLevel", "PLAYERS"); + def.put("logging.logEnvironmentalKills", false); def.put("logging.logPlayerInfo", false); def.put("logging.hiddenPlayers", new ArrayList()); def.put("logging.hiddenBlocks", Arrays.asList(0)); @@ -129,7 +133,7 @@ public class Config if (!config.contains(e.getKey())) config.set(e.getKey(), e.getValue()); logblock.saveConfig(); - url = "jdbc:mysql://" + config.getString("mysql.host") + ":" + config.getInt("mysql.port") + "/" + getStringIncludingInts(config, "mysql.database") + "?useUnicode=true&characterEncoding=utf-8"; + url = "jdbc:mysql://" + config.getString("mysql.host") + ":" + config.getInt("mysql.port") + "/" + getStringIncludingInts(config, "mysql.database") + "?useUnicode=true&characterEncoding=utf-8&rewriteBatchedStatements=true"; user = getStringIncludingInts(config, "mysql.user"); password = getStringIncludingInts(config, "mysql.password"); delayBetweenRuns = config.getInt("consumer.delayBetweenRuns", 2); @@ -147,8 +151,9 @@ public class Config try { logKillsLevel = LogKillsLevel.valueOf(config.getString("logging.logKillsLevel").toUpperCase()); } catch (final IllegalArgumentException ex) { - throw new DataFormatException("lookup.toolblockID doesn't appear to be a valid log level. Allowed are 'PLAYERS', 'MONSTERS' and 'ANIMALS'"); + throw new DataFormatException("logging.logKillsLevel doesn't appear to be a valid log level. Allowed are 'PLAYERS', 'MONSTERS' and 'ANIMALS'"); } + logEnvironmentalKills = config.getBoolean("logging.logEnvironmentalKills", false); hiddenPlayers = new HashSet(); for (final String playerName : config.getStringList("logging.hiddenPlayers")) hiddenPlayers.add(playerName.toLowerCase().trim()); diff --git a/src/main/java/de/diddiz/LogBlock/config/WorldConfig.java b/src/main/java/de/diddiz/LogBlock/config/WorldConfig.java index ee81eda..1fef2d0 100644 --- a/src/main/java/de/diddiz/LogBlock/config/WorldConfig.java +++ b/src/main/java/de/diddiz/LogBlock/config/WorldConfig.java @@ -14,7 +14,9 @@ public class WorldConfig extends LoggingEnabledMapping public WorldConfig(File file) throws IOException { final Map def = new HashMap(); - def.put("table", "lb-" + file.getName().substring(0, file.getName().length() - 4).replace(' ', '_')); + // "Before MySQL 5.1.6, database and table names cannot contain "/", "\", ".", or characters that are not permitted in file names" - MySQL manual + // They _can_ contain spaces, but replace them as well + def.put("table", "lb-" + file.getName().substring(0, file.getName().length() - 4).replaceAll("[ ./\\\\]", "_")); for (final Logging l : Logging.values()) def.put("logging." + l.toString(), l.isDefaultEnabled()); final YamlConfiguration config = YamlConfiguration.loadConfiguration(file); diff --git a/src/main/java/de/diddiz/LogBlock/events/BlockChangePreLogEvent.java b/src/main/java/de/diddiz/LogBlock/events/BlockChangePreLogEvent.java index fa7c668..e2b8f6c 100644 --- a/src/main/java/de/diddiz/LogBlock/events/BlockChangePreLogEvent.java +++ b/src/main/java/de/diddiz/LogBlock/events/BlockChangePreLogEvent.java @@ -1,4 +1,5 @@ package de.diddiz.LogBlock.events; +import de.diddiz.LogBlock.Actor; import de.diddiz.LogBlock.ChestAccess; import org.apache.commons.lang.Validate; import org.bukkit.Location; @@ -13,7 +14,7 @@ public class BlockChangePreLogEvent extends PreLogEvent { private String signText; private ChestAccess chestAccess; - public BlockChangePreLogEvent(String owner, Location location, int typeBefore, int typeAfter, byte data, + public BlockChangePreLogEvent(Actor owner, Location location, int typeBefore, int typeAfter, byte data, String signText, ChestAccess chestAccess) { super(owner); diff --git a/src/main/java/de/diddiz/LogBlock/events/PreLogEvent.java b/src/main/java/de/diddiz/LogBlock/events/PreLogEvent.java index 3c12e6b..49c8598 100644 --- a/src/main/java/de/diddiz/LogBlock/events/PreLogEvent.java +++ b/src/main/java/de/diddiz/LogBlock/events/PreLogEvent.java @@ -1,24 +1,36 @@ package de.diddiz.LogBlock.events; +import de.diddiz.LogBlock.Actor; import org.bukkit.event.Cancellable; import org.bukkit.event.Event; public abstract class PreLogEvent extends Event implements Cancellable { protected boolean cancelled = false; - protected String owner; + protected Actor owner; - public PreLogEvent(String owner) { + public PreLogEvent(Actor owner) { - this.owner = owner.replaceAll("[^a-zA-Z0-9_]", ""); + this.owner = owner; } /** * Returns the player/monster/cause involved in this event * * @return Player/monster/cause who is involved in this event + * @deprecated {@link #getOwnerActor() } returns an object encapsulating + * name and uuid. Names are not guaranteed to be unique. */ public String getOwner() { + return owner.getName(); + } + + /** + * Returns the player/monster/cause involved in this event + * + * @return Player/monster/cause who is involved in this event + */ + public Actor getOwnerActor() { return owner; } @@ -27,9 +39,9 @@ public abstract class PreLogEvent extends Event implements Cancellable { * * @param owner The player/monster/cause who is involved in this event */ - public void setOwner(String owner) { + public void setOwner(Actor owner) { - this.owner = owner.replaceAll("[^a-zA-Z0-9_]", ""); + this.owner = owner; } public boolean isCancelled() { diff --git a/src/main/java/de/diddiz/LogBlock/listeners/BlockBreakLogging.java b/src/main/java/de/diddiz/LogBlock/listeners/BlockBreakLogging.java index d9dc725..22dad56 100644 --- a/src/main/java/de/diddiz/LogBlock/listeners/BlockBreakLogging.java +++ b/src/main/java/de/diddiz/LogBlock/listeners/BlockBreakLogging.java @@ -1,5 +1,6 @@ package de.diddiz.LogBlock.listeners; +import de.diddiz.LogBlock.Actor; import static de.diddiz.LogBlock.config.Config.getWorldConfig; import static de.diddiz.LogBlock.config.Config.isLogging; import static de.diddiz.util.LoggingUtil.smartLogBlockBreak; @@ -29,33 +30,33 @@ public class BlockBreakLogging extends LoggingListener WorldConfig wcfg = getWorldConfig(event.getBlock().getWorld()); if (wcfg == null) return; - final String playerName = event.getPlayer().getName(); + final Actor actor = Actor.actorFromEntity(event.getPlayer()); final Block origin = event.getBlock(); final int typeId = origin.getTypeId(); final Material type = origin.getType(); if (wcfg.isLogging(Logging.SIGNTEXT) && (typeId == 63 || typeId == 68)) { - consumer.queueSignBreak(playerName, (Sign) origin.getState()); + consumer.queueSignBreak(actor, (Sign) origin.getState()); } else if (wcfg.isLogging(Logging.CHESTACCESS) && BukkitUtils.getContainerBlocks().contains(type)) { - consumer.queueContainerBreak(playerName, origin.getState()); + consumer.queueContainerBreak(actor, origin.getState()); } else if (type == Material.ICE) { // When in creative mode ice doesn't form water if (event.getPlayer().getGameMode().equals(GameMode.CREATIVE)) { - consumer.queueBlockBreak(playerName, origin.getState()); + consumer.queueBlockBreak(actor, origin.getState()); } else { - consumer.queueBlockReplace(playerName, origin.getState(), 9, (byte) 0); + consumer.queueBlockReplace(actor, origin.getState(), 9, (byte) 0); } } else { - smartLogBlockBreak(consumer, playerName, origin); + smartLogBlockBreak(consumer, actor, origin); } - smartLogFallables(consumer, playerName, origin); + smartLogFallables(consumer, actor, origin); } } @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) public void onPlayerBucketFill(PlayerBucketFillEvent event) { if (isLogging(event.getBlockClicked().getWorld(), Logging.BLOCKBREAK)) { - consumer.queueBlockBreak(event.getPlayer().getName(), event.getBlockClicked().getState()); + consumer.queueBlockBreak(Actor.actorFromEntity(event.getPlayer()), event.getBlockClicked().getState()); } } } diff --git a/src/main/java/de/diddiz/LogBlock/listeners/BlockBurnLogging.java b/src/main/java/de/diddiz/LogBlock/listeners/BlockBurnLogging.java index 7119ab4..e819b63 100644 --- a/src/main/java/de/diddiz/LogBlock/listeners/BlockBurnLogging.java +++ b/src/main/java/de/diddiz/LogBlock/listeners/BlockBurnLogging.java @@ -3,9 +3,18 @@ package de.diddiz.LogBlock.listeners; import static de.diddiz.LogBlock.config.Config.isLogging; import static de.diddiz.util.LoggingUtil.smartLogBlockBreak; import static de.diddiz.util.LoggingUtil.smartLogFallables; + +import org.bukkit.Material; +import org.bukkit.block.Block; +import org.bukkit.block.BlockFace; +import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; import org.bukkit.event.EventPriority; +import org.bukkit.event.block.Action; import org.bukkit.event.block.BlockBurnEvent; +import org.bukkit.event.player.PlayerInteractEvent; + +import de.diddiz.LogBlock.Actor; import de.diddiz.LogBlock.LogBlock; import de.diddiz.LogBlock.Logging; @@ -18,8 +27,21 @@ public class BlockBurnLogging extends LoggingListener @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) public void onBlockBurn(BlockBurnEvent event) { if (isLogging(event.getBlock().getWorld(), Logging.FIRE)) { - smartLogBlockBreak(consumer, "Fire", event.getBlock()); - smartLogFallables(consumer, "Fire", event.getBlock()); + smartLogBlockBreak(consumer, new Actor("Fire"), event.getBlock()); + smartLogFallables(consumer, new Actor("Fire"), event.getBlock()); + } + } + + @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) + public void onExtinguish(PlayerInteractEvent event) { + if(event.getAction().equals(Action.LEFT_CLICK_BLOCK)){ + Player player = event.getPlayer(); + Block block = event.getClickedBlock().getRelative(event.getBlockFace()); + if (block.getType().equals(Material.FIRE) && isLogging(player.getWorld(), Logging.FIRE)) { + Actor actor = Actor.actorFromEntity(player); + smartLogBlockBreak(consumer, actor, block); + smartLogFallables(consumer, actor, block); + } } } } diff --git a/src/main/java/de/diddiz/LogBlock/listeners/BlockPlaceLogging.java b/src/main/java/de/diddiz/LogBlock/listeners/BlockPlaceLogging.java index cc65377..58ae7fb 100644 --- a/src/main/java/de/diddiz/LogBlock/listeners/BlockPlaceLogging.java +++ b/src/main/java/de/diddiz/LogBlock/listeners/BlockPlaceLogging.java @@ -1,5 +1,6 @@ package de.diddiz.LogBlock.listeners; +import de.diddiz.LogBlock.Actor; import static de.diddiz.LogBlock.config.Config.getWorldConfig; import static de.diddiz.LogBlock.config.Config.isLogging; @@ -29,14 +30,14 @@ public class BlockPlaceLogging extends LoggingListener final Material type = event.getBlock().getType(); final BlockState before = event.getBlockReplacedState(); final BlockState after = event.getBlockPlaced().getState(); - final String playerName = event.getPlayer().getName(); + final Actor actor = Actor.actorFromEntity(event.getPlayer()); //Handle falling blocks if (BukkitUtils.getRelativeTopFallables().contains(type)) { // Catch placed blocks overwriting something if (before.getType() != Material.AIR) { - consumer.queueBlockBreak(playerName, before); + consumer.queueBlockBreak(actor, before); } Location loc = event.getBlock().getLocation(); @@ -55,9 +56,9 @@ public class BlockPlaceLogging extends LoggingListener // Run this check to avoid false positives if (!BukkitUtils.getFallingEntityKillers().contains(finalLoc.getBlock().getType())) { if (finalLoc.getBlock().getType() == Material.AIR || finalLoc.equals(event.getBlock().getLocation())) { - consumer.queueBlockPlace(playerName, finalLoc, type.getId(), event.getBlock().getData()); + consumer.queueBlockPlace(actor, finalLoc, type.getId(), event.getBlock().getData()); } else { - consumer.queueBlockReplace(playerName, finalLoc, finalLoc.getBlock().getTypeId(), finalLoc.getBlock().getData(), type.getId(), event.getBlock().getData()); + consumer.queueBlockReplace(actor, finalLoc, finalLoc.getBlock().getTypeId(), finalLoc.getBlock().getData(), type.getId(), event.getBlock().getData()); } } } @@ -73,9 +74,9 @@ public class BlockPlaceLogging extends LoggingListener @Override public void run() { if (before.getTypeId() == 0) - consumer.queueBlockPlace(playerName, after); + consumer.queueBlockPlace(actor, after); else - consumer.queueBlockReplace(playerName, before, after); + consumer.queueBlockReplace(actor, before, after); } }, 1L); } @@ -84,6 +85,6 @@ public class BlockPlaceLogging extends LoggingListener @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) public void onPlayerBucketEmpty(PlayerBucketEmptyEvent event) { if (isLogging(event.getPlayer().getWorld(), Logging.BLOCKPLACE)) - consumer.queueBlockPlace(event.getPlayer().getName(), event.getBlockClicked().getRelative(event.getBlockFace()).getLocation(), event.getBucket() == Material.WATER_BUCKET ? 9 : 11, (byte)0); + consumer.queueBlockPlace(Actor.actorFromEntity(event.getPlayer()), event.getBlockClicked().getRelative(event.getBlockFace()).getLocation(), event.getBucket() == Material.WATER_BUCKET ? 9 : 11, (byte)0); } } diff --git a/src/main/java/de/diddiz/LogBlock/listeners/BlockSpreadLogging.java b/src/main/java/de/diddiz/LogBlock/listeners/BlockSpreadLogging.java index b1d0f38..1cac2d0 100644 --- a/src/main/java/de/diddiz/LogBlock/listeners/BlockSpreadLogging.java +++ b/src/main/java/de/diddiz/LogBlock/listeners/BlockSpreadLogging.java @@ -1,15 +1,15 @@ package de.diddiz.LogBlock.listeners; +import de.diddiz.LogBlock.Actor; import de.diddiz.LogBlock.LogBlock; import de.diddiz.LogBlock.Logging; +import static de.diddiz.LogBlock.config.Config.isLogging; import org.bukkit.Material; import org.bukkit.World; import org.bukkit.event.EventHandler; import org.bukkit.event.EventPriority; import org.bukkit.event.block.BlockSpreadEvent; -import static de.diddiz.LogBlock.config.Config.isLogging; - public class BlockSpreadLogging extends LoggingListener { @@ -47,6 +47,6 @@ public class BlockSpreadLogging extends LoggingListener return; } - consumer.queueBlockReplace(name, event.getBlock().getState(), event.getNewState()); + consumer.queueBlockReplace(new Actor(name), event.getBlock().getState(), event.getNewState()); } } diff --git a/src/main/java/de/diddiz/LogBlock/listeners/ChatLogging.java b/src/main/java/de/diddiz/LogBlock/listeners/ChatLogging.java index 9f24db3..80078c5 100644 --- a/src/main/java/de/diddiz/LogBlock/listeners/ChatLogging.java +++ b/src/main/java/de/diddiz/LogBlock/listeners/ChatLogging.java @@ -1,13 +1,14 @@ package de.diddiz.LogBlock.listeners; +import de.diddiz.LogBlock.Actor; +import de.diddiz.LogBlock.LogBlock; +import de.diddiz.LogBlock.Logging; import static de.diddiz.LogBlock.config.Config.isLogging; import org.bukkit.event.EventHandler; import org.bukkit.event.EventPriority; import org.bukkit.event.player.AsyncPlayerChatEvent; import org.bukkit.event.player.PlayerCommandPreprocessEvent; import org.bukkit.event.server.ServerCommandEvent; -import de.diddiz.LogBlock.LogBlock; -import de.diddiz.LogBlock.Logging; public class ChatLogging extends LoggingListener { @@ -18,17 +19,17 @@ public class ChatLogging extends LoggingListener @EventHandler(priority = EventPriority.MONITOR) public void onPlayerCommandPreprocess(PlayerCommandPreprocessEvent event) { if (isLogging(event.getPlayer().getWorld(), Logging.CHAT)) - consumer.queueChat(event.getPlayer().getName(), event.getMessage()); + consumer.queueChat(Actor.actorFromEntity(event.getPlayer()), event.getMessage()); } @EventHandler(priority = EventPriority.MONITOR) public void onPlayerChat(AsyncPlayerChatEvent event) { if (isLogging(event.getPlayer().getWorld(), Logging.CHAT)) - consumer.queueChat(event.getPlayer().getName(), event.getMessage()); + consumer.queueChat(Actor.actorFromEntity(event.getPlayer()), event.getMessage()); } @EventHandler(priority = EventPriority.MONITOR) public void onServerCommand(ServerCommandEvent event) { - consumer.queueChat("Console", "/" + event.getCommand()); + consumer.queueChat(new Actor("Console"), "/" + event.getCommand()); } } diff --git a/src/main/java/de/diddiz/LogBlock/listeners/ChestAccessLogging.java b/src/main/java/de/diddiz/LogBlock/listeners/ChestAccessLogging.java index 2be244f..9354556 100644 --- a/src/main/java/de/diddiz/LogBlock/listeners/ChestAccessLogging.java +++ b/src/main/java/de/diddiz/LogBlock/listeners/ChestAccessLogging.java @@ -1,5 +1,6 @@ package de.diddiz.LogBlock.listeners; +import de.diddiz.LogBlock.Actor; import static de.diddiz.LogBlock.config.Config.isLogging; import static de.diddiz.util.BukkitUtils.compareInventories; import static de.diddiz.util.BukkitUtils.compressInventory; @@ -43,7 +44,7 @@ public class ChestAccessLogging extends LoggingListener final ItemStack[] diff = compareInventories(before, after); final Location loc = getInventoryHolderLocation(holder); for (final ItemStack item : diff) { - consumer.queueChestAccess(player.getName(), loc, loc.getWorld().getBlockTypeIdAt(loc), (short)item.getTypeId(), (short)item.getAmount(), rawData(item)); + consumer.queueChestAccess(Actor.actorFromEntity(player), loc, loc.getWorld().getBlockTypeIdAt(loc), (short)item.getTypeId(), (short)item.getAmount(), rawData(item)); } containers.remove(player); } diff --git a/src/main/java/de/diddiz/LogBlock/listeners/CreatureInteractLogging.java b/src/main/java/de/diddiz/LogBlock/listeners/CreatureInteractLogging.java index c7a5f03..940359e 100644 --- a/src/main/java/de/diddiz/LogBlock/listeners/CreatureInteractLogging.java +++ b/src/main/java/de/diddiz/LogBlock/listeners/CreatureInteractLogging.java @@ -1,7 +1,9 @@ package de.diddiz.LogBlock.listeners; +import de.diddiz.LogBlock.Actor; import de.diddiz.LogBlock.LogBlock; import de.diddiz.LogBlock.Logging; +import static de.diddiz.LogBlock.config.Config.getWorldConfig; import de.diddiz.LogBlock.config.WorldConfig; import de.diddiz.util.BukkitUtils; import org.bukkit.Location; @@ -14,8 +16,6 @@ import org.bukkit.event.EventHandler; import org.bukkit.event.EventPriority; import org.bukkit.event.entity.EntityInteractEvent; -import static de.diddiz.LogBlock.config.Config.getWorldConfig; - public class CreatureInteractLogging extends LoggingListener { public CreatureInteractLogging(LogBlock lb) { @@ -42,11 +42,11 @@ public class CreatureInteractLogging extends LoggingListener case SOIL: if (wcfg.isLogging(Logging.CREATURECROPTRAMPLE)) { // 3 = Dirt ID - consumer.queueBlock(entityType.getName(), loc, typeId, 3, blockData); + consumer.queueBlock(Actor.actorFromEntity(entityType), loc, typeId, 3, blockData); // Log the crop on top as being broken Block trampledCrop = clicked.getRelative(BlockFace.UP); if (BukkitUtils.getCropBlocks().contains(trampledCrop.getType())) { - consumer.queueBlockBreak("CreatureTrample", trampledCrop.getState()); + consumer.queueBlockBreak(new Actor("CreatureTrample"), trampledCrop.getState()); } } break; diff --git a/src/main/java/de/diddiz/LogBlock/listeners/EndermenLogging.java b/src/main/java/de/diddiz/LogBlock/listeners/EndermenLogging.java index 4adcd10..1f4bc21 100644 --- a/src/main/java/de/diddiz/LogBlock/listeners/EndermenLogging.java +++ b/src/main/java/de/diddiz/LogBlock/listeners/EndermenLogging.java @@ -1,12 +1,13 @@ package de.diddiz.LogBlock.listeners; +import de.diddiz.LogBlock.Actor; +import de.diddiz.LogBlock.LogBlock; +import de.diddiz.LogBlock.Logging; import static de.diddiz.LogBlock.config.Config.isLogging; import org.bukkit.entity.Enderman; import org.bukkit.event.EventHandler; import org.bukkit.event.EventPriority; import org.bukkit.event.entity.EntityChangeBlockEvent; -import de.diddiz.LogBlock.LogBlock; -import de.diddiz.LogBlock.Logging; public class EndermenLogging extends LoggingListener { @@ -17,6 +18,6 @@ public class EndermenLogging extends LoggingListener @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) public void onEntityChangeBlock(EntityChangeBlockEvent event) { if (event.getEntity() instanceof Enderman && isLogging(event.getBlock().getWorld(), Logging.ENDERMEN)) - consumer.queueBlockReplace("Enderman", event.getBlock().getState(), event.getTo().getId(), (byte)0); // Figure out how to get the data of the placed block; + consumer.queueBlockReplace(new Actor("Enderman"), event.getBlock().getState(), event.getTo().getId(), (byte)0); // Figure out how to get the data of the placed block; } } diff --git a/src/main/java/de/diddiz/LogBlock/listeners/ExplosionLogging.java b/src/main/java/de/diddiz/LogBlock/listeners/ExplosionLogging.java index f16f8ef..4d9bb66 100644 --- a/src/main/java/de/diddiz/LogBlock/listeners/ExplosionLogging.java +++ b/src/main/java/de/diddiz/LogBlock/listeners/ExplosionLogging.java @@ -1,8 +1,10 @@ package de.diddiz.LogBlock.listeners; -import de.diddiz.LogBlock.LogBlock; -import de.diddiz.LogBlock.Logging; -import de.diddiz.LogBlock.config.WorldConfig; +import static de.diddiz.LogBlock.config.Config.getWorldConfig; +import static de.diddiz.LogBlock.config.Config.logCreeperExplosionsAsPlayerWhoTriggeredThese; +import static de.diddiz.util.BukkitUtils.getContainerBlocks; + +import org.bukkit.Material; import org.bukkit.block.Block; import org.bukkit.block.Sign; import org.bukkit.entity.Creeper; @@ -10,17 +12,20 @@ import org.bukkit.entity.EnderDragon; import org.bukkit.entity.Entity; import org.bukkit.entity.Fireball; import org.bukkit.entity.Ghast; -import org.bukkit.entity.minecart.ExplosiveMinecart; import org.bukkit.entity.Player; import org.bukkit.entity.TNTPrimed; import org.bukkit.entity.Wither; import org.bukkit.entity.WitherSkull; +import org.bukkit.entity.minecart.ExplosiveMinecart; import org.bukkit.event.EventHandler; import org.bukkit.event.EventPriority; import org.bukkit.event.entity.EntityExplodeEvent; +import org.bukkit.projectiles.ProjectileSource; -import static de.diddiz.LogBlock.config.Config.getWorldConfig; -import static de.diddiz.LogBlock.config.Config.logCreeperExplosionsAsPlayerWhoTriggeredThese; +import de.diddiz.LogBlock.Actor; +import de.diddiz.LogBlock.LogBlock; +import de.diddiz.LogBlock.Logging; +import de.diddiz.LogBlock.config.WorldConfig; public class ExplosionLogging extends LoggingListener { @@ -32,7 +37,7 @@ public class ExplosionLogging extends LoggingListener public void onEntityExplode(EntityExplodeEvent event) { final WorldConfig wcfg = getWorldConfig(event.getLocation().getWorld()); if (wcfg != null) { - String name = "Explosion"; + Actor actor = new Actor("Explosion"); Entity source = event.getEntity(); if (source == null) { if (!wcfg.isLogging(Logging.MISCEXPLOSION)) @@ -40,22 +45,22 @@ public class ExplosionLogging extends LoggingListener } else if (source instanceof TNTPrimed) { if (!wcfg.isLogging(Logging.TNTEXPLOSION)) return; - name = "TNT"; + actor = new Actor("TNT"); } else if (source instanceof ExplosiveMinecart) { if (!wcfg.isLogging(Logging.TNTMINECARTEXPLOSION)) return; - name = "TNTMinecart"; + actor = new Actor("TNTMinecart"); } else if (source instanceof Creeper) { if (!wcfg.isLogging(Logging.CREEPEREXPLOSION)) return; if (logCreeperExplosionsAsPlayerWhoTriggeredThese) { final Entity target = ((Creeper) source).getTarget(); - name = target instanceof Player ? ((Player)target).getName() : "Creeper"; + actor = target instanceof Player ? Actor.actorFromEntity(target) : new Actor("Creeper"); } else - name = "Creeper"; + new Actor("Creeper"); } else if (source instanceof Fireball) { Fireball fireball = (Fireball) source; - Entity shooter = fireball.getShooter(); + ProjectileSource shooter = fireball.getShooter(); if (shooter == null) { return; } @@ -63,25 +68,25 @@ public class ExplosionLogging extends LoggingListener if (!wcfg.isLogging(Logging.GHASTFIREBALLEXPLOSION)) { return; } - name = "Ghast"; + actor = Actor.actorFromProjectileSource(shooter); } else if (shooter instanceof Wither) { if (!wcfg.isLogging(Logging.WITHER)) { return; } - name = "Wither"; + actor = Actor.actorFromProjectileSource(shooter); } } else if (source instanceof EnderDragon) { if (!wcfg.isLogging(Logging.ENDERDRAGON)) return; - name = "EnderDragon"; + actor = Actor.actorFromEntity(source); } else if (source instanceof Wither) { if(!wcfg.isLogging(Logging.WITHER)) return; - name = "Wither"; + actor = Actor.actorFromEntity(source); } else if (source instanceof WitherSkull) { if(!wcfg.isLogging(Logging.WITHER_SKULL)) return; - name = "WitherSkull"; + actor = Actor.actorFromEntity(source); } else { if (!wcfg.isLogging(Logging.MISCEXPLOSION)) return; @@ -89,11 +94,11 @@ public class ExplosionLogging extends LoggingListener for (final Block block : event.blockList()) { final int type = block.getTypeId(); if (wcfg.isLogging(Logging.SIGNTEXT) & (type == 63 || type == 68)) - consumer.queueSignBreak(name, (Sign)block.getState()); - else if (wcfg.isLogging(Logging.CHESTACCESS) && (type == 23 || type == 54 || type == 61)) - consumer.queueContainerBreak(name, block.getState()); + consumer.queueSignBreak(actor, (Sign)block.getState()); + else if (wcfg.isLogging(Logging.CHESTACCESS) && (getContainerBlocks().contains(Material.getMaterial(type)))) + consumer.queueContainerBreak(actor, block.getState()); else - consumer.queueBlockBreak(name, block.getState()); + consumer.queueBlockBreak(actor, block.getState()); } } } diff --git a/src/main/java/de/diddiz/LogBlock/listeners/FluidFlowLogging.java b/src/main/java/de/diddiz/LogBlock/listeners/FluidFlowLogging.java index 5df6411..d64bfb6 100644 --- a/src/main/java/de/diddiz/LogBlock/listeners/FluidFlowLogging.java +++ b/src/main/java/de/diddiz/LogBlock/listeners/FluidFlowLogging.java @@ -1,6 +1,10 @@ package de.diddiz.LogBlock.listeners; +import de.diddiz.LogBlock.Actor; +import de.diddiz.LogBlock.LogBlock; +import de.diddiz.LogBlock.Logging; import static de.diddiz.LogBlock.config.Config.getWorldConfig; +import de.diddiz.LogBlock.config.WorldConfig; import java.util.Arrays; import java.util.HashSet; import java.util.Set; @@ -9,9 +13,6 @@ import org.bukkit.block.BlockFace; import org.bukkit.event.EventHandler; import org.bukkit.event.EventPriority; import org.bukkit.event.block.BlockFromToEvent; -import de.diddiz.LogBlock.LogBlock; -import de.diddiz.LogBlock.Logging; -import de.diddiz.LogBlock.config.WorldConfig; public class FluidFlowLogging extends LoggingListener { @@ -32,37 +33,37 @@ public class FluidFlowLogging extends LoggingListener if (typeFrom == 10 || typeFrom == 11) { if (canFlow && wcfg.isLogging(Logging.LAVAFLOW)) { if (isSurroundedByWater(to) && event.getBlock().getData() <= 2) - consumer.queueBlockReplace("LavaFlow", to.getState(), 4, (byte)0); + consumer.queueBlockReplace(new Actor("LavaFlow"), to.getState(), 4, (byte)0); else if (typeTo == 0) { - consumer.queueBlockPlace("LavaFlow", to.getLocation(), 10, (byte)(event.getBlock().getData() + 1)); + consumer.queueBlockPlace(new Actor("LavaFlow"), to.getLocation(), 10, (byte)(event.getBlock().getData() + 1)); } else { - consumer.queueBlockReplace("LavaFlow", to.getState(), 10, (byte)(event.getBlock().getData() + 1)); + consumer.queueBlockReplace(new Actor("LavaFlow"), to.getState(), 10, (byte)(event.getBlock().getData() + 1)); } } else if (typeTo == 8 || typeTo == 9) { if (event.getFace() == BlockFace.DOWN) { - consumer.queueBlockReplace("LavaFlow", to.getState(), 1, (byte)0); + consumer.queueBlockReplace(new Actor("LavaFlow"), to.getState(), 1, (byte)0); } else { - consumer.queueBlockReplace("LavaFlow", to.getState(), 4, (byte)0); + consumer.queueBlockReplace(new Actor("LavaFlow"), to.getState(), 4, (byte)0); } } } else if ((typeFrom == 8 || typeFrom == 9) && wcfg.isLogging(Logging.WATERFLOW)) { if (typeTo == 0) { - consumer.queueBlockPlace("WaterFlow", to.getLocation(), 8, (byte)(event.getBlock().getData() + 1)); + consumer.queueBlockPlace(new Actor("WaterFlow"), to.getLocation(), 8, (byte)(event.getBlock().getData() + 1)); } else if (nonFluidProofBlocks.contains(typeTo)) { - consumer.queueBlockReplace("WaterFlow", to.getState(), 8, (byte)(event.getBlock().getData() + 1)); + consumer.queueBlockReplace(new Actor("WaterFlow"), to.getState(), 8, (byte)(event.getBlock().getData() + 1)); } else if (typeTo == 10 || typeTo == 11) { if (to.getData() == 0) { - consumer.queueBlockReplace("WaterFlow", to.getState(), 49, (byte)0); + consumer.queueBlockReplace(new Actor("WaterFlow"), to.getState(), 49, (byte)0); } else if (event.getFace() == BlockFace.DOWN) { - consumer.queueBlockReplace("LavaFlow", to.getState(), 1, (byte)0); + consumer.queueBlockReplace(new Actor("LavaFlow"), to.getState(), 1, (byte)0); } } if (typeTo == 0 || nonFluidProofBlocks.contains(typeTo)) { for (final BlockFace face : new BlockFace[]{BlockFace.DOWN, BlockFace.NORTH, BlockFace.WEST, BlockFace.EAST, BlockFace.SOUTH}) { final Block lower = to.getRelative(face); if (lower.getTypeId() == 10 || lower.getTypeId() == 11) { - consumer.queueBlockReplace("WaterFlow", lower.getState(), lower.getData() == 0 ? 49 : 4, (byte)0); + consumer.queueBlockReplace(new Actor("WaterFlow"), lower.getState(), lower.getData() == 0 ? 49 : 4, (byte)0); } } } diff --git a/src/main/java/de/diddiz/LogBlock/listeners/InteractLogging.java b/src/main/java/de/diddiz/LogBlock/listeners/InteractLogging.java index 6caddb1..086cb33 100644 --- a/src/main/java/de/diddiz/LogBlock/listeners/InteractLogging.java +++ b/src/main/java/de/diddiz/LogBlock/listeners/InteractLogging.java @@ -1,5 +1,6 @@ package de.diddiz.LogBlock.listeners; +import de.diddiz.LogBlock.Actor; import static de.diddiz.LogBlock.config.Config.getWorldConfig; import de.diddiz.util.BukkitUtils; @@ -15,6 +16,7 @@ import org.bukkit.event.player.PlayerInteractEvent; import de.diddiz.LogBlock.LogBlock; import de.diddiz.LogBlock.Logging; import de.diddiz.LogBlock.config.WorldConfig; +import org.bukkit.entity.EntityType; public class InteractLogging extends LoggingListener { @@ -27,6 +29,7 @@ public class InteractLogging extends LoggingListener final WorldConfig wcfg = getWorldConfig(event.getPlayer().getWorld()); if (wcfg != null) { final Block clicked = event.getClickedBlock(); + if (clicked == null) return; final Material type = clicked.getType(); final int typeId = type.getId(); final byte blockData = clicked.getData(); @@ -38,31 +41,31 @@ public class InteractLogging extends LoggingListener case WOOD_BUTTON: case STONE_BUTTON: if (wcfg.isLogging(Logging.SWITCHINTERACT) && event.getAction() == Action.RIGHT_CLICK_BLOCK) - consumer.queueBlock(player.getName(), loc, typeId, typeId, blockData); + consumer.queueBlock(Actor.actorFromEntity(player), loc, typeId, typeId, blockData); break; case FENCE_GATE: case WOODEN_DOOR: case TRAP_DOOR: if (wcfg.isLogging(Logging.DOORINTERACT) && event.getAction() == Action.RIGHT_CLICK_BLOCK) - consumer.queueBlock(player.getName(), loc, typeId, typeId, blockData); + consumer.queueBlock(Actor.actorFromEntity(player), loc, typeId, typeId, blockData); break; case CAKE_BLOCK: if (wcfg.isLogging(Logging.CAKEEAT) && event.getAction() == Action.RIGHT_CLICK_BLOCK && player.getFoodLevel() < 20) - consumer.queueBlock(player.getName(), loc, typeId, typeId, blockData); + consumer.queueBlock(Actor.actorFromEntity(player), loc, typeId, typeId, blockData); break; case NOTE_BLOCK: if (wcfg.isLogging(Logging.NOTEBLOCKINTERACT) && event.getAction() == Action.RIGHT_CLICK_BLOCK) - consumer.queueBlock(player.getName(), loc, typeId, typeId, blockData); + consumer.queueBlock(Actor.actorFromEntity(player), loc, typeId, typeId, blockData); break; case DIODE_BLOCK_OFF: case DIODE_BLOCK_ON: if (wcfg.isLogging(Logging.DIODEINTERACT) && event.getAction() == Action.RIGHT_CLICK_BLOCK) - consumer.queueBlock(player.getName(), loc, typeId, typeId, blockData); + consumer.queueBlock(Actor.actorFromEntity(player), loc, typeId, typeId, blockData); break; case REDSTONE_COMPARATOR_OFF: case REDSTONE_COMPARATOR_ON: if (wcfg.isLogging(Logging.COMPARATORINTERACT) && event.getAction() == Action.RIGHT_CLICK_BLOCK) { - consumer.queueBlock(player.getName(), loc, typeId, typeId, blockData); + consumer.queueBlock(Actor.actorFromEntity(player), loc, typeId, typeId, blockData); } break; case WOOD_PLATE: @@ -70,22 +73,22 @@ public class InteractLogging extends LoggingListener case IRON_PLATE: case GOLD_PLATE: if (wcfg.isLogging(Logging.PRESUREPLATEINTERACT) && event.getAction() == Action.PHYSICAL) { - consumer.queueBlock(player.getName(), loc, typeId, typeId, blockData); + consumer.queueBlock(Actor.actorFromEntity(player), loc, typeId, typeId, blockData); } break; case TRIPWIRE: if (wcfg.isLogging(Logging.TRIPWIREINTERACT) && event.getAction() == Action.PHYSICAL) { - consumer.queueBlock(player.getName(), loc, typeId, typeId, blockData); + consumer.queueBlock(Actor.actorFromEntity(player), loc, typeId, typeId, blockData); } break; case SOIL: if (wcfg.isLogging(Logging.CROPTRAMPLE) && event.getAction() == Action.PHYSICAL) { // 3 = Dirt ID - consumer.queueBlock(player.getName(), loc, typeId, 3, blockData); + consumer.queueBlock(Actor.actorFromEntity(player), loc, typeId, 3, blockData); // Log the crop on top as being broken Block trampledCrop = clicked.getRelative(BlockFace.UP); if (BukkitUtils.getCropBlocks().contains(trampledCrop.getType())) { - consumer.queueBlockBreak(player.getName(), trampledCrop.getState()); + consumer.queueBlockBreak(Actor.actorFromEntity(player), trampledCrop.getState()); } } break; diff --git a/src/main/java/de/diddiz/LogBlock/listeners/KillLogging.java b/src/main/java/de/diddiz/LogBlock/listeners/KillLogging.java index 0f34325..0a8a7b7 100644 --- a/src/main/java/de/diddiz/LogBlock/listeners/KillLogging.java +++ b/src/main/java/de/diddiz/LogBlock/listeners/KillLogging.java @@ -1,9 +1,12 @@ package de.diddiz.LogBlock.listeners; +import de.diddiz.LogBlock.Actor; +import de.diddiz.LogBlock.LogBlock; +import de.diddiz.LogBlock.Logging; +import de.diddiz.LogBlock.config.Config.LogKillsLevel; import static de.diddiz.LogBlock.config.Config.isLogging; +import static de.diddiz.LogBlock.config.Config.logEnvironmentalKills; import static de.diddiz.LogBlock.config.Config.logKillsLevel; -import java.util.HashMap; -import java.util.Map; import org.bukkit.entity.Entity; import org.bukkit.entity.LivingEntity; import org.bukkit.entity.Monster; @@ -12,35 +15,36 @@ import org.bukkit.event.EventHandler; import org.bukkit.event.EventPriority; import org.bukkit.event.entity.EntityDamageByEntityEvent; import org.bukkit.event.entity.EntityDamageEvent; -import de.diddiz.LogBlock.LogBlock; -import de.diddiz.LogBlock.Logging; -import de.diddiz.LogBlock.config.Config.LogKillsLevel; +import org.bukkit.event.entity.EntityDeathEvent; + public class KillLogging extends LoggingListener { - private final Map lastAttackedEntity = new HashMap(); - private final Map lastAttackTime = new HashMap(); public KillLogging(LogBlock lb) { super(lb); } @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) - public void onEntityDamage(EntityDamageEvent event) { - if (isLogging(event.getEntity().getWorld(), Logging.KILL) && event instanceof EntityDamageByEntityEvent && event.getEntity() instanceof LivingEntity) { + public void onEntityDeath(EntityDeathEvent deathEvent) { + EntityDamageEvent event = deathEvent.getEntity().getLastDamageCause(); + // For a death event, there should always be a damage event and it should not be cancelled. Check anyway. + if (event!= null && event.isCancelled() == false && isLogging(event.getEntity().getWorld(), Logging.KILL) && event.getEntity() instanceof LivingEntity) { final LivingEntity victim = (LivingEntity)event.getEntity(); - final Entity killer = ((EntityDamageByEntityEvent)event).getDamager(); - if (victim.getHealth() - event.getDamage() > 0 || victim.getHealth() <= 0) - return; - if (logKillsLevel == LogKillsLevel.PLAYERS && !(victim instanceof Player && killer instanceof Player)) - return; - else if (logKillsLevel == LogKillsLevel.MONSTERS && !((victim instanceof Player || victim instanceof Monster) && killer instanceof Player || killer instanceof Monster)) - return; - if (lastAttackedEntity.containsKey(killer.getEntityId()) && lastAttackedEntity.get(killer.getEntityId()) == victim.getEntityId() && System.currentTimeMillis() - lastAttackTime.get(killer.getEntityId()) < 5000) - return; - consumer.queueKill(killer, victim); - lastAttackedEntity.put(killer.getEntityId(), victim.getEntityId()); - lastAttackTime.put(killer.getEntityId(), System.currentTimeMillis()); + if (event instanceof EntityDamageByEntityEvent) { + final Entity killer = ((EntityDamageByEntityEvent)event).getDamager(); + if (logKillsLevel == LogKillsLevel.PLAYERS && !(victim instanceof Player && killer instanceof Player)) + return; + else if (logKillsLevel == LogKillsLevel.MONSTERS && !((victim instanceof Player || victim instanceof Monster) && killer instanceof Player || killer instanceof Monster)) + return; + consumer.queueKill(killer, victim); + } else if (logEnvironmentalKills) { + if (logKillsLevel == LogKillsLevel.PLAYERS && !(victim instanceof Player)) + return; + else if (logKillsLevel == LogKillsLevel.MONSTERS && !((victim instanceof Player || victim instanceof Monster))) + return; + consumer.queueKill(new Actor(event.getCause().toString()),victim); + } } } } diff --git a/src/main/java/de/diddiz/LogBlock/listeners/LeavesDecayLogging.java b/src/main/java/de/diddiz/LogBlock/listeners/LeavesDecayLogging.java index 204749d..253eaa5 100644 --- a/src/main/java/de/diddiz/LogBlock/listeners/LeavesDecayLogging.java +++ b/src/main/java/de/diddiz/LogBlock/listeners/LeavesDecayLogging.java @@ -1,13 +1,14 @@ package de.diddiz.LogBlock.listeners; +import de.diddiz.LogBlock.Actor; +import de.diddiz.LogBlock.LogBlock; +import de.diddiz.LogBlock.Logging; import static de.diddiz.LogBlock.config.Config.isLogging; import static de.diddiz.util.LoggingUtil.smartLogBlockBreak; import static de.diddiz.util.LoggingUtil.smartLogFallables; import org.bukkit.event.EventHandler; import org.bukkit.event.EventPriority; import org.bukkit.event.block.LeavesDecayEvent; -import de.diddiz.LogBlock.LogBlock; -import de.diddiz.LogBlock.Logging; public class LeavesDecayLogging extends LoggingListener { @@ -18,8 +19,8 @@ public class LeavesDecayLogging extends LoggingListener @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) public void onLeavesDecay(LeavesDecayEvent event) { if (isLogging(event.getBlock().getWorld(), Logging.LEAVESDECAY)) { - smartLogBlockBreak(consumer, "LeavesDecay", event.getBlock()); - smartLogFallables(consumer, "LeavesDecay", event.getBlock()); + smartLogBlockBreak(consumer, new Actor("LeavesDecay"), event.getBlock()); + smartLogFallables(consumer, new Actor("LeavesDecay"), event.getBlock()); } } } diff --git a/src/main/java/de/diddiz/LogBlock/listeners/LockedChestDecayLogging.java b/src/main/java/de/diddiz/LogBlock/listeners/LockedChestDecayLogging.java new file mode 100644 index 0000000..5f1c766 --- /dev/null +++ b/src/main/java/de/diddiz/LogBlock/listeners/LockedChestDecayLogging.java @@ -0,0 +1,25 @@ +package de.diddiz.LogBlock.listeners; + +import de.diddiz.LogBlock.Actor; +import de.diddiz.LogBlock.LogBlock; +import de.diddiz.LogBlock.Logging; +import static de.diddiz.LogBlock.config.Config.isLogging; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.block.BlockFadeEvent; + +public class LockedChestDecayLogging extends LoggingListener +{ + public LockedChestDecayLogging(LogBlock lb) { + super(lb); + } + + @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) + public void onBlockFade(BlockFadeEvent event) { + if (isLogging(event.getBlock().getWorld(), Logging.LOCKEDCHESTDECAY)) { + final int type = event.getBlock().getTypeId(); + if (type == 95) + consumer.queueBlockReplace(new Actor("LockedChestDecay"), event.getBlock().getState(), event.getNewState()); + } + } +} diff --git a/src/main/java/de/diddiz/LogBlock/listeners/SignChangeLogging.java b/src/main/java/de/diddiz/LogBlock/listeners/SignChangeLogging.java index 8d4389d..0b2e823 100644 --- a/src/main/java/de/diddiz/LogBlock/listeners/SignChangeLogging.java +++ b/src/main/java/de/diddiz/LogBlock/listeners/SignChangeLogging.java @@ -1,11 +1,13 @@ package de.diddiz.LogBlock.listeners; +import de.diddiz.LogBlock.Actor; +import de.diddiz.LogBlock.LogBlock; +import de.diddiz.LogBlock.Logging; import static de.diddiz.LogBlock.config.Config.isLogging; +import org.bukkit.entity.Entity; import org.bukkit.event.EventHandler; import org.bukkit.event.EventPriority; import org.bukkit.event.block.SignChangeEvent; -import de.diddiz.LogBlock.LogBlock; -import de.diddiz.LogBlock.Logging; public class SignChangeLogging extends LoggingListener { @@ -16,6 +18,6 @@ public class SignChangeLogging extends LoggingListener @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) public void onSignChange(SignChangeEvent event) { if (isLogging(event.getBlock().getWorld(), Logging.SIGNTEXT)) - consumer.queueSignPlace(event.getPlayer().getName(), event.getBlock().getLocation(), event.getBlock().getTypeId(), event.getBlock().getData(), event.getLines()); + consumer.queueSignPlace(Actor.actorFromEntity(event.getPlayer()), event.getBlock().getLocation(), event.getBlock().getTypeId(), event.getBlock().getData(), event.getLines()); } } diff --git a/src/main/java/de/diddiz/LogBlock/listeners/SnowFadeLogging.java b/src/main/java/de/diddiz/LogBlock/listeners/SnowFadeLogging.java index 042d992..e15c638 100644 --- a/src/main/java/de/diddiz/LogBlock/listeners/SnowFadeLogging.java +++ b/src/main/java/de/diddiz/LogBlock/listeners/SnowFadeLogging.java @@ -1,11 +1,12 @@ package de.diddiz.LogBlock.listeners; +import de.diddiz.LogBlock.Actor; +import de.diddiz.LogBlock.LogBlock; +import de.diddiz.LogBlock.Logging; import static de.diddiz.LogBlock.config.Config.isLogging; import org.bukkit.event.EventHandler; import org.bukkit.event.EventPriority; import org.bukkit.event.block.BlockFadeEvent; -import de.diddiz.LogBlock.LogBlock; -import de.diddiz.LogBlock.Logging; public class SnowFadeLogging extends LoggingListener { @@ -18,7 +19,7 @@ public class SnowFadeLogging extends LoggingListener if (isLogging(event.getBlock().getWorld(), Logging.SNOWFADE)) { final int type = event.getBlock().getTypeId(); if (type == 78 || type == 79) - consumer.queueBlockReplace("SnowFade", event.getBlock().getState(), event.getNewState()); + consumer.queueBlockReplace(new Actor("SnowFade"), event.getBlock().getState(), event.getNewState()); } } } diff --git a/src/main/java/de/diddiz/LogBlock/listeners/SnowFormLogging.java b/src/main/java/de/diddiz/LogBlock/listeners/SnowFormLogging.java index 51bd2f1..f911a56 100644 --- a/src/main/java/de/diddiz/LogBlock/listeners/SnowFormLogging.java +++ b/src/main/java/de/diddiz/LogBlock/listeners/SnowFormLogging.java @@ -1,12 +1,13 @@ package de.diddiz.LogBlock.listeners; +import de.diddiz.LogBlock.Actor; +import de.diddiz.LogBlock.LogBlock; +import de.diddiz.LogBlock.Logging; import static de.diddiz.LogBlock.config.Config.isLogging; import org.bukkit.event.EventHandler; import org.bukkit.event.EventPriority; import org.bukkit.event.block.BlockFormEvent; import org.bukkit.event.block.LeavesDecayEvent; -import de.diddiz.LogBlock.LogBlock; -import de.diddiz.LogBlock.Logging; public class SnowFormLogging extends LoggingListener { @@ -14,18 +15,18 @@ public class SnowFormLogging extends LoggingListener super(lb); } - @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) - public void onLeavesDecay(LeavesDecayEvent event) { - if (isLogging(event.getBlock().getWorld(), Logging.SNOWFORM)) - consumer.queueBlockBreak("LeavesDecay", event.getBlock().getState()); - } +// @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) +// public void onLeavesDecay(LeavesDecayEvent event) { +// if (isLogging(event.getBlock().getWorld(), Logging.SNOWFORM)) +// consumer.queueBlockBreak("LeavesDecay", event.getBlock().getState()); +// } @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) public void onBlockForm(BlockFormEvent event) { if (isLogging(event.getBlock().getWorld(), Logging.SNOWFORM)) { final int type = event.getNewState().getTypeId(); if (type == 78 || type == 79) - consumer.queueBlockReplace("SnowForm", event.getBlock().getState(), event.getNewState()); + consumer.queueBlockReplace(new Actor("SnowForm"), event.getBlock().getState(), event.getNewState()); } } } diff --git a/src/main/java/de/diddiz/LogBlock/listeners/StructureGrowLogging.java b/src/main/java/de/diddiz/LogBlock/listeners/StructureGrowLogging.java index fc57cde..786acfe 100644 --- a/src/main/java/de/diddiz/LogBlock/listeners/StructureGrowLogging.java +++ b/src/main/java/de/diddiz/LogBlock/listeners/StructureGrowLogging.java @@ -1,13 +1,14 @@ package de.diddiz.LogBlock.listeners; +import de.diddiz.LogBlock.Actor; +import de.diddiz.LogBlock.LogBlock; +import de.diddiz.LogBlock.Logging; import static de.diddiz.LogBlock.config.Config.getWorldConfig; +import de.diddiz.LogBlock.config.WorldConfig; import org.bukkit.block.BlockState; import org.bukkit.event.EventHandler; import org.bukkit.event.EventPriority; import org.bukkit.event.world.StructureGrowEvent; -import de.diddiz.LogBlock.LogBlock; -import de.diddiz.LogBlock.Logging; -import de.diddiz.LogBlock.config.WorldConfig; public class StructureGrowLogging extends LoggingListener { @@ -19,18 +20,18 @@ public class StructureGrowLogging extends LoggingListener public void onStructureGrow(StructureGrowEvent event) { final WorldConfig wcfg = getWorldConfig(event.getWorld()); if (wcfg != null) { - final String playerName; + final Actor actor; if (event.getPlayer() != null) { if (!wcfg.isLogging(Logging.BONEMEALSTRUCTUREGROW)) return; - playerName = event.getPlayer().getName(); + actor = Actor.actorFromEntity(event.getPlayer()); } else { if (!wcfg.isLogging(Logging.NATURALSTRUCTUREGROW)) return; - playerName = "NaturalGrow"; + actor = new Actor("NaturalGrow"); } for (final BlockState state : event.getBlocks()) - consumer.queueBlockReplace(playerName, state.getBlock().getState(), state); + consumer.queueBlockReplace(actor, state.getBlock().getState(), state); } } } diff --git a/src/main/java/de/diddiz/LogBlock/listeners/ToolListener.java b/src/main/java/de/diddiz/LogBlock/listeners/ToolListener.java index fd267f1..1a7a622 100644 --- a/src/main/java/de/diddiz/LogBlock/listeners/ToolListener.java +++ b/src/main/java/de/diddiz/LogBlock/listeners/ToolListener.java @@ -60,19 +60,19 @@ public class ToolListener implements Listener params.sel = null; if (behavior == ToolBehavior.BLOCK) params.setLocation(block.getRelative(event.getBlockFace()).getLocation()); - else if (block.getTypeId() != 54 || tool.params.radius != 0) + else if ((block.getTypeId() != 54 && block.getTypeId() != 146) || tool.params.radius != 0) params.setLocation(block.getLocation()); else { if (logblock.getServer().getPluginManager().isPluginEnabled("WorldEdit")) { for (final BlockFace face : new BlockFace[]{BlockFace.NORTH, BlockFace.SOUTH, BlockFace.EAST, BlockFace.WEST}) { - if (block.getRelative(face).getTypeId() == 54) { + if (block.getRelative(face).getTypeId() == block.getTypeId()) { params.setSelection(RegionContainer.fromCorners(event.getPlayer().getWorld(), block.getLocation(), block.getRelative(face).getLocation())); } } - if (params.sel == null) { - params.setLocation(block.getLocation()); - } + } + if (params.sel == null) { + params.setLocation(block.getLocation()); } } try { diff --git a/src/main/java/de/diddiz/LogBlock/listeners/WitherLogging.java b/src/main/java/de/diddiz/LogBlock/listeners/WitherLogging.java index 260ff09..ec5dad2 100644 --- a/src/main/java/de/diddiz/LogBlock/listeners/WitherLogging.java +++ b/src/main/java/de/diddiz/LogBlock/listeners/WitherLogging.java @@ -1,5 +1,6 @@ package de.diddiz.LogBlock.listeners; +import de.diddiz.LogBlock.Actor; import de.diddiz.LogBlock.LogBlock; import de.diddiz.LogBlock.Logging; import org.bukkit.entity.Wither; @@ -18,6 +19,6 @@ public class WitherLogging extends LoggingListener @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) public void onEntityChangeBlock(EntityChangeBlockEvent event) { if (event.getEntity() instanceof Wither && isLogging(event.getBlock().getWorld(), Logging.WITHER)) - consumer.queueBlockReplace("Wither", event.getBlock().getState(), event.getTo().getId(), event.getData()); // Wither walked through a block. + consumer.queueBlockReplace(Actor.actorFromEntity(event.getEntity()), event.getBlock().getState(), event.getTo().getId(), event.getData()); // Wither walked through a block. } } diff --git a/src/main/java/de/diddiz/util/BukkitUtils.java b/src/main/java/de/diddiz/util/BukkitUtils.java index b3c7041..308641e 100644 --- a/src/main/java/de/diddiz/util/BukkitUtils.java +++ b/src/main/java/de/diddiz/util/BukkitUtils.java @@ -6,8 +6,11 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.Comparator; +import java.util.EnumMap; +import java.util.HashMap; import java.util.HashSet; import java.util.List; +import java.util.Map; import java.util.Set; import org.bukkit.ChatColor; import org.bukkit.Chunk; @@ -18,6 +21,7 @@ import org.bukkit.block.BlockFace; import org.bukkit.block.BlockState; import org.bukkit.block.DoubleChest; import org.bukkit.entity.Entity; +import org.bukkit.entity.EntityType; import org.bukkit.entity.Player; import org.bukkit.entity.TNTPrimed; import org.bukkit.inventory.Inventory; @@ -35,6 +39,8 @@ public class BukkitUtils private static final Set cropBlocks; private static final Set containerBlocks; + private static final Map projectileItems; + static { blockEquivalents = new HashSet>(7); blockEquivalents.add(new HashSet(Arrays.asList(2, 3, 60))); @@ -60,7 +66,7 @@ public class BukkitUtils relativeBreakable.add(Material.COCOA); // Blocks that break when they are on top of a block - relativeTopBreakable = new HashSet(32); + relativeTopBreakable = new HashSet(33); relativeTopBreakable.add(Material.SAPLING); relativeTopBreakable.add(Material.LONG_GRASS); relativeTopBreakable.add(Material.DEAD_BUSH); @@ -91,8 +97,9 @@ public class BukkitUtils relativeTopBreakable.add(Material.REDSTONE_COMPARATOR_ON); relativeTopBreakable.add(Material.REDSTONE_COMPARATOR_OFF); relativeTopBreakable.add(Material.WOODEN_DOOR); - relativeTopBreakable.add(Material.IRON_DOOR); + relativeTopBreakable.add(Material.IRON_DOOR_BLOCK); relativeTopBreakable.add(Material.CARPET); + relativeTopBreakable.add(Material.DOUBLE_PLANT); // Blocks that fall relativeTopFallables = new HashSet(4); @@ -152,8 +159,25 @@ public class BukkitUtils containerBlocks.add(Material.DROPPER); containerBlocks.add(Material.HOPPER); containerBlocks.add(Material.BREWING_STAND); + containerBlocks.add(Material.FURNACE); + containerBlocks.add(Material.BURNING_FURNACE); + containerBlocks.add(Material.BEACON); // Doesn't actually have a block inventory // containerBlocks.add(Material.ENDER_CHEST); + + // It doesn't seem like you could injure people with some of these, but they exist, so.... + projectileItems = new EnumMap(EntityType.class); + projectileItems.put(EntityType.ARROW,262); + projectileItems.put(EntityType.EGG,344); + projectileItems.put(EntityType.ENDER_PEARL,368); + projectileItems.put(EntityType.SMALL_FIREBALL,385); // Fire charge + projectileItems.put(EntityType.FIREBALL,385); // Fire charge + projectileItems.put(EntityType.FISHING_HOOK,346); + projectileItems.put(EntityType.SNOWBALL,332); + projectileItems.put(EntityType.SPLASH_POTION,373); + projectileItems.put(EntityType.THROWN_EXP_BOTTLE,384); + projectileItems.put(EntityType.WITHER_SKULL,397); + } private static final BlockFace[] relativeBlockFaces = new BlockFace[] { @@ -177,6 +201,19 @@ public class BukkitUtils return blocks; } + public static boolean isTop(Material mat, byte data) { + + switch (mat) { + case DOUBLE_PLANT: + return data > 5; + case IRON_DOOR_BLOCK: + case WOODEN_DOOR: + return data == 8 || data == 9; + default: + return false; + } + } + public static int getInventoryHolderType(InventoryHolder holder) { if (holder instanceof DoubleChest) { return ((DoubleChest)holder).getLocation().getBlock().getTypeId(); @@ -240,7 +277,7 @@ public class BukkitUtils for (final ItemStack item : items) if (item != null) { final int type = item.getTypeId(); - final byte data = rawData(item); + final short data = rawData(item); boolean found = false; for (final ItemStack item2 : compressed) if (type == item2.getTypeId() && data == rawData(item2)) { @@ -249,7 +286,7 @@ public class BukkitUtils break; } if (!found) - compressed.add(new ItemStack(type, item.getAmount(), (short)0, data)); + compressed.add(new ItemStack(type, item.getAmount(), data)); } Collections.sort(compressed, new ItemStackComparator()); return compressed.toArray(new ItemStack[compressed.size()]); @@ -320,8 +357,8 @@ public class BukkitUtils } } - public static byte rawData(ItemStack item) { - return item.getType() != null ? item.getData() != null ? item.getData().getData() : 0 : 0; + public static short rawData(ItemStack item) { + return item.getType() != null ? item.getData() != null ? item.getDurability() : 0 : 0; } public static int saveSpawnHeight(Location loc) { @@ -379,7 +416,7 @@ public class BukkitUtils return -1; if (aType > bType) return 1; - final byte aData = rawData(a), bData = rawData(b); + final short aData = rawData(a), bData = rawData(b); if (aData < bData) return -1; if (aData > bData) @@ -387,4 +424,9 @@ public class BukkitUtils return 0; } } + + public static int itemIDfromProjectileEntity(Entity e) { + Integer i = projectileItems.get(e.getType()); + return (i == null) ? 0 : i; + } } diff --git a/src/main/java/de/diddiz/util/LoggingUtil.java b/src/main/java/de/diddiz/util/LoggingUtil.java index b53c6a8..0b4d67a 100644 --- a/src/main/java/de/diddiz/util/LoggingUtil.java +++ b/src/main/java/de/diddiz/util/LoggingUtil.java @@ -1,6 +1,11 @@ package de.diddiz.util; +import de.diddiz.LogBlock.Actor; +import de.diddiz.LogBlock.Consumer; +import de.diddiz.LogBlock.Logging; import static de.diddiz.LogBlock.config.Config.getWorldConfig; +import static de.diddiz.LogBlock.config.Config.mb4; +import de.diddiz.LogBlock.config.WorldConfig; import java.util.List; import org.bukkit.Location; import org.bukkit.Material; @@ -17,13 +22,10 @@ import org.bukkit.material.RedstoneTorch; import org.bukkit.material.Torch; import org.bukkit.material.TrapDoor; import org.bukkit.material.TripwireHook; -import de.diddiz.LogBlock.Consumer; -import de.diddiz.LogBlock.Logging; -import de.diddiz.LogBlock.config.WorldConfig; public class LoggingUtil { - public static void smartLogFallables(Consumer consumer, String playerName, Block origin) { + public static void smartLogFallables(Consumer consumer, Actor actor, Block origin) { WorldConfig wcfg = getWorldConfig(origin.getWorld()); if (wcfg == null) return; @@ -35,7 +37,7 @@ public class LoggingUtil { while (BukkitUtils.getRelativeTopFallables().contains(checkBlock.getType())) { // Record this block as falling - consumer.queueBlockBreak(playerName, checkBlock.getState()); + consumer.queueBlockBreak(actor, checkBlock.getState()); // Guess where the block is going (This could be thrown of by explosions, but it is better than nothing) Location loc = origin.getLocation(); @@ -52,9 +54,9 @@ public class LoggingUtil { if (!BukkitUtils.getFallingEntityKillers().contains(finalLoc.getBlock().getType())) { finalLoc.add(0, up, 0); // Add this here after checking for block breakers if (finalLoc.getBlock().getType() == Material.AIR || BukkitUtils.getRelativeTopFallables().contains(finalLoc.getBlock().getType())) { - consumer.queueBlockPlace(playerName, finalLoc, checkBlock.getTypeId(), checkBlock.getData()); + consumer.queueBlockPlace(actor, finalLoc, checkBlock.getTypeId(), checkBlock.getData()); } else { - consumer.queueBlockReplace(playerName, finalLoc, finalLoc.getBlock().getTypeId(), finalLoc.getBlock().getData(), checkBlock.getTypeId(), checkBlock.getData()); + consumer.queueBlockReplace(actor, finalLoc, finalLoc.getBlock().getTypeId(), finalLoc.getBlock().getData(), checkBlock.getTypeId(), checkBlock.getData()); } up++; } @@ -64,7 +66,7 @@ public class LoggingUtil { } } - public static void smartLogBlockBreak(Consumer consumer, String playerName, Block origin) { + public static void smartLogBlockBreak(Consumer consumer, Actor actor, Block origin) { WorldConfig wcfg = getWorldConfig(origin.getWorld()); if (wcfg == null) return; @@ -72,21 +74,33 @@ public class LoggingUtil { Block checkBlock = origin.getRelative(BlockFace.UP); if (BukkitUtils.getRelativeTopBreakabls().contains(checkBlock.getType())) { if (wcfg.isLogging(Logging.SIGNTEXT) && checkBlock.getType() == Material.SIGN_POST) { - consumer.queueSignBreak(playerName, (Sign) checkBlock.getState()); - } else if (checkBlock.getType() == Material.IRON_DOOR || checkBlock.getType() == Material.WOOD_DOOR) { + consumer.queueSignBreak(actor, (Sign) checkBlock.getState()); + } else if (checkBlock.getType() == Material.IRON_DOOR_BLOCK || checkBlock.getType() == Material.WOODEN_DOOR) { Block doorBlock = checkBlock; // If the doorBlock is the top half a door the player simply punched a door // this will be handled later. - if (doorBlock.getData() != 8 && doorBlock.getData() != 9) { + if (!BukkitUtils.isTop(doorBlock.getType(), doorBlock.getData())) { doorBlock = doorBlock.getRelative(BlockFace.UP); // Fall back check just in case the top half wasn't a door - if (doorBlock.getType() == Material.IRON_DOOR || doorBlock.getType() == Material.WOOD_DOOR) { - consumer.queueBlockBreak(playerName, doorBlock.getState()); + if (doorBlock.getType() == Material.IRON_DOOR_BLOCK || doorBlock.getType() == Material.WOODEN_DOOR) { + consumer.queueBlockBreak(actor, doorBlock.getState()); } - consumer.queueBlockBreak(playerName, checkBlock.getState()); + consumer.queueBlockBreak(actor, checkBlock.getState()); + } + } else if (checkBlock.getType() == Material.DOUBLE_PLANT) { + Block plantBlock = checkBlock; + // If the plantBlock is the top half of a double plant the player simply + // punched the plant this will be handled later. + if (!BukkitUtils.isTop(plantBlock.getType(), plantBlock.getData())) { + plantBlock = plantBlock.getRelative(BlockFace.UP); + // Fall back check just in case the top half wasn't a plant + if (plantBlock.getType() == Material.DOUBLE_PLANT) { + consumer.queueBlockBreak(actor, plantBlock.getState()); + } + consumer.queueBlockBreak(actor, checkBlock.getState()); } } else { - consumer.queueBlockBreak(playerName, checkBlock.getState()); + consumer.queueBlockBreak(actor, checkBlock.getState()); } } @@ -100,78 +114,97 @@ public class LoggingUtil { case REDSTONE_TORCH_ON: case REDSTONE_TORCH_OFF: if (blockState.getBlock().getRelative(((RedstoneTorch) data).getAttachedFace()).equals(origin)) { - consumer.queueBlockBreak(playerName, blockState); + consumer.queueBlockBreak(actor, blockState); } break; case TORCH: if (blockState.getBlock().getRelative(((Torch) data).getAttachedFace()).equals(origin)) { - consumer.queueBlockBreak(playerName, blockState); + consumer.queueBlockBreak(actor, blockState); } break; case COCOA: - if (blockState.getBlock().getRelative(((CocoaPlant) data).getAttachedFace()).equals(origin)) { - consumer.queueBlockBreak(playerName, blockState); + if (blockState.getBlock().getRelative(((CocoaPlant) data).getAttachedFace().getOppositeFace()).equals(origin)) { + consumer.queueBlockBreak(actor, blockState); } break; case LADDER: if (blockState.getBlock().getRelative(((Ladder) data).getAttachedFace()).equals(origin)) { - consumer.queueBlockBreak(playerName, blockState); + consumer.queueBlockBreak(actor, blockState); } break; case LEVER: if (blockState.getBlock().getRelative(((Lever) data).getAttachedFace()).equals(origin)) { - consumer.queueBlockBreak(playerName, blockState); + consumer.queueBlockBreak(actor, blockState); } break; case TRIPWIRE_HOOK: if (blockState.getBlock().getRelative(((TripwireHook) data).getAttachedFace()).equals(origin)) { - consumer.queueBlockBreak(playerName, blockState); + consumer.queueBlockBreak(actor, blockState); } break; case WOOD_BUTTON: case STONE_BUTTON: if (blockState.getBlock().getRelative(((Button) data).getAttachedFace()).equals(origin)) { - consumer.queueBlockBreak(playerName, blockState); + consumer.queueBlockBreak(actor, blockState); } break; case WALL_SIGN: if (blockState.getBlock().getRelative(((org.bukkit.material.Sign) data).getAttachedFace()).equals(origin)) { if (wcfg.isLogging(Logging.SIGNTEXT)) { - consumer.queueSignBreak(playerName, (Sign) blockState); + consumer.queueSignBreak(actor, (Sign) blockState); } else { - consumer.queueBlockBreak(playerName, blockState); + consumer.queueBlockBreak(actor, blockState); } } break; case TRAP_DOOR: if (blockState.getBlock().getRelative(((TrapDoor) data).getAttachedFace()).equals(origin)) { - consumer.queueBlockBreak(playerName, blockState); + consumer.queueBlockBreak(actor, blockState); } break; default: - consumer.queueBlockBreak(playerName, blockState); + consumer.queueBlockBreak(actor, blockState); break; } } } // Special door check - if (origin.getType() == Material.IRON_DOOR || origin.getType() == Material.WOOD_DOOR) { + if (origin.getType() == Material.IRON_DOOR_BLOCK || origin.getType() == Material.WOODEN_DOOR) { Block doorBlock = origin; // Up or down? - if (origin.getData() != 8 && origin.getData() != 9) { + if (!BukkitUtils.isTop(doorBlock.getType(), doorBlock.getData())) { doorBlock = doorBlock.getRelative(BlockFace.UP); } else { doorBlock = doorBlock.getRelative(BlockFace.DOWN); } - if (doorBlock.getType() == Material.IRON_DOOR || doorBlock.getType() == Material.WOOD_DOOR) { - consumer.queueBlockBreak(playerName, doorBlock.getState()); + if (doorBlock.getType() == Material.IRON_DOOR_BLOCK || doorBlock.getType() == Material.WOODEN_DOOR) { + consumer.queueBlockBreak(actor, doorBlock.getState()); + } + } else if (origin.getType() == Material.DOUBLE_PLANT) { // Special double plant check + Block plantBlock = origin; + + // Up or down? + if (!BukkitUtils.isTop(origin.getType(), origin.getData())) { + plantBlock = plantBlock.getRelative(BlockFace.UP); + } else { + plantBlock = plantBlock.getRelative(BlockFace.DOWN); + } + + if (plantBlock.getType() == Material.DOUBLE_PLANT) { + consumer.queueBlockBreak(actor, plantBlock.getState()); } } // Do this down here so that the block is added after blocks sitting on it - consumer.queueBlockBreak(playerName, origin.getState()); + consumer.queueBlockBreak(actor, origin.getState()); + } + + public static String checkText(String text) { + if (text==null) return text; + if (mb4) return text; + return text.replaceAll("[^\\u0000-\\uFFFF]", "?"); } } diff --git a/src/main/java/de/diddiz/util/MaterialName.java b/src/main/java/de/diddiz/util/MaterialName.java index ca9c92f..2be9310 100644 --- a/src/main/java/de/diddiz/util/MaterialName.java +++ b/src/main/java/de/diddiz/util/MaterialName.java @@ -1,7 +1,7 @@ package de.diddiz.util; -import static de.diddiz.util.Utils.isByte; import static de.diddiz.util.Utils.isInt; +import static de.diddiz.util.Utils.isShort; import static org.bukkit.Bukkit.getLogger; import java.io.File; import java.io.IOException; @@ -15,8 +15,10 @@ import org.bukkit.material.MaterialData; public class MaterialName { + private static final String[] COLORS = {"white","orange","magenta","light blue","yellow","lime","pink","gray","silver","cyan","purple","blue","brown","green","red","black"}; private static final Map materialNames = new HashMap(); - private static final Map> materialDataNames = new HashMap>(); + private static final Map> materialDataNames = new HashMap>(); + private static final Map nameTypes = new HashMap(); static { // Add all known materials @@ -28,23 +30,161 @@ public class MaterialName if (cfg.getKeys(false).isEmpty()) { // Generate defaults cfg.options().header("Add block or item names you want to be overridden or also names for custom blocks"); + cfg.set("1.1", "granite"); + cfg.set("1.2", "polished granite"); + cfg.set("1.3", "diorite"); + cfg.set("1.4", "polished diorite"); + cfg.set("1.5", "andesite"); + cfg.set("1.6", "polished andesite"); + cfg.set("5.0", "oak wood"); + cfg.set("5.1", "spruce wood"); + cfg.set("5.2", "birch wood"); + cfg.set("5.3", "jungle wood"); + cfg.set("5.4", "acacia wood"); + cfg.set("5.5", "dark oak wood"); + cfg.set("3.1", "coarse dirt"); + cfg.set("3.2", "podzol"); cfg.set("6.1", "redwood sapling"); cfg.set("6.2", "birch sapling"); + cfg.set("6.3", "jungle sapling"); + cfg.set("6.4", "acacia sapling"); + cfg.set("6.5", "dark oak sapling"); cfg.set("9", "water"); cfg.set("11", "lava"); - cfg.set("17.1", "redwood log"); + cfg.set("12.1", "red sand"); + cfg.set("17.0", "oak log"); + cfg.set("17.1", "spruce log"); cfg.set("17.2", "birch log"); - cfg.set("18.1", "redwood leaves"); + cfg.set("17.3", "jungle log"); + cfg.set("17.4", "oak log"); + cfg.set("17.5", "spruce log"); + cfg.set("17.6", "birch log"); + cfg.set("17.7", "jungle log"); + cfg.set("17.8", "oak log"); + cfg.set("17.9", "spruce log"); + cfg.set("17.10", "birch log"); + cfg.set("17.11", "jungle log"); + cfg.set("17.12", "oak log"); + cfg.set("17.13", "spruce log"); + cfg.set("17.14", "birch log"); + cfg.set("17.15", "jungle log"); + cfg.set("18.1", "spruce leaves"); cfg.set("18.2", "birch leaves"); - cfg.set("31.0", "dead long grass"); + cfg.set("18.3", "jungle leaves"); + cfg.set("18.4", "oak leaves"); + cfg.set("18.5", "spruce leaves"); + cfg.set("18.6", "birch leaves"); + cfg.set("18.7", "jungle leaves"); + cfg.set("18.8", "oak leaves"); + cfg.set("18.9", "spruce leaves"); + cfg.set("18.10", "birch leaves"); + cfg.set("18.11", "jungle leaves"); + cfg.set("18.12", "oak leaves"); + cfg.set("18.13", "spruce leaves"); + cfg.set("18.14", "birch leaves"); + cfg.set("18.15", "jungle leaves"); + cfg.set("19.1", "wet sponge"); + cfg.set("37.0", "dandelion"); + cfg.set("38.0", "poppy"); + cfg.set("38.1", "blue orchid"); + cfg.set("38.2", "allium"); + cfg.set("38.3", "azure bluet"); + cfg.set("38.4", "red tulip"); + cfg.set("38.5", "orange tulip"); + cfg.set("38.6", "white tulip"); + cfg.set("38.7", "pink tulip"); + cfg.set("38.8", "oxeye daisy"); + cfg.set("24.1", "chiseled sandstone"); + cfg.set("24.2", "smooth sandstone"); + cfg.set("31.0", "dead bush"); + cfg.set("31.1", "tall grass"); cfg.set("31.2", "fern"); - for (byte i = 0; i < 7; i++) { - cfg.set("35." + i, toReadable(Material.STEP.getNewData(i))); - cfg.set("351." + i, toReadable(Material.DOUBLE_STEP.getNewData(i))); + cfg.set("98.0", "stone brick"); + cfg.set("98.1", "mossy stone brick"); + cfg.set("98.2", "cracked stone brick"); + cfg.set("98.3", "chiseled stone brick"); + cfg.set("125.0","oak double step"); + cfg.set("125.1","spruce double step"); + cfg.set("125.2","birch double step"); + cfg.set("125.3","jungle double step"); + cfg.set("125.4","acacia double step"); + cfg.set("125.5","dark oak double step"); + cfg.set("126.0", "oak step"); + cfg.set("126.1", "spruce step"); + cfg.set("126.2", "birch step"); + cfg.set("126.3", "jungle step"); + cfg.set("126.4", "acacia step"); + cfg.set("126.5", "dark oak step"); + cfg.set("126.8", "oak step"); + cfg.set("126.9", "spruce step"); + cfg.set("126.10", "birch step"); + cfg.set("126.11", "jungle step"); + cfg.set("126.12", "acacia step"); + cfg.set("126.13", "dark oak step"); + cfg.set("139.1", "mossy cobble wall"); + cfg.set("155.1", "chiseled quartz block"); + cfg.set("155.2", "pillar quartz block"); + cfg.set("155.3", "pillar quartz block"); + cfg.set("155.4", "pillar quartz block"); + cfg.set("161.0", "acacia leaves"); + cfg.set("161.1", "dark oak leaves"); + cfg.set("161.4", "acacia leaves"); + cfg.set("161.5", "dark oak leaves"); + cfg.set("161.8", "acacia leaves"); + cfg.set("161.9", "dark oak leaves"); + cfg.set("161.12", "acacia leaves"); + cfg.set("161.13", "dark oak leaves"); + cfg.set("162.0", "acacia log"); + cfg.set("162.1", "dark oak log"); + cfg.set("162.4", "acacia log"); + cfg.set("162.5", "dark oak log"); + cfg.set("162.8", "acacia log"); + cfg.set("162.9", "dark oak log"); + cfg.set("162.12", "acacia log"); + cfg.set("162.13", "dark oak log"); + cfg.set("168.1", "prismarine brick"); + cfg.set("168.2", "dark prismarine"); + cfg.set("181.0", "red sandstone double step"); + cfg.set("181.8", "smooth red sandstone double step"); + cfg.set("162.13", "dark oak log"); + cfg.set("175.0", "sunflower"); + cfg.set("175.1", "lilac"); + cfg.set("175.2", "double tall grass"); + cfg.set("175.3", "large fern"); + cfg.set("175.4", "rose bush"); + cfg.set("175.5", "peony"); + cfg.set("175.8", "sunflower"); + cfg.set("175.9", "lilac"); + cfg.set("175.10", "double tall grass"); + cfg.set("175.11", "large fern"); + cfg.set("175.12", "rose bush"); + cfg.set("175.13", "peony"); + cfg.set("179.1", "chiseled sandstone"); + cfg.set("179.2", "smooth sandstone"); + cfg.set("263.1", "charcoal"); + for (byte i = 0; i < 10; i++) { + cfg.set("43." + i, toReadable(Material.DOUBLE_STEP.getNewData(i))); + } + cfg.set("43.8", "stone double step"); + cfg.set("43.9", "sandstone double step"); + cfg.set("43.15", "quartz double step"); + for (byte i = 0; i < 8; i++) { + cfg.set("44." + i, toReadable(Material.STEP.getNewData(i))); + // The second half of this data list should read the same as the first half + cfg.set("44." + (i+7), toReadable(Material.STEP.getNewData(i))); } for (byte i = 0; i < 16; i++) { - cfg.set("35." + i, toReadable(Material.WOOL.getNewData(i))); cfg.set("351." + i, toReadable(Material.INK_SACK.getNewData(i))); + cfg.set("35." + i, COLORS[i] + " wool"); + cfg.set("159." + i, COLORS[i] + " stained clay"); + cfg.set("95." + i, COLORS[i] + " stained glass"); + cfg.set("160." + i, COLORS[i] + " stained glass pane"); + cfg.set("171." + i, COLORS[i] + " carpet"); + } + for (byte i = 0; i < 6; i++) { + cfg.set("125." + i, toReadable(Material.WOOD_DOUBLE_STEP.getNewData(i))); + cfg.set("126." + i, toReadable(Material.WOOD_STEP.getNewData(i))); + cfg.set("126." + i+8, toReadable(Material.WOOD_STEP.getNewData(i))); } try { cfg.save(file); @@ -52,18 +192,26 @@ public class MaterialName getLogger().log(Level.WARNING, "Unable to save material.yml: ", ex); } } + if (cfg.getString("263.1") == null) { + getLogger().info("[Logblock-names] Logblock's default materials.yml file has been updated with more names"); + getLogger().info("[Logblock-names] Consider deleting your current materials.yml file to allow it to be recreated"); + } for (final String entry : cfg.getKeys(false)) if (isInt(entry)) { - if (cfg.isString(entry)) + if (cfg.isString(entry)) { materialNames.put(Integer.valueOf(entry), cfg.getString(entry)); + nameTypes.put(cfg.getString(entry), Integer.valueOf(entry)); + } else if (cfg.isConfigurationSection(entry)) { - final Map dataNames = new HashMap(); + final Map dataNames = new HashMap(); materialDataNames.put(Integer.valueOf(entry), dataNames); final ConfigurationSection sec = cfg.getConfigurationSection(entry); for (final String data : sec.getKeys(false)) - if (isByte(data)) { - if (sec.isString(data)) - dataNames.put(Byte.valueOf(data), sec.getString(data)); + if (isShort(data)) { + if (sec.isString(data)) { + dataNames.put(Short.valueOf(data), sec.getString(data)); + nameTypes.put(sec.getString(data), Integer.valueOf(entry)); + } else getLogger().warning("Parsing materials.yml: '" + data + "' is not a string."); } else @@ -84,15 +232,27 @@ public class MaterialName /** * @return Name of the material regarding it's data, or if it's unknown, the basic name. */ - public static String materialName(int type, byte data) { - final Map dataNames = materialDataNames.get(type); + public static String materialName(int type, short data) { + final Map dataNames = materialDataNames.get(type); if (dataNames != null) if (dataNames.containsKey(data)) return dataNames.get(data); return materialName(type); } + + public static Integer typeFromName(String name) { + Integer answer = nameTypes.get(toReadable(name)); + if (answer != null) return answer; + final Material mat = Material.matchMaterial(name); + if (mat == null) throw new IllegalArgumentException("No material matching: '" + name + "'"); + return mat.getId(); + } private static String toReadable(MaterialData matData) { return matData.toString().toLowerCase().replace('_', ' ').replaceAll("[^a-z ]", ""); } + + private static String toReadable(String matData) { + return matData.toLowerCase().replace('_', ' ').replaceAll("[^a-z ]", ""); + } } diff --git a/src/main/java/de/diddiz/util/MySQLConnectionPool.java b/src/main/java/de/diddiz/util/MySQLConnectionPool.java index d78af21..a986976 100644 --- a/src/main/java/de/diddiz/util/MySQLConnectionPool.java +++ b/src/main/java/de/diddiz/util/MySQLConnectionPool.java @@ -1,5 +1,6 @@ package de.diddiz.util; +import static de.diddiz.LogBlock.config.Config.mb4; import java.io.Closeable; import java.sql.Array; import java.sql.Blob; @@ -76,6 +77,7 @@ public class MySQLConnectionPool implements Closeable throw new SQLException("Failed to validate a brand new connection"); } connections.add(conn); + if (mb4) conn.createStatement().executeQuery("SET NAMES utf8mb4"); return conn; } finally { lock.unlock(); diff --git a/src/main/java/de/diddiz/util/UUIDFetcher.java b/src/main/java/de/diddiz/util/UUIDFetcher.java new file mode 100644 index 0000000..a699c95 --- /dev/null +++ b/src/main/java/de/diddiz/util/UUIDFetcher.java @@ -0,0 +1,79 @@ +package de.diddiz.util; + +import org.json.simple.JSONArray; +import org.json.simple.JSONObject; +import org.json.simple.parser.JSONParser; + +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.net.HttpURLConnection; +import java.net.URL; +import java.nio.ByteBuffer; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.UUID; + +// Adapted from https://gist.github.com/evilmidget38/26d70114b834f71fb3b4 + +public class UUIDFetcher { + + private static final String PROFILE_URL = "https://api.mojang.com/profiles/minecraft"; + private static final JSONParser jsonParser = new JSONParser(); + + public static Map getUUIDs(List names) throws Exception { + Map uuidMap = new HashMap(); + HttpURLConnection connection = createConnection(); + String body = JSONArray.toJSONString(names); + writeBody(connection, body); + JSONArray array = (JSONArray) jsonParser.parse(new InputStreamReader(connection.getInputStream())); + for (Object profile : array) { + JSONObject jsonProfile = (JSONObject) profile; + String id = (String) jsonProfile.get("id"); + String name = (String) jsonProfile.get("name"); + UUID uuid = UUIDFetcher.getUUID(id); + uuidMap.put(name, uuid); + } + return uuidMap; + } + + private static void writeBody(HttpURLConnection connection, String body) throws Exception { + OutputStream stream = connection.getOutputStream(); + stream.write(body.getBytes()); + stream.flush(); + stream.close(); + } + + private static HttpURLConnection createConnection() throws Exception { + URL url = new URL(PROFILE_URL); + HttpURLConnection connection = (HttpURLConnection) url.openConnection(); + connection.setRequestMethod("POST"); + connection.setRequestProperty("Content-Type", "application/json"); + connection.setUseCaches(false); + connection.setDoInput(true); + connection.setDoOutput(true); + return connection; + } + + private static UUID getUUID(String id) { + return UUID.fromString(id.substring(0, 8) + "-" + id.substring(8, 12) + "-" + id.substring(12, 16) + "-" + id.substring(16, 20) + "-" + id.substring(20, 32)); + } + + public static byte[] toBytes(UUID uuid) { + ByteBuffer byteBuffer = ByteBuffer.wrap(new byte[16]); + byteBuffer.putLong(uuid.getMostSignificantBits()); + byteBuffer.putLong(uuid.getLeastSignificantBits()); + return byteBuffer.array(); + } + + public static UUID fromBytes(byte[] array) { + if (array.length != 16) { + throw new IllegalArgumentException("Illegal byte array length: " + array.length); + } + ByteBuffer byteBuffer = ByteBuffer.wrap(array); + long mostSignificant = byteBuffer.getLong(); + long leastSignificant = byteBuffer.getLong(); + return new UUID(mostSignificant, leastSignificant); + } + +} diff --git a/src/main/java/de/diddiz/util/Utils.java b/src/main/java/de/diddiz/util/Utils.java index 2929ce5..7959b63 100644 --- a/src/main/java/de/diddiz/util/Utils.java +++ b/src/main/java/de/diddiz/util/Utils.java @@ -19,6 +19,15 @@ public class Utils return false; } + public static boolean isShort(String str) { + try { + Short.parseShort(str); + return true; + } catch (final NumberFormatException ex) { + } + return false; + } + public static boolean isByte(String str) { try { Byte.parseByte(str); @@ -76,6 +85,8 @@ public class Utils while (currIndex <= spec[0].length() && isInt(spec[0].substring(lastIndex, currIndex))) currIndex++; if (currIndex - 1 != lastIndex) { + if (currIndex > spec[0].length()) + return -1; final String param = spec[0].substring(currIndex - 1, currIndex).toLowerCase(); if (param.equals("d")) days = Integer.parseInt(spec[0].substring(lastIndex, currIndex - 1)); diff --git a/src/main/java/de/diddiz/worldedit/LogBlockEditSession.java b/src/main/java/de/diddiz/worldedit/LogBlockEditSession.java deleted file mode 100644 index f87c786..0000000 --- a/src/main/java/de/diddiz/worldedit/LogBlockEditSession.java +++ /dev/null @@ -1,78 +0,0 @@ -package de.diddiz.worldedit; - -import com.sk89q.worldedit.EditSession; -import com.sk89q.worldedit.LocalPlayer; -import com.sk89q.worldedit.LocalWorld; -import com.sk89q.worldedit.Vector; -import com.sk89q.worldedit.bags.BlockBag; -import com.sk89q.worldedit.blocks.BaseBlock; -import com.sk89q.worldedit.bukkit.BukkitWorld; -import de.diddiz.LogBlock.LogBlock; -import de.diddiz.LogBlock.Logging; -import de.diddiz.LogBlock.config.Config; -import org.bukkit.Location; -import org.bukkit.Material; -import org.bukkit.block.BlockState; -import org.bukkit.block.Sign; - -public class LogBlockEditSession extends EditSession { - - private LocalPlayer player; - private LogBlock plugin; - - /** - * {@inheritDoc} - */ - public LogBlockEditSession(LocalWorld world, int maxBlocks, LocalPlayer player, LogBlock lb) { - super(world, maxBlocks); - this.player = player; - this.plugin = lb; - } - - /** - * {@inheritDoc} - */ - public LogBlockEditSession(LocalWorld world, int maxBlocks, BlockBag blockBag, LocalPlayer player, LogBlock lb) { - super(world, maxBlocks, blockBag); - this.player = player; - this.plugin = lb; - } - - @Override - public boolean rawSetBlock(Vector pt, BaseBlock block) { - if (!(player.getWorld() instanceof BukkitWorld) || !(Config.isLogging(player.getWorld().getName(), Logging.WORLDEDIT))) { - return super.rawSetBlock(pt, block); - } - - int typeBefore = ((BukkitWorld) player.getWorld()).getWorld().getBlockTypeIdAt(pt.getBlockX(), pt.getBlockY(), pt.getBlockZ()); - byte dataBefore = ((BukkitWorld) player.getWorld()).getWorld().getBlockAt(pt.getBlockX(), pt.getBlockY(), pt.getBlockZ()).getData(); - // If we're dealing with a sign, store the block state to read the text off - BlockState stateBefore = null; - if (typeBefore == Material.SIGN_POST.getId() || typeBefore == Material.SIGN.getId()) { - stateBefore = ((BukkitWorld) player.getWorld()).getWorld().getBlockAt(pt.getBlockX(), pt.getBlockY(), pt.getBlockZ()).getState(); - } - boolean success = super.rawSetBlock(pt, block); - if (success) { - Location location = new Location(((BukkitWorld) player.getWorld()).getWorld(), pt.getBlockX(), pt.getBlockY(), pt.getBlockZ()); - - // Check to see if we've broken a sign - if (Config.isLogging(location.getWorld().getName(), Logging.SIGNTEXT) && (typeBefore == Material.SIGN_POST.getId() || typeBefore == Material.SIGN.getId())) { - plugin.getConsumer().queueSignBreak(player.getName(), (Sign) stateBefore); - if (block.getType() != Material.AIR.getId()) { - plugin.getConsumer().queueBlockPlace(player.getName(), location, block.getType(), (byte) block.getData()); - } - } else { - if (dataBefore != 0) { - plugin.getConsumer().queueBlockBreak(player.getName(), location, typeBefore, dataBefore); - if (block.getType() != Material.AIR.getId()) { - plugin.getConsumer().queueBlockPlace(player.getName(), location, block.getType(), (byte) block.getData()); - } - } else { - plugin.getConsumer().queueBlock(player.getName(), location, typeBefore, block.getType(), (byte) block.getData()); - } - } - } - return success; - } - -} diff --git a/src/main/java/de/diddiz/worldedit/LogBlockEditSessionFactory.java b/src/main/java/de/diddiz/worldedit/LogBlockEditSessionFactory.java deleted file mode 100644 index 50bf4b9..0000000 --- a/src/main/java/de/diddiz/worldedit/LogBlockEditSessionFactory.java +++ /dev/null @@ -1,39 +0,0 @@ -package de.diddiz.worldedit; - -import com.sk89q.worldedit.EditSession; -import com.sk89q.worldedit.EditSessionFactory; -import com.sk89q.worldedit.LocalPlayer; -import com.sk89q.worldedit.LocalWorld; -import com.sk89q.worldedit.WorldEdit; -import com.sk89q.worldedit.bags.BlockBag; -import de.diddiz.LogBlock.LogBlock; - -public class LogBlockEditSessionFactory extends EditSessionFactory { - - private LogBlock plugin; - - public LogBlockEditSessionFactory(LogBlock lb) { - this.plugin = lb; - } - - @Override - public EditSession getEditSession(LocalWorld world, int maxBlocks, LocalPlayer player) { - return new LogBlockEditSession(world, maxBlocks, player, plugin); - } - - @Override - public EditSession getEditSession(LocalWorld world, int maxBlocks, BlockBag blockBag, LocalPlayer player) { - return new LogBlockEditSession(world, maxBlocks, blockBag, player, plugin); - } - - public static void initialize(LogBlock logBlock) { - try { - // Check to see if the world edit version is compatible - Class.forName("com.sk89q.worldedit.EditSessionFactory").getDeclaredMethod("getEditSession", LocalWorld.class, int.class, BlockBag.class, LocalPlayer.class); - WorldEdit.getInstance().setEditSessionFactory(new LogBlockEditSessionFactory(logBlock)); - } catch (Throwable ignored) { - - } - } - -} diff --git a/src/main/java/de/diddiz/worldedit/WorldEditLoggingHook.java b/src/main/java/de/diddiz/worldedit/WorldEditLoggingHook.java new file mode 100644 index 0000000..7acc140 --- /dev/null +++ b/src/main/java/de/diddiz/worldedit/WorldEditLoggingHook.java @@ -0,0 +1,114 @@ +package de.diddiz.worldedit; + +import com.sk89q.worldedit.EditSession; +import com.sk89q.worldedit.Vector; +import com.sk89q.worldedit.WorldEdit; +import com.sk89q.worldedit.blocks.BaseBlock; +import com.sk89q.worldedit.bukkit.BukkitWorld; +import com.sk89q.worldedit.event.extent.EditSessionEvent; +//...so they ALSO have a class called Actor... need to fully-qualify when we use ours +import com.sk89q.worldedit.extension.platform.Actor; +import com.sk89q.worldedit.extent.logging.AbstractLoggingExtent; +import com.sk89q.worldedit.util.eventbus.Subscribe; +import de.diddiz.LogBlock.LogBlock; +import de.diddiz.LogBlock.Logging; +import de.diddiz.LogBlock.config.Config; +import java.util.logging.Level; +import org.bukkit.Bukkit; +import org.bukkit.Location; +import org.bukkit.Material; +import org.bukkit.World; +import org.bukkit.block.Block; +import org.bukkit.block.BlockState; +import org.bukkit.block.Sign; + +public class WorldEditLoggingHook { + + private LogBlock plugin; + + public WorldEditLoggingHook(LogBlock plugin) { + this.plugin = plugin; + } + + // Convert WE Actor to LB Actor + private de.diddiz.LogBlock.Actor AtoA(Actor weActor) { + if (weActor.isPlayer()) { + return new de.diddiz.LogBlock.Actor(weActor.getName(),weActor.getUniqueId()); + } + return new de.diddiz.LogBlock.Actor(weActor.getName()); + } + + private World adapt(com.sk89q.worldedit.world.World weWorld) { + if (weWorld == null) { + throw new NullPointerException("[Logblock-Worldedit] The provided world was null."); + } + if (weWorld instanceof BukkitWorld) return ((BukkitWorld) weWorld).getWorld(); + World world = Bukkit.getServer().getWorld(weWorld.getName()); + if (world == null) throw new IllegalArgumentException("Can't find a Bukkit world for " + weWorld); + return world; + } + + public void hook() { + WorldEdit.getInstance().getEventBus().register(new Object() { + @Subscribe + public void wrapForLogging(final EditSessionEvent event) { + final Actor actor = event.getActor(); + if (actor == null ) return; + final de.diddiz.LogBlock.Actor lbActor = AtoA(actor); + + // Check to ensure the world should be logged + final World world; + final com.sk89q.worldedit.world.World k = event.getWorld(); + try { + world = adapt(k); + } catch (RuntimeException ex) { + plugin.getLogger().warning("Failed to register logging for WorldEdit!"); + plugin.getLogger().log(Level.WARNING, ex.getMessage(),ex); + return; + } + + // If config becomes reloadable, this check should be moved + if (!(Config.isLogging(world, Logging.WORLDEDIT))) { + return; + } + + event.setExtent(new AbstractLoggingExtent(event.getExtent()) { + @Override + protected void onBlockChange(Vector pt, BaseBlock block) { + + if (event.getStage() != EditSession.Stage.BEFORE_CHANGE) { + return; + } + + Location location = new Location(world, pt.getBlockX(), pt.getBlockY(), pt.getBlockZ()); + Block origin = location.getBlock(); + int typeBefore = origin.getTypeId(); + byte dataBefore = origin.getData(); + // If we're dealing with a sign, store the block state to read the text off + BlockState stateBefore = null; + if (typeBefore == Material.SIGN_POST.getId() || typeBefore == Material.SIGN.getId()) { + stateBefore = origin.getState(); + } + + // Check to see if we've broken a sign + if (Config.isLogging(location.getWorld().getName(), Logging.SIGNTEXT) && (typeBefore == Material.SIGN_POST.getId() || typeBefore == Material.SIGN.getId())) { + plugin.getConsumer().queueSignBreak(lbActor, (Sign) stateBefore); + if (block.getType() != Material.AIR.getId()) { + plugin.getConsumer().queueBlockPlace(lbActor, location, block.getType(), (byte) block.getData()); + } + } else { + if (dataBefore != 0) { + plugin.getConsumer().queueBlockBreak(lbActor, location, typeBefore, dataBefore); + if (block.getType() != Material.AIR.getId()) { + plugin.getConsumer().queueBlockPlace(lbActor, location, block.getType(), (byte) block.getData()); + } + } else { + plugin.getConsumer().queueBlock(lbActor, location, typeBefore, block.getType(), (byte) block.getData()); + } + } + } + }); + } + }); + } +} diff --git a/src/main/resources/plugin.yml b/src/main/resources/plugin.yml index 637fe4e..cb3d5ca 100644 --- a/src/main/resources/plugin.yml +++ b/src/main/resources/plugin.yml @@ -1,7 +1,7 @@ name: ${project.name} -version: '1.70' +version: '1.93' author: DiddiZ -authors: [md_5, ammar2] +authors: [md_5, ammar2, frymaster] website: http://dev.bukkit.org/server-mods/logblock/ main: de.diddiz.LogBlock.LogBlock description: ${project.description}