diff --git a/pom.xml b/pom.xml
index 6207091..31ddf18 100644
--- a/pom.xml
+++ b/pom.xml
@@ -4,7 +4,7 @@
de.diddiz
logblock
- 1.13-SNAPSHOT
+ 1.13.1-SNAPSHOT
jar
LogBlock
diff --git a/src/main/java/de/diddiz/LogBlock/BlockChange.java b/src/main/java/de/diddiz/LogBlock/BlockChange.java
index 30af6f7..75351ab 100644
--- a/src/main/java/de/diddiz/LogBlock/BlockChange.java
+++ b/src/main/java/de/diddiz/LogBlock/BlockChange.java
@@ -1,5 +1,6 @@
package de.diddiz.LogBlock;
+import de.diddiz.LogBlock.blockstate.BlockStateCodecs;
import de.diddiz.LogBlock.config.Config;
import de.diddiz.util.BukkitUtils;
import de.diddiz.util.Utils;
@@ -14,29 +15,28 @@ import org.bukkit.inventory.ItemStack;
import java.sql.ResultSet;
import java.sql.SQLException;
-
-import static de.diddiz.util.LoggingUtil.checkText;
+import java.util.logging.Level;
public class BlockChange implements LookupCacheElement {
public final long id, date;
public final Location loc;
public final Actor actor;
public final String playerName;
- // public final BlockData replaced, type;
public final int replacedMaterial, replacedData, typeMaterial, typeData;
- public final String signtext;
+ public final byte[] replacedState, typeState;
public final ChestAccess ca;
- public BlockChange(long date, Location loc, Actor actor, int replaced, int replacedData, int type, int typeData, String signtext, ChestAccess ca) {
+ public BlockChange(long date, Location loc, Actor actor, int replaced, int replacedData, byte[] replacedState, int type, int typeData, byte[] typeState, ChestAccess ca) {
id = 0;
this.date = date;
this.loc = loc;
this.actor = actor;
this.replacedMaterial = replaced;
this.replacedData = replacedData;
+ this.replacedState = replacedState;
this.typeMaterial = type;
this.typeData = typeData;
- this.signtext = checkText(signtext);
+ this.typeState = typeState;
this.ca = ca;
this.playerName = actor == null ? null : actor.getName();
}
@@ -51,7 +51,8 @@ public class BlockChange implements LookupCacheElement {
replacedData = p.needType ? rs.getInt("replacedData") : -1;
typeMaterial = p.needType ? rs.getInt("type") : 0;
typeData = p.needType ? rs.getInt("typeData") : -1;
- signtext = p.needSignText ? rs.getString("signtext") : null;
+ replacedState = p.needType ? rs.getBytes("replacedState") : null;
+ typeState = p.needType ? rs.getBytes("typeState") : null;
ChestAccess catemp = null;
if (p.needChestAccess) {
ItemStack stack = Utils.loadItemStack(rs.getBytes("item"));
@@ -66,6 +67,32 @@ public class BlockChange implements LookupCacheElement {
public String toString() {
BlockData type = MaterialConverter.getBlockData(typeMaterial, typeData);
BlockData replaced = MaterialConverter.getBlockData(replacedMaterial, replacedData);
+ String typeDetails = null;
+ if (BlockStateCodecs.hasCodec(type.getMaterial())) {
+ try {
+ typeDetails = BlockStateCodecs.toString(type.getMaterial(), typeState);
+ } catch (Exception e) {
+ LogBlock.getInstance().getLogger().log(Level.SEVERE, "Could not parse BlockState for " + type.getMaterial(), e);
+ }
+ }
+ if (typeDetails == null) {
+ typeDetails = "";
+ } else {
+ typeDetails = " " + typeDetails;
+ }
+ String replacedDetails = null;
+ if (BlockStateCodecs.hasCodec(replaced.getMaterial())) {
+ try {
+ replacedDetails = BlockStateCodecs.toString(replaced.getMaterial(), replacedState);
+ } catch (Exception e) {
+ LogBlock.getInstance().getLogger().log(Level.SEVERE, "Could not parse BlockState for " + replaced.getMaterial(), e);
+ }
+ }
+ if (replacedDetails == null) {
+ replacedDetails = "";
+ } else {
+ replacedDetails = " " + replacedDetails;
+ }
final StringBuilder msg = new StringBuilder();
if (date > 0) {
msg.append(Config.formatter.format(date)).append(" ");
@@ -73,14 +100,7 @@ public class BlockChange implements LookupCacheElement {
if (actor != null) {
msg.append(actor.getName()).append(" ");
}
- if (signtext != null) {
- final String action = BukkitUtils.isEmpty(type.getMaterial()) ? "destroyed " : "created ";
- if (!signtext.contains("\0")) {
- msg.append(action).append(signtext);
- } else {
- msg.append(action).append((!BukkitUtils.isEmpty(type.getMaterial()) ? type : replaced).getMaterial().name()).append(" [").append(signtext.replace("\0", "] [")).append("]");
- }
- } else if (type.equals(replaced)) {
+ if (type.equals(replaced)) {
if (BukkitUtils.isEmpty(type.getMaterial())) {
msg.append("did an unspecified action");
} else if (ca != null) {
@@ -110,11 +130,11 @@ public class BlockChange implements LookupCacheElement {
msg.append("ran into ").append(type.getMaterial().name());
}
} else if (BukkitUtils.isEmpty(type.getMaterial())) {
- msg.append("destroyed ").append(replaced.getMaterial().name());
+ msg.append("destroyed ").append(replaced.getMaterial().name()).append(replacedDetails);
} else if (BukkitUtils.isEmpty(replaced.getMaterial())) {
- msg.append("created ").append(type.getMaterial().name());
+ msg.append("created ").append(type.getMaterial().name()).append(typeDetails);
} else {
- msg.append("replaced ").append(replaced.getMaterial().name()).append(" with ").append(type.getMaterial().name());
+ msg.append("replaced ").append(replaced.getMaterial().name()).append(replacedDetails).append(" with ").append(type.getMaterial().name()).append(typeDetails);
}
if (loc != null) {
msg.append(" at ").append(loc.getBlockX()).append(":").append(loc.getBlockY()).append(":").append(loc.getBlockZ());
diff --git a/src/main/java/de/diddiz/LogBlock/CommandsHandler.java b/src/main/java/de/diddiz/LogBlock/CommandsHandler.java
index 6ae53cf..fe74da1 100755
--- a/src/main/java/de/diddiz/LogBlock/CommandsHandler.java
+++ b/src/main/java/de/diddiz/LogBlock/CommandsHandler.java
@@ -10,7 +10,6 @@ import de.diddiz.util.Utils;
import org.bukkit.ChatColor;
import org.bukkit.Location;
-import org.bukkit.Material;
import org.bukkit.World;
import org.bukkit.command.Command;
import org.bukkit.command.CommandExecutor;
@@ -456,9 +455,6 @@ public class CommandsHandler implements CommandExecutor {
params.needType = true;
params.needData = true;
params.needPlayer = true;
- if (params.types.isEmpty() || params.types.contains(Material.SIGN) || params.types.contains(Material.WALL_SIGN)) {
- params.needSignText = true;
- }
if (params.bct == BlockChangeType.CHESTACCESS || params.bct == BlockChangeType.ALL) {
params.needChestAccess = true;
}
@@ -521,9 +517,6 @@ public class CommandsHandler implements CommandExecutor {
params.needType = true;
params.needData = true;
params.needPlayer = true;
- if (params.types.isEmpty() || params.types.contains(Material.SIGN) || params.types.contains(Material.WALL_SIGN)) {
- params.needSignText = true;
- }
if (params.bct == BlockChangeType.CHESTACCESS || params.bct == BlockChangeType.ALL) {
params.needChestAccess = true;
}
@@ -646,7 +639,6 @@ public class CommandsHandler implements CommandExecutor {
params.needCoords = true;
params.needType = true;
params.needData = true;
- params.needSignText = true;
params.needChestAccess = true;
params.order = Order.DESC;
params.sum = SummarizationMode.NONE;
@@ -674,7 +666,7 @@ public class CommandsHandler implements CommandExecutor {
if (stack != null) {
chestaccess = new ChestAccess(stack, rs.getBoolean("itemremove"));
}
- editor.queueEdit(rs.getInt("x"), rs.getInt("y"), rs.getInt("z"), rs.getInt("replaced"), rs.getInt("replacedData"), rs.getInt("type"), rs.getInt("typeData"), rs.getString("signtext"), chestaccess);
+ editor.queueEdit(rs.getInt("x"), rs.getInt("y"), rs.getInt("z"), rs.getInt("replaced"), rs.getInt("replacedData"), rs.getBytes("replacedState"), rs.getInt("type"), rs.getInt("typeData"), rs.getBytes("typeState"), chestaccess);
}
final int changes = editor.getSize();
if (changes > 10000) {
@@ -725,7 +717,6 @@ public class CommandsHandler implements CommandExecutor {
params.needCoords = true;
params.needType = true;
params.needData = true;
- params.needSignText = true;
params.needChestAccess = true;
params.order = Order.ASC;
params.sum = SummarizationMode.NONE;
@@ -749,7 +740,7 @@ public class CommandsHandler implements CommandExecutor {
if (stack != null) {
chestaccess = new ChestAccess(stack, !rs.getBoolean("itemremove"));
}
- editor.queueEdit(rs.getInt("x"), rs.getInt("y"), rs.getInt("z"), rs.getInt("type"), rs.getInt("typeData"), rs.getInt("replaced"), rs.getInt("replacedData"), rs.getString("signtext"), chestaccess);
+ editor.queueEdit(rs.getInt("x"), rs.getInt("y"), rs.getInt("z"), rs.getInt("type"), rs.getInt("typeData"), rs.getBytes("typeState"), rs.getInt("replaced"), rs.getInt("replacedData"), rs.getBytes("replacedState"), chestaccess);
}
final int changes = editor.getSize();
if (!params.silent) {
@@ -824,23 +815,23 @@ public class CommandsHandler implements CommandExecutor {
state.execute("DELETE `" + table + "` FROM `" + table + "-blocks` " + join + params.getWhere());
sender.sendMessage(ChatColor.GREEN + "Cleared out table " + table + ". Deleted " + deleted + " entries.");
}
- rs = state.executeQuery("SELECT COUNT(*) FROM `" + table + "-sign` LEFT JOIN `" + table + "-blocks` USING (id) WHERE `" + table + "-blocks`.id IS NULL");
+ rs = state.executeQuery("SELECT COUNT(*) FROM `" + table + "-state` LEFT JOIN `" + table + "-blocks` USING (id) WHERE `" + table + "-blocks`.id IS NULL");
rs.next();
if ((deleted = rs.getInt(1)) > 0) {
if (dumpDeletedLog) {
- state.execute("SELECT id, signtext FROM `" + table + "-sign` LEFT JOIN `" + table + "-blocks` USING (id) WHERE `" + table + "-blocks`.id IS NULL INTO OUTFILE '" + new File(dumpFolder, time + " " + table + "-sign " + params.getTitle() + ".csv").getAbsolutePath().replace("\\", "\\\\") + "' FIELDS TERMINATED BY ',' OPTIONALLY ENCLOSED BY '\"' LINES TERMINATED BY '\n'");
+ state.execute("SELECT id, replacedState, typeState FROM `" + table + "-state` LEFT JOIN `" + table + "-blocks` USING (id) WHERE `" + table + "-blocks`.id IS NULL INTO OUTFILE '" + new File(dumpFolder, time + " " + table + "-state " + params.getTitle() + ".csv").getAbsolutePath().replace("\\", "\\\\") + "' FIELDS TERMINATED BY ',' OPTIONALLY ENCLOSED BY '\"' LINES TERMINATED BY '\n'");
}
- state.execute("DELETE `" + table + "-sign` FROM `" + table + "-sign` LEFT JOIN `" + table + "-blocks` USING (id) WHERE `" + table + "-blocks`.id IS NULL;");
- sender.sendMessage(ChatColor.GREEN + "Cleared out table " + table + "-sign. Deleted " + deleted + " entries.");
+ state.execute("DELETE `" + table + "-state` FROM `" + table + "-state` LEFT JOIN `" + table + "-blocks` USING (id) WHERE `" + table + "-blocks`.id IS NULL;");
+ sender.sendMessage(ChatColor.GREEN + "Cleared out table " + table + "-state. Deleted " + deleted + " entries.");
}
rs = state.executeQuery("SELECT COUNT(*) FROM `" + table + "-chestdata` LEFT JOIN `" + table + "-blocks` USING (id) WHERE `" + table + "-blocks`.id IS NULL");
rs.next();
if ((deleted = rs.getInt(1)) > 0) {
if (dumpDeletedLog) {
- state.execute("SELECT id, item, itemremove FROM `" + table + "-chest` LEFT JOIN `" + table + "-blocks` USING (id) WHERE `" + table + "-blocks`.id IS NULL INTO OUTFILE '" + new File(dumpFolder, time + " " + table + "-chest " + params.getTitle() + ".csv").getAbsolutePath().replace("\\", "\\\\") + "' FIELDS TERMINATED BY ',' OPTIONALLY ENCLOSED BY '\"' LINES TERMINATED BY '\n'");
+ state.execute("SELECT id, item, itemremove FROM `" + table + "-chestdata` LEFT JOIN `" + table + "-blocks` USING (id) WHERE `" + table + "-blocks`.id IS NULL INTO OUTFILE '" + new File(dumpFolder, time + " " + table + "-chest " + params.getTitle() + ".csv").getAbsolutePath().replace("\\", "\\\\") + "' FIELDS TERMINATED BY ',' OPTIONALLY ENCLOSED BY '\"' LINES TERMINATED BY '\n'");
}
- state.execute("DELETE `" + table + "-chest` FROM `" + table + "-chest` LEFT JOIN `" + table + "-blocks` USING (id) WHERE `" + table + "-blocks`.id IS NULL;");
- sender.sendMessage(ChatColor.GREEN + "Cleared out table " + table + "-chest. Deleted " + deleted + " entries.");
+ state.execute("DELETE `" + table + "-chestdata` FROM `" + table + "-chestdata` LEFT JOIN `" + table + "-blocks` USING (id) WHERE `" + table + "-blocks`.id IS NULL;");
+ sender.sendMessage(ChatColor.GREEN + "Cleared out table " + table + "-chestdata. Deleted " + deleted + " entries.");
}
} catch (final Exception ex) {
sender.sendMessage(ChatColor.RED + "Exception, check error log");
diff --git a/src/main/java/de/diddiz/LogBlock/Consumer.java b/src/main/java/de/diddiz/LogBlock/Consumer.java
index 20bdb12..c01371c 100644
--- a/src/main/java/de/diddiz/LogBlock/Consumer.java
+++ b/src/main/java/de/diddiz/LogBlock/Consumer.java
@@ -1,5 +1,7 @@
package de.diddiz.LogBlock;
+import de.diddiz.LogBlock.blockstate.BlockStateCodecSign;
+import de.diddiz.LogBlock.blockstate.BlockStateCodecs;
import de.diddiz.LogBlock.config.Config;
import de.diddiz.LogBlock.events.BlockChangePreLogEvent;
import de.diddiz.util.Utils;
@@ -63,7 +65,7 @@ public class Consumer extends TimerTask {
* Data of the block after the change
*/
public void queueBlock(Actor actor, Location loc, BlockData typeBefore, BlockData typeAfter) {
- queueBlock(actor, loc, typeBefore, typeAfter, null, null);
+ queueBlock(actor, loc, typeBefore, typeAfter, null, null, null);
}
/**
@@ -75,7 +77,7 @@ public class Consumer extends TimerTask {
* Blockstate of the block before actually being destroyed.
*/
public void queueBlockBreak(Actor actor, BlockState before) {
- queueBlockBreak(actor, new Location(before.getWorld(), before.getX(), before.getY(), before.getZ()), before.getBlockData());
+ queueBlock(actor, new Location(before.getWorld(), before.getX(), before.getY(), before.getZ()), before.getBlockData(), null, BlockStateCodecs.serialize(before), null, null);
}
/**
@@ -103,7 +105,7 @@ public class Consumer extends TimerTask {
* Blockstate of the block after actually being placed.
*/
public void queueBlockPlace(Actor actor, BlockState after) {
- queueBlockPlace(actor, new Location(after.getWorld(), after.getX(), after.getY(), after.getZ()), after.getBlockData());
+ queueBlock(actor, new Location(after.getWorld(), after.getX(), after.getY(), after.getZ()), null, after.getBlockData(), null, BlockStateCodecs.serialize(after), null);
}
/**
@@ -133,7 +135,7 @@ public class Consumer extends TimerTask {
* Blockstate of the block after actually being placed.
*/
public void queueBlockReplace(Actor actor, BlockState before, BlockState after) {
- queueBlockReplace(actor, new Location(before.getWorld(), before.getX(), before.getY(), before.getZ()), before.getBlockData(), after.getBlockData());
+ queueBlock(actor, new Location(before.getWorld(), before.getX(), before.getY(), before.getZ()), before.getBlockData(), after.getBlockData(), BlockStateCodecs.serialize(before), BlockStateCodecs.serialize(after), null);
}
/**
@@ -149,7 +151,7 @@ public class Consumer extends TimerTask {
* Data of the block after being replaced
*/
public void queueBlockReplace(Actor actor, BlockState before, BlockData typeAfter) {
- queueBlockReplace(actor, new Location(before.getWorld(), before.getX(), before.getY(), before.getZ()), before.getBlockData(), typeAfter);
+ queueBlock(actor, new Location(before.getWorld(), before.getX(), before.getY(), before.getZ()), before.getBlockData(), typeAfter, BlockStateCodecs.serialize(before), null, null);
}
/**
@@ -165,12 +167,11 @@ public class Consumer extends TimerTask {
* Blockstate of the block after actually being placed.
*/
public void queueBlockReplace(Actor actor, BlockData typeBefore, BlockState after) {
- queueBlockReplace(actor, new Location(after.getWorld(), after.getX(), after.getY(), after.getZ()), typeBefore, after.getBlockData());
+ queueBlock(actor, new Location(after.getWorld(), after.getX(), after.getY(), after.getZ()), typeBefore, after.getBlockData(), null, BlockStateCodecs.serialize(after), null);
}
public void queueBlockReplace(Actor actor, Location loc, BlockData typeBefore, BlockData typeAfter) {
- queueBlockBreak(actor, loc, typeBefore);
- queueBlockPlace(actor, loc, typeAfter);
+ queueBlock(actor, loc, typeBefore, typeAfter, null, null, null);
}
/**
@@ -211,7 +212,7 @@ public class Consumer extends TimerTask {
* Data of the item taken/stored
*/
public void queueChestAccess(Actor actor, Location loc, BlockData type, ItemStack itemStack, boolean remove) {
- queueBlock(actor, loc, type, type, null, new ChestAccess(itemStack, remove));
+ queueBlock(actor, loc, type, type, null, null, new ChestAccess(itemStack, remove));
}
/**
@@ -326,11 +327,8 @@ public class Consumer extends TimerTask {
* @param lines
* The four lines on the sign.
*/
- public void queueSignBreak(Actor actor, Location loc, BlockData type, String[] lines) {
- if ((type.getMaterial() != Material.SIGN && type.getMaterial() != Material.WALL_SIGN) || lines == null || lines.length != 4) {
- return;
- }
- queueBlock(actor, loc, type, null, lines[0] + "\0" + lines[1] + "\0" + lines[2] + "\0" + lines[3], null);
+ public void queueSignBreak(Actor actor, Location loc, BlockData type, byte[] typeState) {
+ queueBlock(actor, loc, type, null, typeState, null, null);
}
/**
@@ -342,7 +340,7 @@ public class Consumer extends TimerTask {
* The sign being broken
*/
public void queueSignBreak(Actor actor, Sign sign) {
- queueSignBreak(actor, new Location(sign.getWorld(), sign.getX(), sign.getY(), sign.getZ()), sign.getBlockData(), sign.getLines());
+ queueSignBreak(actor, new Location(sign.getWorld(), sign.getX(), sign.getY(), sign.getZ()), sign.getBlockData(), BlockStateCodecs.serialize(sign));
}
/**
@@ -363,7 +361,7 @@ public class Consumer extends TimerTask {
if ((type.getMaterial() != Material.SIGN && type.getMaterial() != Material.WALL_SIGN) || lines == null || lines.length != 4) {
return;
}
- queueBlock(actor, loc, null, type, lines[0] + "\0" + lines[1] + "\0" + lines[2] + "\0" + lines[3], null);
+ queueBlock(actor, loc, null, type, null, Utils.serializeYamlConfiguration(BlockStateCodecSign.serialize(lines)), null);
}
/**
@@ -585,7 +583,7 @@ public class Consumer extends TimerTask {
return playerIds.containsKey(actor);
}
- private void queueBlock(Actor actor, Location loc, BlockData typeBefore, BlockData typeAfter, String signtext, ChestAccess ca) {
+ private void queueBlock(Actor actor, Location loc, BlockData typeBefore, BlockData typeAfter, byte[] stateBefore, byte[] stateAfter, ChestAccess ca) {
if (typeBefore == null) {
typeBefore = Bukkit.createBlockData(Material.AIR);
}
@@ -594,7 +592,7 @@ public class Consumer extends TimerTask {
}
if (Config.fireCustomEvents) {
// Create and call the event
- BlockChangePreLogEvent event = new BlockChangePreLogEvent(actor, loc, typeBefore, typeAfter, signtext, ca);
+ BlockChangePreLogEvent event = new BlockChangePreLogEvent(actor, loc, typeBefore, typeAfter, null, ca);
logblock.getServer().getPluginManager().callEvent(event);
if (event.isCancelled()) {
return;
@@ -605,7 +603,7 @@ public class Consumer extends TimerTask {
loc = event.getLocation();
typeBefore = event.getTypeBefore();
typeAfter = event.getTypeAfter();
- signtext = event.getSignText();
+ // signtext = event.getSignText();
ca = event.getChestAccess();
}
// Do this last so LogBlock still has final say in what is being added
@@ -620,7 +618,7 @@ public class Consumer extends TimerTask {
int typeMaterialId = MaterialConverter.getOrAddMaterialId(typeString);
int typeStateId = MaterialConverter.getOrAddBlockStateId(typeString);
- queue.add(new BlockRow(loc, actor, replacedMaterialId, replacedStateId, typeMaterialId, typeStateId, signtext, ca));
+ queue.add(new BlockRow(loc, actor, replacedMaterialId, replacedStateId, stateBefore, typeMaterialId, typeStateId, stateAfter, ca));
}
private String playerID(Actor actor) {
@@ -670,19 +668,19 @@ public class Consumer extends TimerTask {
private class BlockRow extends BlockChange implements MergeableRow {
private Connection connection;
- public BlockRow(Location loc, Actor actor, int replaced, int replacedData, int type, int typeData, String signtext, ChestAccess ca) {
- super(System.currentTimeMillis() / 1000, loc, actor, replaced, replacedData, type, typeData, signtext, ca);
+ public BlockRow(Location loc, Actor actor, int replaced, int replacedData, byte[] replacedState, int type, int typeData, byte[] typeState, ChestAccess ca) {
+ super(System.currentTimeMillis() / 1000, loc, actor, replaced, replacedData, replacedState, type, typeData, typeState, ca);
}
@Override
public String[] getInserts() {
final String table = getWorldConfig(loc.getWorld()).table;
- final String[] inserts = new String[ca != null || signtext != null ? 2 : 1];
+ final String[] inserts = new String[ca != null || replacedState != null || typeState != null ? 2 : 1];
inserts[0] = "INSERT INTO `" + table + "-blocks` (date, playerid, replaced, replaceddata, type, typedata, x, y, z) VALUES (FROM_UNIXTIME(" + date + "), " + playerID(actor) + ", " + replacedMaterial + ", " + replacedData + ", " + typeMaterial + ", " + typeData + ", '" + loc.getBlockX()
+ "', " + safeY(loc) + ", '" + loc.getBlockZ() + "');";
- if (signtext != null) {
- inserts[1] = "INSERT INTO `" + table + "-sign` (id, signtext) values (LAST_INSERT_ID(), '" + mysqlTextEscape(signtext) + "');";
+ if (replacedState != null || typeState != null) {
+ inserts[1] = "INSERT INTO `" + table + "-state` (replacedState, typeState, id) VALUES('" + Utils.mysqlEscapeBytes(replacedState) + "', '" + Utils.mysqlEscapeBytes(typeState) + "', LAST_INSERT_ID());";
} else if (ca != null) {
inserts[1] = "INSERT INTO `" + table + "-chestdata` (id, item, itemremoved) values (LAST_INSERT_ID(), '" + Utils.mysqlEscapeBytes(Utils.saveItemStack(ca.itemStack)) + "', " + (ca.remove ? 1 : 0) + ");";
}
@@ -727,10 +725,11 @@ public class Consumer extends TimerTask {
rs.next();
id = rs.getInt(1);
- if (signtext != null) {
- ps = connection.prepareStatement("INSERT INTO `" + table + "-sign` (signtext, id) VALUES(?, ?)");
- ps.setString(1, signtext);
- ps.setInt(2, id);
+ if (typeState != null || replacedState != null) {
+ ps = connection.prepareStatement("INSERT INTO `" + table + "-state` (replacedState, typeState, id) VALUES(?, ?, ?)");
+ ps.setBytes(1, replacedState);
+ ps.setBytes(2, typeState);
+ ps.setInt(3, id);
ps.executeUpdate();
} else if (ca != null) {
ps = connection.prepareStatement("INSERT INTO `" + table + "-chestdata` (item, itemremove, id) values (?, ?, ?)");
@@ -772,7 +771,7 @@ public class Consumer extends TimerTask {
@Override
public boolean isUnique() {
- return !(signtext == null && ca == null && playerIds.containsKey(actor));
+ return !(typeState == null && replacedState == null && ca == null && playerIds.containsKey(actor));
}
@Override
diff --git a/src/main/java/de/diddiz/LogBlock/QueryParams.java b/src/main/java/de/diddiz/LogBlock/QueryParams.java
index 3d0a151..b97ee03 100644
--- a/src/main/java/de/diddiz/LogBlock/QueryParams.java
+++ b/src/main/java/de/diddiz/LogBlock/QueryParams.java
@@ -34,7 +34,7 @@ public final class QueryParams implements Cloneable {
public List typeIds = new ArrayList();
public World world = null;
public String match = null;
- public boolean needCount = false, needId = false, needDate = false, needType = false, needData = false, needPlayer = false, needCoords = false, needSignText = false, needChestAccess = false, needMessage = false, needKiller = false, needVictim = false, needWeapon = false;
+ public boolean needCount = false, needId = false, needDate = false, needType = false, needData = false, needPlayer = false, needCoords = false, needChestAccess = false, needMessage = false, needKiller = false, needVictim = false, needWeapon = false;
private final LogBlock logblock;
public QueryParams(LogBlock logblock) {
@@ -145,8 +145,8 @@ public final class QueryParams implements Cloneable {
if (needCoords) {
select += "x, y, z, ";
}
- if (needSignText) {
- select += "signtext, ";
+ if (needData) {
+ select += "replacedState, typeState, ";
}
if (needChestAccess) {
select += "item, itemremove, ";
@@ -157,8 +157,8 @@ public final class QueryParams implements Cloneable {
if (needPlayer || players.size() > 0) {
from += "INNER JOIN `lb-players` USING (playerid) ";
}
- if (needSignText) {
- from += "LEFT JOIN `" + getTable() + "-sign` USING (id) ";
+ if (!needCount && needData) {
+ from += "LEFT JOIN `" + getTable() + "-state` USING (id) ";
}
if (needChestAccess)
// If BlockChangeType is CHESTACCESS, we can use more efficient query
diff --git a/src/main/java/de/diddiz/LogBlock/Updater.java b/src/main/java/de/diddiz/LogBlock/Updater.java
index dde3cc3..123be33 100644
--- a/src/main/java/de/diddiz/LogBlock/Updater.java
+++ b/src/main/java/de/diddiz/LogBlock/Updater.java
@@ -1,5 +1,6 @@
package de.diddiz.LogBlock;
+import de.diddiz.LogBlock.blockstate.BlockStateCodecSign;
import de.diddiz.LogBlock.config.Config;
import de.diddiz.LogBlock.config.WorldConfig;
import de.diddiz.util.UUIDFetcher;
@@ -386,7 +387,7 @@ class Updater {
}
for (final WorldConfig wcfg : getLoggedWorlds()) {
if (wcfg.isLogging(Logging.SIGNTEXT)) {
- checkCharset(wcfg.table + "-sign","signtext",st);
+ // checkCharset(wcfg.table + "-sign","signtext",st);
}
}
st.close();
@@ -565,7 +566,7 @@ class Updater {
}
rs.close();
- PreparedStatement updateWeaponStatement = conn.prepareStatement("UPDATE `" + wcfg.table + "`-kills SET weapon = ? WHERE id = ?");
+ PreparedStatement updateWeaponStatement = conn.prepareStatement("UPDATE `" + wcfg.table + "-kills` SET weapon = ? WHERE id = ?");
for (int start = 0;; start += 10000) {
rs = st.executeQuery("SELECT id, weapon FROM `" + wcfg.table + "-kills` ORDER BY id ASC LIMIT " + start + ",10000");
boolean anyUpdate = false;
@@ -622,7 +623,85 @@ class Updater {
}
config.set("version", "1.13.0");
}
+
+ if (configVersion.compareTo(new ComparableVersion("1.13.1")) < 0) {
+ getLogger().info("Updating tables to 1.13.1 ...");
+ try {
+ final Connection conn = logblock.getConnection();
+ conn.setAutoCommit(false);
+ final Statement st = conn.createStatement();
+ for (final WorldConfig wcfg : getLoggedWorlds()) {
+ getLogger().info("Processing world " + wcfg.world + "...");
+ if (wcfg.isLogging(Logging.SIGNTEXT)) {
+ int rowsToConvert = 0;
+ int done = 0;
+ try {
+ ResultSet rs = st.executeQuery("SELECT count(*) as rowcount FROM `" + wcfg.table + "-sign`");
+ if (rs.next()) {
+ rowsToConvert = rs.getInt(1);
+ getLogger().info("Converting " + rowsToConvert + " entries in " + wcfg.table + "-sign");
+ }
+ rs.close();
+ PreparedStatement insertSignState = conn.prepareStatement("INSERT INTO `" + wcfg.table + "-state` (id, replacedState, typeState) VALUES (?, ?, ?)");
+ PreparedStatement deleteSign = conn.prepareStatement("DELETE FROM `" + wcfg.table + "-sign` WHERE id = ?");
+ while (true) {
+ rs = st.executeQuery("SELECT id, signtext, replaced, type FROM `" + wcfg.table + "-sign` LEFT JOIN `" + wcfg.table + "-blocks` USING (id) ORDER BY id ASC LIMIT 10000");
+ boolean anyRow = false;
+ while (rs.next()) {
+ anyRow = true;
+ int id = rs.getInt("id");
+ String signText = rs.getString("signtext");
+ int replaced = rs.getInt("replaced");
+ boolean nullBlock = rs.wasNull();
+ int type = rs.getInt("type");
+
+ if (!nullBlock && signText != null) {
+ String[] lines = signText.split("\0", 4);
+ byte[] bytes = Utils.serializeYamlConfiguration(BlockStateCodecSign.serialize(lines));
+
+ Material replacedMaterial = MaterialConverter.getBlockData(replaced, -1).getMaterial();
+ Material typeMaterial = MaterialConverter.getBlockData(type, -1).getMaterial();
+ boolean wasSign = replacedMaterial == Material.SIGN || replacedMaterial == Material.WALL_SIGN;
+ boolean isSign = typeMaterial == Material.SIGN || typeMaterial == Material.WALL_SIGN;
+
+ insertSignState.setInt(1, id);
+ insertSignState.setBytes(2, wasSign ? bytes : null);
+ insertSignState.setBytes(3, isSign ? bytes : null);
+ insertSignState.addBatch();
+ }
+
+ deleteSign.setInt(1, id);
+ deleteSign.addBatch();
+ done++;
+ }
+ rs.close();
+ if (!anyRow) {
+ break;
+ }
+ insertSignState.executeBatch();
+ deleteSign.executeBatch();
+ conn.commit();
+ getLogger().info("Done: " + done + "/" + rowsToConvert + " (" + (rowsToConvert > 0 ? (done * 100 / rowsToConvert) : 100) + "%)");
+ }
+ insertSignState.close();
+ deleteSign.close();
+ } catch (SQLException e) {
+ getLogger().info("Could not convert " + wcfg.table + "-sign: " + e.getMessage());
+ }
+ }
+ }
+
+ st.close();
+ conn.close();
+ } catch (final SQLException ex) {
+ Bukkit.getLogger().log(Level.SEVERE, "[Updater] Error: ", ex);
+ return false;
+ }
+
+ config.set("version", "1.13.1");
+ }
+
logblock.saveConfig();
return true;
}
@@ -667,8 +746,9 @@ class Updater {
for (final WorldConfig wcfg : getLoggedWorlds()) {
createTable(dbm, state, wcfg.table + "-blocks", "(id INT UNSIGNED NOT NULL AUTO_INCREMENT, date DATETIME NOT NULL, playerid INT UNSIGNED NOT NULL, replaced SMALLINT UNSIGNED NOT NULL, replacedData SMALLINT NOT NULL, type SMALLINT UNSIGNED NOT NULL, typeData SMALLINT 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 " + charset);
+ // createTable(dbm, state, wcfg.table + "-sign", "(id INT UNSIGNED NOT NULL, signtext VARCHAR(255) NOT NULL, PRIMARY KEY (id)) DEFAULT CHARSET " + charset);
createTable(dbm, state, wcfg.table + "-chestdata", "(id INT UNSIGNED NOT NULL, item MEDIUMBLOB, itemremove TINYINT, PRIMARY KEY (id))");
+ createTable(dbm, state, wcfg.table + "-state", "(id INT UNSIGNED NOT NULL, replacedState MEDIUMBLOB NULL, typeState MEDIUMBLOB 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 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))");
}
diff --git a/src/main/java/de/diddiz/LogBlock/WorldEditor.java b/src/main/java/de/diddiz/LogBlock/WorldEditor.java
index 813493a..a83cf7b 100644
--- a/src/main/java/de/diddiz/LogBlock/WorldEditor.java
+++ b/src/main/java/de/diddiz/LogBlock/WorldEditor.java
@@ -7,7 +7,6 @@ import org.bukkit.World;
import org.bukkit.block.Block;
import org.bukkit.block.BlockFace;
import org.bukkit.block.BlockState;
-import org.bukkit.block.Sign;
import org.bukkit.block.data.Bisected.Half;
import org.bukkit.block.data.BlockData;
import org.bukkit.block.data.type.Bed;
@@ -20,6 +19,7 @@ import org.bukkit.command.CommandSender;
import org.bukkit.inventory.InventoryHolder;
import org.bukkit.inventory.ItemStack;
+import de.diddiz.LogBlock.blockstate.BlockStateCodecs;
import de.diddiz.util.BukkitUtils;
import java.io.File;
@@ -76,8 +76,8 @@ public class WorldEditor implements Runnable {
this.sender = sender;
}
- public void queueEdit(int x, int y, int z, int replaced, int replaceData, int type, int typeData, String signtext, ChestAccess item) {
- edits.add(new Edit(0, new Location(world, x, y, z), null, replaced, replaceData, type, typeData, signtext, item));
+ public void queueEdit(int x, int y, int z, int replaced, int replaceData, byte[] replacedState, int type, int typeData, byte[] typeState, ChestAccess item) {
+ edits.add(new Edit(0, new Location(world, x, y, z), null, replaced, replaceData, replacedState, type, typeData, typeState, item));
}
public long getElapsedTime() {
@@ -153,8 +153,8 @@ public class WorldEditor implements Runnable {
}
private class Edit extends BlockChange {
- public Edit(long time, Location loc, Actor actor, int replaced, int replaceData, int type, int typeData, String signtext, ChestAccess ca) {
- super(time, loc, actor, replaced, replaceData, type, typeData, signtext, ca);
+ public Edit(long time, Location loc, Actor actor, int replaced, int replaceData, byte[] replacedState, int type, int typeData, byte[] typeState, ChestAccess ca) {
+ super(time, loc, actor, replaced, replaceData,replacedState , type, typeData, typeState, ca);
}
PerformResult perform() throws WorldEditorException {
@@ -172,7 +172,7 @@ public class WorldEditor implements Runnable {
if (BukkitUtils.isEmpty(replacedBlock.getMaterial()) && BukkitUtils.isEmpty(block.getType())) {
return PerformResult.NO_ACTION;
}
- final BlockState state = block.getState();
+ BlockState state = block.getState();
if (!world.isChunkLoaded(block.getChunk())) {
world.loadChunk(block.getChunk());
}
@@ -217,21 +217,18 @@ public class WorldEditor implements Runnable {
}
block.setBlockData(replacedBlock);
BlockData newData = block.getBlockData();
+ if (BlockStateCodecs.hasCodec(replacedBlock.getMaterial())) {
+ state = block.getState();
+ try {
+ BlockStateCodecs.deserialize(state, replacedState);
+ state.update();
+ } catch (Exception e) {
+ throw new WorldEditorException("Failed to restore blockstate of " + block.getType() + ": " + e, block.getLocation());
+ }
+ }
final Material curtype = block.getType();
- if (signtext != null && (curtype == Material.SIGN || curtype == Material.WALL_SIGN)) {
- final Sign sign = (Sign) block.getState();
- final String[] lines = signtext.split("\0", 4);
- if (lines.length < 4) {
- return PerformResult.NO_ACTION;
- }
- for (int i = 0; i < 4; i++) {
- sign.setLine(i, lines[i]);
- }
- if (!sign.update()) {
- throw new WorldEditorException("Failed to update signtext of " + block.getType(), block.getLocation());
- }
- } else if (newData instanceof Bed) {
+ if (newData instanceof Bed) {
final Bed bed = (Bed) newData;
final Block secBlock = bed.getPart() == Part.HEAD ? block.getRelative(bed.getFacing().getOppositeFace()) : block.getRelative(bed.getFacing());
if (secBlock.isEmpty()) {
diff --git a/src/main/java/de/diddiz/LogBlock/blockstate/BlockStateCodec.java b/src/main/java/de/diddiz/LogBlock/blockstate/BlockStateCodec.java
new file mode 100644
index 0000000..a3e5b0b
--- /dev/null
+++ b/src/main/java/de/diddiz/LogBlock/blockstate/BlockStateCodec.java
@@ -0,0 +1,15 @@
+package de.diddiz.LogBlock.blockstate;
+
+import org.bukkit.Material;
+import org.bukkit.block.BlockState;
+import org.bukkit.configuration.file.YamlConfiguration;
+
+public interface BlockStateCodec {
+ Material[] getApplicableMaterials();
+
+ YamlConfiguration serialize(BlockState state);
+
+ void deserialize(BlockState state, YamlConfiguration conf);
+
+ String toString(YamlConfiguration conf);
+}
diff --git a/src/main/java/de/diddiz/LogBlock/blockstate/BlockStateCodecBanner.java b/src/main/java/de/diddiz/LogBlock/blockstate/BlockStateCodecBanner.java
new file mode 100644
index 0000000..65359eb
--- /dev/null
+++ b/src/main/java/de/diddiz/LogBlock/blockstate/BlockStateCodecBanner.java
@@ -0,0 +1,70 @@
+package de.diddiz.LogBlock.blockstate;
+
+import java.util.List;
+
+import org.bukkit.DyeColor;
+import org.bukkit.Material;
+import org.bukkit.block.Banner;
+import org.bukkit.block.BlockState;
+import org.bukkit.block.banner.Pattern;
+import org.bukkit.block.banner.PatternType;
+import org.bukkit.configuration.ConfigurationSection;
+import org.bukkit.configuration.file.YamlConfiguration;
+
+public class BlockStateCodecBanner implements BlockStateCodec {
+ @Override
+ public Material[] getApplicableMaterials() {
+ return new Material[] { Material.WHITE_BANNER, Material.ORANGE_BANNER, Material.MAGENTA_BANNER, Material.LIGHT_BLUE_BANNER, Material.YELLOW_BANNER, Material.LIME_BANNER, Material.PINK_BANNER, Material.GRAY_BANNER, Material.LIGHT_GRAY_BANNER, Material.CYAN_BANNER, Material.PURPLE_BANNER,
+ Material.BLUE_BANNER, Material.BROWN_BANNER, Material.GREEN_BANNER, Material.RED_BANNER, Material.BLACK_BANNER, Material.WHITE_WALL_BANNER, Material.ORANGE_WALL_BANNER, Material.MAGENTA_WALL_BANNER, Material.LIGHT_BLUE_WALL_BANNER, Material.YELLOW_WALL_BANNER,
+ Material.LIME_WALL_BANNER, Material.PINK_WALL_BANNER, Material.GRAY_WALL_BANNER, Material.LIGHT_GRAY_WALL_BANNER, Material.CYAN_WALL_BANNER, Material.PURPLE_WALL_BANNER, Material.BLUE_WALL_BANNER, Material.BROWN_WALL_BANNER, Material.GREEN_WALL_BANNER, Material.RED_WALL_BANNER,
+ Material.BLACK_WALL_BANNER };
+ }
+
+ @Override
+ public YamlConfiguration serialize(BlockState state) {
+ if (state instanceof Banner) {
+ Banner banner = (Banner) state;
+ int nr = 0;
+ List patterns = banner.getPatterns();
+ if (!patterns.isEmpty()) {
+ YamlConfiguration conf = new YamlConfiguration();
+ ConfigurationSection patternsSection = conf.createSection("patterns");
+ for (Pattern pattern : patterns) {
+ ConfigurationSection section = patternsSection.createSection(Integer.toString(nr));
+ section.set("color", pattern.getColor().name());
+ section.set("pattern", pattern.getPattern().name());
+ nr++;
+ }
+ return conf;
+ }
+ }
+ return null;
+ }
+
+ @Override
+ public void deserialize(BlockState state, YamlConfiguration conf) {
+ if (state instanceof Banner) {
+ Banner banner = (Banner) state;
+ int oldPatterns = banner.getPatterns().size();
+ for (int i = 0; i < oldPatterns; i++) {
+ banner.removePattern(0);
+ }
+ ConfigurationSection patternsSection = conf == null ? null : conf.getConfigurationSection("patterns");
+ if (patternsSection != null) {
+ for (String key : patternsSection.getKeys(false)) {
+ ConfigurationSection section = patternsSection.getConfigurationSection(key);
+ if (section != null) {
+ DyeColor color = DyeColor.valueOf(section.getString("color"));
+ PatternType type = PatternType.valueOf(section.getString("pattern"));
+ banner.addPattern(new Pattern(color, type));
+ }
+ }
+ }
+ }
+ }
+
+ @Override
+ public String toString(YamlConfiguration conf) {
+ return null;
+ }
+}
diff --git a/src/main/java/de/diddiz/LogBlock/blockstate/BlockStateCodecSign.java b/src/main/java/de/diddiz/LogBlock/blockstate/BlockStateCodecSign.java
new file mode 100644
index 0000000..04e6e9d
--- /dev/null
+++ b/src/main/java/de/diddiz/LogBlock/blockstate/BlockStateCodecSign.java
@@ -0,0 +1,76 @@
+package de.diddiz.LogBlock.blockstate;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+import org.bukkit.Material;
+import org.bukkit.block.BlockState;
+import org.bukkit.block.Sign;
+import org.bukkit.configuration.file.YamlConfiguration;
+
+public class BlockStateCodecSign implements BlockStateCodec {
+ @Override
+ public Material[] getApplicableMaterials() {
+ return new Material[] { Material.WALL_SIGN, Material.SIGN };
+ }
+
+ @Override
+ public YamlConfiguration serialize(BlockState state) {
+ if (state instanceof Sign) {
+ Sign sign = (Sign) state;
+ String[] lines = sign.getLines();
+ boolean hasText = false;
+ for (int i = 0; i < lines.length; i++) {
+ if (lines[i] != null && lines[i].length() > 0) {
+ hasText = true;
+ break;
+ }
+ }
+ if (hasText) {
+ YamlConfiguration conf = new YamlConfiguration();
+ conf.set("lines", Arrays.asList(lines));
+ return conf;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * This is required for the SignChangeEvent, because we have no BlockState there.
+ */
+ public static YamlConfiguration serialize(String[] lines) {
+ YamlConfiguration conf = new YamlConfiguration();
+ conf.set("lines", Arrays.asList(lines));
+ return conf;
+ }
+
+ @Override
+ public void deserialize(BlockState state, YamlConfiguration conf) {
+ if (state instanceof Sign) {
+ Sign sign = (Sign) state;
+ List lines = Collections.emptyList();
+ if (conf != null) {
+ lines = conf.getStringList("lines");
+ }
+ for (int i = 0; i < 4; i++) {
+ String line = lines.size() > i && lines.get(i) != null ? lines.get(i) : "";
+ sign.setLine(i, line);
+ }
+ }
+ }
+
+ @Override
+ public String toString(YamlConfiguration conf) {
+ if (conf != null) {
+ StringBuilder sb = new StringBuilder();
+ for (String line : conf.getStringList("lines")) {
+ if (sb.length() > 0)
+ sb.append(" ");
+ sb.append("[").append(line).append("]");
+ }
+ return sb.toString();
+ }
+ return null;
+ }
+}
diff --git a/src/main/java/de/diddiz/LogBlock/blockstate/BlockStateCodecSkull.java b/src/main/java/de/diddiz/LogBlock/blockstate/BlockStateCodecSkull.java
new file mode 100644
index 0000000..dbeeaf8
--- /dev/null
+++ b/src/main/java/de/diddiz/LogBlock/blockstate/BlockStateCodecSkull.java
@@ -0,0 +1,54 @@
+package de.diddiz.LogBlock.blockstate;
+
+import java.util.UUID;
+
+import org.bukkit.Bukkit;
+import org.bukkit.Material;
+import org.bukkit.OfflinePlayer;
+import org.bukkit.block.BlockState;
+import org.bukkit.block.Skull;
+import org.bukkit.configuration.file.YamlConfiguration;
+
+public class BlockStateCodecSkull implements BlockStateCodec {
+ @Override
+ public Material[] getApplicableMaterials() {
+ return new Material[] { Material.PLAYER_WALL_HEAD, Material.PLAYER_HEAD };
+ }
+
+ @Override
+ public YamlConfiguration serialize(BlockState state) {
+ if (state instanceof Skull) {
+ Skull skull = (Skull) state;
+ OfflinePlayer owner = skull.hasOwner() ? skull.getOwningPlayer() : null;
+ if (owner != null) {
+ YamlConfiguration conf = new YamlConfiguration();
+ conf.set("owner", owner.getUniqueId().toString());
+ return conf;
+ }
+ }
+ return null;
+ }
+
+ @Override
+ public void deserialize(BlockState state, YamlConfiguration conf) {
+ if (state instanceof Skull) {
+ Skull skull = (Skull) state;
+ UUID ownerId = conf == null ? null : UUID.fromString(conf.getString("owner"));
+ if (ownerId == null) {
+ skull.setOwningPlayer(null);
+ } else {
+ skull.setOwningPlayer(Bukkit.getOfflinePlayer(ownerId));
+ }
+ }
+ }
+
+ @Override
+ public String toString(YamlConfiguration conf) {
+ UUID ownerId = conf == null ? null : UUID.fromString(conf.getString("owner"));
+ if (ownerId != null) {
+ OfflinePlayer owner = Bukkit.getOfflinePlayer(ownerId);
+ return "[" + (owner.getName() != null ? owner.getName() : owner.getUniqueId().toString()) + "]";
+ }
+ return null;
+ }
+}
diff --git a/src/main/java/de/diddiz/LogBlock/blockstate/BlockStateCodecSpawner.java b/src/main/java/de/diddiz/LogBlock/blockstate/BlockStateCodecSpawner.java
new file mode 100644
index 0000000..3269684
--- /dev/null
+++ b/src/main/java/de/diddiz/LogBlock/blockstate/BlockStateCodecSpawner.java
@@ -0,0 +1,60 @@
+package de.diddiz.LogBlock.blockstate;
+
+import org.bukkit.Material;
+import org.bukkit.block.BlockState;
+import org.bukkit.block.CreatureSpawner;
+import org.bukkit.configuration.file.YamlConfiguration;
+import org.bukkit.entity.EntityType;
+
+public class BlockStateCodecSpawner implements BlockStateCodec {
+ @Override
+ public Material[] getApplicableMaterials() {
+ return new Material[] { Material.SPAWNER };
+ }
+
+ @Override
+ public YamlConfiguration serialize(BlockState state) {
+ if (state instanceof CreatureSpawner) {
+ CreatureSpawner spawner = (CreatureSpawner) state;
+ YamlConfiguration conf = new YamlConfiguration();
+ conf.set("delay", spawner.getDelay());
+ conf.set("maxNearbyEntities", spawner.getMaxNearbyEntities());
+ conf.set("maxSpawnDelay", spawner.getMaxSpawnDelay());
+ conf.set("minSpawnDelay", spawner.getMinSpawnDelay());
+ conf.set("requiredPlayerRange", spawner.getRequiredPlayerRange());
+ conf.set("spawnCount", spawner.getSpawnCount());
+ conf.set("spawnedType", spawner.getSpawnedType().name());
+ conf.set("spawnRange", spawner.getSpawnRange());
+ return conf;
+ }
+ return null;
+ }
+
+ @Override
+ public void deserialize(BlockState state, YamlConfiguration conf) {
+ if (state instanceof CreatureSpawner) {
+ CreatureSpawner spawner = (CreatureSpawner) state;
+ if (conf != null) {
+ spawner.setDelay(conf.getInt("delay"));
+ spawner.setMaxNearbyEntities(conf.getInt("maxNearbyEntities"));
+ spawner.setMaxSpawnDelay(conf.getInt("maxSpawnDelay"));
+ spawner.setMinSpawnDelay(conf.getInt("minSpawnDelay"));
+ spawner.setRequiredPlayerRange(conf.getInt("requiredPlayerRange"));
+ spawner.setSpawnCount(conf.getInt("spawnCount"));
+ spawner.setSpawnedType(EntityType.valueOf(conf.getString("spawnedType")));
+ spawner.setSpawnRange(conf.getInt("spawnRange"));
+ }
+ }
+ }
+
+ @Override
+ public String toString(YamlConfiguration conf) {
+ if (conf != null) {
+ EntityType entity = EntityType.valueOf(conf.getString("spawnedType"));
+ if (entity != null) {
+ return "[" + entity + "]";
+ }
+ }
+ return null;
+ }
+}
diff --git a/src/main/java/de/diddiz/LogBlock/blockstate/BlockStateCodecs.java b/src/main/java/de/diddiz/LogBlock/blockstate/BlockStateCodecs.java
new file mode 100644
index 0000000..8cac08a
--- /dev/null
+++ b/src/main/java/de/diddiz/LogBlock/blockstate/BlockStateCodecs.java
@@ -0,0 +1,80 @@
+package de.diddiz.LogBlock.blockstate;
+
+import java.util.EnumMap;
+import java.util.Map;
+import java.util.logging.Level;
+
+import org.bukkit.Material;
+import org.bukkit.block.BlockState;
+import org.bukkit.configuration.InvalidConfigurationException;
+import org.bukkit.configuration.file.YamlConfiguration;
+
+import de.diddiz.LogBlock.LogBlock;
+import de.diddiz.util.Utils;
+
+public class BlockStateCodecs {
+ private static Map codecs = new EnumMap<>(Material.class);
+
+ public static void registerCodec(BlockStateCodec codec) {
+ Material[] materials = codec.getApplicableMaterials();
+ for (Material material : materials) {
+ if (codecs.containsKey(material)) {
+ throw new IllegalArgumentException("BlockStateCodec for " + material + " already registered!");
+ }
+ codecs.put(material, codec);
+ }
+ }
+
+ static {
+ registerCodec(new BlockStateCodecSign());
+ registerCodec(new BlockStateCodecSkull());
+ registerCodec(new BlockStateCodecBanner());
+ registerCodec(new BlockStateCodecSpawner());
+ }
+
+ public static boolean hasCodec(Material material) {
+ return codecs.containsKey(material);
+ }
+
+ public static byte[] serialize(BlockState state) {
+ BlockStateCodec codec = codecs.get(state.getType());
+ if (codec != null) {
+ YamlConfiguration serialized = codec.serialize(state);
+ if (serialized != null && !serialized.getKeys(false).isEmpty()) {
+ return Utils.serializeYamlConfiguration(serialized);
+ }
+ }
+ return null;
+ }
+
+ public static void deserialize(BlockState block, byte[] state) {
+ BlockStateCodec codec = codecs.get(block.getType());
+ if (codec != null) {
+ YamlConfiguration conf = null;
+ try {
+ if (state != null) {
+ conf = Utils.deserializeYamlConfiguration(state);
+ }
+ } catch (InvalidConfigurationException e) {
+ LogBlock.getInstance().getLogger().log(Level.SEVERE, "Exception while deserializing BlockState", e);
+ }
+ codec.deserialize(block, conf);
+ }
+ }
+
+ public static String toString(Material material, byte[] state) {
+ BlockStateCodec codec = codecs.get(material);
+ if (codec != null) {
+ YamlConfiguration conf = null;
+ try {
+ if (state != null) {
+ conf = Utils.deserializeYamlConfiguration(state);
+ }
+ } catch (InvalidConfigurationException e) {
+ LogBlock.getInstance().getLogger().log(Level.SEVERE, "Exception while deserializing BlockState", e);
+ }
+ return codec.toString(conf);
+ }
+ return null;
+ }
+}
diff --git a/src/main/java/de/diddiz/worldedit/WorldEditLoggingHook.java b/src/main/java/de/diddiz/worldedit/WorldEditLoggingHook.java
index 64b2a8f..783c9dd 100644
--- a/src/main/java/de/diddiz/worldedit/WorldEditLoggingHook.java
+++ b/src/main/java/de/diddiz/worldedit/WorldEditLoggingHook.java
@@ -14,6 +14,7 @@ import com.sk89q.worldedit.world.block.BlockStateHolder;
import de.diddiz.LogBlock.LogBlock;
import de.diddiz.LogBlock.Logging;
+import de.diddiz.LogBlock.blockstate.BlockStateCodecs;
import de.diddiz.LogBlock.config.Config;
import de.diddiz.util.BukkitUtils;
@@ -22,8 +23,6 @@ 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;
import org.bukkit.block.data.BlockData;
import java.util.logging.Level;
@@ -104,9 +103,8 @@ public class WorldEditLoggingHook {
Material typeBefore = origin.getType();
// Check to see if we've broken a sign
- if (Config.isLogging(location.getWorld().getName(), Logging.SIGNTEXT) && (typeBefore == Material.SIGN || typeBefore == Material.WALL_SIGN)) {
- BlockState stateBefore = origin.getState();
- plugin.getConsumer().queueSignBreak(lbActor, (Sign) stateBefore);
+ if (BlockStateCodecs.hasCodec(typeBefore)) {
+ plugin.getConsumer().queueBlockBreak(lbActor, origin.getState());
} else if (!origin.isEmpty()) {
plugin.getConsumer().queueBlockBreak(lbActor, location, origin.getBlockData());
}