forked from LogBlock/LogBlock
- 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
828 lines
35 KiB
Java
828 lines
35 KiB
Java
package de.diddiz.LogBlock;
|
|
|
|
import de.diddiz.util.Block;
|
|
import de.diddiz.worldedit.RegionContainer;
|
|
import org.bukkit.Location;
|
|
import org.bukkit.Material;
|
|
import org.bukkit.World;
|
|
import org.bukkit.command.CommandSender;
|
|
import org.bukkit.entity.Player;
|
|
import org.bukkit.plugin.Plugin;
|
|
|
|
import java.util.ArrayList;
|
|
import java.util.Arrays;
|
|
import java.util.HashSet;
|
|
import java.util.List;
|
|
import java.util.Set;
|
|
|
|
import static de.diddiz.LogBlock.Session.getSession;
|
|
import static de.diddiz.LogBlock.config.Config.*;
|
|
import static de.diddiz.util.BukkitUtils.friendlyWorldname;
|
|
import static de.diddiz.util.BukkitUtils.getBlockEquivalents;
|
|
import static de.diddiz.util.MaterialName.materialName;
|
|
import static de.diddiz.util.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(), "both".hashCode()));
|
|
public BlockChangeType bct = BlockChangeType.BOTH;
|
|
public int limit = -1, before = 0, since = 0, radius = -1;
|
|
public Location loc = null;
|
|
public Order order = Order.DESC;
|
|
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, excludeBlocksMode = false, prepareToolQuery = false, silent = false;
|
|
public RegionContainer sel = null;
|
|
public SummarizationMode sum = SummarizationMode.NONE;
|
|
public List<Block> types = new ArrayList<Block>();
|
|
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;
|
|
private final LogBlock logblock;
|
|
|
|
public QueryParams(LogBlock logblock) {
|
|
this.logblock = logblock;
|
|
}
|
|
|
|
public QueryParams(LogBlock logblock, CommandSender sender, List<String> args) throws IllegalArgumentException {
|
|
this.logblock = logblock;
|
|
parseArgs(sender, args);
|
|
}
|
|
|
|
public static boolean isKeyWord(String param) {
|
|
return keywords.contains(param.toLowerCase().hashCode());
|
|
}
|
|
|
|
public String getLimit() {
|
|
return limit > 0 ? "LIMIT " + limit : "";
|
|
}
|
|
|
|
public String getQuery() {
|
|
if (bct == BlockChangeType.CHAT) {
|
|
String select = "SELECT ";
|
|
if (needCount)
|
|
select += "COUNT(*) AS count";
|
|
else {
|
|
if (needId)
|
|
select += "id, ";
|
|
if (needDate)
|
|
select += "date, ";
|
|
if (needPlayer)
|
|
select += "playername, ";
|
|
if (needMessage)
|
|
select += "message, ";
|
|
select = select.substring(0, select.length() - 2);
|
|
}
|
|
String from = "FROM `lb-chat` ";
|
|
|
|
if (needPlayer || players.size() > 0)
|
|
from += "INNER JOIN `lb-players` USING (playerid) ";
|
|
return select + " " + from + getWhere() + "ORDER BY date " + order + ", id " + order + " " + getLimit();
|
|
}
|
|
if (bct == BlockChangeType.KILLS) {
|
|
if (sum == SummarizationMode.NONE) {
|
|
String select = "SELECT ";
|
|
if (needCount)
|
|
select += "COUNT(*) AS count";
|
|
else {
|
|
if (needId)
|
|
select += "id, ";
|
|
if (needDate)
|
|
select += "date, ";
|
|
if (needPlayer || needKiller)
|
|
select += "killers.playername as killer, ";
|
|
if (needPlayer || needVictim)
|
|
select += "victims.playername as victim, ";
|
|
if (needWeapon)
|
|
select += "weapon, ";
|
|
if (needCoords)
|
|
select += "x, y, z, ";
|
|
select = select.substring(0, select.length() - 2);
|
|
}
|
|
String from = "FROM `" + getTable() + "-kills` ";
|
|
|
|
if (needPlayer || needKiller || killers.size() > 0)
|
|
from += "INNER JOIN `lb-players` as killers ON (killer=killers.playerid) ";
|
|
|
|
if (needPlayer || needVictim || victims.size() > 0)
|
|
from += "INNER JOIN `lb-players` as victims ON (victim=victims.playerid) ";
|
|
|
|
return select + " " + from + getWhere() + "ORDER BY date " + order + ", id " + order + " " + getLimit();
|
|
} else if (sum == SummarizationMode.PLAYERS)
|
|
return "SELECT playername, SUM(kills) AS kills, SUM(killed) AS killed FROM ((SELECT killer AS playerid, count(*) AS kills, 0 as killed FROM `" + getTable() + "-kills` INNER JOIN `lb-players` as killers ON (killer=killers.playerid) INNER JOIN `lb-players` as victims ON (victim=victims.playerid) " + getWhere(BlockChangeType.KILLS) + "GROUP BY killer) UNION (SELECT victim AS playerid, 0 as kills, count(*) AS killed FROM `" + getTable() + "-kills` INNER JOIN `lb-players` as killers ON (killer=killers.playerid) INNER JOIN `lb-players` as victims ON (victim=victims.playerid) " + getWhere(BlockChangeType.KILLS) + "GROUP BY victim)) AS t INNER JOIN `lb-players` USING (playerid) GROUP BY playerid ORDER BY SUM(kills) + SUM(killed) " + order + " " + getLimit();
|
|
}
|
|
if (sum == SummarizationMode.NONE) {
|
|
String select = "SELECT ";
|
|
if (needCount)
|
|
select += "COUNT(*) AS count";
|
|
else {
|
|
if (needId)
|
|
select += "`" + getTable() + "`.id, ";
|
|
if (needDate)
|
|
select += "date, ";
|
|
if (needType)
|
|
select += "replaced, type, ";
|
|
if (needData)
|
|
select += "data, ";
|
|
if (needPlayer)
|
|
select += "playername, ";
|
|
if (needCoords)
|
|
select += "x, y, z, ";
|
|
if (needSignText)
|
|
select += "signtext, ";
|
|
if (needChestAccess)
|
|
select += "itemtype, itemamount, itemdata, ";
|
|
select = select.substring(0, select.length() - 2);
|
|
}
|
|
String from = "FROM `" + getTable() + "` ";
|
|
if (needPlayer || players.size() > 0)
|
|
from += "INNER JOIN `lb-players` USING (playerid) ";
|
|
if (needSignText)
|
|
from += "LEFT JOIN `" + getTable() + "-sign` USING (id) ";
|
|
if (needChestAccess)
|
|
// If BlockChangeType is CHESTACCESS, we can use more efficient query
|
|
if (bct == BlockChangeType.CHESTACCESS) {
|
|
from += "RIGHT JOIN `" + getTable() + "-chest` USING (id) ";
|
|
} else {
|
|
from += "LEFT JOIN `" + getTable() + "-chest` USING (id) ";
|
|
}
|
|
return select + " " + from + getWhere() + "ORDER BY date " + order + ", id " + order + " " + getLimit();
|
|
} else if (sum == SummarizationMode.TYPES)
|
|
return "SELECT type, SUM(created) AS created, SUM(destroyed) AS destroyed FROM ((SELECT type, count(*) AS created, 0 AS destroyed FROM `" + getTable() + "` INNER JOIN `lb-players` USING (playerid) " + getWhere(BlockChangeType.CREATED) + "GROUP BY type) UNION (SELECT replaced AS type, 0 AS created, count(*) AS destroyed FROM `" + getTable() + "` INNER JOIN `lb-players` USING (playerid) " + getWhere(BlockChangeType.DESTROYED) + "GROUP BY replaced)) AS t GROUP BY type ORDER BY SUM(created) + SUM(destroyed) " + order + " " + getLimit();
|
|
else
|
|
return "SELECT playername, SUM(created) AS created, SUM(destroyed) AS destroyed FROM ((SELECT playerid, count(*) AS created, 0 AS destroyed FROM `" + getTable() + "` " + getWhere(BlockChangeType.CREATED) + "GROUP BY playerid) UNION (SELECT playerid, 0 AS created, count(*) AS destroyed FROM `" + getTable() + "` " + getWhere(BlockChangeType.DESTROYED) + "GROUP BY playerid)) AS t INNER JOIN `lb-players` USING (playerid) GROUP BY playerid ORDER BY SUM(created) + SUM(destroyed) " + order + " " + getLimit();
|
|
}
|
|
|
|
public String getTable() {
|
|
return getWorldConfig(world).table;
|
|
}
|
|
|
|
public String getTitle() {
|
|
final StringBuilder title = new StringBuilder();
|
|
if (bct == BlockChangeType.CHESTACCESS)
|
|
title.append("chest accesses ");
|
|
else if (bct == BlockChangeType.CHAT)
|
|
title.append("chat messages ");
|
|
else if (bct == BlockChangeType.KILLS)
|
|
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());
|
|
title.append(listing(blocknames, ", ", " and ")).append(" ");
|
|
} else
|
|
title.append("block ");
|
|
if (bct == BlockChangeType.CREATED)
|
|
title.append("creations ");
|
|
else if (bct == BlockChangeType.DESTROYED)
|
|
title.append("destructions ");
|
|
else
|
|
title.append("changes ");
|
|
}
|
|
if (killers.size() > 10)
|
|
title.append(excludeKillersMode ? "without" : "from").append(" many killers ");
|
|
else if (!killers.isEmpty())
|
|
title.append(excludeKillersMode ? "without" : "from").append(" ").append(listing(killers.toArray(new String[killers.size()]), ", ", " and ")).append(" ");
|
|
if (victims.size() > 10)
|
|
title.append(excludeVictimsMode ? "without" : "of").append(" many victims ");
|
|
else if (!victims.isEmpty())
|
|
title.append(excludeVictimsMode ? "without" : "of").append(" victim").append(victims.size() != 1 ? "s" : "").append(" ").append(listing(victims.toArray(new String[victims.size()]), ", ", " and ")).append(" ");
|
|
if (players.size() > 10)
|
|
title.append(excludePlayersMode ? "without" : "from").append(" many players ");
|
|
else if (!players.isEmpty())
|
|
title.append(excludePlayersMode ? "without" : "from").append(" player").append(players.size() != 1 ? "s" : "").append(" ").append(listing(players.toArray(new String[players.size()]), ", ", " and ")).append(" ");
|
|
if (match != null && match.length() > 0)
|
|
title.append("matching '").append(match).append("' ");
|
|
if (before > 0 && since > 0)
|
|
title.append("between ").append(since).append(" and ").append(before).append(" minutes ago ");
|
|
else if (since > 0)
|
|
title.append("in the last ").append(since).append(" minutes ");
|
|
else if (before > 0)
|
|
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" : "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)
|
|
title.append(prepareToolQuery ? "at double chest " : "inside selection ");
|
|
else if (prepareToolQuery)
|
|
if (radius > 0)
|
|
title.append("within ").append(radius).append(" blocks of clicked block ");
|
|
else if (radius == 0)
|
|
title.append("at clicked block ");
|
|
if (world != null && !(sel != null && prepareToolQuery))
|
|
title.append("in ").append(friendlyWorldname(world.getName())).append(" ");
|
|
if (sum != SummarizationMode.NONE)
|
|
title.append("summed up by ").append(sum == SummarizationMode.TYPES ? "blocks" : "players").append(" ");
|
|
title.deleteCharAt(title.length() - 1);
|
|
title.setCharAt(0, String.valueOf(title.charAt(0)).toUpperCase().toCharArray()[0]);
|
|
return title.toString();
|
|
}
|
|
|
|
public String getWhere() {
|
|
return getWhere(bct);
|
|
}
|
|
|
|
public String getWhere(BlockChangeType blockChangeType) {
|
|
final StringBuilder where = new StringBuilder("WHERE ");
|
|
if (blockChangeType == BlockChangeType.CHAT) {
|
|
if (match != null && match.length() > 0) {
|
|
final boolean unlike = match.startsWith("-");
|
|
if (match.length() > 3 && !unlike || match.length() > 4)
|
|
where.append("MATCH (message) AGAINST ('").append(match).append("' IN BOOLEAN MODE) AND ");
|
|
else
|
|
where.append("message ").append(unlike ? "NOT " : "").append("LIKE '%").append(unlike ? match.substring(1) : match).append("%' AND ");
|
|
}
|
|
} else if (blockChangeType == BlockChangeType.KILLS) {
|
|
if (!players.isEmpty())
|
|
if (!excludePlayersMode) {
|
|
where.append('(');
|
|
for (final String killerName : players)
|
|
where.append("killers.playername = '").append(killerName).append("' OR ");
|
|
for (final String victimName : players)
|
|
where.append("victims.playername = '").append(victimName).append("' OR ");
|
|
where.delete(where.length() - 4, where.length());
|
|
where.append(") AND ");
|
|
} else {
|
|
for (final String killerName : players)
|
|
where.append("killers.playername != '").append(killerName).append("' AND ");
|
|
for (final String victimName : players)
|
|
where.append("victims.playername != '").append(victimName).append("' AND ");
|
|
}
|
|
|
|
if (!killers.isEmpty())
|
|
if (!excludeKillersMode) {
|
|
where.append('(');
|
|
for (final String killerName : killers)
|
|
where.append("killers.playername = '").append(killerName).append("' OR ");
|
|
where.delete(where.length() - 4, where.length());
|
|
where.append(") AND ");
|
|
} else
|
|
for (final String killerName : killers)
|
|
where.append("killers.playername != '").append(killerName).append("' AND ");
|
|
|
|
if (!victims.isEmpty())
|
|
if (!excludeVictimsMode) {
|
|
where.append('(');
|
|
for (final String victimName : victims)
|
|
where.append("victims.playername = '").append(victimName).append("' OR ");
|
|
where.delete(where.length() - 4, where.length());
|
|
where.append(") AND ");
|
|
} else
|
|
for (final String victimName : victims)
|
|
where.append("victims.playername != '").append(victimName).append("' AND ");
|
|
|
|
if (loc != null) {
|
|
if (radius == 0)
|
|
compileLocationQuery(
|
|
where,
|
|
loc.getBlockX(), loc.getBlockX(),
|
|
loc.getBlockY(), loc.getBlockY(),
|
|
loc.getBlockZ(), loc.getBlockZ()
|
|
);
|
|
|
|
else if (radius > 0)
|
|
compileLocationQuery(
|
|
where,
|
|
loc.getBlockX() - radius + 1, loc.getBlockX() + radius - 1,
|
|
loc.getBlockY() - radius + 1, loc.getBlockY() + radius - 1,
|
|
loc.getBlockZ() - radius + 1, loc.getBlockZ() + radius - 1
|
|
);
|
|
|
|
} else if (sel != null)
|
|
compileLocationQuery(
|
|
where,
|
|
sel.getSelection().getMinimumPoint().getBlockX(), sel.getSelection().getMaximumPoint().getBlockX(),
|
|
sel.getSelection().getMinimumPoint().getBlockY(), sel.getSelection().getMaximumPoint().getBlockY(),
|
|
sel.getSelection().getMinimumPoint().getBlockZ(), sel.getSelection().getMaximumPoint().getBlockZ()
|
|
);
|
|
|
|
} else {
|
|
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());
|
|
if (block.getData() != -1) {
|
|
where.append(") AND data = ").append(block.getData());
|
|
} else {
|
|
where.append(")");
|
|
}
|
|
where.append(") OR ");
|
|
}
|
|
where.delete(where.length() - 4, where.length() - 1);
|
|
where.append(") AND ");
|
|
}
|
|
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());
|
|
if (block.getData() != -1) {
|
|
where.append(") AND data = ").append(block.getData());
|
|
} else {
|
|
where.append(")");
|
|
}
|
|
where.append(") OR ");
|
|
}
|
|
where.delete(where.length() - 4, where.length());
|
|
where.append(") AND ");
|
|
}
|
|
where.append("type != replaced AND ");
|
|
break;
|
|
case CREATED:
|
|
if (!types.isEmpty()) {
|
|
if (excludeBlocksMode)
|
|
where.append("NOT ");
|
|
where.append('(');
|
|
for (final Block block : types) {
|
|
where.append("((type = ").append(block.getBlock());
|
|
if (block.getData() != -1) {
|
|
where.append(") AND data = ").append(block.getData());
|
|
} else {
|
|
where.append(")");
|
|
}
|
|
where.append(") OR ");
|
|
}
|
|
where.delete(where.length() - 4, where.length());
|
|
where.append(") 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());
|
|
if (block.getData() != -1) {
|
|
where.append(") AND data = ").append(block.getData());
|
|
} else {
|
|
where.append(")");
|
|
}
|
|
where.append(") OR ");
|
|
}
|
|
where.delete(where.length() - 4, where.length());
|
|
where.append(") 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());
|
|
if (block.getData() != -1) {
|
|
where.append(") AND itemdata = ").append(block.getData());
|
|
} else {
|
|
where.append(")");
|
|
}
|
|
where.append(") OR ");
|
|
}
|
|
where.delete(where.length() - 4, where.length());
|
|
where.append(") AND ");
|
|
}
|
|
break;
|
|
}
|
|
if (loc != null) {
|
|
if (radius == 0)
|
|
compileLocationQuery(
|
|
where,
|
|
loc.getBlockX(), loc.getBlockX(),
|
|
loc.getBlockY(), loc.getBlockY(),
|
|
loc.getBlockZ(), loc.getBlockZ()
|
|
);
|
|
|
|
else if (radius > 0)
|
|
compileLocationQuery(
|
|
where,
|
|
loc.getBlockX() - radius + 1, loc.getBlockX() + radius - 1,
|
|
loc.getBlockY() - radius + 1, loc.getBlockY() + radius - 1,
|
|
loc.getBlockZ() - radius + 1, loc.getBlockZ() + radius - 1
|
|
);
|
|
|
|
} else if (sel != null)
|
|
compileLocationQuery(
|
|
where,
|
|
sel.getSelection().getMinimumPoint().getBlockX(), sel.getSelection().getMaximumPoint().getBlockX(),
|
|
sel.getSelection().getMinimumPoint().getBlockY(), sel.getSelection().getMaximumPoint().getBlockY(),
|
|
sel.getSelection().getMinimumPoint().getBlockZ(), sel.getSelection().getMaximumPoint().getBlockZ()
|
|
);
|
|
|
|
}
|
|
if (!players.isEmpty() && sum != SummarizationMode.PLAYERS && blockChangeType != BlockChangeType.KILLS)
|
|
if (!excludePlayersMode) {
|
|
where.append('(');
|
|
for (final String playerName : players)
|
|
where.append("playername = '").append(playerName).append("' OR ");
|
|
where.delete(where.length() - 4, where.length());
|
|
where.append(") AND ");
|
|
} else
|
|
for (final String playerName : players)
|
|
where.append("playername != '").append(playerName).append("' AND ");
|
|
if (since > 0)
|
|
where.append("date > date_sub(now(), INTERVAL ").append(since).append(" MINUTE) AND ");
|
|
if (before > 0)
|
|
where.append("date < date_sub(now(), INTERVAL ").append(before).append(" MINUTE) AND ");
|
|
if (where.length() > 6)
|
|
where.delete(where.length() - 4, where.length());
|
|
else
|
|
where.delete(0, where.length());
|
|
return where.toString();
|
|
}
|
|
|
|
private void compileLocationQuery(StringBuilder where, int blockX, int blockX2, int blockY, int blockY2, int blockZ, int blockZ2) {
|
|
compileLocationQueryPart(where, "x", blockX, blockX2);
|
|
where.append(" AND ");
|
|
compileLocationQueryPart(where, "y", blockY, blockY2);
|
|
where.append(" AND ");
|
|
compileLocationQueryPart(where, "z", blockZ, blockZ2);
|
|
where.append(" AND ");
|
|
}
|
|
|
|
private void compileLocationQueryPart(StringBuilder where, String locValue, int loc, int loc2) {
|
|
int min = Math.min(loc, loc2);
|
|
int max = Math.max(loc2, loc);
|
|
|
|
if (min == max)
|
|
where.append(locValue).append(" = ").append(min);
|
|
else if (max - min > 50)
|
|
where.append(locValue).append(" >= ").append(min).append(" AND ").append(locValue).append(" <= ").append(max);
|
|
else {
|
|
where.append(locValue).append(" in (");
|
|
for (int c = min; c < max; c++) {
|
|
where.append(c).append(",");
|
|
}
|
|
where.append(max);
|
|
where.append(")");
|
|
}
|
|
}
|
|
|
|
public void parseArgs(CommandSender sender, List<String> args) throws IllegalArgumentException {
|
|
if (args == null || args.isEmpty())
|
|
throw new IllegalArgumentException("No parameters specified.");
|
|
final Player player = sender instanceof Player ? (Player)sender : null;
|
|
final Session session = prepareToolQuery ? null : getSession(sender);
|
|
if (player != null && world == null)
|
|
world = player.getWorld();
|
|
for (int i = 0; i < args.size(); i++) {
|
|
final String param = args.get(i).toLowerCase();
|
|
final String[] values = getValues(args, i + 1);
|
|
if (param.equals("last")) {
|
|
if (session.lastQuery == null)
|
|
throw new IllegalArgumentException("This is your first command, you can't use last.");
|
|
merge(session.lastQuery);
|
|
} else if (param.equals("player")) {
|
|
if (values.length < 1)
|
|
throw new IllegalArgumentException("No or wrong count of arguments for '" + param + "'");
|
|
for (final String playerName : values)
|
|
if (playerName.length() > 0) {
|
|
if (playerName.contains("!"))
|
|
excludePlayersMode = true;
|
|
if (playerName.contains("\""))
|
|
players.add(playerName.replaceAll("[^a-zA-Z0-9_]", ""));
|
|
else {
|
|
final List<Player> matches = logblock.getServer().matchPlayer(playerName);
|
|
if (matches.size() > 1)
|
|
throw new IllegalArgumentException("Ambiguous playername '" + param + "'");
|
|
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 + "'");
|
|
for (final String killerName : values)
|
|
if (killerName.length() > 0) {
|
|
if (killerName.contains("!"))
|
|
excludeVictimsMode = true;
|
|
if (killerName.contains("\""))
|
|
killers.add(killerName.replaceAll("[^a-zA-Z0-9_]", ""));
|
|
else {
|
|
final List<Player> matches = logblock.getServer().matchPlayer(killerName);
|
|
if (matches.size() > 1)
|
|
throw new IllegalArgumentException("Ambiguous victimname '" + param + "'");
|
|
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 + "'");
|
|
for (final String victimName : values)
|
|
if (victimName.length() > 0) {
|
|
if (victimName.contains("!"))
|
|
excludeVictimsMode = true;
|
|
if (victimName.contains("\""))
|
|
victims.add(victimName.replaceAll("[^a-zA-Z0-9_]", ""));
|
|
else {
|
|
final List<Player> matches = logblock.getServer().matchPlayer(victimName);
|
|
if (matches.size() > 1)
|
|
throw new IllegalArgumentException("Ambiguous victimname '" + param + "'");
|
|
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 + "'");
|
|
for (final String weaponName : values) {
|
|
Material mat = Material.matchMaterial(weaponName);
|
|
if (mat == null)
|
|
try {
|
|
mat = Material.getMaterial(Integer.parseInt(weaponName));
|
|
} catch (NumberFormatException e) {
|
|
throw new IllegalArgumentException("Data type not a valid number: '" + weaponName + "'");
|
|
}
|
|
if (mat == null)
|
|
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 (String blockName : values) {
|
|
if (blockName.startsWith("!")) {
|
|
excludeBlocksMode = true;
|
|
blockName = blockName.substring(1);
|
|
}
|
|
if (blockName.contains(":")) {
|
|
String[] blockNameSplit = blockName.split(":");
|
|
if (blockNameSplit.length > 2)
|
|
throw new IllegalArgumentException("No material matching: '" + blockName + "'");
|
|
final int data;
|
|
try {
|
|
data = Integer.parseInt(blockNameSplit[1]);
|
|
} catch (NumberFormatException e) {
|
|
throw new IllegalArgumentException("Data type not a valid number: '" + blockNameSplit[1] + "'");
|
|
}
|
|
if (data > 255 || data < 0)
|
|
throw new IllegalArgumentException("Data type out of range (0-255): '" + data + "'");
|
|
final Material mat = Material.matchMaterial(blockNameSplit[0]);
|
|
if (mat == null)
|
|
throw new IllegalArgumentException("No material matching: '" + blockName + "'");
|
|
types.add(new Block(mat.getId(), data));
|
|
} else {
|
|
final Material mat = Material.matchMaterial(blockName);
|
|
if (mat == null)
|
|
throw new IllegalArgumentException("No material matching: '" + blockName + "'");
|
|
types.add(new Block(mat.getId(), -1));
|
|
}
|
|
}
|
|
} else if (param.equals("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 && 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 && loc == null)
|
|
loc = player.getLocation();
|
|
}
|
|
} else if (param.equals("selection") || param.equals("sel")) {
|
|
if (player == null)
|
|
throw new IllegalArgumentException("You have to ba a player to use selection");
|
|
final Plugin we = player.getServer().getPluginManager().getPlugin("WorldEdit");
|
|
if (we != null) {
|
|
setSelection(RegionContainer.fromPlayerSelection(player, we));
|
|
} else {
|
|
throw new IllegalArgumentException("WorldEdit not found!");
|
|
}
|
|
} else if (param.equals("time") || param.equals("since")) {
|
|
since = values.length > 0 ? parseTimeSpec(values) : defaultTime;
|
|
if (since == -1)
|
|
throw new IllegalArgumentException("Failed to parse time spec for '" + param + "'");
|
|
} else if (param.equals("before")) {
|
|
before = values.length > 0 ? parseTimeSpec(values) : defaultTime;
|
|
if (before == -1)
|
|
throw new IllegalArgumentException("Faile to parse time spec for '" + param + "'");
|
|
} else if (param.equals("sum")) {
|
|
if (values.length != 1)
|
|
throw new IllegalArgumentException("No or wrong count of arguments for '" + param + "'");
|
|
if (values[0].startsWith("p"))
|
|
sum = SummarizationMode.PLAYERS;
|
|
else if (values[0].startsWith("b"))
|
|
sum = SummarizationMode.TYPES;
|
|
else if (values[0].startsWith("n"))
|
|
sum = SummarizationMode.NONE;
|
|
else
|
|
throw new IllegalArgumentException("Wrong summarization mode");
|
|
} else if (param.equals("created"))
|
|
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"))
|
|
bct = BlockChangeType.CHAT;
|
|
else if (param.equals("kills")) {
|
|
bct = BlockChangeType.KILLS;
|
|
}
|
|
else if (param.equals("all"))
|
|
bct = BlockChangeType.ALL;
|
|
else if (param.equals("limit")) {
|
|
if (values.length != 1)
|
|
throw new IllegalArgumentException("Wrong count of arguments for '" + param + "'");
|
|
if (!isInt(values[0]))
|
|
throw new IllegalArgumentException("Not a number: '" + values[0] + "'");
|
|
limit = Integer.parseInt(values[0]);
|
|
} else if (param.equals("world")) {
|
|
if (values.length != 1)
|
|
throw new IllegalArgumentException("Wrong count of arguments for '" + param + "'");
|
|
final World w = sender.getServer().getWorld(values[0].replace("\"", ""));
|
|
if (w == null)
|
|
throw new IllegalArgumentException("There is no world called '" + values[0] + "'");
|
|
world = w;
|
|
} else if (param.equals("asc"))
|
|
order = Order.ASC;
|
|
else if (param.equals("desc"))
|
|
order = Order.DESC;
|
|
else if (param.equals("coords"))
|
|
needCoords = true;
|
|
else if (param.equals("silent"))
|
|
silent = true;
|
|
else if (param.equals("search") || param.equals("match")) {
|
|
if (values.length == 0)
|
|
throw new IllegalArgumentException("No arguments for '" + param + "'");
|
|
match = join(values, " ").replace("\\", "\\\\").replace("'", "\\'");
|
|
} else if (param.equals("loc") || param.equals("location")) {
|
|
final String[] vectors = values.length == 1 ? values[0].split(":") : values;
|
|
if (vectors.length != 3)
|
|
throw new IllegalArgumentException("Wrong count arguments for '" + param + "'");
|
|
for (final String vec : vectors)
|
|
if (!isInt(vec))
|
|
throw new IllegalArgumentException("Not a number: '" + vec + "'");
|
|
loc = new Location(null, Integer.valueOf(vectors[0]), Integer.valueOf(vectors[1]), Integer.valueOf(vectors[2]));
|
|
radius = 0;
|
|
} else
|
|
throw new IllegalArgumentException("Not a valid argument: '" + param + "'");
|
|
i += values.length;
|
|
}
|
|
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;
|
|
for (final Block block : types)
|
|
if (equivalent.contains(block.getBlock())) {
|
|
found = true;
|
|
break;
|
|
}
|
|
if (found)
|
|
for (final Integer type : equivalent)
|
|
if (!Block.inList(types, type))
|
|
types.add(new Block(type, -1));
|
|
}
|
|
if (!prepareToolQuery && bct != BlockChangeType.CHAT) {
|
|
if (world == null)
|
|
throw new IllegalArgumentException("No world specified");
|
|
if (!isLogged(world))
|
|
throw new IllegalArgumentException("This world ('" + world.getName() + "') isn't logged");
|
|
}
|
|
if (session != null)
|
|
session.lastQuery = clone();
|
|
}
|
|
|
|
public void setLocation(Location loc) {
|
|
this.loc = loc;
|
|
world = loc.getWorld();
|
|
}
|
|
|
|
public void setSelection(RegionContainer container) {
|
|
this.sel = container;
|
|
world = sel.getSelection().getWorld();
|
|
}
|
|
|
|
public void setPlayer(String playerName) {
|
|
players.clear();
|
|
players.add(playerName);
|
|
}
|
|
|
|
@Override
|
|
protected QueryParams clone() {
|
|
try {
|
|
final QueryParams params = (QueryParams)super.clone();
|
|
params.players = new ArrayList<String>(players);
|
|
params.types = new ArrayList<Block>(types);
|
|
return params;
|
|
} catch (final CloneNotSupportedException ex) {
|
|
}
|
|
return null;
|
|
}
|
|
|
|
private static String[] getValues(List<String> args, int offset) {
|
|
// The variable i will store the last value's index
|
|
int i;
|
|
// Iterate over the all the values from the offset up till the end
|
|
for (i = offset; i < args.size(); i++) {
|
|
// We found a keyword, break here since anything after this isn't a value.
|
|
if (isKeyWord(args.get(i))) {
|
|
break;
|
|
}
|
|
}
|
|
// If the offset equals to the last value index, return an empty string array
|
|
if (i == offset) {
|
|
return new String[0];
|
|
}
|
|
// Instantiate a new string array with the total indexes required
|
|
final List<String> values = new ArrayList<String>();
|
|
// Buffer for the value
|
|
String value = "";
|
|
// Iterate over the offset up till the last index value
|
|
for (int j = offset; j < i; j++) {
|
|
// If the value starts with a double quote or we're already dealing with a quoted value
|
|
if (args.get(j).startsWith("\"") || !value.equals("")) {
|
|
// If the value doesn't end with a double quote
|
|
if (!args.get(j).endsWith("\"")) {
|
|
// Add the argument to the value buffer after stripping out the initial quote
|
|
|
|
// If the argument starts with a quote we wanna strip that out otherwise add it normally
|
|
if (args.get(j).startsWith("\"")) {
|
|
value += args.get(j).substring(1) + " ";
|
|
} else {
|
|
value += args.get(j) + " ";
|
|
}
|
|
|
|
} else {
|
|
// The value ends with a double quote
|
|
|
|
// If the argument starts with a double quote we wanna strip that out too along with the end quote
|
|
if (args.get(j).startsWith("\"")) {
|
|
value += args.get(j).substring(0, args.get(j).length() - 1).substring(1);
|
|
} else {
|
|
// Looks like its just the end quote here, just need to strip that out
|
|
value += args.get(j).substring(0, args.get(j).length() - 1);
|
|
}
|
|
// Add the value to the main values list
|
|
values.add(value);
|
|
// Reset the buffer
|
|
value = "";
|
|
}
|
|
} else {
|
|
// Set the value in the array to be returned to the one from the main arguments list
|
|
values.add(args.get(j));
|
|
}
|
|
}
|
|
// Return the values array
|
|
return values.toArray(new String[values.size()]);
|
|
}
|
|
|
|
public void merge(QueryParams p) {
|
|
players = p.players;
|
|
excludePlayersMode = p.excludePlayersMode;
|
|
types = p.types;
|
|
loc = p.loc;
|
|
radius = p.radius;
|
|
sel = p.sel;
|
|
if (p.since != 0 || since != defaultTime)
|
|
since = p.since;
|
|
before = p.before;
|
|
sum = p.sum;
|
|
bct = p.bct;
|
|
limit = p.limit;
|
|
world = p.world;
|
|
order = p.order;
|
|
match = p.match;
|
|
}
|
|
|
|
public static enum BlockChangeType
|
|
{
|
|
ALL, BOTH, CHESTACCESS, CREATED, DESTROYED, CHAT, KILLS
|
|
}
|
|
|
|
public static enum Order
|
|
{
|
|
ASC, DESC
|
|
}
|
|
|
|
public static enum SummarizationMode
|
|
{
|
|
NONE, PLAYERS, TYPES
|
|
}
|
|
}
|