diff --git a/README b/README.md similarity index 89% rename from README rename to README.md index 4490e63..31a3270 100644 --- a/README +++ b/README.md @@ -1,3 +1,7 @@ -This plugin logs block changes such as breaking, placing, modifying, or burning to a MySQL Database. It can be used as an anti-griefing tool to find out who made a particular edit, or even roll back changes by certain players. +LogBlock +========== -Originally written by bootswithdefer, for hMod, ported to Bukkit by me, because of the inability to identfy griefers. BigBrother also did't work, so I was forced to do it myself. The honor belongs to bootswithdefer for the sourcecode, I only spent about 8 hours to transcribe. All functions except sign text logging shold work as in hMod. The use of permissions plugin is possible, but not necessary. \ No newline at end of file +This plugin logs block changes such as breaking, placing, modifying, or burning to a MySQL Database. It can be used as an anti-griefing tool to find out who made a particular edit, or even roll back changes by certain players. +Originally written by bootswithdefer, for hMod, ported to Bukkit by me, because of the inability to identfy griefers. BigBrother also did't work, so I was forced to do it myself. The honor belongs to bootswithdefer for the sourcecode, I only spent about 8 hours to transcribe. All functions except sign text logging shold work as in hMod. The use of permissions plugin is possible, but not necessary. + +Questioner: http://git.io/u2MxKQ diff --git a/pom.xml b/pom.xml index f493155..f8e3766 100644 --- a/pom.xml +++ b/pom.xml @@ -45,7 +45,7 @@ org.bukkit bukkit - 1.4.6-R0.3 + 1.6.1-R0.1-SNAPSHOT ${project.groupId} diff --git a/src/main/java/de/diddiz/LogBlock/BlockChange.java b/src/main/java/de/diddiz/LogBlock/BlockChange.java index 6dd582d..3e839c4 100644 --- a/src/main/java/de/diddiz/LogBlock/BlockChange.java +++ b/src/main/java/de/diddiz/LogBlock/BlockChange.java @@ -3,9 +3,12 @@ package de.diddiz.LogBlock; import static de.diddiz.util.MaterialName.materialName; import java.sql.ResultSet; import java.sql.SQLException; + +import de.diddiz.util.BukkitUtils; import org.bukkit.Location; import de.diddiz.LogBlock.config.Config; +import org.bukkit.Material; public class BlockChange implements LookupCacheElement { @@ -64,18 +67,31 @@ public class BlockChange implements LookupCacheElement msg.append("took ").append(-ca.itemAmount).append("x ").append(materialName(ca.itemType, ca.itemData)); else msg.append("put in ").append(ca.itemAmount).append("x ").append(materialName(ca.itemType, ca.itemData)); - } else if (type == 23 || type == 54 || type == 61 || type == 62) + } else if (BukkitUtils.getContainerBlocks().contains(Material.getMaterial(type))) msg.append("opened ").append(materialName(type)); - else if (type == 64 || type == 71 || type == 96 || type == 107) - msg.append(data == 0 ? "opened" : "closed").append(" ").append(materialName(type)); + else if (type == 64 || type == 71) + // This is a problem that will have to be addressed in LB 2, + // there is no way to tell from the top half of the block if + // the door is opened or closed. + msg.append("moved ").append(materialName(type)); + // Trapdoor + else if (type == 96) + msg.append((data < 8 || data > 11) ? "opened" : "closed").append(" ").append(materialName(type)); + // Fence gate + else if (type == 107) + msg.append(data > 3 ? "opened" : "closed").append(" ").append(materialName(type)); else if (type == 69) msg.append("switched ").append(materialName(type)); else if (type == 77 || type == 143) msg.append("pressed ").append(materialName(type)); else if (type == 92) msg.append("ate a piece of ").append(materialName(type)); - else if (type == 25 || type == 93 || type == 94) + else if (type == 25 || type == 93 || type == 94 || type == 149 || type == 150) msg.append("changed ").append(materialName(type)); + else if (type == 70 || type == 72 || type == 147 || type == 148) + msg.append("stepped on ").append(materialName(type)); + else if (type == 132) + msg.append("ran into ").append(materialName(type)); } else if (type == 0) msg.append("destroyed ").append(materialName(replaced, data)); else if (replaced == 0) diff --git a/src/main/java/de/diddiz/LogBlock/Consumer.java b/src/main/java/de/diddiz/LogBlock/Consumer.java index 8f22a29..48d8ff2 100644 --- a/src/main/java/de/diddiz/LogBlock/Consumer.java +++ b/src/main/java/de/diddiz/LogBlock/Consumer.java @@ -464,8 +464,8 @@ 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(?, " + playerID(playerName) + ", ?, ?, ?, ?, ?, ?)", Statement.RETURN_GENERATED_KEYS); - ps1.setTimestamp(1, new Timestamp(date * 1000)); + ps1 = connection.prepareStatement("INSERT INTO `" + table + "` (date, playerid, replaced, type, data, x, y, z) VALUES(FROM_UNIXTIME(?), " + playerID(playerName) + ", ?, ?, ?, ?, ?, ?)", Statement.RETURN_GENERATED_KEYS); + ps1.setLong(1, date ); ps1.setInt(2, replaced); ps1.setInt(3, type); ps1.setInt(4, data); @@ -574,7 +574,7 @@ public class Consumer extends TimerTask boolean noID = false; Integer id; - String sql = "INSERT INTO `lb-chat` (date, playerid, message) VALUES (?, "; + String sql = "INSERT INTO `lb-chat` (date, playerid, message) VALUES (FROM_UNIXTIME(?), "; if ((id = playerIDAsInt(playerName)) == null) { noID = true; sql += playerID(playerName) + ", "; @@ -586,7 +586,7 @@ public class Consumer extends TimerTask PreparedStatement ps = null; try { ps = connection.prepareStatement(sql); - ps.setTimestamp(1, new Timestamp(date * 1000)); + ps.setLong(1, date); if (!noID) { ps.setInt(2, id); ps.setString(3, message); diff --git a/src/main/java/de/diddiz/LogBlock/LogBlock.java b/src/main/java/de/diddiz/LogBlock/LogBlock.java index 714f4da..0e31eff 100644 --- a/src/main/java/de/diddiz/LogBlock/LogBlock.java +++ b/src/main/java/de/diddiz/LogBlock/LogBlock.java @@ -149,8 +149,11 @@ public class LogBlock extends JavaPlugin if (isLogging(Logging.CHESTACCESS)) { pm.registerEvents(new ChestAccessLogging(this), this); } - if (isLogging(Logging.SWITCHINTERACT) || isLogging(Logging.DOORINTERACT) || isLogging(Logging.CAKEEAT) || isLogging(Logging.DIODEINTERACT) || isLogging(Logging.NOTEBLOCKINTERACT)) + if (isLogging(Logging.SWITCHINTERACT) || isLogging(Logging.DOORINTERACT) || isLogging(Logging.CAKEEAT) || isLogging(Logging.DIODEINTERACT) || isLogging(Logging.COMPARATORINTERACT) || isLogging(Logging.NOTEBLOCKINTERACT) || isLogging(Logging.PRESUREPLATEINTERACT) || isLogging(Logging.TRIPWIREINTERACT) || isLogging(Logging.CROPTRAMPLE)) pm.registerEvents(new InteractLogging(this), this); + if (isLogging(Logging.CREATURECROPTRAMPLE)) { + pm.registerEvents(new CreatureInteractLogging(this), this); + } if (isLogging(Logging.KILL)) pm.registerEvents(new KillLogging(this), this); if (isLogging(Logging.CHAT)) @@ -161,6 +164,8 @@ public class LogBlock extends JavaPlugin pm.registerEvents(new WitherLogging(this), this); if (isLogging(Logging.NATURALSTRUCTUREGROW) || isLogging(Logging.BONEMEALSTRUCTUREGROW)) 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); } diff --git a/src/main/java/de/diddiz/LogBlock/Logging.java b/src/main/java/de/diddiz/LogBlock/Logging.java index 0c25411..de0c817 100644 --- a/src/main/java/de/diddiz/LogBlock/Logging.java +++ b/src/main/java/de/diddiz/LogBlock/Logging.java @@ -5,9 +5,12 @@ public enum Logging BLOCKPLACE(true), BLOCKBREAK(true), SIGNTEXT, TNTEXPLOSION(true), CREEPEREXPLOSION(true), GHASTFIREBALLEXPLOSION(true), ENDERDRAGON(true), MISCEXPLOSION, FIRE(true), LEAVESDECAY, LAVAFLOW, WATERFLOW, CHESTACCESS, KILL, CHAT, SNOWFORM, SNOWFADE, DOORINTERACT, - SWITCHINTERACT, CAKEEAT, ENDERMEN, NOTEBLOCKINTERACT, DIODEINTERACT, NATURALSTRUCTUREGROW, - WITHER(true), WITHER_SKULL(true), - BONEMEALSTRUCTUREGROW, WORLDEDIT; + SWITCHINTERACT, CAKEEAT, ENDERMEN, NOTEBLOCKINTERACT, DIODEINTERACT, COMPARATORINTERACT, + PRESUREPLATEINTERACT, TRIPWIREINTERACT, CREATURECROPTRAMPLE, CROPTRAMPLE, + NATURALSTRUCTUREGROW, GRASSGROWTH, MYCELIUMSPREAD, VINEGROWTH, MUSHROOMSPREAD, + WITHER(true), WITHER_SKULL(true), BONEMEALSTRUCTUREGROW, + WORLDEDIT, TNTMINECARTEXPLOSION(true); + public static final int length = Logging.values().length; private final boolean defaultEnabled; diff --git a/src/main/java/de/diddiz/LogBlock/Updater.java b/src/main/java/de/diddiz/LogBlock/Updater.java index 84c6759..01fe971 100644 --- a/src/main/java/de/diddiz/LogBlock/Updater.java +++ b/src/main/java/de/diddiz/LogBlock/Updater.java @@ -197,6 +197,26 @@ class Updater } config.set("version", "1.52"); } + this.logblock.getServer().getScheduler().runTaskAsynchronously(this.logblock, new Thread() { + + @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()) { + 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); + } + } + + }); logblock.saveConfig(); return true; } @@ -208,15 +228,15 @@ 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, 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))"); 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"); 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, "(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))"); 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))"); 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 SMALLINT NOT NULL, y TINYINT UNSIGNED NOT NULL, z SMALLINT NOT NULL, PRIMARY KEY (id))"); } state.close(); conn.close(); diff --git a/src/main/java/de/diddiz/LogBlock/WorldEditor.java b/src/main/java/de/diddiz/LogBlock/WorldEditor.java index 90e04d8..4654224 100644 --- a/src/main/java/de/diddiz/LogBlock/WorldEditor.java +++ b/src/main/java/de/diddiz/LogBlock/WorldEditor.java @@ -209,11 +209,6 @@ public class WorldEditor implements Runnable final Block secBlock = bed.isHeadOfBed() ? block.getRelative(bed.getFacing().getOppositeFace()) : block.getRelative(bed.getFacing()); if (secBlock.getTypeId() == 0 && !secBlock.setTypeIdAndData(26, (byte)(bed.getData() | 8), true)) throw new WorldEditorException(secBlock.getTypeId(), 26, secBlock.getLocation()); - } else if (curtype == 64 || curtype == 71) { - final byte blockData = block.getData(); - final Block secBlock = (blockData & 8) == 8 ? block.getRelative(BlockFace.DOWN) : block.getRelative(BlockFace.UP); - if (secBlock.getTypeId() == 0 && !secBlock.setTypeIdAndData(curtype, (byte)(blockData | 8), true)) - throw new WorldEditorException(secBlock.getTypeId(), curtype, secBlock.getLocation()); } else if ((curtype == 29 || curtype == 33) && (block.getData() & 8) > 0) { final PistonBaseMaterial piston = (PistonBaseMaterial)block.getState().getData(); final Block secBlock = block.getRelative(piston.getFacing()); diff --git a/src/main/java/de/diddiz/LogBlock/config/Config.java b/src/main/java/de/diddiz/LogBlock/config/Config.java index 9d05e46..e93c861 100644 --- a/src/main/java/de/diddiz/LogBlock/config/Config.java +++ b/src/main/java/de/diddiz/LogBlock/config/Config.java @@ -129,7 +129,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"); + url = "jdbc:mysql://" + config.getString("mysql.host") + ":" + config.getInt("mysql.port") + "/" + getStringIncludingInts(config, "mysql.database") + "?useUnicode=true&characterEncoding=utf-8"; user = getStringIncludingInts(config, "mysql.user"); password = getStringIncludingInts(config, "mysql.password"); delayBetweenRuns = config.getInt("consumer.delayBetweenRuns", 2); diff --git a/src/main/java/de/diddiz/LogBlock/listeners/BlockBreakLogging.java b/src/main/java/de/diddiz/LogBlock/listeners/BlockBreakLogging.java index da4e1ae..d9dc725 100644 --- a/src/main/java/de/diddiz/LogBlock/listeners/BlockBreakLogging.java +++ b/src/main/java/de/diddiz/LogBlock/listeners/BlockBreakLogging.java @@ -31,13 +31,14 @@ public class BlockBreakLogging extends LoggingListener final String playerName = event.getPlayer().getName(); final Block origin = event.getBlock(); - final int type = origin.getTypeId(); + final int typeId = origin.getTypeId(); + final Material type = origin.getType(); - if (wcfg.isLogging(Logging.SIGNTEXT) && (type == 63 || type == 68)) { + if (wcfg.isLogging(Logging.SIGNTEXT) && (typeId == 63 || typeId == 68)) { consumer.queueSignBreak(playerName, (Sign) origin.getState()); - } else if (wcfg.isLogging(Logging.CHESTACCESS) && (type == 23 || type == 54 || type == 61)) { + } else if (wcfg.isLogging(Logging.CHESTACCESS) && BukkitUtils.getContainerBlocks().contains(type)) { consumer.queueContainerBreak(playerName, origin.getState()); - } else if (origin.getType() == Material.ICE) { + } 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()); diff --git a/src/main/java/de/diddiz/LogBlock/listeners/BlockPlaceLogging.java b/src/main/java/de/diddiz/LogBlock/listeners/BlockPlaceLogging.java index 23441c5..cc65377 100644 --- a/src/main/java/de/diddiz/LogBlock/listeners/BlockPlaceLogging.java +++ b/src/main/java/de/diddiz/LogBlock/listeners/BlockPlaceLogging.java @@ -26,7 +26,7 @@ public class BlockPlaceLogging extends LoggingListener public void onBlockPlace(BlockPlaceEvent event) { final WorldConfig wcfg = getWorldConfig(event.getBlock().getWorld()); if (wcfg != null && wcfg.isLogging(Logging.BLOCKPLACE)) { - final int type = event.getBlock().getTypeId(); + final Material type = event.getBlock().getType(); final BlockState before = event.getBlockReplacedState(); final BlockState after = event.getBlockPlaced().getState(); final String playerName = event.getPlayer().getName(); @@ -53,11 +53,11 @@ public class BlockPlaceLogging extends LoggingListener if (y != 0) { Location finalLoc = new Location(loc.getWorld(), x, y, z); // Run this check to avoid false positives - if (!BukkitUtils.getFallingEntityKillers().contains(finalLoc.getBlock().getTypeId())) { + if (!BukkitUtils.getFallingEntityKillers().contains(finalLoc.getBlock().getType())) { if (finalLoc.getBlock().getType() == Material.AIR || finalLoc.equals(event.getBlock().getLocation())) { - consumer.queueBlockPlace(playerName, finalLoc, type, event.getBlock().getData()); + consumer.queueBlockPlace(playerName, finalLoc, type.getId(), event.getBlock().getData()); } else { - consumer.queueBlockReplace(playerName, finalLoc, finalLoc.getBlock().getTypeId(), finalLoc.getBlock().getData(), type, event.getBlock().getData()); + consumer.queueBlockReplace(playerName, finalLoc, finalLoc.getBlock().getTypeId(), finalLoc.getBlock().getData(), type.getId(), event.getBlock().getData()); } } } @@ -65,8 +65,7 @@ public class BlockPlaceLogging extends LoggingListener } //Sign logging is handled elsewhere - if (wcfg.isLogging(Logging.SIGNTEXT) && (type == 63 || type == 68)) - return; + if (wcfg.isLogging(Logging.SIGNTEXT) && (type.getId() == 63 || type.getId() == 68)) return; //Delay queuing by one tick to allow data to be updated LogBlock.getInstance().getServer().getScheduler().scheduleSyncDelayedTask(LogBlock.getInstance(), new Runnable() diff --git a/src/main/java/de/diddiz/LogBlock/listeners/BlockSpreadLogging.java b/src/main/java/de/diddiz/LogBlock/listeners/BlockSpreadLogging.java new file mode 100644 index 0000000..b1d0f38 --- /dev/null +++ b/src/main/java/de/diddiz/LogBlock/listeners/BlockSpreadLogging.java @@ -0,0 +1,52 @@ +package de.diddiz.LogBlock.listeners; + +import de.diddiz.LogBlock.LogBlock; +import de.diddiz.LogBlock.Logging; +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 +{ + + public BlockSpreadLogging(LogBlock lb) { + super(lb); + } + + @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) + public void onBlockSpread(BlockSpreadEvent event) { + + String name; + + World world = event.getBlock().getWorld(); + Material type = event.getSource().getType(); + + switch (type) { + case GRASS: + if (!isLogging(world, Logging.GRASSGROWTH)) return; + name = "GrassGrowth"; + break; + case MYCEL: + if (!isLogging(world, Logging.MYCELIUMSPREAD)) return; + name = "MyceliumSpread"; + break; + case VINE: + if (!isLogging(world, Logging.VINEGROWTH)) return; + name = "VineGrowth"; + break; + case RED_MUSHROOM: + case BROWN_MUSHROOM: + if (!isLogging(world, Logging.MUSHROOMSPREAD)) return; + name = "MushroomSpread"; + break; + default: + return; + } + + consumer.queueBlockReplace(name, event.getBlock().getState(), event.getNewState()); + } +} diff --git a/src/main/java/de/diddiz/LogBlock/listeners/ChestAccessLogging.java b/src/main/java/de/diddiz/LogBlock/listeners/ChestAccessLogging.java index 8559578..2be244f 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 static de.diddiz.LogBlock.config.Config.isLogging; import static de.diddiz.util.BukkitUtils.compareInventories; import static de.diddiz.util.BukkitUtils.compressInventory; import static de.diddiz.util.BukkitUtils.getInventoryHolderLocation; @@ -7,6 +8,8 @@ import static de.diddiz.util.BukkitUtils.getInventoryHolderType; import static de.diddiz.util.BukkitUtils.rawData; import java.util.HashMap; import java.util.Map; + +import de.diddiz.LogBlock.Logging; import org.bukkit.Location; import org.bukkit.block.BlockState; import org.bukkit.block.DoubleChest; @@ -29,6 +32,8 @@ public class ChestAccessLogging extends LoggingListener @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) public void onInventoryClose(InventoryCloseEvent event) { + + if (!isLogging(event.getPlayer().getWorld(), Logging.CHESTACCESS)) return; InventoryHolder holder = event.getInventory().getHolder(); if (holder instanceof BlockState || holder instanceof DoubleChest) { final HumanEntity player = event.getPlayer(); @@ -47,6 +52,8 @@ public class ChestAccessLogging extends LoggingListener @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) public void onInventoryOpen(InventoryOpenEvent event) { + + if (!isLogging(event.getPlayer().getWorld(), Logging.CHESTACCESS)) return; if (event.getInventory() != null) { InventoryHolder holder = event.getInventory().getHolder(); if (holder instanceof BlockState || holder instanceof DoubleChest) { diff --git a/src/main/java/de/diddiz/LogBlock/listeners/CreatureInteractLogging.java b/src/main/java/de/diddiz/LogBlock/listeners/CreatureInteractLogging.java new file mode 100644 index 0000000..c7a5f03 --- /dev/null +++ b/src/main/java/de/diddiz/LogBlock/listeners/CreatureInteractLogging.java @@ -0,0 +1,57 @@ +package de.diddiz.LogBlock.listeners; + +import de.diddiz.LogBlock.LogBlock; +import de.diddiz.LogBlock.Logging; +import de.diddiz.LogBlock.config.WorldConfig; +import de.diddiz.util.BukkitUtils; +import org.bukkit.Location; +import org.bukkit.Material; +import org.bukkit.block.Block; +import org.bukkit.block.BlockFace; +import org.bukkit.entity.EntityType; +import org.bukkit.entity.Player; +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) { + super(lb); + } + + @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) + public void onEntityInteract(EntityInteractEvent event) { + final WorldConfig wcfg = getWorldConfig(event.getEntity().getWorld()); + + final EntityType entityType = event.getEntityType(); + + // Mobs only + if (event.getEntity() instanceof Player || entityType == null) return; + + if (wcfg != null) { + final Block clicked = event.getBlock(); + final Material type = clicked.getType(); + final int typeId = type.getId(); + final byte blockData = clicked.getData(); + final Location loc = clicked.getLocation(); + + switch (type) { + case SOIL: + if (wcfg.isLogging(Logging.CREATURECROPTRAMPLE)) { + // 3 = Dirt ID + consumer.queueBlock(entityType.getName(), 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()); + } + } + break; + } + } + } +} + diff --git a/src/main/java/de/diddiz/LogBlock/listeners/ExplosionLogging.java b/src/main/java/de/diddiz/LogBlock/listeners/ExplosionLogging.java index 8f324d5..f16f8ef 100644 --- a/src/main/java/de/diddiz/LogBlock/listeners/ExplosionLogging.java +++ b/src/main/java/de/diddiz/LogBlock/listeners/ExplosionLogging.java @@ -10,6 +10,7 @@ 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; @@ -31,25 +32,29 @@ public class ExplosionLogging extends LoggingListener public void onEntityExplode(EntityExplodeEvent event) { final WorldConfig wcfg = getWorldConfig(event.getLocation().getWorld()); if (wcfg != null) { - String name = "Unknown"; - if (event.getEntity() == null) { + String name = "Explosion"; + Entity source = event.getEntity(); + if (source == null) { if (!wcfg.isLogging(Logging.MISCEXPLOSION)) return; - name = "Explosion"; - } else if (event.getEntity() instanceof TNTPrimed) { + } else if (source instanceof TNTPrimed) { if (!wcfg.isLogging(Logging.TNTEXPLOSION)) return; name = "TNT"; - } else if (event.getEntity() instanceof Creeper) { + } else if (source instanceof ExplosiveMinecart) { + if (!wcfg.isLogging(Logging.TNTMINECARTEXPLOSION)) + return; + name = "TNTMinecart"; + } else if (source instanceof Creeper) { if (!wcfg.isLogging(Logging.CREEPEREXPLOSION)) return; if (logCreeperExplosionsAsPlayerWhoTriggeredThese) { - final Entity target = ((Creeper)event.getEntity()).getTarget(); + final Entity target = ((Creeper) source).getTarget(); name = target instanceof Player ? ((Player)target).getName() : "Creeper"; } else name = "Creeper"; - } else if (event.getEntity() instanceof Fireball) { - Fireball fireball = (Fireball) event.getEntity(); + } else if (source instanceof Fireball) { + Fireball fireball = (Fireball) source; Entity shooter = fireball.getShooter(); if (shooter == null) { return; @@ -65,22 +70,21 @@ public class ExplosionLogging extends LoggingListener } name = "Wither"; } - } else if (event.getEntity() instanceof EnderDragon) { + } else if (source instanceof EnderDragon) { if (!wcfg.isLogging(Logging.ENDERDRAGON)) return; name = "EnderDragon"; - } else if (event.getEntity() instanceof Wither) { + } else if (source instanceof Wither) { if(!wcfg.isLogging(Logging.WITHER)) return; name = "Wither"; - } else if (event.getEntity() instanceof WitherSkull) { + } else if (source instanceof WitherSkull) { if(!wcfg.isLogging(Logging.WITHER_SKULL)) return; name = "WitherSkull"; } else { if (!wcfg.isLogging(Logging.MISCEXPLOSION)) return; - name = "Explosion"; } for (final Block block : event.blockList()) { final int type = block.getTypeId(); diff --git a/src/main/java/de/diddiz/LogBlock/listeners/InteractLogging.java b/src/main/java/de/diddiz/LogBlock/listeners/InteractLogging.java index 6d96757..6caddb1 100644 --- a/src/main/java/de/diddiz/LogBlock/listeners/InteractLogging.java +++ b/src/main/java/de/diddiz/LogBlock/listeners/InteractLogging.java @@ -1,8 +1,12 @@ package de.diddiz.LogBlock.listeners; import static de.diddiz.LogBlock.config.Config.getWorldConfig; + +import de.diddiz.util.BukkitUtils; import org.bukkit.Location; 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; @@ -21,34 +25,33 @@ public class InteractLogging extends LoggingListener @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) public void onPlayerInteract(PlayerInteractEvent event) { final WorldConfig wcfg = getWorldConfig(event.getPlayer().getWorld()); - if (wcfg != null && (event.getAction() == Action.LEFT_CLICK_BLOCK || event.getAction() == Action.RIGHT_CLICK_BLOCK)) { - final Material type = event.getClickedBlock().getType(); + if (wcfg != null) { + final Block clicked = event.getClickedBlock(); + final Material type = clicked.getType(); final int typeId = type.getId(); - final byte blockData = event.getClickedBlock().getData(); + final byte blockData = clicked.getData(); final Player player = event.getPlayer(); - final Location loc = event.getClickedBlock().getLocation(); + final Location loc = clicked.getLocation(); switch (type) { case LEVER: case WOOD_BUTTON: case STONE_BUTTON: - if (wcfg.isLogging(Logging.SWITCHINTERACT)) + if (wcfg.isLogging(Logging.SWITCHINTERACT) && event.getAction() == Action.RIGHT_CLICK_BLOCK) consumer.queueBlock(player.getName(), loc, typeId, typeId, blockData); break; case FENCE_GATE: - if (event.getAction() != Action.RIGHT_CLICK_BLOCK) - break; case WOODEN_DOOR: case TRAP_DOOR: - if (wcfg.isLogging(Logging.DOORINTERACT)) + if (wcfg.isLogging(Logging.DOORINTERACT) && event.getAction() == Action.RIGHT_CLICK_BLOCK) consumer.queueBlock(player.getName(), loc, typeId, typeId, blockData); break; case CAKE_BLOCK: - if (wcfg.isLogging(Logging.CAKEEAT) && player.getFoodLevel() < 20) + if (wcfg.isLogging(Logging.CAKEEAT) && event.getAction() == Action.RIGHT_CLICK_BLOCK && player.getFoodLevel() < 20) consumer.queueBlock(player.getName(), loc, typeId, typeId, blockData); break; case NOTE_BLOCK: - if (wcfg.isLogging(Logging.NOTEBLOCKINTERACT)) + if (wcfg.isLogging(Logging.NOTEBLOCKINTERACT) && event.getAction() == Action.RIGHT_CLICK_BLOCK) consumer.queueBlock(player.getName(), loc, typeId, typeId, blockData); break; case DIODE_BLOCK_OFF: @@ -56,6 +59,36 @@ public class InteractLogging extends LoggingListener if (wcfg.isLogging(Logging.DIODEINTERACT) && event.getAction() == Action.RIGHT_CLICK_BLOCK) consumer.queueBlock(player.getName(), 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); + } + break; + case WOOD_PLATE: + case STONE_PLATE: + case IRON_PLATE: + case GOLD_PLATE: + if (wcfg.isLogging(Logging.PRESUREPLATEINTERACT) && event.getAction() == Action.PHYSICAL) { + consumer.queueBlock(player.getName(), loc, typeId, typeId, blockData); + } + break; + case TRIPWIRE: + if (wcfg.isLogging(Logging.TRIPWIREINTERACT) && event.getAction() == Action.PHYSICAL) { + consumer.queueBlock(player.getName(), 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); + // 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()); + } + } + break; } } } diff --git a/src/main/java/de/diddiz/LogBlock/listeners/ToolListener.java b/src/main/java/de/diddiz/LogBlock/listeners/ToolListener.java index 4b557f2..fd267f1 100644 --- a/src/main/java/de/diddiz/LogBlock/listeners/ToolListener.java +++ b/src/main/java/de/diddiz/LogBlock/listeners/ToolListener.java @@ -45,10 +45,15 @@ public class ToolListener implements Listener final int type = event.getMaterial().getId(); final Tool tool = toolsByType.get(type); final Player player = event.getPlayer(); - if (tool != null && (action == Action.RIGHT_CLICK_BLOCK || action == Action.LEFT_CLICK_BLOCK) && isLogged(player.getWorld()) && logblock.hasPermission(player, "logblock.tools." + tool.name)) { + if (tool != null && (action == Action.RIGHT_CLICK_BLOCK || action == Action.LEFT_CLICK_BLOCK) && logblock.hasPermission(player, "logblock.tools." + tool.name)) { final ToolBehavior behavior = action == Action.RIGHT_CLICK_BLOCK ? tool.rightClickBehavior : tool.leftClickBehavior; final ToolData toolData = getSession(player).toolData.get(tool); if (behavior != ToolBehavior.NONE && toolData.enabled) { + if (!isLogged(player.getWorld())) { + player.sendMessage(ChatColor.RED + "This world is not currently logged."); + event.setCancelled(true); + return; + } final Block block = event.getClickedBlock(); final QueryParams params = toolData.params; params.loc = null; diff --git a/src/main/java/de/diddiz/util/BukkitUtils.java b/src/main/java/de/diddiz/util/BukkitUtils.java index 097f3be..b3c7041 100644 --- a/src/main/java/de/diddiz/util/BukkitUtils.java +++ b/src/main/java/de/diddiz/util/BukkitUtils.java @@ -32,6 +32,9 @@ public class BukkitUtils private static final Set relativeTopFallables; private static final Set fallingEntityKillers; + private static final Set cropBlocks; + private static final Set containerBlocks; + static { blockEquivalents = new HashSet>(7); blockEquivalents.add(new HashSet(Arrays.asList(2, 3, 60))); @@ -57,7 +60,7 @@ public class BukkitUtils relativeBreakable.add(Material.COCOA); // Blocks that break when they are on top of a block - relativeTopBreakable = new HashSet(26); + relativeTopBreakable = new HashSet(32); relativeTopBreakable.add(Material.SAPLING); relativeTopBreakable.add(Material.LONG_GRASS); relativeTopBreakable.add(Material.DEAD_BUSH); @@ -74,16 +77,22 @@ public class BukkitUtils relativeTopBreakable.add(Material.FLOWER_POT); relativeTopBreakable.add(Material.POWERED_RAIL); relativeTopBreakable.add(Material.DETECTOR_RAIL); + relativeTopBreakable.add(Material.ACTIVATOR_RAIL); relativeTopBreakable.add(Material.RAILS); relativeTopBreakable.add(Material.REDSTONE_WIRE); relativeTopBreakable.add(Material.SIGN_POST); relativeTopBreakable.add(Material.STONE_PLATE); relativeTopBreakable.add(Material.WOOD_PLATE); + relativeTopBreakable.add(Material.IRON_PLATE); + relativeTopBreakable.add(Material.GOLD_PLATE); relativeTopBreakable.add(Material.SNOW); relativeTopBreakable.add(Material.DIODE_BLOCK_ON); relativeTopBreakable.add(Material.DIODE_BLOCK_OFF); + 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.CARPET); // Blocks that fall relativeTopFallables = new HashSet(4); @@ -93,9 +102,13 @@ public class BukkitUtils relativeTopFallables.add(Material.ANVIL); // Blocks that break falling entities - fallingEntityKillers = new HashSet(23); + fallingEntityKillers = new HashSet(32); fallingEntityKillers.add(Material.SIGN_POST); fallingEntityKillers.add(Material.WALL_SIGN); + fallingEntityKillers.add(Material.STONE_PLATE); + fallingEntityKillers.add(Material.WOOD_PLATE); + fallingEntityKillers.add(Material.IRON_PLATE); + fallingEntityKillers.add(Material.GOLD_PLATE); fallingEntityKillers.add(Material.SAPLING); fallingEntityKillers.add(Material.YELLOW_FLOWER); fallingEntityKillers.add(Material.RED_ROSE); @@ -110,6 +123,7 @@ public class BukkitUtils fallingEntityKillers.add(Material.FLOWER_POT); fallingEntityKillers.add(Material.POWERED_RAIL); fallingEntityKillers.add(Material.DETECTOR_RAIL); + fallingEntityKillers.add(Material.ACTIVATOR_RAIL); fallingEntityKillers.add(Material.RAILS); fallingEntityKillers.add(Material.LEVER); fallingEntityKillers.add(Material.REDSTONE_WIRE); @@ -117,6 +131,29 @@ public class BukkitUtils fallingEntityKillers.add(Material.REDSTONE_TORCH_OFF); fallingEntityKillers.add(Material.DIODE_BLOCK_ON); fallingEntityKillers.add(Material.DIODE_BLOCK_OFF); + fallingEntityKillers.add(Material.REDSTONE_COMPARATOR_ON); + fallingEntityKillers.add(Material.REDSTONE_COMPARATOR_OFF); + fallingEntityKillers.add(Material.DAYLIGHT_DETECTOR); + fallingEntityKillers.add(Material.CARPET); + + // Crop Blocks + cropBlocks = new HashSet(5); + cropBlocks.add(Material.CROPS); + cropBlocks.add(Material.MELON_STEM); + cropBlocks.add(Material.PUMPKIN_STEM); + cropBlocks.add(Material.CARROT); + cropBlocks.add(Material.POTATO); + + // Container Blocks + containerBlocks = new HashSet(6); + containerBlocks.add(Material.CHEST); + containerBlocks.add(Material.TRAPPED_CHEST); + containerBlocks.add(Material.DISPENSER); + containerBlocks.add(Material.DROPPER); + containerBlocks.add(Material.HOPPER); + containerBlocks.add(Material.BREWING_STAND); + // Doesn't actually have a block inventory + // containerBlocks.add(Material.ENDER_CHEST); } private static final BlockFace[] relativeBlockFaces = new BlockFace[] { @@ -251,6 +288,14 @@ public class BukkitUtils return fallingEntityKillers; } + public static Set getCropBlocks() { + return cropBlocks; + } + + public static Set getContainerBlocks() { + return containerBlocks; + } + public static String entityName(Entity entity) { if (entity instanceof Player) return ((Player)entity).getName(); @@ -319,7 +364,7 @@ public class BukkitUtils return true; } else if (mat == Material.WATER || mat == Material.STATIONARY_WATER || mat == Material.LAVA || mat == Material.STATIONARY_LAVA) { // Fluids return true; - } else if (getFallingEntityKillers().contains(mat.getId()) || mat == Material.FIRE || mat == Material.VINE || mat == Material.LONG_GRASS || mat == Material.DEAD_BUSH) { // Misc. + } else if (getFallingEntityKillers().contains(mat) || mat == Material.FIRE || mat == Material.VINE || mat == Material.LONG_GRASS || mat == Material.DEAD_BUSH) { // Misc. return true; } return false; diff --git a/src/main/java/de/diddiz/util/LoggingUtil.java b/src/main/java/de/diddiz/util/LoggingUtil.java index b7969f1..b53c6a8 100644 --- a/src/main/java/de/diddiz/util/LoggingUtil.java +++ b/src/main/java/de/diddiz/util/LoggingUtil.java @@ -32,7 +32,7 @@ public class LoggingUtil { Block checkBlock = origin.getRelative(BlockFace.UP); int up = 0; final int highestBlock = checkBlock.getWorld().getHighestBlockYAt(checkBlock.getLocation()); - while (BukkitUtils.getRelativeTopFallables().contains(checkBlock.getTypeId())) { + while (BukkitUtils.getRelativeTopFallables().contains(checkBlock.getType())) { // Record this block as falling consumer.queueBlockBreak(playerName, checkBlock.getState()); @@ -49,9 +49,9 @@ public class LoggingUtil { if (y != 0) { Location finalLoc = new Location(loc.getWorld(), x, y, z); // Run this check to avoid false positives - if (!BukkitUtils.getFallingEntityKillers().contains(finalLoc.getBlock().getTypeId())) { + 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().getTypeId())) { + if (finalLoc.getBlock().getType() == Material.AIR || BukkitUtils.getRelativeTopFallables().contains(finalLoc.getBlock().getType())) { consumer.queueBlockPlace(playerName, finalLoc, checkBlock.getTypeId(), checkBlock.getData()); } else { consumer.queueBlockReplace(playerName, finalLoc, finalLoc.getBlock().getTypeId(), finalLoc.getBlock().getData(), checkBlock.getTypeId(), checkBlock.getData()); @@ -70,9 +70,21 @@ public class LoggingUtil { if (wcfg == null) return; Block checkBlock = origin.getRelative(BlockFace.UP); - if (BukkitUtils.getRelativeTopBreakabls().contains(checkBlock.getTypeId())) { + 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) { + 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) { + 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()); + } + consumer.queueBlockBreak(playerName, checkBlock.getState()); + } } else { consumer.queueBlockBreak(playerName, checkBlock.getState()); } @@ -142,6 +154,23 @@ public class LoggingUtil { } } } + + // Special door check + if (origin.getType() == Material.IRON_DOOR || origin.getType() == Material.WOOD_DOOR) { + Block doorBlock = origin; + + // Up or down? + if (origin.getData() != 8 && origin.getData() != 9) { + 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()); + } + } + // Do this down here so that the block is added after blocks sitting on it consumer.queueBlockBreak(playerName, origin.getState()); }