Various minor improvements to querying and logging

- Allow use of "both" as a query parameter. This will search for created and destroyed blocks, but not chestaccess. This is useful for situations where the query defaults to "all" e.g. rollbacks. Fixes #210, #252
- Add block negation query parameter. Like with player names, you can now precede a block name with "!" to query for all except the specified blocks. Fixes #452, #216
- Allow use of area parameter with location parameter, as long as location is specified first. Fixes #458
- Better error when asking for kill log without world specified
- Don't try to process events where the block is null.  Fixes #463
- Replace problematic characters in world names with underscores when deciding on a default table name.  Fixes #409
- Adjust for cocoa beans having reversed attachment direction in smart logging
- Prevent out of bounds exception if a single-word timespec ends in a number.  Fixes #354
- Prevent error when specifying a killer or victim in a teleport lookup.  Fixes #333
This commit is contained in:
Philip Cass
2013-11-08 13:56:05 +00:00
parent 8c9f78a985
commit eb84fd1380
6 changed files with 49 additions and 20 deletions

View File

@@ -551,8 +551,8 @@ public class CommandsHandler implements CommandExecutor
rs = state.executeQuery(params.getQuery()); rs = state.executeQuery(params.getQuery());
if (rs.next()) { if (rs.next()) {
final Player player = (Player)sender; final Player player = (Player)sender;
final int y = rs.getInt(2); final int y = rs.getInt("y");
final Location loc = new Location(params.world, rs.getInt(1) + 0.5, y, rs.getInt(3) + 0.5, player.getLocation().getYaw(), 90); 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 // Teleport the player sync because omg thread safety
logblock.getServer().getScheduler().scheduleSyncDelayedTask(logblock, new Runnable() { logblock.getServer().getScheduler().scheduleSyncDelayedTask(logblock, new Runnable() {

56
src/main/java/de/diddiz/LogBlock/QueryParams.java Executable file → Normal file
View File

@@ -24,7 +24,7 @@ import static de.diddiz.util.Utils.*;
public final class QueryParams implements Cloneable public final class QueryParams implements Cloneable
{ {
private static final Set<Integer> keywords = new HashSet<Integer>(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<Integer> keywords = new HashSet<Integer>(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 BlockChangeType bct = BlockChangeType.BOTH;
public int limit = -1, before = 0, since = 0, radius = -1; public int limit = -1, before = 0, since = 0, radius = -1;
public Location loc = null; public Location loc = null;
@@ -32,7 +32,7 @@ public final class QueryParams implements Cloneable
public List<String> players = new ArrayList<String>(); public List<String> players = new ArrayList<String>();
public List<String> killers = new ArrayList<String>(); public List<String> killers = new ArrayList<String>();
public List<String> victims = new ArrayList<String>(); public List<String> victims = new ArrayList<String>();
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 RegionContainer sel = null;
public SummarizationMode sum = SummarizationMode.NONE; public SummarizationMode sum = SummarizationMode.NONE;
public List<Block> types = new ArrayList<Block>(); public List<Block> types = new ArrayList<Block>();
@@ -168,6 +168,8 @@ public final class QueryParams implements Cloneable
title.append("kills "); title.append("kills ");
else { else {
if (!types.isEmpty()) { if (!types.isEmpty()) {
if (excludeBlocksMode)
title.append("all blocks except ");
final String[] blocknames = new String[types.size()]; final String[] blocknames = new String[types.size()];
for (int i = 0; i < types.size(); i++) for (int i = 0; i < types.size(); i++)
blocknames[i] = materialName(types.get(i).getBlock()); blocknames[i] = materialName(types.get(i).getBlock());
@@ -203,7 +205,7 @@ public final class QueryParams implements Cloneable
title.append("more than ").append(before * -1).append(" minutes ago "); title.append("more than ").append(before * -1).append(" minutes ago ");
if (loc != null) { if (loc != null) {
if (radius > 0) 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) else if (radius == 0)
title.append("at ").append(loc.getBlockX()).append(":").append(loc.getBlockY()).append(":").append(loc.getBlockZ()).append(" "); title.append("at ").append(loc.getBlockX()).append(":").append(loc.getBlockY()).append(":").append(loc.getBlockZ()).append(" ");
} else if (sel != null) } else if (sel != null)
@@ -304,6 +306,8 @@ public final class QueryParams implements Cloneable
switch (blockChangeType) { switch (blockChangeType) {
case ALL: case ALL:
if (!types.isEmpty()) { if (!types.isEmpty()) {
if (excludeBlocksMode)
where.append("NOT ");
where.append('('); where.append('(');
for (final Block block : types) { for (final Block block : types) {
where.append("((type = ").append(block.getBlock()).append(" OR replaced = ").append(block.getBlock()); where.append("((type = ").append(block.getBlock()).append(" OR replaced = ").append(block.getBlock());
@@ -320,6 +324,8 @@ public final class QueryParams implements Cloneable
break; break;
case BOTH: case BOTH:
if (!types.isEmpty()) { if (!types.isEmpty()) {
if (excludeBlocksMode)
where.append("NOT ");
where.append('('); where.append('(');
for (final Block block : types) { for (final Block block : types) {
where.append("((type = ").append(block.getBlock()).append(" OR replaced = ").append(block.getBlock()); where.append("((type = ").append(block.getBlock()).append(" OR replaced = ").append(block.getBlock());
@@ -337,6 +343,8 @@ public final class QueryParams implements Cloneable
break; break;
case CREATED: case CREATED:
if (!types.isEmpty()) { if (!types.isEmpty()) {
if (excludeBlocksMode)
where.append("NOT ");
where.append('('); where.append('(');
for (final Block block : types) { for (final Block block : types) {
where.append("((type = ").append(block.getBlock()); where.append("((type = ").append(block.getBlock());
@@ -349,12 +357,13 @@ public final class QueryParams implements Cloneable
} }
where.delete(where.length() - 4, where.length()); where.delete(where.length() - 4, where.length());
where.append(") AND "); where.append(") AND ");
} else }
where.append("type != 0 AND "); where.append("type != 0 AND type != replaced AND ");
where.append("type != replaced AND ");
break; break;
case DESTROYED: case DESTROYED:
if (!types.isEmpty()) { if (!types.isEmpty()) {
if (excludeBlocksMode)
where.append("NOT ");
where.append('('); where.append('(');
for (final Block block : types) { for (final Block block : types) {
where.append("((replaced = ").append(block.getBlock()); where.append("((replaced = ").append(block.getBlock());
@@ -367,12 +376,13 @@ public final class QueryParams implements Cloneable
} }
where.delete(where.length() - 4, where.length()); where.delete(where.length() - 4, where.length());
where.append(") AND "); where.append(") AND ");
} else }
where.append("replaced != 0 AND "); where.append("replaced != 0 AND type != replaced AND ");
where.append("type != replaced AND ");
break; break;
case CHESTACCESS: case CHESTACCESS:
if (!types.isEmpty()) { if (!types.isEmpty()) {
if (excludeBlocksMode)
where.append("NOT ");
where.append('('); where.append('(');
for (final Block block : types) { for (final Block block : types) {
where.append("((itemtype = ").append(block.getBlock()); where.append("((itemtype = ").append(block.getBlock());
@@ -492,6 +502,7 @@ public final class QueryParams implements Cloneable
players.add(matches.size() == 1 ? matches.get(0).getName() : playerName.replaceAll("[^a-zA-Z0-9_]", "")); players.add(matches.size() == 1 ? matches.get(0).getName() : playerName.replaceAll("[^a-zA-Z0-9_]", ""));
} }
} }
needPlayer = true;
} else if (param.equals("killer")) { } else if (param.equals("killer")) {
if (values.length < 1) if (values.length < 1)
throw new IllegalArgumentException("No or wrong count of arguments for '" + param + "'"); throw new IllegalArgumentException("No or wrong count of arguments for '" + param + "'");
@@ -508,6 +519,7 @@ public final class QueryParams implements Cloneable
killers.add(matches.size() == 1 ? matches.get(0).getName() : killerName.replaceAll("[^a-zA-Z0-9_]", "")); killers.add(matches.size() == 1 ? matches.get(0).getName() : killerName.replaceAll("[^a-zA-Z0-9_]", ""));
} }
} }
needKiller = true;
} else if (param.equals("victim")) { } else if (param.equals("victim")) {
if (values.length < 1) if (values.length < 1)
throw new IllegalArgumentException("No or wrong count of arguments for '" + param + "'"); throw new IllegalArgumentException("No or wrong count of arguments for '" + param + "'");
@@ -524,6 +536,7 @@ public final class QueryParams implements Cloneable
victims.add(matches.size() == 1 ? matches.get(0).getName() : victimName.replaceAll("[^a-zA-Z0-9_]", "")); victims.add(matches.size() == 1 ? matches.get(0).getName() : victimName.replaceAll("[^a-zA-Z0-9_]", ""));
} }
} }
needVictim = true;
} else if (param.equals("weapon")) { } else if (param.equals("weapon")) {
if (values.length < 1) if (values.length < 1)
throw new IllegalArgumentException("No or wrong count of arguments for '" + param + "'"); throw new IllegalArgumentException("No or wrong count of arguments for '" + param + "'");
@@ -539,10 +552,15 @@ public final class QueryParams implements Cloneable
throw new IllegalArgumentException("No material matching: '" + weaponName + "'"); throw new IllegalArgumentException("No material matching: '" + weaponName + "'");
types.add(new Block(mat.getId(), -1)); types.add(new Block(mat.getId(), -1));
} }
needWeapon = true;
} else if (param.equals("block") || param.equals("type")) { } else if (param.equals("block") || param.equals("type")) {
if (values.length < 1) if (values.length < 1)
throw new IllegalArgumentException("No or wrong count of arguments for '" + param + "'"); 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(":")) { if (blockName.contains(":")) {
String[] blockNameSplit = blockName.split(":"); String[] blockNameSplit = blockName.split(":");
if (blockNameSplit.length > 2) if (blockNameSplit.length > 2)
@@ -567,17 +585,17 @@ public final class QueryParams implements Cloneable
} }
} }
} else if (param.equals("area")) { } else if (param.equals("area")) {
if (player == null && !prepareToolQuery) if (player == null && !prepareToolQuery && loc == null)
throw new IllegalArgumentException("You have to ba a player to use area"); throw new IllegalArgumentException("You have to be a player to use area, or specify a location first");
if (values.length == 0) { if (values.length == 0) {
radius = defaultDist; radius = defaultDist;
if (!prepareToolQuery) if (!prepareToolQuery && loc == null)
loc = player.getLocation(); loc = player.getLocation();
} else { } else {
if (!isInt(values[0])) if (!isInt(values[0]))
throw new IllegalArgumentException("Not a number: '" + values[0] + "'"); throw new IllegalArgumentException("Not a number: '" + values[0] + "'");
radius = Integer.parseInt(values[0]); radius = Integer.parseInt(values[0]);
if (!prepareToolQuery) if (!prepareToolQuery && loc == null)
loc = player.getLocation(); loc = player.getLocation();
} }
} else if (param.equals("selection") || param.equals("sel")) { } else if (param.equals("selection") || param.equals("sel")) {
@@ -612,6 +630,8 @@ public final class QueryParams implements Cloneable
bct = BlockChangeType.CREATED; bct = BlockChangeType.CREATED;
else if (param.equals("destroyed")) else if (param.equals("destroyed"))
bct = BlockChangeType.DESTROYED; bct = BlockChangeType.DESTROYED;
else if (param.equals("both"))
bct = BlockChangeType.BOTH;
else if (param.equals("chestaccess")) else if (param.equals("chestaccess"))
bct = BlockChangeType.CHESTACCESS; bct = BlockChangeType.CHESTACCESS;
else if (param.equals("chat")) else if (param.equals("chat"))
@@ -659,8 +679,12 @@ public final class QueryParams implements Cloneable
throw new IllegalArgumentException("Not a valid argument: '" + param + "'"); throw new IllegalArgumentException("Not a valid argument: '" + param + "'");
i += values.length; i += values.length;
} }
if (bct == BlockChangeType.KILLS && !getWorldConfig(world).isLogging(Logging.KILL)) if (bct == BlockChangeType.KILLS) {
throw new IllegalArgumentException("Kill logging not enabled for world '" + world.getName() + "'"); 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) if (types.size() > 0)
for (final Set<Integer> equivalent : getBlockEquivalents()) { for (final Set<Integer> equivalent : getBlockEquivalents()) {
boolean found = false; boolean found = false;

View File

@@ -14,7 +14,9 @@ public class WorldConfig extends LoggingEnabledMapping
public WorldConfig(File file) throws IOException { public WorldConfig(File file) throws IOException {
final Map<String, Object> def = new HashMap<String, Object>(); final Map<String, Object> def = new HashMap<String, Object>();
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()) for (final Logging l : Logging.values())
def.put("logging." + l.toString(), l.isDefaultEnabled()); def.put("logging." + l.toString(), l.isDefaultEnabled());
final YamlConfiguration config = YamlConfiguration.loadConfiguration(file); final YamlConfiguration config = YamlConfiguration.loadConfiguration(file);

View File

@@ -27,6 +27,7 @@ public class InteractLogging extends LoggingListener
final WorldConfig wcfg = getWorldConfig(event.getPlayer().getWorld()); final WorldConfig wcfg = getWorldConfig(event.getPlayer().getWorld());
if (wcfg != null) { if (wcfg != null) {
final Block clicked = event.getClickedBlock(); final Block clicked = event.getClickedBlock();
if (clicked == null) return;
final Material type = clicked.getType(); final Material type = clicked.getType();
final int typeId = type.getId(); final int typeId = type.getId();
final byte blockData = clicked.getData(); final byte blockData = clicked.getData();

View File

@@ -109,7 +109,7 @@ public class LoggingUtil {
} }
break; break;
case COCOA: case COCOA:
if (blockState.getBlock().getRelative(((CocoaPlant) data).getAttachedFace()).equals(origin)) { if (blockState.getBlock().getRelative(((CocoaPlant) data).getAttachedFace().getOppositeFace()).equals(origin)) {
consumer.queueBlockBreak(playerName, blockState); consumer.queueBlockBreak(playerName, blockState);
} }
break; break;

View File

@@ -76,6 +76,8 @@ public class Utils
while (currIndex <= spec[0].length() && isInt(spec[0].substring(lastIndex, currIndex))) while (currIndex <= spec[0].length() && isInt(spec[0].substring(lastIndex, currIndex)))
currIndex++; currIndex++;
if (currIndex - 1 != lastIndex) { if (currIndex - 1 != lastIndex) {
if (currIndex > spec[0].length())
return -1;
final String param = spec[0].substring(currIndex - 1, currIndex).toLowerCase(); final String param = spec[0].substring(currIndex - 1, currIndex).toLowerCase();
if (param.equals("d")) if (param.equals("d"))
days = Integer.parseInt(spec[0].substring(lastIndex, currIndex - 1)); days = Integer.parseInt(spec[0].substring(lastIndex, currIndex - 1));