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

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

View File

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

View File

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

View File

@ -109,7 +109,7 @@ public class LoggingUtil {
}
break;
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);
}
break;

View File

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