forked from LogBlock/LogBlock
Mavenize this project so we can use Jenkins and easily enable developers to compile from source.
This commit is contained in:
@@ -0,0 +1,29 @@
|
||||
package de.diddiz.LogBlock;
|
||||
|
||||
import static de.diddiz.LogBlock.config.Config.autoClearLog;
|
||||
import static org.bukkit.Bukkit.getConsoleSender;
|
||||
import static org.bukkit.Bukkit.getLogger;
|
||||
import static org.bukkit.Bukkit.getServer;
|
||||
import java.util.Arrays;
|
||||
import java.util.logging.Level;
|
||||
|
||||
public class AutoClearLog implements Runnable
|
||||
{
|
||||
private final LogBlock logblock;
|
||||
|
||||
AutoClearLog(LogBlock logblock) {
|
||||
this.logblock = logblock;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
final CommandsHandler handler = logblock.getCommandsHandler();
|
||||
for (final String paramStr : autoClearLog)
|
||||
try {
|
||||
final QueryParams params = new QueryParams(logblock, getConsoleSender(), Arrays.asList(paramStr.split(" ")));
|
||||
handler.new CommandClearLog(getServer().getConsoleSender(), params, false);
|
||||
} catch (final Exception ex) {
|
||||
getLogger().log(Level.SEVERE, "Failed to schedule auto ClearLog: ", ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
package de.diddiz.LogBlock;
|
||||
|
||||
import static de.diddiz.util.MaterialName.materialName;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.text.SimpleDateFormat;
|
||||
import org.bukkit.Location;
|
||||
|
||||
public class BlockChange implements LookupCacheElement
|
||||
{
|
||||
private final static SimpleDateFormat formatter = new SimpleDateFormat("MM-dd HH:mm:ss");
|
||||
public final long id, date;
|
||||
public final Location loc;
|
||||
public final String playerName;
|
||||
public final int replaced, type;
|
||||
public final byte data;
|
||||
public final String signtext;
|
||||
public final ChestAccess ca;
|
||||
|
||||
public BlockChange(long date, Location loc, String playerName, int replaced, int type, byte data, String signtext, ChestAccess ca) {
|
||||
id = 0;
|
||||
this.date = date;
|
||||
this.loc = loc;
|
||||
this.playerName = playerName;
|
||||
this.replaced = replaced;
|
||||
this.type = type;
|
||||
this.data = data;
|
||||
this.signtext = signtext;
|
||||
this.ca = ca;
|
||||
}
|
||||
|
||||
public BlockChange(ResultSet rs, QueryParams p) throws SQLException {
|
||||
id = p.needId ? rs.getInt("id") : 0;
|
||||
date = p.needDate ? rs.getTimestamp("date").getTime() : 0;
|
||||
loc = p.needCoords ? new Location(p.world, rs.getInt("x"), rs.getInt("y"), rs.getInt("z")) : null;
|
||||
playerName = p.needPlayer ? rs.getString("playername") : null;
|
||||
replaced = p.needType ? rs.getInt("replaced") : 0;
|
||||
type = p.needType ? rs.getInt("type") : 0;
|
||||
data = p.needData ? rs.getByte("data") : (byte)0;
|
||||
signtext = p.needSignText ? rs.getString("signtext") : null;
|
||||
ca = p.needChestAccess && rs.getShort("itemtype") != 0 && rs.getShort("itemamount") != 0 ? new ChestAccess(rs.getShort("itemtype"), rs.getShort("itemamount"), rs.getByte("itemdata")) : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
final StringBuilder msg = new StringBuilder();
|
||||
if (date > 0)
|
||||
msg.append(formatter.format(date) + " ");
|
||||
if (playerName != null)
|
||||
msg.append(playerName + " ");
|
||||
if (signtext != null) {
|
||||
final String action = type == 0 ? "destroyed " : "created ";
|
||||
if (!signtext.contains("\0"))
|
||||
msg.append(action + signtext);
|
||||
else
|
||||
msg.append(action + materialName(type != 0 ? type : replaced) + " [" + signtext.replace("\0", "] [") + "]");
|
||||
} else if (type == replaced) {
|
||||
if (type == 0)
|
||||
msg.append("did an unspecified action");
|
||||
else if (ca != null) {
|
||||
if (ca.itemType == 0 || ca.itemAmount == 0)
|
||||
msg.append("looked inside " + materialName(type));
|
||||
else if (ca.itemAmount < 0)
|
||||
msg.append("took " + -ca.itemAmount + "x " + materialName(ca.itemType, ca.itemData));
|
||||
else
|
||||
msg.append("put in " + ca.itemAmount + "x " + materialName(ca.itemType, ca.itemData));
|
||||
} else if (type == 23 || type == 54 || type == 61 || type == 62)
|
||||
msg.append("opened " + materialName(type));
|
||||
else if (type == 64 || type == 71 || type == 96 || type == 107)
|
||||
msg.append((data == 0 ? "opened" : "closed") + " " + materialName(type));
|
||||
else if (type == 69)
|
||||
msg.append("switched " + materialName(type));
|
||||
else if (type == 77)
|
||||
msg.append("pressed " + materialName(type));
|
||||
else if (type == 92)
|
||||
msg.append("ate a piece of " + materialName(type));
|
||||
else if (type == 25 || type == 93 || type == 94)
|
||||
msg.append("changed " + materialName(type));
|
||||
} else if (type == 0)
|
||||
msg.append("destroyed " + materialName(replaced, data));
|
||||
else if (replaced == 0)
|
||||
msg.append("created " + materialName(type, data));
|
||||
else
|
||||
msg.append("replaced " + materialName(replaced, (byte)0) + " with " + materialName(type, data));
|
||||
if (loc != null)
|
||||
msg.append(" at " + loc.getBlockX() + ":" + loc.getBlockY() + ":" + loc.getBlockZ());
|
||||
return msg.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Location getLocation() {
|
||||
return loc;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getMessage() {
|
||||
return toString();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
package de.diddiz.LogBlock;
|
||||
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import org.bukkit.Location;
|
||||
|
||||
public class ChatMessage implements LookupCacheElement
|
||||
{
|
||||
final long id, date;
|
||||
final String playerName, message;
|
||||
|
||||
public ChatMessage(String playerName, String message) {
|
||||
id = 0;
|
||||
date = System.currentTimeMillis() / 1000;
|
||||
this.playerName = playerName;
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
public ChatMessage(ResultSet rs, QueryParams p) throws SQLException {
|
||||
id = p.needId ? rs.getInt("id") : 0;
|
||||
date = p.needDate ? rs.getTimestamp("date").getTime() : 0;
|
||||
playerName = p.needPlayer ? rs.getString("playername") : null;
|
||||
message = p.needMessage ? rs.getString("message") : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Location getLocation() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getMessage() {
|
||||
return (playerName != null ? "<" + playerName + "> " : "") + (message != null ? message : "");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package de.diddiz.LogBlock;
|
||||
|
||||
public class ChestAccess
|
||||
{
|
||||
final short itemType, itemAmount;
|
||||
final byte itemData;
|
||||
|
||||
public ChestAccess(short itemType, short itemAmount, byte itemData) {
|
||||
this.itemType = itemType;
|
||||
this.itemAmount = itemAmount;
|
||||
this.itemData = itemData >= 0 ? itemData : 0;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,762 @@
|
||||
package de.diddiz.LogBlock;
|
||||
|
||||
import static de.diddiz.LogBlock.Session.getSession;
|
||||
import static de.diddiz.LogBlock.config.Config.askClearLogAfterRollback;
|
||||
import static de.diddiz.LogBlock.config.Config.askClearLogs;
|
||||
import static de.diddiz.LogBlock.config.Config.askRedos;
|
||||
import static de.diddiz.LogBlock.config.Config.askRollbacks;
|
||||
import static de.diddiz.LogBlock.config.Config.defaultTime;
|
||||
import static de.diddiz.LogBlock.config.Config.dumpDeletedLog;
|
||||
import static de.diddiz.LogBlock.config.Config.getWorldConfig;
|
||||
import static de.diddiz.LogBlock.config.Config.linesLimit;
|
||||
import static de.diddiz.LogBlock.config.Config.linesPerPage;
|
||||
import static de.diddiz.LogBlock.config.Config.rollbackMaxArea;
|
||||
import static de.diddiz.LogBlock.config.Config.rollbackMaxTime;
|
||||
import static de.diddiz.LogBlock.config.Config.toolsByName;
|
||||
import static de.diddiz.LogBlock.config.Config.toolsByType;
|
||||
import static de.diddiz.util.BukkitUtils.giveTool;
|
||||
import static de.diddiz.util.BukkitUtils.saveSpawnHeight;
|
||||
import static de.diddiz.util.Utils.isInt;
|
||||
import static de.diddiz.util.Utils.listing;
|
||||
import static org.bukkit.Bukkit.getLogger;
|
||||
import static org.bukkit.Bukkit.getServer;
|
||||
import java.io.Closeable;
|
||||
import java.io.File;
|
||||
import java.io.FileWriter;
|
||||
import java.sql.Connection;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.Statement;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.logging.Level;
|
||||
import org.bukkit.ChatColor;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.command.Command;
|
||||
import org.bukkit.command.CommandExecutor;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.bukkit.scheduler.BukkitScheduler;
|
||||
import de.diddiz.LogBlock.QueryParams.BlockChangeType;
|
||||
import de.diddiz.LogBlock.QueryParams.Order;
|
||||
import de.diddiz.LogBlock.QueryParams.SummarizationMode;
|
||||
import de.diddiz.LogBlock.config.Config;
|
||||
import de.diddiz.LogBlock.config.WorldConfig;
|
||||
import de.diddiz.LogBlockQuestioner.LogBlockQuestioner;
|
||||
import de.diddiz.util.Block;
|
||||
|
||||
public class CommandsHandler implements CommandExecutor
|
||||
{
|
||||
private final LogBlock logblock;
|
||||
private final BukkitScheduler scheduler;
|
||||
private final LogBlockQuestioner questioner;
|
||||
|
||||
CommandsHandler(LogBlock logblock) {
|
||||
this.logblock = logblock;
|
||||
scheduler = logblock.getServer().getScheduler();
|
||||
questioner = (LogBlockQuestioner)logblock.getServer().getPluginManager().getPlugin("LogBlockQuestioner");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCommand(CommandSender sender, Command cmd, String commandLabel, String[] args) {
|
||||
try {
|
||||
if (args.length == 0) {
|
||||
sender.sendMessage(ChatColor.LIGHT_PURPLE + "LogBlock v" + logblock.getDescription().getVersion() + " by DiddiZ");
|
||||
sender.sendMessage(ChatColor.LIGHT_PURPLE + "Type /lb help for help");
|
||||
} else {
|
||||
final String command = args[0].toLowerCase();
|
||||
if (command.equals("help")) {
|
||||
sender.sendMessage(ChatColor.DARK_AQUA + "LogBlock Help:");
|
||||
sender.sendMessage(ChatColor.GOLD + "For the commands list type '/lb commands'");
|
||||
sender.sendMessage(ChatColor.GOLD + "For the parameters list type '/lb params'");
|
||||
sender.sendMessage(ChatColor.GOLD + "For the list of permissions you got type '/lb permissions'");
|
||||
} else if (command.equals("commands")) {
|
||||
sender.sendMessage(ChatColor.DARK_AQUA + "LogBlock Commands:");
|
||||
sender.sendMessage(ChatColor.GOLD + "/lb tool -- Gives you the lb tool");
|
||||
sender.sendMessage(ChatColor.GOLD + "/lb tool [on|off] -- Enables/Disables tool");
|
||||
sender.sendMessage(ChatColor.GOLD + "/lb tool [params] -- Sets the tool lookup query");
|
||||
sender.sendMessage(ChatColor.GOLD + "/lb tool default -- Sets the tool lookup query to default");
|
||||
sender.sendMessage(ChatColor.GOLD + "/lb toolblock -- Analog to tool");
|
||||
sender.sendMessage(ChatColor.GOLD + "/lb hide -- Hides you from log");
|
||||
sender.sendMessage(ChatColor.GOLD + "/lb rollback [params] -- Rollback");
|
||||
sender.sendMessage(ChatColor.GOLD + "/lb redo [params] -- Redo");
|
||||
sender.sendMessage(ChatColor.GOLD + "/lb tp [params] -- Teleports you to the location of griefing");
|
||||
sender.sendMessage(ChatColor.GOLD + "/lb writelogfile [params] -- Writes a log file");
|
||||
sender.sendMessage(ChatColor.GOLD + "/lb lookup [params] -- Lookup");
|
||||
sender.sendMessage(ChatColor.GOLD + "/lb prev|next -- Browse lookup result pages");
|
||||
sender.sendMessage(ChatColor.GOLD + "/lb page -- Shows a specific lookup result page");
|
||||
sender.sendMessage(ChatColor.GOLD + "/lb me -- Displays your stats");
|
||||
sender.sendMessage(ChatColor.GOLD + "Look at github.com/DiddiZ/LogBlock/wiki/Commands for the full commands reference");
|
||||
} else if (command.equals("params")) {
|
||||
sender.sendMessage(ChatColor.DARK_AQUA + "LogBlock Query Parameters:");
|
||||
sender.sendMessage(ChatColor.GOLD + "Use doublequotes to escape a keyword: world \"world\"");
|
||||
sender.sendMessage(ChatColor.GOLD + "player [name1] <name2> <name3> -- List of players");
|
||||
sender.sendMessage(ChatColor.GOLD + "block [type1] <type2> <type3> -- List of block types");
|
||||
sender.sendMessage(ChatColor.GOLD + "created, destroyed -- Show only created/destroyed blocks");
|
||||
sender.sendMessage(ChatColor.GOLD + "chestaccess -- Show only chest accesses");
|
||||
sender.sendMessage(ChatColor.GOLD + "area <radius> -- Area around you");
|
||||
sender.sendMessage(ChatColor.GOLD + "selection, sel -- Inside current WorldEdit selection");
|
||||
sender.sendMessage(ChatColor.GOLD + "world [worldname] -- Changes the world");
|
||||
sender.sendMessage(ChatColor.GOLD + "time [number] [minutes|hours|days] -- Limits time");
|
||||
sender.sendMessage(ChatColor.GOLD + "since <dd.MM.yyyy> <HH:mm:ss> -- Limits time to a fixed point");
|
||||
sender.sendMessage(ChatColor.GOLD + "before <dd.MM.yyyy> <HH:mm:ss> -- Affects only blocks before a fixed time");
|
||||
sender.sendMessage(ChatColor.GOLD + "limit <row count> -- Limits the result to count of rows");
|
||||
sender.sendMessage(ChatColor.GOLD + "sum [none|blocks|players] -- Sums the result");
|
||||
sender.sendMessage(ChatColor.GOLD + "asc, desc -- Changes the order of the displayed log");
|
||||
sender.sendMessage(ChatColor.GOLD + "coords -- Shows coordinates for each block");
|
||||
sender.sendMessage(ChatColor.GOLD + "silent -- Displays lesser messages");
|
||||
} else if (command.equals("permissions")) {
|
||||
sender.sendMessage(ChatColor.DARK_AQUA + "You've got the following permissions:");
|
||||
for (final String permission : new String[]{"me", "lookup", "tp", "rollback", "clearlog", "hide", "ignoreRestrictions", "spawnTools"})
|
||||
if (logblock.hasPermission(sender, "logblock." + permission))
|
||||
sender.sendMessage(ChatColor.GOLD + "logblock." + permission);
|
||||
for (final Tool tool : toolsByType.values())
|
||||
if (logblock.hasPermission(sender, "logblock.tools." + tool.name))
|
||||
sender.sendMessage(ChatColor.GOLD + "logblock.tools." + tool.name);
|
||||
} else if (command.equals("logging")) {
|
||||
if (logblock.hasPermission(sender, "logblock.lookup")) {
|
||||
World world = null;
|
||||
if (args.length > 1)
|
||||
world = getServer().getWorld(args[1]);
|
||||
else if (sender instanceof Player)
|
||||
world = ((Player)sender).getWorld();
|
||||
if (world != null) {
|
||||
final WorldConfig wcfg = getWorldConfig(world.getName());
|
||||
if (wcfg != null) {
|
||||
sender.sendMessage(ChatColor.DARK_AQUA + "Currently logging in " + world.getName() + ":");
|
||||
final List<String> logging = new ArrayList<String>();
|
||||
for (final Logging l : Logging.values())
|
||||
if (wcfg.isLogging(l))
|
||||
logging.add(l.toString());
|
||||
sender.sendMessage(ChatColor.GOLD + listing(logging, ", ", " and "));
|
||||
} else {
|
||||
sender.sendMessage(ChatColor.RED + "World not logged: '" + world.getName() + "'");
|
||||
sender.sendMessage(ChatColor.LIGHT_PURPLE + "Make the world name is listed at loggedWorlds in config. World names are case sensitive and must contains the path (if any), exactly like in the message above.");
|
||||
}
|
||||
} else
|
||||
sender.sendMessage(ChatColor.RED + "No world specified");
|
||||
} else
|
||||
sender.sendMessage(ChatColor.RED + "You aren't allowed to do this.");
|
||||
} else if (toolsByName.get(command) != null) {
|
||||
final Tool tool = toolsByName.get(command);
|
||||
if (logblock.hasPermission(sender, "logblock.tools." + tool.name)) {
|
||||
if (sender instanceof Player) {
|
||||
final Player player = (Player)sender;
|
||||
final Session session = Session.getSession(player.getName());
|
||||
final ToolData toolData = session.toolData.get(tool);
|
||||
if (args.length == 1) {
|
||||
if (logblock.hasPermission(player, "logblock.spawnTools")) {
|
||||
giveTool(player, tool.item);
|
||||
session.toolData.get(tool).enabled = true;
|
||||
} else
|
||||
sender.sendMessage(ChatColor.RED + "You aren't allowed to do this.");
|
||||
} else if (args[1].equalsIgnoreCase("enable") || args[1].equalsIgnoreCase("on")) {
|
||||
toolData.enabled = true;
|
||||
player.sendMessage(ChatColor.GREEN + "Tool enabled.");
|
||||
} else if (args[1].equalsIgnoreCase("disable") || args[1].equalsIgnoreCase("off")) {
|
||||
toolData.enabled = false;
|
||||
player.getInventory().removeItem(new ItemStack(tool.item, 1));
|
||||
player.sendMessage(ChatColor.GREEN + "Tool disabled.");
|
||||
} else if (args[1].equalsIgnoreCase("mode")) {
|
||||
if (args.length == 3) {
|
||||
final ToolMode mode;
|
||||
try {
|
||||
mode = ToolMode.valueOf(args[2].toUpperCase());
|
||||
} catch (final IllegalArgumentException ex) {
|
||||
sender.sendMessage(ChatColor.RED + "Can't find mode " + args[2]);
|
||||
return true;
|
||||
}
|
||||
if (logblock.hasPermission(player, mode.getPermission())) {
|
||||
toolData.mode = mode;
|
||||
sender.sendMessage(ChatColor.GREEN + "Tool mode set to " + args[2]);
|
||||
} else
|
||||
sender.sendMessage(ChatColor.RED + "You aren't allowed to use mode " + args[2]);
|
||||
} else
|
||||
player.sendMessage(ChatColor.RED + "No mode specified");
|
||||
} else if (args[1].equalsIgnoreCase("default")) {
|
||||
toolData.params = tool.params.clone();
|
||||
toolData.mode = tool.mode;
|
||||
sender.sendMessage(ChatColor.GREEN + "Tool set to default.");
|
||||
} else if (logblock.hasPermission(player, "logblock.lookup"))
|
||||
try {
|
||||
final QueryParams params = tool.params.clone();
|
||||
params.parseArgs(sender, argsToList(args, 1));
|
||||
toolData.params = params;
|
||||
sender.sendMessage(ChatColor.GREEN + "Set tool query to: " + params.getTitle());
|
||||
} catch (final Exception ex) {
|
||||
sender.sendMessage(ChatColor.RED + ex.getMessage());
|
||||
}
|
||||
else
|
||||
sender.sendMessage(ChatColor.RED + "You aren't allowed to do this.");
|
||||
} else
|
||||
sender.sendMessage(ChatColor.RED + "You have to be a player.");
|
||||
} else
|
||||
sender.sendMessage(ChatColor.RED + "You aren't allowed to do this.");
|
||||
} else if (command.equals("hide")) {
|
||||
if (sender instanceof Player) {
|
||||
if (logblock.hasPermission(sender, "logblock.hide")) {
|
||||
if (Consumer.hide((Player)sender))
|
||||
sender.sendMessage(ChatColor.GREEN + "You are now hidden and aren't logged. Type '/lb hide' again to unhide");
|
||||
else
|
||||
sender.sendMessage(ChatColor.GREEN + "You aren't hidden anylonger.");
|
||||
} else
|
||||
sender.sendMessage(ChatColor.RED + "You aren't allowed to do this.");
|
||||
} else
|
||||
sender.sendMessage(ChatColor.RED + "You have to be a player.");
|
||||
} else if (command.equals("page")) {
|
||||
if (args.length == 2 && isInt(args[1]))
|
||||
showPage(sender, Integer.valueOf(args[1]));
|
||||
else
|
||||
sender.sendMessage(ChatColor.RED + "You have to specify a page");
|
||||
} else if (command.equals("next") || command.equals("+"))
|
||||
showPage(sender, getSession(sender).page + 1);
|
||||
else if (command.equals("prev") || command.equals("-"))
|
||||
showPage(sender, getSession(sender).page - 1);
|
||||
else if (args[0].equalsIgnoreCase("savequeue")) {
|
||||
if (logblock.hasPermission(sender, "logblock.rollback"))
|
||||
new CommandSaveQueue(sender, null, true);
|
||||
else
|
||||
sender.sendMessage(ChatColor.RED + "You aren't allowed to do this.");
|
||||
} else if (args[0].equalsIgnoreCase("queuesize")) {
|
||||
if (logblock.hasPermission(sender, "logblock.rollback"))
|
||||
sender.sendMessage("Current queue size: " + logblock.getConsumer().getQueueSize());
|
||||
else
|
||||
sender.sendMessage(ChatColor.RED + "You aren't allowed to do this.");
|
||||
} else if (command.equals("rollback") || command.equals("undo") || command.equals("rb")) {
|
||||
if (logblock.hasPermission(sender, "logblock.rollback")) {
|
||||
final QueryParams params = new QueryParams(logblock);
|
||||
params.since = defaultTime;
|
||||
params.bct = BlockChangeType.ALL;
|
||||
params.parseArgs(sender, argsToList(args, 1));
|
||||
new CommandRollback(sender, params, true);
|
||||
} else
|
||||
sender.sendMessage(ChatColor.RED + "You aren't allowed to do this.");
|
||||
} else if (command.equals("redo")) {
|
||||
if (logblock.hasPermission(sender, "logblock.rollback")) {
|
||||
final QueryParams params = new QueryParams(logblock);
|
||||
params.since = defaultTime;
|
||||
params.bct = BlockChangeType.ALL;
|
||||
params.parseArgs(sender, argsToList(args, 1));
|
||||
new CommandRedo(sender, params, true);
|
||||
} else
|
||||
sender.sendMessage(ChatColor.RED + "You aren't allowed to do this.");
|
||||
} else if (command.equals("me")) {
|
||||
if (sender instanceof Player) {
|
||||
if (logblock.hasPermission(sender, "logblock.me")) {
|
||||
final Player player = (Player)sender;
|
||||
if (Config.isLogged(player.getWorld())) {
|
||||
final QueryParams params = new QueryParams(logblock);
|
||||
params.setPlayer(player.getName());
|
||||
params.world = player.getWorld();
|
||||
player.sendMessage("Total block changes: " + logblock.getCount(params));
|
||||
params.sum = SummarizationMode.TYPES;
|
||||
new CommandLookup(sender, params, true);
|
||||
} else
|
||||
sender.sendMessage(ChatColor.RED + "This world isn't logged");
|
||||
} else
|
||||
sender.sendMessage(ChatColor.RED + "You aren't allowed to do this.");
|
||||
} else
|
||||
sender.sendMessage(ChatColor.RED + "You have to be a player.");
|
||||
} else if (command.equals("writelogfile")) {
|
||||
if (logblock.hasPermission(sender, "logblock.rollback")) {
|
||||
final QueryParams params = new QueryParams(logblock);
|
||||
params.limit = -1;
|
||||
params.bct = BlockChangeType.ALL;
|
||||
params.parseArgs(sender, argsToList(args, 1));
|
||||
new CommandWriteLogFile(sender, params, true);
|
||||
} else
|
||||
sender.sendMessage(ChatColor.RED + "You aren't allowed to do this");
|
||||
} else if (command.equals("clearlog")) {
|
||||
if (logblock.hasPermission(sender, "logblock.clearlog")) {
|
||||
final QueryParams params = new QueryParams(logblock, sender, argsToList(args, 1));
|
||||
params.bct = BlockChangeType.ALL;
|
||||
params.limit = -1;
|
||||
new CommandClearLog(sender, params, true);
|
||||
} else
|
||||
sender.sendMessage(ChatColor.RED + "You aren't allowed to do this");
|
||||
} else if (command.equals("tp")) {
|
||||
if (sender instanceof Player) {
|
||||
if (logblock.hasPermission(sender, "logblock.tp"))
|
||||
if (args.length == 2 || isInt(args[1])) {
|
||||
final int pos = Integer.parseInt(args[1]) - 1;
|
||||
final Player player = (Player)sender;
|
||||
final Session session = getSession(player);
|
||||
if (session.lookupCache != null)
|
||||
if (pos >= 0 && pos < session.lookupCache.length) {
|
||||
final Location loc = session.lookupCache[pos].getLocation();
|
||||
if (loc != null) {
|
||||
player.teleport(new Location(loc.getWorld(), loc.getX() + 0.5, saveSpawnHeight(loc), loc.getZ() + 0.5, player.getLocation().getYaw(), 90));
|
||||
player.sendMessage(ChatColor.LIGHT_PURPLE + "Teleported to " + loc.getBlockX() + ":" + loc.getBlockY() + ":" + loc.getBlockZ());
|
||||
} else
|
||||
sender.sendMessage(ChatColor.RED + "There is no location associated with that. Did you forget coords parameter?");
|
||||
} else
|
||||
sender.sendMessage(ChatColor.RED + "'" + args[1] + " is out of range");
|
||||
else
|
||||
sender.sendMessage(ChatColor.RED + "You havn't done a lookup yet");
|
||||
} else
|
||||
new CommandTeleport(sender, new QueryParams(logblock, sender, argsToList(args, 1)), true);
|
||||
else
|
||||
sender.sendMessage(ChatColor.RED + "You aren't allowed to do this");
|
||||
} else
|
||||
sender.sendMessage(ChatColor.RED + "You have to be a player.");
|
||||
} else if (command.equals("lookup") || QueryParams.isKeyWord(args[0])) {
|
||||
if (logblock.hasPermission(sender, "logblock.lookup")) {
|
||||
final List<String> argsList = new ArrayList<String>(Arrays.asList(args));
|
||||
if (command.equals("lookup"))
|
||||
argsList.remove(0);
|
||||
new CommandLookup(sender, new QueryParams(logblock, sender, argsList), true);
|
||||
} else
|
||||
sender.sendMessage(ChatColor.RED + "You aren't allowed to do this");
|
||||
} else
|
||||
sender.sendMessage(ChatColor.RED + "Unknown command '" + args[0] + "'");
|
||||
}
|
||||
} catch (final IllegalArgumentException ex) {
|
||||
sender.sendMessage(ChatColor.RED + ex.getMessage());
|
||||
} catch (final Exception ex) {
|
||||
sender.sendMessage(ChatColor.RED + "Error, check server.log");
|
||||
getLogger().log(Level.WARNING, "[LogBlock] Exception in commands handler: ", ex);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private static void showPage(CommandSender sender, int page) {
|
||||
final Session session = getSession(sender);
|
||||
if (session.lookupCache != null && session.lookupCache.length > 0) {
|
||||
final int startpos = (page - 1) * linesPerPage;
|
||||
if (page > 0 && startpos <= session.lookupCache.length - 1) {
|
||||
final int stoppos = startpos + linesPerPage >= session.lookupCache.length ? session.lookupCache.length - 1 : startpos + linesPerPage - 1;
|
||||
final int numberOfPages = (int)Math.ceil(session.lookupCache.length / (double)linesPerPage);
|
||||
if (numberOfPages != 1)
|
||||
sender.sendMessage(ChatColor.DARK_AQUA + "Page " + page + "/" + numberOfPages);
|
||||
for (int i = startpos; i <= stoppos; i++)
|
||||
sender.sendMessage(ChatColor.GOLD + (session.lookupCache[i].getLocation() != null ? "(" + (i + 1) + ") " : "") + session.lookupCache[i].getMessage());
|
||||
session.page = page;
|
||||
} else
|
||||
sender.sendMessage(ChatColor.RED + "There isn't a page '" + page + "'");
|
||||
} else
|
||||
sender.sendMessage(ChatColor.RED + "No blocks in lookup cache");
|
||||
}
|
||||
|
||||
private boolean checkRestrictions(CommandSender sender, QueryParams params) {
|
||||
if (sender.isOp() || logblock.hasPermission(sender, "logblock.ignoreRestrictions"))
|
||||
return true;
|
||||
if (rollbackMaxTime > 0 && (params.before > 0 || params.since > rollbackMaxTime)) {
|
||||
sender.sendMessage(ChatColor.RED + "You are not allowed to rollback more than " + rollbackMaxTime + " minutes");
|
||||
return false;
|
||||
}
|
||||
if (rollbackMaxArea > 0 && (params.sel == null && params.loc == null || params.radius > rollbackMaxArea || params.sel != null && (params.sel.getLength() > rollbackMaxArea || params.sel.getWidth() > rollbackMaxArea))) {
|
||||
sender.sendMessage(ChatColor.RED + "You are not allowed to rollback an area larger than " + rollbackMaxArea + " blocks");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public abstract class AbstractCommand implements Runnable, Closeable
|
||||
{
|
||||
protected CommandSender sender;
|
||||
protected QueryParams params;
|
||||
protected Connection conn = null;
|
||||
protected Statement state = null;
|
||||
protected ResultSet rs = null;
|
||||
|
||||
protected AbstractCommand(CommandSender sender, QueryParams params, boolean async) throws Exception {
|
||||
this.sender = sender;
|
||||
this.params = params;
|
||||
if (async) {
|
||||
if (scheduler.scheduleAsyncDelayedTask(logblock, this) == -1)
|
||||
throw new Exception("Failed to schedule the command");
|
||||
} else
|
||||
run();
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void close() {
|
||||
try {
|
||||
if (conn != null)
|
||||
conn.close();
|
||||
if (state != null)
|
||||
state.close();
|
||||
if (rs != null)
|
||||
rs.close();
|
||||
} catch (final SQLException ex) {
|
||||
getLogger().log(Level.SEVERE, "[LogBlock CommandsHandler] SQL exception on close", ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class CommandLookup extends AbstractCommand
|
||||
{
|
||||
public CommandLookup(CommandSender sender, QueryParams params, boolean async) throws Exception {
|
||||
super(sender, params, async);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
if (params.bct == BlockChangeType.CHAT) {
|
||||
params.needDate = true;
|
||||
params.needPlayer = true;
|
||||
params.needMessage = true;
|
||||
} else {
|
||||
params.needDate = true;
|
||||
params.needType = true;
|
||||
params.needData = true;
|
||||
params.needPlayer = true;
|
||||
if (params.types.size() == 0 || Block.inList(params.types, 63) || Block.inList(params.types, 68))
|
||||
params.needSignText = true;
|
||||
if (params.bct == BlockChangeType.CHESTACCESS || params.types.size() == 0 || Block.inList(params.types, 23) || Block.inList(params.types, 54) || Block.inList(params.types, 61) || Block.inList(params.types, 62))
|
||||
params.needChestAccess = true;
|
||||
}
|
||||
conn = logblock.getConnection();
|
||||
if (conn == null) {
|
||||
sender.sendMessage(ChatColor.RED + "MySQL connection lost");
|
||||
return;
|
||||
}
|
||||
state = conn.createStatement();
|
||||
rs = state.executeQuery(params.getQuery());
|
||||
sender.sendMessage(ChatColor.DARK_AQUA + params.getTitle() + ":");
|
||||
if (rs.next()) {
|
||||
rs.beforeFirst();
|
||||
final List<LookupCacheElement> blockchanges = new ArrayList<LookupCacheElement>();
|
||||
final LookupCacheElementFactory factory = new LookupCacheElementFactory(params, sender instanceof Player ? 2 / 3f : 1);
|
||||
while (rs.next())
|
||||
blockchanges.add(factory.getLookupCacheElement(rs));
|
||||
getSession(sender).lookupCache = blockchanges.toArray(new LookupCacheElement[blockchanges.size()]);
|
||||
if (blockchanges.size() > linesPerPage)
|
||||
sender.sendMessage(ChatColor.DARK_AQUA.toString() + blockchanges.size() + " changes found." + (blockchanges.size() == linesLimit ? " Use 'limit -1' to see all changes." : ""));
|
||||
if (params.sum != SummarizationMode.NONE)
|
||||
sender.sendMessage(ChatColor.GOLD + "Created - Destroyed - " + (params.sum == SummarizationMode.TYPES ? "Block" : "Player"));
|
||||
showPage(sender, 1);
|
||||
} else {
|
||||
sender.sendMessage(ChatColor.DARK_AQUA + "No results found.");
|
||||
getSession(sender).lookupCache = null;
|
||||
}
|
||||
} catch (final Exception ex) {
|
||||
sender.sendMessage(ChatColor.RED + "Exception, check error log");
|
||||
getLogger().log(Level.SEVERE, "[LogBlock Lookup] " + params.getQuery() + ": ", ex);
|
||||
} finally {
|
||||
close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class CommandWriteLogFile extends AbstractCommand
|
||||
{
|
||||
public CommandWriteLogFile(CommandSender sender, QueryParams params, boolean async) throws Exception {
|
||||
super(sender, params, async);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
File file = null;
|
||||
try {
|
||||
if (params.bct == BlockChangeType.CHAT) {
|
||||
params.needDate = true;
|
||||
params.needPlayer = true;
|
||||
params.needMessage = true;
|
||||
} else {
|
||||
params.needDate = true;
|
||||
params.needType = true;
|
||||
params.needData = true;
|
||||
params.needPlayer = true;
|
||||
if (params.types.size() == 0 || Block.inList(params.types, 63) || Block.inList(params.types, 68))
|
||||
params.needSignText = true;
|
||||
if (params.types.size() == 0 || Block.inList(params.types, 23) || Block.inList(params.types, 54) || Block.inList(params.types, 61) || Block.inList(params.types, 62))
|
||||
params.needChestAccess = true;
|
||||
}
|
||||
conn = logblock.getConnection();
|
||||
if (conn == null) {
|
||||
sender.sendMessage(ChatColor.RED + "MySQL connection lost");
|
||||
return;
|
||||
}
|
||||
state = conn.createStatement();
|
||||
file = new File("plugins/LogBlock/log/" + params.getTitle().replace(":", ".") + ".log");
|
||||
sender.sendMessage(ChatColor.GREEN + "Creating " + file.getName());
|
||||
rs = state.executeQuery(params.getQuery());
|
||||
file.getParentFile().mkdirs();
|
||||
file.createNewFile();
|
||||
final FileWriter writer = new FileWriter(file);
|
||||
final String newline = System.getProperty("line.separator");
|
||||
file.getParentFile().mkdirs();
|
||||
int counter = 0;
|
||||
if (params.sum != SummarizationMode.NONE)
|
||||
writer.write("Created - Destroyed - " + (params.sum == SummarizationMode.TYPES ? "Block" : "Player") + newline);
|
||||
final LookupCacheElementFactory factory = new LookupCacheElementFactory(params, sender instanceof Player ? 2 / 3f : 1);
|
||||
while (rs.next()) {
|
||||
writer.write(factory.getLookupCacheElement(rs).getMessage() + newline);
|
||||
counter++;
|
||||
}
|
||||
writer.close();
|
||||
sender.sendMessage(ChatColor.GREEN + "Wrote " + counter + " lines.");
|
||||
} catch (final Exception ex) {
|
||||
sender.sendMessage(ChatColor.RED + "Exception, check error log");
|
||||
getLogger().log(Level.SEVERE, "[LogBlock WriteLogFile] " + params.getQuery() + " (file was " + file.getAbsolutePath() + "): ", ex);
|
||||
} finally {
|
||||
close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class CommandSaveQueue extends AbstractCommand
|
||||
{
|
||||
public CommandSaveQueue(CommandSender sender, QueryParams params, boolean async) throws Exception {
|
||||
super(sender, params, async);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
final Consumer consumer = logblock.getConsumer();
|
||||
if (consumer.getQueueSize() > 0) {
|
||||
sender.sendMessage(ChatColor.DARK_AQUA + "Current queue size: " + consumer.getQueueSize());
|
||||
int lastSize = -1, fails = 0;
|
||||
while (consumer.getQueueSize() > 0) {
|
||||
fails = lastSize == consumer.getQueueSize() ? fails + 1 : 0;
|
||||
if (fails > 10) {
|
||||
sender.sendMessage(ChatColor.RED + "Unable to save queue");
|
||||
return;
|
||||
}
|
||||
lastSize = consumer.getQueueSize();
|
||||
consumer.run();
|
||||
}
|
||||
sender.sendMessage(ChatColor.GREEN + "Queue saved successfully");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class CommandTeleport extends AbstractCommand
|
||||
{
|
||||
public CommandTeleport(CommandSender sender, QueryParams params, boolean async) throws Exception {
|
||||
super(sender, params, async);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
params.needCoords = true;
|
||||
params.limit = 1;
|
||||
params.sum = SummarizationMode.NONE;
|
||||
conn = logblock.getConnection();
|
||||
if (conn == null) {
|
||||
sender.sendMessage(ChatColor.RED + "MySQL connection lost");
|
||||
return;
|
||||
}
|
||||
state = conn.createStatement();
|
||||
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 y2 = saveSpawnHeight(loc);
|
||||
loc.setY(y2);
|
||||
player.teleport(loc);
|
||||
sender.sendMessage(ChatColor.GREEN + "You were teleported " + Math.abs(y2 - y) + " blocks " + (y2 - y > 0 ? "above" : "below"));
|
||||
} else
|
||||
sender.sendMessage(ChatColor.RED + "No block change found to teleport to");
|
||||
} catch (final Exception ex) {
|
||||
sender.sendMessage(ChatColor.RED + "Exception, check error log");
|
||||
getLogger().log(Level.SEVERE, "[LogBlock Teleport] " + params.getQuery() + ": ", ex);
|
||||
} finally {
|
||||
close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class CommandRollback extends AbstractCommand
|
||||
{
|
||||
public CommandRollback(CommandSender sender, QueryParams params, boolean async) throws Exception {
|
||||
super(sender, params, async);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
params.needCoords = true;
|
||||
params.needType = true;
|
||||
params.needData = true;
|
||||
params.needSignText = true;
|
||||
params.needChestAccess = true;
|
||||
params.order = Order.DESC;
|
||||
params.sum = SummarizationMode.NONE;
|
||||
conn = logblock.getConnection();
|
||||
if (conn == null) {
|
||||
sender.sendMessage(ChatColor.RED + "MySQL connection lost");
|
||||
return;
|
||||
}
|
||||
state = conn.createStatement();
|
||||
if (!checkRestrictions(sender, params))
|
||||
return;
|
||||
if (logblock.getConsumer().getQueueSize() > 0)
|
||||
new CommandSaveQueue(sender, null, false);
|
||||
if (!params.silent)
|
||||
sender.sendMessage(ChatColor.DARK_AQUA + "Searching " + params.getTitle() + ":");
|
||||
rs = state.executeQuery(params.getQuery());
|
||||
final WorldEditor editor = new WorldEditor(logblock, params.world);
|
||||
while (rs.next())
|
||||
editor.queueEdit(rs.getInt("x"), rs.getInt("y"), rs.getInt("z"), rs.getInt("replaced"), rs.getInt("type"), rs.getByte("data"), rs.getString("signtext"), rs.getShort("itemtype"), rs.getShort("itemamount"), rs.getByte("itemdata"));
|
||||
final int changes = editor.getSize();
|
||||
if (!params.silent)
|
||||
sender.sendMessage(ChatColor.GREEN.toString() + changes + " blocks found.");
|
||||
if (changes == 0) {
|
||||
if (!params.silent)
|
||||
sender.sendMessage(ChatColor.RED + "Rollback aborted");
|
||||
return;
|
||||
}
|
||||
if (!params.silent && askRollbacks && questioner != null && sender instanceof Player && !questioner.ask((Player)sender, "Are you sure you want to continue?", "yes", "no").equals("yes")) {
|
||||
sender.sendMessage(ChatColor.RED + "Rollback aborted");
|
||||
return;
|
||||
}
|
||||
editor.start();
|
||||
getSession(sender).lookupCache = editor.errors;
|
||||
sender.sendMessage(ChatColor.GREEN + "Rollback finished successfully (" + editor.getElapsedTime() + " ms, " + editor.getSuccesses() + "/" + changes + " blocks" + (editor.getErrors() > 0 ? ", " + ChatColor.RED + editor.getErrors() + " errors" + ChatColor.GREEN : "") + (editor.getBlacklistCollisions() > 0 ? ", " + editor.getBlacklistCollisions() + " blacklist collisions" : "") + ")");
|
||||
if (!params.silent && askClearLogAfterRollback && logblock.hasPermission(sender, "logblock.clearlog") && questioner != null && sender instanceof Player) {
|
||||
Thread.sleep(1000);
|
||||
if (questioner.ask((Player)sender, "Do you want to delete the rollbacked log?", "yes", "no").equals("yes")) {
|
||||
params.silent = true;
|
||||
new CommandClearLog(sender, params, false);
|
||||
} else
|
||||
sender.sendMessage(ChatColor.LIGHT_PURPLE + "Clearlog cancelled");
|
||||
}
|
||||
} catch (final Exception ex) {
|
||||
sender.sendMessage(ChatColor.RED + "Exception, check error log");
|
||||
getLogger().log(Level.SEVERE, "[LogBlock Rollback] " + params.getQuery() + ": ", ex);
|
||||
} finally {
|
||||
close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class CommandRedo extends AbstractCommand
|
||||
{
|
||||
public CommandRedo(CommandSender sender, QueryParams params, boolean async) throws Exception {
|
||||
super(sender, params, async);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
params.needCoords = true;
|
||||
params.needType = true;
|
||||
params.needData = true;
|
||||
params.needSignText = true;
|
||||
params.needChestAccess = true;
|
||||
params.order = Order.ASC;
|
||||
params.sum = SummarizationMode.NONE;
|
||||
conn = logblock.getConnection();
|
||||
if (conn == null) {
|
||||
sender.sendMessage(ChatColor.RED + "MySQL connection lost");
|
||||
return;
|
||||
}
|
||||
state = conn.createStatement();
|
||||
if (!checkRestrictions(sender, params))
|
||||
return;
|
||||
rs = state.executeQuery(params.getQuery());
|
||||
if (!params.silent)
|
||||
sender.sendMessage(ChatColor.DARK_AQUA + "Searching " + params.getTitle() + ":");
|
||||
final WorldEditor editor = new WorldEditor(logblock, params.world);
|
||||
while (rs.next())
|
||||
editor.queueEdit(rs.getInt("x"), rs.getInt("y"), rs.getInt("z"), rs.getInt("type"), rs.getInt("replaced"), rs.getByte("data"), rs.getString("signtext"), rs.getShort("itemtype"), (short)-rs.getShort("itemamount"), rs.getByte("itemdata"));
|
||||
final int changes = editor.getSize();
|
||||
if (!params.silent)
|
||||
sender.sendMessage(ChatColor.GREEN.toString() + changes + " blocks found.");
|
||||
if (changes == 0) {
|
||||
if (!params.silent)
|
||||
sender.sendMessage(ChatColor.RED + "Redo aborted");
|
||||
return;
|
||||
}
|
||||
if (!params.silent && askRedos && questioner != null && sender instanceof Player && !questioner.ask((Player)sender, "Are you sure you want to continue?", "yes", "no").equals("yes")) {
|
||||
sender.sendMessage(ChatColor.RED + "Redo aborted");
|
||||
return;
|
||||
}
|
||||
editor.start();
|
||||
sender.sendMessage(ChatColor.GREEN + "Redo finished successfully (" + editor.getElapsedTime() + " ms, " + editor.getSuccesses() + "/" + changes + " blocks" + (editor.getErrors() > 0 ? ", " + ChatColor.RED + editor.getErrors() + " errors" + ChatColor.GREEN : "") + (editor.getBlacklistCollisions() > 0 ? ", " + editor.getBlacklistCollisions() + " blacklist collisions" : "") + ")");
|
||||
} catch (final Exception ex) {
|
||||
sender.sendMessage(ChatColor.RED + "Exception, check error log");
|
||||
getLogger().log(Level.SEVERE, "[LogBlock Redo] " + params.getQuery() + ": ", ex);
|
||||
} finally {
|
||||
close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class CommandClearLog extends AbstractCommand
|
||||
{
|
||||
public CommandClearLog(CommandSender sender, QueryParams params, boolean async) throws Exception {
|
||||
super(sender, params, async);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
conn = logblock.getConnection();
|
||||
state = conn.createStatement();
|
||||
if (conn == null) {
|
||||
sender.sendMessage(ChatColor.RED + "MySQL connection lost");
|
||||
return;
|
||||
}
|
||||
if (!checkRestrictions(sender, params))
|
||||
return;
|
||||
final File dumpFolder = new File(logblock.getDataFolder(), "dump");
|
||||
if (!dumpFolder.exists())
|
||||
dumpFolder.mkdirs();
|
||||
final String time = new SimpleDateFormat("yyMMddHHmmss").format(System.currentTimeMillis());
|
||||
int deleted;
|
||||
final String table = params.getTable();
|
||||
final String join = params.players.size() > 0 ? "INNER JOIN `lb-players` USING (playerid) " : "";
|
||||
rs = state.executeQuery("SELECT count(*) FROM `" + table + "` " + join + params.getWhere());
|
||||
rs.next();
|
||||
if ((deleted = rs.getInt(1)) > 0) {
|
||||
if (!params.silent && askClearLogs && sender instanceof Player && questioner != null) {
|
||||
sender.sendMessage(ChatColor.DARK_AQUA + "Searching " + params.getTitle() + ":");
|
||||
sender.sendMessage(ChatColor.GREEN.toString() + deleted + " blocks found.");
|
||||
if (!questioner.ask((Player)sender, "Are you sure you want to continue?", "yes", "no").equals("yes")) {
|
||||
sender.sendMessage(ChatColor.RED + "ClearLog aborted");
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (dumpDeletedLog)
|
||||
try {
|
||||
state.execute("SELECT * FROM `" + table + "` " + join + params.getWhere() + "INTO OUTFILE '" + new File(dumpFolder, time + " " + table + " " + params.getTitle().replace(":", ".") + ".csv").getAbsolutePath().replace("\\", "\\\\") + "' FIELDS TERMINATED BY ',' OPTIONALLY ENCLOSED BY '\"' LINES TERMINATED BY '\n'");
|
||||
} catch (final SQLException ex) {
|
||||
sender.sendMessage(ChatColor.RED + "Error while dumping log. Make sure your MySQL user has access to the LogBlock folder, or disable clearlog.dumpDeletedLog");
|
||||
getLogger().log(Level.SEVERE, "[LogBlock ClearLog] Exception while dumping log: ", ex);
|
||||
return;
|
||||
}
|
||||
state.execute("DELETE `" + table + "` FROM `" + table + "` " + 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 + "` USING (id) WHERE `" + table + "`.id IS NULL");
|
||||
rs.next();
|
||||
if ((deleted = rs.getInt(1)) > 0) {
|
||||
if (dumpDeletedLog)
|
||||
state.execute("SELECT id, signtext FROM `" + table + "-sign` LEFT JOIN `" + table + "` USING (id) WHERE `" + table + "`.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("DELETE `" + table + "-sign` FROM `" + table + "-sign` LEFT JOIN `" + table + "` USING (id) WHERE `" + table + "`.id IS NULL;");
|
||||
sender.sendMessage(ChatColor.GREEN + "Cleared out table " + table + "-sign. Deleted " + deleted + " entries.");
|
||||
}
|
||||
rs = state.executeQuery("SELECT COUNT(*) FROM `" + table + "-chest` LEFT JOIN `" + table + "` USING (id) WHERE `" + table + "`.id IS NULL");
|
||||
rs.next();
|
||||
if ((deleted = rs.getInt(1)) > 0) {
|
||||
if (dumpDeletedLog)
|
||||
state.execute("SELECT id, itemtype, itemamount, itemdata FROM `" + table + "-chest` LEFT JOIN `" + table + "` USING (id) WHERE `" + table + "`.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 + "` USING (id) WHERE `" + table + "`.id IS NULL;");
|
||||
sender.sendMessage(ChatColor.GREEN + "Cleared out table " + table + "-chest. Deleted " + deleted + " entries.");
|
||||
}
|
||||
} catch (final Exception ex) {
|
||||
sender.sendMessage(ChatColor.RED + "Exception, check error log");
|
||||
getLogger().log(Level.SEVERE, "[LogBlock ClearLog] Exception: ", ex);
|
||||
} finally {
|
||||
close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static List<String> argsToList(String[] arr, int offset) {
|
||||
final List<String> list = new ArrayList<String>(Arrays.asList(arr));
|
||||
for (int i = 0; i < offset; i++)
|
||||
list.remove(0);
|
||||
return list;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,498 @@
|
||||
package de.diddiz.LogBlock;
|
||||
|
||||
import static de.diddiz.LogBlock.config.Config.forceToProcessAtLeast;
|
||||
import static de.diddiz.LogBlock.config.Config.getWorldConfig;
|
||||
import static de.diddiz.LogBlock.config.Config.hiddenBlocks;
|
||||
import static de.diddiz.LogBlock.config.Config.hiddenPlayers;
|
||||
import static de.diddiz.LogBlock.config.Config.isLogged;
|
||||
import static de.diddiz.LogBlock.config.Config.timePerRun;
|
||||
import static de.diddiz.util.BukkitUtils.compressInventory;
|
||||
import static de.diddiz.util.BukkitUtils.entityName;
|
||||
import static de.diddiz.util.BukkitUtils.rawData;
|
||||
import static org.bukkit.Bukkit.getLogger;
|
||||
import java.io.File;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.PrintWriter;
|
||||
import java.sql.Connection;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.Statement;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Queue;
|
||||
import java.util.Set;
|
||||
import java.util.TimerTask;
|
||||
import java.util.concurrent.LinkedBlockingQueue;
|
||||
import java.util.concurrent.locks.Lock;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
import java.util.logging.Level;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.block.BlockState;
|
||||
import org.bukkit.block.Sign;
|
||||
import org.bukkit.entity.Entity;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.inventory.Inventory;
|
||||
import org.bukkit.inventory.InventoryHolder;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
|
||||
public class Consumer extends TimerTask
|
||||
{
|
||||
private final Queue<Row> queue = new LinkedBlockingQueue<Row>();
|
||||
private final Set<String> failedPlayers = new HashSet<String>();
|
||||
private final LogBlock logblock;
|
||||
private final Map<String, Integer> playerIds = new HashMap<String, Integer>();
|
||||
private final Lock lock = new ReentrantLock();
|
||||
|
||||
Consumer(LogBlock logblock) {
|
||||
this.logblock = logblock;
|
||||
try {
|
||||
Class.forName("PlayerLeaveRow");
|
||||
} catch (final ClassNotFoundException ex) {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Logs any block change. Don't try to combine broken and placed blocks. Queue two block changes or use the queueBLockReplace methods.
|
||||
*/
|
||||
public void queueBlock(String playerName, Location loc, int typeBefore, int typeAfter, byte data) {
|
||||
queueBlock(playerName, loc, typeBefore, typeAfter, data, null, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Logs a block break. The type afterwards is assumed to be o (air).
|
||||
*
|
||||
* @param before
|
||||
* Blockstate of the block before actually being destroyed.
|
||||
*/
|
||||
public void queueBlockBreak(String playerName, BlockState before) {
|
||||
queueBlockBreak(playerName, new Location(before.getWorld(), before.getX(), before.getY(), before.getZ()), before.getTypeId(), before.getRawData());
|
||||
}
|
||||
|
||||
/**
|
||||
* Logs a block break. The block type afterwards is assumed to be o (air).
|
||||
*/
|
||||
public void queueBlockBreak(String playerName, Location loc, int typeBefore, byte dataBefore) {
|
||||
queueBlock(playerName, loc, typeBefore, 0, dataBefore);
|
||||
}
|
||||
|
||||
/**
|
||||
* Logs a block place. The block type before is assumed to be o (air).
|
||||
*
|
||||
* @param after
|
||||
* Blockstate of the block after actually being placed.
|
||||
*/
|
||||
public void queueBlockPlace(String playerName, BlockState after) {
|
||||
queueBlockPlace(playerName, new Location(after.getWorld(), after.getX(), after.getY(), after.getZ()), after.getTypeId(), after.getRawData());
|
||||
}
|
||||
|
||||
/**
|
||||
* Logs a block place. The block type before is assumed to be o (air).
|
||||
*/
|
||||
public void queueBlockPlace(String playerName, Location loc, int type, byte data) {
|
||||
queueBlock(playerName, loc, 0, type, data);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param before
|
||||
* Blockstate of the block before actually being destroyed.
|
||||
* @param after
|
||||
* Blockstate of the block after actually being placed.
|
||||
*/
|
||||
public void queueBlockReplace(String playerName, BlockState before, BlockState after) {
|
||||
queueBlockReplace(playerName, new Location(before.getWorld(), before.getX(), before.getY(), before.getZ()), before.getTypeId(), before.getRawData(), after.getTypeId(), after.getRawData());
|
||||
}
|
||||
|
||||
/**
|
||||
* @param before
|
||||
* Blockstate of the block before actually being destroyed.
|
||||
*/
|
||||
public void queueBlockReplace(String playerName, BlockState before, int typeAfter, byte dataAfter) {
|
||||
queueBlockReplace(playerName, new Location(before.getWorld(), before.getX(), before.getY(), before.getZ()), before.getTypeId(), before.getRawData(), typeAfter, dataAfter);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param after
|
||||
* Blockstate of the block after actually being placed.
|
||||
*/
|
||||
public void queueBlockReplace(String playerName, int typeBefore, byte dataBefore, BlockState after) {
|
||||
queueBlockReplace(playerName, new Location(after.getWorld(), after.getX(), after.getY(), after.getZ()), typeBefore, dataBefore, after.getTypeId(), after.getRawData());
|
||||
}
|
||||
|
||||
public void queueBlockReplace(String playerName, Location loc, int typeBefore, byte dataBefore, int typeAfter, byte dataAfter) {
|
||||
if (dataBefore == 0 && (typeBefore != typeAfter))
|
||||
queueBlock(playerName, loc, typeBefore, typeAfter, dataAfter);
|
||||
else {
|
||||
queueBlockBreak(playerName, loc, typeBefore, dataBefore);
|
||||
queueBlockPlace(playerName, loc, typeAfter, dataAfter);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param container
|
||||
* The respective container. Must be an instance of Chest, Dispencer or Furnace.
|
||||
*/
|
||||
public void queueChestAccess(String playerName, BlockState container, short itemType, short itemAmount, byte itemData) {
|
||||
if (!(container instanceof InventoryHolder))
|
||||
return;
|
||||
queueChestAccess(playerName, new Location(container.getWorld(), container.getX(), container.getY(), container.getZ()), container.getTypeId(), itemType, itemAmount, itemData);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param type
|
||||
* Type id of the container. Must be 63 or 68.
|
||||
*/
|
||||
public void queueChestAccess(String playerName, Location loc, int type, short itemType, short itemAmount, byte itemData) {
|
||||
queueBlock(playerName, loc, type, type, (byte)0, null, new ChestAccess(itemType, itemAmount, itemData));
|
||||
}
|
||||
|
||||
/**
|
||||
* Logs a container block break. The block type before is assumed to be o (air). All content is assumed to be taken.
|
||||
*
|
||||
* @param container
|
||||
* Must be instanceof InventoryHolder
|
||||
*/
|
||||
public void queueContainerBreak(String playerName, BlockState container) {
|
||||
if (!(container instanceof InventoryHolder))
|
||||
return;
|
||||
queueContainerBreak(playerName, new Location(container.getWorld(), container.getX(), container.getY(), container.getZ()), container.getTypeId(), container.getRawData(), ((InventoryHolder)container).getInventory());
|
||||
}
|
||||
|
||||
/**
|
||||
* Logs a container block break. The block type before is assumed to be o (air). All content is assumed to be taken.
|
||||
*/
|
||||
public void queueContainerBreak(String playerName, Location loc, int type, byte data, Inventory inv) {
|
||||
final ItemStack[] items = compressInventory(inv.getContents());
|
||||
for (final ItemStack item : items)
|
||||
queueChestAccess(playerName, loc, type, (short)item.getTypeId(), (short)(item.getAmount() * -1), rawData(item));
|
||||
queueBlockBreak(playerName, loc, type, data);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param killer
|
||||
* Can' be null
|
||||
* @param victim
|
||||
* Can' be null
|
||||
*/
|
||||
public void queueKill(Entity killer, Entity victim) {
|
||||
if (killer == null || victim == null)
|
||||
return;
|
||||
int weapon = 0;
|
||||
if (killer instanceof Player && ((Player)killer).getItemInHand() != null)
|
||||
weapon = ((Player)killer).getItemInHand().getTypeId();
|
||||
queueKill(victim.getLocation(), entityName(killer), entityName(victim), weapon);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param world
|
||||
* World the victim was inside.
|
||||
* @param killerName
|
||||
* Name of the killer. Can be null.
|
||||
* @param victimName
|
||||
* Name of the victim. Can't be null.
|
||||
* @param weapon
|
||||
* Item id of the weapon. 0 for no weapon.
|
||||
* @deprecated Use {@link #queueKill(Location,String,String,int)} instead
|
||||
*/
|
||||
@Deprecated
|
||||
public void queueKill(World world, String killerName, String victimName, int weapon) {
|
||||
queueKill(new Location(world, 0, 0, 0), killerName, victimName, weapon);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param location
|
||||
* Location of the victim.
|
||||
* @param killerName
|
||||
* Name of the killer. Can be null.
|
||||
* @param victimName
|
||||
* Name of the victim. Can't be null.
|
||||
* @param weapon
|
||||
* Item id of the weapon. 0 for no weapon.
|
||||
*/
|
||||
public void queueKill(Location location, String killerName, String victimName, int weapon) {
|
||||
if (victimName == null || !isLogged(location.getWorld()))
|
||||
return;
|
||||
queue.add(new KillRow(location, killerName == null ? null : killerName.replaceAll("[^a-zA-Z0-9_]", ""), victimName.replaceAll("[^a-zA-Z0-9_]", ""), weapon));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param type
|
||||
* Type of the sign. Must be 63 or 68.
|
||||
* @param lines
|
||||
* The four lines on the sign.
|
||||
*/
|
||||
public void queueSignBreak(String playerName, Location loc, int type, byte data, String[] lines) {
|
||||
if (type != 63 && type != 68 || lines == null || lines.length != 4)
|
||||
return;
|
||||
queueBlock(playerName, loc, type, 0, data, lines[0] + "\0" + lines[1] + "\0" + lines[2] + "\0" + lines[3], null);
|
||||
}
|
||||
|
||||
public void queueSignBreak(String playerName, Sign sign) {
|
||||
queueSignBreak(playerName, new Location(sign.getWorld(), sign.getX(), sign.getY(), sign.getZ()), sign.getTypeId(), sign.getRawData(), sign.getLines());
|
||||
}
|
||||
|
||||
/**
|
||||
* @param type
|
||||
* Type of the sign. Must be 63 or 68.
|
||||
* @param lines
|
||||
* The four lines on the sign.
|
||||
*/
|
||||
public void queueSignPlace(String playerName, Location loc, int type, byte data, String[] lines) {
|
||||
if (type != 63 && type != 68 || lines == null || lines.length != 4)
|
||||
return;
|
||||
queueBlock(playerName, loc, 0, type, data, lines[0] + "\0" + lines[1] + "\0" + lines[2] + "\0" + lines[3], null);
|
||||
}
|
||||
|
||||
public void queueSignPlace(String playerName, Sign sign) {
|
||||
queueSignPlace(playerName, new Location(sign.getWorld(), sign.getX(), sign.getY(), sign.getZ()), sign.getTypeId(), sign.getRawData(), sign.getLines());
|
||||
}
|
||||
|
||||
public void queueChat(String player, String message) {
|
||||
queue.add(new ChatRow(player, message.replace("\\", "\\\\").replace("'", "\\'")));
|
||||
}
|
||||
|
||||
public void queueJoin(Player player) {
|
||||
queue.add(new PlayerJoinRow(player));
|
||||
}
|
||||
|
||||
public void queueLeave(Player player) {
|
||||
queue.add(new PlayerLeaveRow(player));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
if (queue.isEmpty() || !lock.tryLock())
|
||||
return;
|
||||
final Connection conn = logblock.getConnection();
|
||||
Statement state = null;
|
||||
if (getQueueSize() > 1000)
|
||||
getLogger().info("[LogBlock Consumer] Queue overloaded. Size: " + getQueueSize());
|
||||
try {
|
||||
if (conn == null)
|
||||
return;
|
||||
conn.setAutoCommit(false);
|
||||
state = conn.createStatement();
|
||||
final long start = System.currentTimeMillis();
|
||||
int count = 0;
|
||||
process:
|
||||
while (!queue.isEmpty() && (System.currentTimeMillis() - start < timePerRun || count < forceToProcessAtLeast)) {
|
||||
final Row r = queue.poll();
|
||||
if (r == null)
|
||||
continue;
|
||||
for (final String player : r.getPlayers())
|
||||
if (!playerIds.containsKey(player))
|
||||
if (!addPlayer(state, player)) {
|
||||
if (!failedPlayers.contains(player)) {
|
||||
failedPlayers.add(player);
|
||||
getLogger().warning("[LogBlock Consumer] Failed to add player " + player);
|
||||
}
|
||||
continue process;
|
||||
}
|
||||
for (final String insert : r.getInserts())
|
||||
try {
|
||||
state.execute(insert);
|
||||
} catch (final SQLException ex) {
|
||||
getLogger().log(Level.SEVERE, "[LogBlock Consumer] SQL exception on " + insert + ": ", ex);
|
||||
break process;
|
||||
}
|
||||
count++;
|
||||
}
|
||||
conn.commit();
|
||||
} catch (final SQLException ex) {
|
||||
getLogger().log(Level.SEVERE, "[LogBlock Consumer] SQL exception", ex);
|
||||
} finally {
|
||||
try {
|
||||
if (state != null)
|
||||
state.close();
|
||||
if (conn != null)
|
||||
conn.close();
|
||||
} catch (final SQLException ex) {
|
||||
getLogger().log(Level.SEVERE, "[LogBlock Consumer] SQL exception on close", ex);
|
||||
}
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
public void writeToFile() throws FileNotFoundException {
|
||||
final long time = System.currentTimeMillis();
|
||||
final Set<String> insertedPlayers = new HashSet<String>();
|
||||
int counter = 0;
|
||||
new File("plugins/LogBlock/import/").mkdirs();
|
||||
PrintWriter writer = new PrintWriter(new File("plugins/LogBlock/import/queue-" + time + "-0.sql"));
|
||||
while (!queue.isEmpty()) {
|
||||
final Row r = queue.poll();
|
||||
if (r == null)
|
||||
continue;
|
||||
for (final String player : r.getPlayers())
|
||||
if (!playerIds.containsKey(player) && !insertedPlayers.contains(player)) {
|
||||
writer.println("INSERT IGNORE INTO `lb-players` (playername) VALUES ('" + player + "');");
|
||||
insertedPlayers.add(player);
|
||||
}
|
||||
for (final String insert : r.getInserts())
|
||||
writer.println(insert);
|
||||
counter++;
|
||||
if (counter % 1000 == 0) {
|
||||
writer.close();
|
||||
writer = new PrintWriter(new File("plugins/LogBlock/import/queue-" + time + "-" + counter / 1000 + ".sql"));
|
||||
}
|
||||
}
|
||||
writer.close();
|
||||
}
|
||||
|
||||
int getQueueSize() {
|
||||
return queue.size();
|
||||
}
|
||||
|
||||
static boolean hide(Player player) {
|
||||
final String playerName = player.getName();
|
||||
if (hiddenPlayers.contains(playerName)) {
|
||||
hiddenPlayers.remove(playerName);
|
||||
return false;
|
||||
}
|
||||
hiddenPlayers.add(playerName);
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean addPlayer(Statement state, String playerName) throws SQLException {
|
||||
state.execute("INSERT IGNORE INTO `lb-players` (playername) VALUES ('" + playerName + "')");
|
||||
final ResultSet rs = state.executeQuery("SELECT playerid FROM `lb-players` WHERE playername = '" + playerName + "'");
|
||||
if (rs.next())
|
||||
playerIds.put(playerName, rs.getInt(1));
|
||||
rs.close();
|
||||
return playerIds.containsKey(playerName);
|
||||
}
|
||||
|
||||
private void queueBlock(String playerName, Location loc, int typeBefore, int typeAfter, byte data, String signtext, ChestAccess ca) {
|
||||
if (playerName == null || loc == null || typeBefore < 0 || typeAfter < 0 || typeBefore > 255 || typeAfter > 255 || hiddenPlayers.contains(playerName) || !isLogged(loc.getWorld()) || typeBefore != typeAfter && hiddenBlocks.contains(typeBefore) && hiddenBlocks.contains(typeAfter))
|
||||
return;
|
||||
queue.add(new BlockRow(loc, playerName.replaceAll("[^a-zA-Z0-9_]", ""), typeBefore, typeAfter, data, signtext != null ? signtext.replace("\\", "\\\\").replace("'", "\\'") : null, ca));
|
||||
}
|
||||
|
||||
private String playerID(String playerName) {
|
||||
if (playerName == null)
|
||||
return "NULL";
|
||||
final Integer id = playerIds.get(playerName);
|
||||
if (id != null)
|
||||
return id.toString();
|
||||
return "(SELECT playerid FROM `lb-players` WHERE playername = '" + playerName + "')";
|
||||
}
|
||||
|
||||
private static interface Row
|
||||
{
|
||||
String[] getInserts();
|
||||
|
||||
String[] getPlayers();
|
||||
}
|
||||
|
||||
private class BlockRow extends BlockChange implements Row
|
||||
{
|
||||
public BlockRow(Location loc, String playerName, int replaced, int type, byte data, String signtext, ChestAccess ca) {
|
||||
super(System.currentTimeMillis() / 1000, loc, playerName, replaced, type, data, signtext, ca);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] getInserts() {
|
||||
final String table = getWorldConfig(loc.getWorld()).table;
|
||||
final String[] inserts = new String[ca != null || signtext != null ? 2 : 1];
|
||||
inserts[0] = "INSERT INTO `" + table + "` (date, playerid, replaced, type, data, x, y, z) VALUES (FROM_UNIXTIME(" + date + "), " + playerID(playerName) + ", " + replaced + ", " + type + ", " + data + ", '" + loc.getBlockX() + "', " + loc.getBlockY() + ", '" + loc.getBlockZ() + "');";
|
||||
if (signtext != null)
|
||||
inserts[1] = "INSERT INTO `" + table + "-sign` (id, signtext) values (LAST_INSERT_ID(), '" + signtext + "');";
|
||||
else if (ca != null)
|
||||
inserts[1] = "INSERT INTO `" + table + "-chest` (id, itemtype, itemamount, itemdata) values (LAST_INSERT_ID(), " + ca.itemType + ", " + ca.itemAmount + ", " + ca.itemData + ");";
|
||||
return inserts;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] getPlayers() {
|
||||
return new String[]{playerName};
|
||||
}
|
||||
}
|
||||
|
||||
private class KillRow implements Row
|
||||
{
|
||||
final long date;
|
||||
final String killer, victim;
|
||||
final int weapon;
|
||||
final Location loc;
|
||||
|
||||
KillRow(Location loc, String attacker, String defender, int weapon) {
|
||||
date = System.currentTimeMillis() / 1000;
|
||||
this.loc = loc;
|
||||
killer = attacker;
|
||||
victim = defender;
|
||||
this.weapon = weapon;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] getInserts() {
|
||||
return new String[]{"INSERT INTO `" + getWorldConfig(loc.getWorld()).table + "-kills` (date, killer, victim, weapon, x, y, z) VALUES (FROM_UNIXTIME(" + date + "), " + playerID(killer) + ", " + playerID(victim) + ", " + weapon + ", " + loc.getBlockX() + ", " + loc.getBlockY() + ", " + loc.getBlockZ() + ");"};
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] getPlayers() {
|
||||
return new String[]{killer, victim};
|
||||
}
|
||||
}
|
||||
|
||||
private class ChatRow extends ChatMessage implements Row
|
||||
{
|
||||
ChatRow(String player, String message) {
|
||||
super(player, message);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] getInserts() {
|
||||
return new String[]{"INSERT INTO `lb-chat` (date, playerid, message) VALUES (FROM_UNIXTIME(" + date + "), " + playerID(playerName) + ", '" + message + "');"};
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] getPlayers() {
|
||||
return new String[]{playerName};
|
||||
}
|
||||
}
|
||||
|
||||
private class PlayerJoinRow implements Row
|
||||
{
|
||||
private final String playerName;
|
||||
private final long lastLogin;
|
||||
private final String ip;
|
||||
|
||||
PlayerJoinRow(Player player) {
|
||||
playerName = player.getName();
|
||||
lastLogin = System.currentTimeMillis() / 1000;
|
||||
ip = player.getAddress().toString().replace("'", "\\'");
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] getInserts() {
|
||||
return new String[]{"UPDATE `lb-players` SET lastlogin = FROM_UNIXTIME(" + lastLogin + "), firstlogin = IF(firstlogin = 0, FROM_UNIXTIME(" + lastLogin + "), firstlogin), ip = '" + ip + "' WHERE " + (playerIds.containsKey(playerName) ? "playerid = " + playerIds.get(playerName) : "playerName = '" + playerName + "'") + ";"};
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] getPlayers() {
|
||||
return new String[]{playerName};
|
||||
}
|
||||
}
|
||||
|
||||
private class PlayerLeaveRow implements Row
|
||||
{
|
||||
private final String playerName;
|
||||
private final long leaveTime;
|
||||
|
||||
PlayerLeaveRow(Player player) {
|
||||
playerName = player.getName();
|
||||
leaveTime = System.currentTimeMillis() / 1000;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] getInserts() {
|
||||
return new String[]{"UPDATE `lb-players` SET onlinetime = onlinetime + TIMESTAMPDIFF(SECOND, lastlogin, FROM_UNIXTIME('" + leaveTime + "')) WHERE lastlogin > 0 && " + (playerIds.containsKey(playerName) ? "playerid = " + playerIds.get(playerName) : "playerName = '" + playerName + "'") + ";"};
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] getPlayers() {
|
||||
return new String[]{playerName};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
package de.diddiz.LogBlock;
|
||||
|
||||
import static de.diddiz.util.Utils.newline;
|
||||
import static org.bukkit.Bukkit.getLogger;
|
||||
import java.io.BufferedReader;
|
||||
import java.io.BufferedWriter;
|
||||
import java.io.File;
|
||||
import java.io.FileReader;
|
||||
import java.io.FileWriter;
|
||||
import java.sql.Connection;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.Statement;
|
||||
import java.util.logging.Level;
|
||||
import de.diddiz.util.Utils.ExtensionFilenameFilter;
|
||||
|
||||
public class DumpedLogImporter implements Runnable
|
||||
{
|
||||
private final LogBlock logblock;
|
||||
|
||||
DumpedLogImporter(LogBlock logblock) {
|
||||
this.logblock = logblock;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
final File[] imports = new File("plugins/LogBlock/import/").listFiles(new ExtensionFilenameFilter("sql"));
|
||||
if (imports != null && imports.length > 0) {
|
||||
getLogger().info("[LogBlock] Found " + imports.length + " imports.");
|
||||
Connection conn = null;
|
||||
try {
|
||||
conn = logblock.getConnection();
|
||||
if (conn == null)
|
||||
return;
|
||||
conn.setAutoCommit(false);
|
||||
final Statement st = conn.createStatement();
|
||||
final BufferedWriter writer = new BufferedWriter(new FileWriter(new File(logblock.getDataFolder(), "import/failed.txt")));
|
||||
int successes = 0, errors = 0;
|
||||
for (final File sqlFile : imports) {
|
||||
getLogger().info("[LogBlock] Trying to import " + sqlFile.getName() + " ...");
|
||||
final BufferedReader reader = new BufferedReader(new FileReader(sqlFile));
|
||||
String line = null;
|
||||
while ((line = reader.readLine()) != null)
|
||||
try {
|
||||
st.execute(line);
|
||||
successes++;
|
||||
} catch (final Exception ex) {
|
||||
getLogger().warning("[LogBlock] Error while importing: '" + line + "': " + ex.getMessage());
|
||||
writer.write(line + newline);
|
||||
errors++;
|
||||
}
|
||||
conn.commit();
|
||||
reader.close();
|
||||
sqlFile.delete();
|
||||
getLogger().info("[LogBlock] Successfully imported " + sqlFile.getName() + ".");
|
||||
}
|
||||
writer.close();
|
||||
st.close();
|
||||
getLogger().info("[LogBlock] Successfully imported stored queue. (" + successes + " rows imported, " + errors + " errors)");
|
||||
} catch (final Exception ex) {
|
||||
getLogger().log(Level.WARNING, "[LogBlock] Error while importing: ", ex);
|
||||
} finally {
|
||||
if (conn != null)
|
||||
try {
|
||||
conn.close();
|
||||
} catch (final SQLException ex) {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,307 @@
|
||||
package de.diddiz.LogBlock;
|
||||
|
||||
import static de.diddiz.LogBlock.config.Config.askRollbackAfterBan;
|
||||
import static de.diddiz.LogBlock.config.Config.autoClearLogDelay;
|
||||
import static de.diddiz.LogBlock.config.Config.delayBetweenRuns;
|
||||
import static de.diddiz.LogBlock.config.Config.enableAutoClearLog;
|
||||
import static de.diddiz.LogBlock.config.Config.isLogging;
|
||||
import static de.diddiz.LogBlock.config.Config.load;
|
||||
import static de.diddiz.LogBlock.config.Config.logPlayerInfo;
|
||||
import static de.diddiz.LogBlock.config.Config.password;
|
||||
import static de.diddiz.LogBlock.config.Config.toolsByType;
|
||||
import static de.diddiz.LogBlock.config.Config.url;
|
||||
import static de.diddiz.LogBlock.config.Config.useBukkitScheduler;
|
||||
import static de.diddiz.LogBlock.config.Config.user;
|
||||
import static de.diddiz.util.Utils.download;
|
||||
import static org.bukkit.Bukkit.getPluginManager;
|
||||
import java.io.File;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.net.URL;
|
||||
import java.sql.Connection;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.Statement;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Timer;
|
||||
import java.util.logging.Level;
|
||||
import org.bukkit.ChatColor;
|
||||
import org.bukkit.command.Command;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.permissions.Permission;
|
||||
import org.bukkit.plugin.PluginManager;
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
import de.diddiz.LogBlock.config.Config;
|
||||
import de.diddiz.LogBlock.listeners.BanListener;
|
||||
import de.diddiz.LogBlock.listeners.BlockBreakLogging;
|
||||
import de.diddiz.LogBlock.listeners.BlockBurnLogging;
|
||||
import de.diddiz.LogBlock.listeners.BlockPlaceLogging;
|
||||
import de.diddiz.LogBlock.listeners.ChatLogging;
|
||||
import de.diddiz.LogBlock.listeners.ChestAccessLogging;
|
||||
import de.diddiz.LogBlock.listeners.EndermenLogging;
|
||||
import de.diddiz.LogBlock.listeners.ExplosionLogging;
|
||||
import de.diddiz.LogBlock.listeners.FluidFlowLogging;
|
||||
import de.diddiz.LogBlock.listeners.InteractLogging;
|
||||
import de.diddiz.LogBlock.listeners.KillLogging;
|
||||
import de.diddiz.LogBlock.listeners.LeavesDecayLogging;
|
||||
import de.diddiz.LogBlock.listeners.PlayerInfoLogging;
|
||||
import de.diddiz.LogBlock.listeners.SignChangeLogging;
|
||||
import de.diddiz.LogBlock.listeners.SnowFadeLogging;
|
||||
import de.diddiz.LogBlock.listeners.SnowFormLogging;
|
||||
import de.diddiz.LogBlock.listeners.StructureGrowLogging;
|
||||
import de.diddiz.LogBlock.listeners.ToolListener;
|
||||
import de.diddiz.util.MySQLConnectionPool;
|
||||
|
||||
public class LogBlock extends JavaPlugin
|
||||
{
|
||||
private static LogBlock logblock = null;
|
||||
private MySQLConnectionPool pool;
|
||||
private Consumer consumer = null;
|
||||
private CommandsHandler commandsHandler;
|
||||
private Updater updater = null;
|
||||
private Timer timer = null;
|
||||
private boolean errorAtLoading = false, noDb = false, connected = true;
|
||||
|
||||
public static LogBlock getInstance() {
|
||||
return logblock;
|
||||
}
|
||||
|
||||
public Consumer getConsumer() {
|
||||
return consumer;
|
||||
}
|
||||
|
||||
public CommandsHandler getCommandsHandler() {
|
||||
return commandsHandler;
|
||||
}
|
||||
|
||||
Updater getUpdater() {
|
||||
return updater;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLoad() {
|
||||
logblock = this;
|
||||
try {
|
||||
updater = new Updater(this);
|
||||
Config.load(this);
|
||||
getLogger().info("[LogBlock] Connecting to " + user + "@" + url + "...");
|
||||
pool = new MySQLConnectionPool(url, user, password);
|
||||
final Connection conn = getConnection();
|
||||
if (conn == null) {
|
||||
noDb = true;
|
||||
return;
|
||||
}
|
||||
conn.close();
|
||||
if (updater.update())
|
||||
load(this);
|
||||
updater.checkTables();
|
||||
} catch (final NullPointerException ex) {
|
||||
getLogger().log(Level.SEVERE, "[LogBlock] Error while loading: ", ex);
|
||||
} catch (final Exception ex) {
|
||||
getLogger().severe("[LogBlock] Error while loading: " + ex.getMessage());
|
||||
errorAtLoading = true;
|
||||
return;
|
||||
}
|
||||
consumer = new Consumer(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onEnable() {
|
||||
final PluginManager pm = getPluginManager();
|
||||
if (errorAtLoading) {
|
||||
pm.disablePlugin(this);
|
||||
return;
|
||||
}
|
||||
if (noDb)
|
||||
return;
|
||||
if (pm.getPlugin("WorldEdit") == null && !new File("lib/WorldEdit.jar").exists() && !new File("WorldEdit.jar").exists())
|
||||
try {
|
||||
download(getLogger(), new URL("http://diddiz.insane-architects.net/download/WorldEdit.jar"), new File("lib/WorldEdit.jar"));
|
||||
getLogger().info("[LogBlock] You've to restart/reload your server now.");
|
||||
pm.disablePlugin(this);
|
||||
return;
|
||||
} catch (final Exception ex) {
|
||||
getLogger().warning("[LogBlock] Failed to download WorldEdit. You may have to download it manually. You don't have to install it, just place the jar in the lib folder.");
|
||||
}
|
||||
commandsHandler = new CommandsHandler(this);
|
||||
getCommand("lb").setExecutor(commandsHandler);
|
||||
if (enableAutoClearLog && autoClearLogDelay > 0)
|
||||
getServer().getScheduler().scheduleAsyncRepeatingTask(this, new AutoClearLog(this), 6000, autoClearLogDelay * 60 * 20);
|
||||
getServer().getScheduler().scheduleAsyncDelayedTask(this, new DumpedLogImporter(this));
|
||||
registerEvents();
|
||||
if (useBukkitScheduler) {
|
||||
if (getServer().getScheduler().scheduleAsyncRepeatingTask(this, consumer, delayBetweenRuns * 20, delayBetweenRuns * 20) > 0)
|
||||
getLogger().info("[LogBlock] Scheduled consumer with bukkit scheduler.");
|
||||
else {
|
||||
getLogger().warning("[LogBlock] Failed to schedule consumer with bukkit scheduler. Now trying schedule with timer.");
|
||||
timer = new Timer();
|
||||
timer.scheduleAtFixedRate(consumer, delayBetweenRuns * 1000, delayBetweenRuns * 1000);
|
||||
}
|
||||
} else {
|
||||
timer = new Timer();
|
||||
timer.scheduleAtFixedRate(consumer, delayBetweenRuns * 1000, delayBetweenRuns * 1000);
|
||||
getLogger().info("[LogBlock] Scheduled consumer with timer.");
|
||||
}
|
||||
for (final Tool tool : toolsByType.values())
|
||||
if (pm.getPermission("logblock.tools." + tool.name) == null) {
|
||||
final Permission perm = new Permission("logblock.tools." + tool.name, tool.permissionDefault);
|
||||
pm.addPermission(perm);
|
||||
}
|
||||
try {
|
||||
Metrics metrics = new Metrics(this);
|
||||
metrics.start();
|
||||
} catch (IOException ex) {
|
||||
getLogger().info("Could not start metrics: " + ex.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
private void registerEvents() {
|
||||
final PluginManager pm = getPluginManager();
|
||||
pm.registerEvents(new ToolListener(this), this);
|
||||
if (askRollbackAfterBan)
|
||||
pm.registerEvents(new BanListener(this), this);
|
||||
if (isLogging(Logging.BLOCKPLACE))
|
||||
pm.registerEvents(new BlockPlaceLogging(this), this);
|
||||
if (isLogging(Logging.BLOCKPLACE) || isLogging(Logging.LAVAFLOW) || isLogging(Logging.WATERFLOW))
|
||||
pm.registerEvents(new FluidFlowLogging(this), this);
|
||||
if (isLogging(Logging.BLOCKBREAK))
|
||||
pm.registerEvents(new BlockBreakLogging(this), this);
|
||||
if (isLogging(Logging.SIGNTEXT))
|
||||
pm.registerEvents(new SignChangeLogging(this), this);
|
||||
if (isLogging(Logging.FIRE))
|
||||
pm.registerEvents(new BlockBurnLogging(this), this);
|
||||
if (isLogging(Logging.SNOWFORM))
|
||||
pm.registerEvents(new SnowFormLogging(this), this);
|
||||
if (isLogging(Logging.SNOWFADE))
|
||||
pm.registerEvents(new SnowFadeLogging(this), this);
|
||||
if (isLogging(Logging.CREEPEREXPLOSION) || isLogging(Logging.TNTEXPLOSION) || isLogging(Logging.GHASTFIREBALLEXPLOSION) || isLogging(Logging.ENDERDRAGON) || isLogging(Logging.MISCEXPLOSION))
|
||||
pm.registerEvents(new ExplosionLogging(this), this);
|
||||
if (isLogging(Logging.LEAVESDECAY))
|
||||
pm.registerEvents(new LeavesDecayLogging(this), this);
|
||||
if (isLogging(Logging.CHESTACCESS)) {
|
||||
pm.registerEvents(new ChestAccessLogging(this), this);
|
||||
}
|
||||
if (isLogging(Logging.SWITCHINTERACT) || isLogging(Logging.DOORINTERACT) || isLogging(Logging.CAKEEAT) || isLogging(Logging.DIODEINTERACT) || isLogging(Logging.NOTEBLOCKINTERACT))
|
||||
pm.registerEvents(new InteractLogging(this), this);
|
||||
if (isLogging(Logging.KILL))
|
||||
pm.registerEvents(new KillLogging(this), this);
|
||||
if (isLogging(Logging.CHAT))
|
||||
pm.registerEvents(new ChatLogging(this), this);
|
||||
if (isLogging(Logging.ENDERMEN))
|
||||
pm.registerEvents(new EndermenLogging(this), this);
|
||||
if (isLogging(Logging.NATURALSTRUCTUREGROW) || isLogging(Logging.BONEMEALSTRUCTUREGROW))
|
||||
pm.registerEvents(new StructureGrowLogging(this), this);
|
||||
if (logPlayerInfo)
|
||||
pm.registerEvents(new PlayerInfoLogging(this), this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDisable() {
|
||||
if (timer != null)
|
||||
timer.cancel();
|
||||
getServer().getScheduler().cancelTasks(this);
|
||||
if (consumer != null) {
|
||||
if (logPlayerInfo && getServer().getOnlinePlayers() != null)
|
||||
for (final Player player : getServer().getOnlinePlayers())
|
||||
consumer.queueLeave(player);
|
||||
if (consumer.getQueueSize() > 0) {
|
||||
getLogger().info("[LogBlock] Waiting for consumer ...");
|
||||
int tries = 10;
|
||||
while (consumer.getQueueSize() > 0) {
|
||||
getLogger().info("[LogBlock] Remaining queue size: " + consumer.getQueueSize());
|
||||
if (tries > 0)
|
||||
getLogger().info("[LogBlock] Remaining tries: " + tries);
|
||||
else {
|
||||
getLogger().info("Unable to save queue to database. Trying to write to a local file.");
|
||||
try {
|
||||
consumer.writeToFile();
|
||||
getLogger().info("Successfully dumped queue.");
|
||||
} catch (final FileNotFoundException ex) {
|
||||
getLogger().info("Failed to write. Given up.");
|
||||
break;
|
||||
}
|
||||
}
|
||||
consumer.run();
|
||||
tries--;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (pool != null)
|
||||
pool.close();
|
||||
getLogger().info("LogBlock disabled.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCommand(CommandSender sender, Command cmd, String commandLabel, String[] args) {
|
||||
if (noDb)
|
||||
sender.sendMessage(ChatColor.RED + "No database connected. Check your MySQL user/pw and database for typos. Start/restart your MySQL server.");
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean hasPermission(CommandSender sender, String permission) {
|
||||
return sender.hasPermission(permission);
|
||||
}
|
||||
|
||||
public Connection getConnection() {
|
||||
try {
|
||||
final Connection conn = pool.getConnection();
|
||||
if (!connected) {
|
||||
getLogger().info("[LogBlock] MySQL connection rebuild");
|
||||
connected = true;
|
||||
}
|
||||
return conn;
|
||||
} catch (final Exception ex) {
|
||||
if (connected) {
|
||||
getLogger().log(Level.SEVERE, "[LogBlock] Error while fetching connection: ", ex);
|
||||
connected = false;
|
||||
} else
|
||||
getLogger().severe("[LogBlock] MySQL connection lost");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param params
|
||||
* QueryParams that contains the needed columns (all other will be filled with default values) and the params. World is required.
|
||||
*/
|
||||
public List<BlockChange> getBlockChanges(QueryParams params) throws SQLException {
|
||||
final Connection conn = getConnection();
|
||||
Statement state = null;
|
||||
if (conn == null)
|
||||
throw new SQLException("No connection");
|
||||
try {
|
||||
state = conn.createStatement();
|
||||
final ResultSet rs = state.executeQuery(params.getQuery());
|
||||
final List<BlockChange> blockchanges = new ArrayList<BlockChange>();
|
||||
while (rs.next())
|
||||
blockchanges.add(new BlockChange(rs, params));
|
||||
return blockchanges;
|
||||
} finally {
|
||||
if (state != null)
|
||||
state.close();
|
||||
conn.close();
|
||||
}
|
||||
}
|
||||
|
||||
public int getCount(QueryParams params) throws SQLException {
|
||||
final Connection conn = getConnection();
|
||||
Statement state = null;
|
||||
if (conn == null)
|
||||
throw new SQLException("No connection");
|
||||
try {
|
||||
state = conn.createStatement();
|
||||
final QueryParams p = params.clone();
|
||||
p.needCount = true;
|
||||
final ResultSet rs = state.executeQuery(p.getQuery());
|
||||
if (!rs.next())
|
||||
return 0;
|
||||
return rs.getInt(1);
|
||||
} finally {
|
||||
if (state != null)
|
||||
state.close();
|
||||
conn.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
package de.diddiz.LogBlock;
|
||||
|
||||
public enum Logging
|
||||
{
|
||||
BLOCKPLACE(true), BLOCKBREAK(true), SIGNTEXT, TNTEXPLOSION(true), CREEPEREXPLOSION(true),
|
||||
GHASTFIREBALLEXPLOSION(true), ENDERDRAGON(true), MISCEXPLOSION, FIRE(true), LEAVESDECAY,
|
||||
LAVAFLOW, WATERFLOW, CHESTACCESS, KILL, CHAT, SNOWFORM, SNOWFADE, DOORINTERACT,
|
||||
SWITCHINTERACT, CAKEEAT, ENDERMEN, NOTEBLOCKINTERACT, DIODEINTERACT, NATURALSTRUCTUREGROW,
|
||||
BONEMEALSTRUCTUREGROW;
|
||||
public static final int length = Logging.values().length;
|
||||
private final boolean defaultEnabled;
|
||||
|
||||
private Logging() {
|
||||
this(false);
|
||||
}
|
||||
|
||||
private Logging(boolean defaultEnabled) {
|
||||
this.defaultEnabled = defaultEnabled;
|
||||
}
|
||||
|
||||
public boolean isDefaultEnabled() {
|
||||
return defaultEnabled;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
package de.diddiz.LogBlock;
|
||||
|
||||
import org.bukkit.Location;
|
||||
|
||||
public interface LookupCacheElement
|
||||
{
|
||||
public Location getLocation();
|
||||
|
||||
public String getMessage();
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package de.diddiz.LogBlock;
|
||||
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import de.diddiz.LogBlock.QueryParams.BlockChangeType;
|
||||
import de.diddiz.LogBlock.QueryParams.SummarizationMode;
|
||||
|
||||
public class LookupCacheElementFactory
|
||||
{
|
||||
private final QueryParams params;
|
||||
private final float spaceFactor;
|
||||
|
||||
public LookupCacheElementFactory(QueryParams params, float spaceFactor) {
|
||||
this.params = params;
|
||||
this.spaceFactor = spaceFactor;
|
||||
}
|
||||
|
||||
public LookupCacheElement getLookupCacheElement(ResultSet rs) throws SQLException {
|
||||
if (params.bct == BlockChangeType.CHAT)
|
||||
return new ChatMessage(rs, params);
|
||||
if (params.sum == SummarizationMode.NONE)
|
||||
return new BlockChange(rs, params);
|
||||
return new SummedBlockChanges(rs, params, spaceFactor);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,347 @@
|
||||
/*
|
||||
* Copyright 2011 Tyler Blair. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification, are
|
||||
* permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this list of
|
||||
* conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice, this list
|
||||
* of conditions and the following disclaimer in the documentation and/or other materials
|
||||
* provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ''AS IS'' AND ANY EXPRESS OR IMPLIED
|
||||
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR
|
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
||||
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* The views and conclusions contained in the software and documentation are those of the
|
||||
* authors and contributors and should not be interpreted as representing official policies,
|
||||
* either expressed or implied, of anybody else.
|
||||
*/
|
||||
|
||||
package de.diddiz.LogBlock;
|
||||
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.configuration.InvalidConfigurationException;
|
||||
import org.bukkit.configuration.file.YamlConfiguration;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
import org.bukkit.plugin.PluginDescriptionFile;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.OutputStreamWriter;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.net.Proxy;
|
||||
import java.net.URL;
|
||||
import java.net.URLConnection;
|
||||
import java.net.URLEncoder;
|
||||
import java.util.UUID;
|
||||
import java.util.logging.Level;
|
||||
|
||||
public class Metrics {
|
||||
|
||||
/**
|
||||
* The current revision number
|
||||
*/
|
||||
private final static int REVISION = 5;
|
||||
|
||||
/**
|
||||
* The base url of the metrics domain
|
||||
*/
|
||||
private static final String BASE_URL = "http://mcstats.org";
|
||||
|
||||
/**
|
||||
* The url used to report a server's status
|
||||
*/
|
||||
private static final String REPORT_URL = "/report/%s";
|
||||
|
||||
/**
|
||||
* The file where guid and opt out is stored in
|
||||
*/
|
||||
private static final String CONFIG_FILE = "plugins/PluginMetrics/config.yml";
|
||||
|
||||
/**
|
||||
* Interval of time to ping (in minutes)
|
||||
*/
|
||||
private final static int PING_INTERVAL = 10;
|
||||
|
||||
/**
|
||||
* The plugin this metrics submits for
|
||||
*/
|
||||
private final Plugin plugin;
|
||||
|
||||
/**
|
||||
* The plugin configuration file
|
||||
*/
|
||||
private final YamlConfiguration configuration;
|
||||
|
||||
/**
|
||||
* The plugin configuration file
|
||||
*/
|
||||
private final File configurationFile;
|
||||
|
||||
/**
|
||||
* Unique server id
|
||||
*/
|
||||
private final String guid;
|
||||
|
||||
/**
|
||||
* Lock for synchronization
|
||||
*/
|
||||
private final Object optOutLock = new Object();
|
||||
|
||||
/**
|
||||
* Id of the scheduled task
|
||||
*/
|
||||
private volatile int taskId = -1;
|
||||
|
||||
public Metrics(Plugin plugin) throws IOException {
|
||||
if (plugin == null) {
|
||||
throw new IllegalArgumentException("Plugin cannot be null");
|
||||
}
|
||||
|
||||
this.plugin = plugin;
|
||||
|
||||
// load the config
|
||||
configurationFile = new File(CONFIG_FILE);
|
||||
configuration = YamlConfiguration.loadConfiguration(configurationFile);
|
||||
|
||||
// add some defaults
|
||||
configuration.addDefault("opt-out", false);
|
||||
configuration.addDefault("guid", UUID.randomUUID().toString());
|
||||
|
||||
// Do we need to create the file?
|
||||
if (configuration.get("guid", null) == null) {
|
||||
configuration.options().header("http://mcstats.org").copyDefaults(true);
|
||||
configuration.save(configurationFile);
|
||||
}
|
||||
|
||||
// Load the guid then
|
||||
guid = configuration.getString("guid");
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Start measuring statistics. This will immediately create an async repeating task as the plugin and send
|
||||
* the initial data to the metrics backend, and then after that it will post in increments of
|
||||
* PING_INTERVAL * 1200 ticks.
|
||||
*
|
||||
* @return True if statistics measuring is running, otherwise false.
|
||||
*/
|
||||
public boolean start() {
|
||||
synchronized (optOutLock) {
|
||||
// Did we opt out?
|
||||
if (isOptOut()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Is metrics already running?
|
||||
if (taskId >= 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Begin hitting the server with glorious data
|
||||
taskId = plugin.getServer().getScheduler().scheduleAsyncRepeatingTask(plugin, new Runnable() {
|
||||
|
||||
private boolean firstPost = true;
|
||||
|
||||
public void run() {
|
||||
try {
|
||||
// This has to be synchronized or it can collide with the disable method.
|
||||
synchronized (optOutLock) {
|
||||
// Disable Task, if it is running and the server owner decided to opt-out
|
||||
if (isOptOut() && taskId > 0) {
|
||||
plugin.getServer().getScheduler().cancelTask(taskId);
|
||||
taskId = -1;
|
||||
}
|
||||
}
|
||||
|
||||
// We use the inverse of firstPost because if it is the first time we are posting,
|
||||
// it is not a interval ping, so it evaluates to FALSE
|
||||
// Each time thereafter it will evaluate to TRUE, i.e PING!
|
||||
postPlugin(!firstPost);
|
||||
|
||||
// After the first post we set firstPost to false
|
||||
// Each post thereafter will be a ping
|
||||
firstPost = false;
|
||||
} catch (IOException e) {
|
||||
Bukkit.getLogger().log(Level.INFO, "[Metrics] " + e.getMessage());
|
||||
}
|
||||
}
|
||||
}, 0, PING_INTERVAL * 1200);
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Has the server owner denied plugin metrics?
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public boolean isOptOut() {
|
||||
synchronized(optOutLock) {
|
||||
try {
|
||||
// Reload the metrics file
|
||||
configuration.load(CONFIG_FILE);
|
||||
} catch (IOException ex) {
|
||||
Bukkit.getLogger().log(Level.INFO, "[Metrics] " + ex.getMessage());
|
||||
return true;
|
||||
} catch (InvalidConfigurationException ex) {
|
||||
Bukkit.getLogger().log(Level.INFO, "[Metrics] " + ex.getMessage());
|
||||
return true;
|
||||
}
|
||||
return configuration.getBoolean("opt-out", false);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Enables metrics for the server by setting "opt-out" to false in the config file and starting the metrics task.
|
||||
*
|
||||
* @throws IOException
|
||||
*/
|
||||
public void enable() throws IOException {
|
||||
// This has to be synchronized or it can collide with the check in the task.
|
||||
synchronized (optOutLock) {
|
||||
// Check if the server owner has already set opt-out, if not, set it.
|
||||
if (isOptOut()) {
|
||||
configuration.set("opt-out", false);
|
||||
configuration.save(configurationFile);
|
||||
}
|
||||
|
||||
// Enable Task, if it is not running
|
||||
if (taskId < 0) {
|
||||
start();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Disables metrics for the server by setting "opt-out" to true in the config file and canceling the metrics task.
|
||||
*
|
||||
* @throws IOException
|
||||
*/
|
||||
public void disable() throws IOException {
|
||||
// This has to be synchronized or it can collide with the check in the task.
|
||||
synchronized (optOutLock) {
|
||||
// Check if the server owner has already set opt-out, if not, set it.
|
||||
if (!isOptOut()) {
|
||||
configuration.set("opt-out", true);
|
||||
configuration.save(configurationFile);
|
||||
}
|
||||
|
||||
// Disable Task, if it is running
|
||||
if (taskId > 0) {
|
||||
this.plugin.getServer().getScheduler().cancelTask(taskId);
|
||||
taskId = -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generic method that posts a plugin to the metrics website
|
||||
*/
|
||||
private void postPlugin(boolean isPing) throws IOException {
|
||||
// The plugin's description file containg all of the plugin data such as name, version, author, etc
|
||||
final PluginDescriptionFile description = plugin.getDescription();
|
||||
|
||||
// Construct the post data
|
||||
final StringBuilder data = new StringBuilder();
|
||||
data.append(encode("guid")).append('=').append(encode(guid));
|
||||
encodeDataPair(data, "version", description.getVersion());
|
||||
encodeDataPair(data, "server", Bukkit.getVersion());
|
||||
encodeDataPair(data, "players", Integer.toString(Bukkit.getServer().getOnlinePlayers().length));
|
||||
encodeDataPair(data, "revision", String.valueOf(REVISION));
|
||||
|
||||
// If we're pinging, append it
|
||||
if (isPing) {
|
||||
encodeDataPair(data, "ping", "true");
|
||||
}
|
||||
|
||||
// Create the url
|
||||
URL url = new URL(BASE_URL + String.format(REPORT_URL, encode(plugin.getDescription().getName())));
|
||||
|
||||
// Connect to the website
|
||||
URLConnection connection;
|
||||
|
||||
// Mineshafter creates a socks proxy, so we can safely bypass it
|
||||
// It does not reroute POST requests so we need to go around it
|
||||
if (isMineshafterPresent()) {
|
||||
connection = url.openConnection(Proxy.NO_PROXY);
|
||||
} else {
|
||||
connection = url.openConnection();
|
||||
}
|
||||
|
||||
connection.setDoOutput(true);
|
||||
|
||||
// Write the data
|
||||
final OutputStreamWriter writer = new OutputStreamWriter(connection.getOutputStream());
|
||||
writer.write(data.toString());
|
||||
writer.flush();
|
||||
|
||||
// Now read the response
|
||||
final BufferedReader reader = new BufferedReader(new InputStreamReader(connection.getInputStream()));
|
||||
final String response = reader.readLine();
|
||||
|
||||
// close resources
|
||||
writer.close();
|
||||
reader.close();
|
||||
|
||||
if (response == null || response.startsWith("ERR")) {
|
||||
throw new IOException(response); //Throw the exception
|
||||
}
|
||||
//if (response.startsWith("OK")) - We should get "OK" followed by an optional description if everything goes right
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if mineshafter is present. If it is, we need to bypass it to send POST requests
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
private boolean isMineshafterPresent() {
|
||||
try {
|
||||
Class.forName("mineshafter.MineServer");
|
||||
return true;
|
||||
} catch (Exception e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Encode a key/value data pair to be used in a HTTP post request. This INCLUDES a & so the first
|
||||
* key/value pair MUST be included manually, e.g:</p>
|
||||
* <code>
|
||||
* StringBuffer data = new StringBuffer();
|
||||
* data.append(encode("guid")).append('=').append(encode(guid));
|
||||
* encodeDataPair(data, "version", description.getVersion());
|
||||
* </code>
|
||||
*
|
||||
* @param buffer
|
||||
* @param key
|
||||
* @param value
|
||||
* @return
|
||||
*/
|
||||
private static void encodeDataPair(final StringBuilder buffer, final String key, final String value) throws UnsupportedEncodingException {
|
||||
buffer.append('&').append(encode(key)).append('=').append(encode(value));
|
||||
}
|
||||
|
||||
/**
|
||||
* Encode text as UTF-8
|
||||
*
|
||||
* @param text
|
||||
* @return
|
||||
*/
|
||||
private static String encode(String text) throws UnsupportedEncodingException {
|
||||
return URLEncoder.encode(text, "UTF-8");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,559 @@
|
||||
package de.diddiz.LogBlock;
|
||||
|
||||
import static de.diddiz.LogBlock.Session.getSession;
|
||||
import static de.diddiz.LogBlock.config.Config.defaultDist;
|
||||
import static de.diddiz.LogBlock.config.Config.defaultTime;
|
||||
import static de.diddiz.LogBlock.config.Config.getWorldConfig;
|
||||
import static de.diddiz.LogBlock.config.Config.isLogged;
|
||||
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.isInt;
|
||||
import static de.diddiz.util.Utils.join;
|
||||
import static de.diddiz.util.Utils.listing;
|
||||
import static de.diddiz.util.Utils.parseTimeSpec;
|
||||
import com.sk89q.worldedit.bukkit.WorldEditPlugin;
|
||||
import com.sk89q.worldedit.bukkit.selections.CuboidSelection;
|
||||
import com.sk89q.worldedit.bukkit.selections.Selection;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
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 de.diddiz.util.Block;
|
||||
|
||||
public 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()));
|
||||
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 boolean excludePlayersMode = false, prepareToolQuery = false, silent = false;
|
||||
public Selection 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;
|
||||
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 (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)
|
||||
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 (!types.isEmpty()) {
|
||||
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 ") + " ");
|
||||
} else
|
||||
title.append("block ");
|
||||
if (bct == BlockChangeType.CREATED)
|
||||
title.append("creations ");
|
||||
else if (bct == BlockChangeType.DESTROYED)
|
||||
title.append("destructions ");
|
||||
else
|
||||
title.append("changes ");
|
||||
}
|
||||
if (players.size() > 10)
|
||||
title.append((excludePlayersMode ? "without" : "from") + " many players ");
|
||||
else if (!players.isEmpty())
|
||||
title.append((excludePlayersMode ? "without" : "from") + " player" + (players.size() != 1 ? "s" : "") + " " + listing(players.toArray(new String[players.size()]), ", ", " and ") + " ");
|
||||
if (match != null && match.length() > 0)
|
||||
title.append("matching '" + match + "' ");
|
||||
if (before > 0 && since > 0)
|
||||
title.append("between " + since + " and " + before + " minutes ago ");
|
||||
else if (since > 0)
|
||||
title.append("in the last " + since + " minutes ");
|
||||
else if (before > 0)
|
||||
title.append("more than " + before * -1 + " minutes ago ");
|
||||
if (loc != null) {
|
||||
if (radius > 0)
|
||||
title.append("within " + radius + " blocks of " + (prepareToolQuery ? "clicked block" : "you") + " ");
|
||||
else if (radius == 0)
|
||||
title.append("at " + loc.getBlockX() + ":" + loc.getBlockY() + ":" + loc.getBlockZ() + " ");
|
||||
} else if (sel != null)
|
||||
title.append(prepareToolQuery ? "at double chest " : "inside selection ");
|
||||
else if (prepareToolQuery)
|
||||
if (radius > 0)
|
||||
title.append("within " + radius + " blocks of clicked block ");
|
||||
else if (radius == 0)
|
||||
title.append("at clicked block ");
|
||||
if (world != null && !(sel != null && prepareToolQuery))
|
||||
title.append("in " + friendlyWorldname(world.getName()) + " ");
|
||||
if (sum != SummarizationMode.NONE)
|
||||
title.append("summed up by " + (sum == SummarizationMode.TYPES ? "blocks" : "players") + " ");
|
||||
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 ('" + match + "' IN BOOLEAN MODE) AND ");
|
||||
else
|
||||
where.append("message " + (unlike ? "NOT " : "") + "LIKE '%" + (unlike ? match.substring(1) : match) + "%' AND ");
|
||||
}
|
||||
} else {
|
||||
switch (blockChangeType) {
|
||||
case ALL:
|
||||
if (!types.isEmpty()) {
|
||||
where.append('(');
|
||||
for (final Block block : types) {
|
||||
where.append("((type = " + block.getBlock() + " OR replaced = " + block.getBlock());
|
||||
if (block.getData() != -1) {
|
||||
where.append(") AND data = " + 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()) {
|
||||
where.append('(');
|
||||
for (final Block block : types) {
|
||||
where.append("((type = " + block.getBlock() + " OR replaced = " + block.getBlock());
|
||||
if (block.getData() != -1) {
|
||||
where.append(") AND data = " + 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()) {
|
||||
where.append('(');
|
||||
for (final Block block : types) {
|
||||
where.append("((type = " + block.getBlock());
|
||||
if (block.getData() != -1) {
|
||||
where.append(") AND data = " + block.getData());
|
||||
} else {
|
||||
where.append(")");
|
||||
}
|
||||
where.append(") OR ");
|
||||
}
|
||||
where.delete(where.length() - 4, where.length());
|
||||
where.append(") AND ");
|
||||
} else
|
||||
where.append("type != 0 AND ");
|
||||
where.append("type != replaced AND ");
|
||||
break;
|
||||
case DESTROYED:
|
||||
if (!types.isEmpty()) {
|
||||
where.append('(');
|
||||
for (final Block block : types) {
|
||||
where.append("((replaced = " + block.getBlock());
|
||||
if (block.getData() != -1) {
|
||||
where.append(") AND data = " + block.getData());
|
||||
} else {
|
||||
where.append(")");
|
||||
}
|
||||
where.append(") OR ");
|
||||
}
|
||||
where.delete(where.length() - 4, where.length());
|
||||
where.append(") AND ");
|
||||
} else
|
||||
where.append("replaced != 0 AND ");
|
||||
where.append("type != replaced AND ");
|
||||
break;
|
||||
case CHESTACCESS:
|
||||
where.append("(type = 23 OR type = 54 OR type = 61 OR type = 62) AND type = replaced AND ");
|
||||
if (!types.isEmpty()) {
|
||||
where.append('(');
|
||||
for (final Block block : types) {
|
||||
where.append("((itemtype = " + block.getBlock());
|
||||
if (block.getData() != -1) {
|
||||
where.append(") AND itemdata = " + 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)
|
||||
where.append("x = '" + loc.getBlockX() + "' AND y = '" + loc.getBlockY() + "' AND z = '" + loc.getBlockZ() + "' AND ");
|
||||
else if (radius > 0)
|
||||
where.append("x > '" + (loc.getBlockX() - radius) + "' AND x < '" + (loc.getBlockX() + radius) + "' AND z > '" + (loc.getBlockZ() - radius) + "' AND z < '" + (loc.getBlockZ() + radius) + "' AND ");
|
||||
} else if (sel != null)
|
||||
where.append("x >= '" + sel.getMinimumPoint().getBlockX() + "' AND x <= '" + sel.getMaximumPoint().getBlockX() + "' AND y >= '" + sel.getMinimumPoint().getBlockY() + "' AND y <= '" + sel.getMaximumPoint().getBlockY() + "' AND z >= '" + sel.getMinimumPoint().getBlockZ() + "' AND z <= '" + sel.getMaximumPoint().getBlockZ() + "' AND ");
|
||||
}
|
||||
if (!players.isEmpty() && sum != SummarizationMode.PLAYERS)
|
||||
if (!excludePlayersMode) {
|
||||
where.append('(');
|
||||
for (final String playerName : players)
|
||||
where.append("playername = '" + playerName + "' OR ");
|
||||
where.delete(where.length() - 4, where.length());
|
||||
where.append(") AND ");
|
||||
} else
|
||||
for (final String playerName : players)
|
||||
where.append("playername != '" + playerName + "' AND ");
|
||||
if (since > 0)
|
||||
where.append("date > date_sub(now(), INTERVAL " + since + " MINUTE) AND ");
|
||||
if (before > 0)
|
||||
where.append("date < date_sub(now(), INTERVAL " + before + " MINUTE) AND ");
|
||||
if (where.length() > 6)
|
||||
where.delete(where.length() - 4, where.length());
|
||||
else
|
||||
where.delete(0, where.length());
|
||||
return where.toString();
|
||||
}
|
||||
|
||||
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_]", ""));
|
||||
}
|
||||
}
|
||||
} 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) {
|
||||
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)
|
||||
throw new IllegalArgumentException("You have to ba a player to use area");
|
||||
if (values.length == 0) {
|
||||
radius = defaultDist;
|
||||
if (!prepareToolQuery)
|
||||
loc = player.getLocation();
|
||||
} else {
|
||||
if (!isInt(values[0]))
|
||||
throw new IllegalArgumentException("Not a number: '" + values[0] + "'");
|
||||
radius = Integer.parseInt(values[0]);
|
||||
if (!prepareToolQuery)
|
||||
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)
|
||||
throw new IllegalArgumentException("WorldEdit plugin not found");
|
||||
final Selection selection = ((WorldEditPlugin)we).getSelection(player);
|
||||
if (selection == null)
|
||||
throw new IllegalArgumentException("No selection defined");
|
||||
if (!(selection instanceof CuboidSelection))
|
||||
throw new IllegalArgumentException("You have to define a cuboid selection");
|
||||
setSelection(selection);
|
||||
} 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("chestaccess"))
|
||||
bct = BlockChangeType.CHESTACCESS;
|
||||
else if (param.equals("chat"))
|
||||
bct = BlockChangeType.CHAT;
|
||||
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 (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(Selection sel) {
|
||||
this.sel = sel;
|
||||
world = sel.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) {
|
||||
int i;
|
||||
for (i = offset; i < args.size(); i++)
|
||||
if (isKeyWord(args.get(i)))
|
||||
break;
|
||||
if (i == offset)
|
||||
return new String[0];
|
||||
final String[] values = new String[i - offset];
|
||||
for (int j = offset; j < i; j++)
|
||||
values[j - offset] = args.get(j);
|
||||
return values;
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
public static enum Order
|
||||
{
|
||||
ASC, DESC
|
||||
}
|
||||
|
||||
public static enum SummarizationMode
|
||||
{
|
||||
NONE, PLAYERS, TYPES
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
package de.diddiz.LogBlock;
|
||||
|
||||
import static de.diddiz.LogBlock.config.Config.toolsByType;
|
||||
import static org.bukkit.Bukkit.getServer;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
public class Session
|
||||
{
|
||||
private static final Map<String, Session> sessions = new HashMap<String, Session>();
|
||||
public QueryParams lastQuery = null;
|
||||
public LookupCacheElement[] lookupCache = null;
|
||||
public int page = 1;
|
||||
public Map<Tool, ToolData> toolData;
|
||||
|
||||
private Session(Player player) {
|
||||
toolData = new HashMap<Tool, ToolData>();
|
||||
final LogBlock logblock = LogBlock.getInstance();
|
||||
if (player != null)
|
||||
for (final Tool tool : toolsByType.values())
|
||||
toolData.put(tool, new ToolData(tool, logblock, player));
|
||||
}
|
||||
|
||||
public static boolean hasSession(CommandSender sender) {
|
||||
return sessions.containsKey(sender.getName().toLowerCase());
|
||||
}
|
||||
|
||||
public static boolean hasSession(String playerName) {
|
||||
return sessions.containsKey(playerName.toLowerCase());
|
||||
}
|
||||
|
||||
public static Session getSession(CommandSender sender) {
|
||||
return getSession(sender.getName());
|
||||
}
|
||||
|
||||
public static Session getSession(String playerName) {
|
||||
Session session = sessions.get(playerName.toLowerCase());
|
||||
if (session == null) {
|
||||
session = new Session(getServer().getPlayer(playerName));
|
||||
sessions.put(playerName.toLowerCase(), session);
|
||||
}
|
||||
return session;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
package de.diddiz.LogBlock;
|
||||
|
||||
import static de.diddiz.util.MaterialName.materialName;
|
||||
import static de.diddiz.util.Utils.spaces;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import org.bukkit.Location;
|
||||
import de.diddiz.LogBlock.QueryParams.SummarizationMode;
|
||||
|
||||
public class SummedBlockChanges implements LookupCacheElement
|
||||
{
|
||||
private final String group;
|
||||
private final int created, destroyed;
|
||||
private final float spaceFactor;
|
||||
|
||||
public SummedBlockChanges(ResultSet rs, QueryParams p, float spaceFactor) throws SQLException {
|
||||
group = p.sum == SummarizationMode.PLAYERS ? rs.getString(1) : materialName(rs.getInt(1));
|
||||
created = rs.getInt(2);
|
||||
destroyed = rs.getInt(3);
|
||||
this.spaceFactor = spaceFactor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Location getLocation() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getMessage() {
|
||||
return created + spaces((int)((10 - String.valueOf(created).length()) / spaceFactor)) + destroyed + spaces((int)((10 - String.valueOf(destroyed).length()) / spaceFactor)) + group;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
package de.diddiz.LogBlock;
|
||||
|
||||
import java.util.List;
|
||||
import org.bukkit.permissions.PermissionDefault;
|
||||
|
||||
public class Tool
|
||||
{
|
||||
public final String name;
|
||||
public final List<String> aliases;
|
||||
public final ToolBehavior leftClickBehavior, rightClickBehavior;
|
||||
public final boolean defaultEnabled;
|
||||
public final int item;
|
||||
public final boolean canDrop;
|
||||
public final QueryParams params;
|
||||
public final ToolMode mode;
|
||||
public final PermissionDefault permissionDefault;
|
||||
|
||||
public Tool(String name, List<String> aliases, ToolBehavior leftClickBehavior, ToolBehavior rightClickBehavior, boolean defaultEnabled, int item, boolean canDrop, QueryParams params, ToolMode mode, PermissionDefault permissionDefault) {
|
||||
this.name = name;
|
||||
this.aliases = aliases;
|
||||
this.leftClickBehavior = leftClickBehavior;
|
||||
this.rightClickBehavior = rightClickBehavior;
|
||||
this.defaultEnabled = defaultEnabled;
|
||||
this.item = item;
|
||||
this.canDrop = canDrop;
|
||||
this.params = params;
|
||||
this.mode = mode;
|
||||
this.permissionDefault = permissionDefault;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
package de.diddiz.LogBlock;
|
||||
|
||||
public enum ToolBehavior
|
||||
{
|
||||
TOOL, BLOCK, NONE
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package de.diddiz.LogBlock;
|
||||
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
public class ToolData
|
||||
{
|
||||
public boolean enabled;
|
||||
public QueryParams params;
|
||||
public ToolMode mode;
|
||||
|
||||
public ToolData(Tool tool, LogBlock logblock, Player player) {
|
||||
enabled = tool.defaultEnabled && logblock.hasPermission(player, "logblock.tools." + tool.name);
|
||||
params = tool.params.clone();
|
||||
mode = tool.mode;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
package de.diddiz.LogBlock;
|
||||
|
||||
public enum ToolMode
|
||||
{
|
||||
CLEARLOG("logblock.clearlog"), LOOKUP("logblock.lookup"), REDO("logblock.rollback"), ROLLBACK("logblock.rollback"), WRITELOGFILE("logblock.rollback");
|
||||
private final String permission;
|
||||
|
||||
private ToolMode(String permission) {
|
||||
this.permission = permission;
|
||||
}
|
||||
|
||||
public String getPermission() {
|
||||
return permission;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,235 @@
|
||||
package de.diddiz.LogBlock;
|
||||
|
||||
import static de.diddiz.LogBlock.config.Config.getLoggedWorlds;
|
||||
import static de.diddiz.LogBlock.config.Config.isLogging;
|
||||
import static de.diddiz.util.BukkitUtils.friendlyWorldname;
|
||||
import static de.diddiz.util.Utils.readURL;
|
||||
import static org.bukkit.Bukkit.getLogger;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.net.URL;
|
||||
import java.sql.Connection;
|
||||
import java.sql.DatabaseMetaData;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.Statement;
|
||||
import java.util.logging.Level;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.configuration.ConfigurationSection;
|
||||
import org.bukkit.configuration.file.YamlConfiguration;
|
||||
import de.diddiz.LogBlock.config.WorldConfig;
|
||||
|
||||
class Updater
|
||||
{
|
||||
private final LogBlock logblock;
|
||||
|
||||
Updater(LogBlock logblock) {
|
||||
this.logblock = logblock;
|
||||
}
|
||||
|
||||
boolean update() {
|
||||
final ConfigurationSection config = logblock.getConfig();
|
||||
if (config.getString("version").compareTo(logblock.getDescription().getVersion()) >= 0)
|
||||
return false;
|
||||
if (config.getString("version").compareTo("1.27") < 0) {
|
||||
getLogger().info("[LogBlock] Updating tables to 1.27 ...");
|
||||
if (isLogging(Logging.CHAT)) {
|
||||
final Connection conn = logblock.getConnection();
|
||||
try {
|
||||
conn.setAutoCommit(true);
|
||||
final Statement st = conn.createStatement();
|
||||
st.execute("ALTER TABLE `lb-chat` ENGINE = MyISAM, ADD FULLTEXT message (message)");
|
||||
st.close();
|
||||
conn.close();
|
||||
} catch (final SQLException ex) {
|
||||
Bukkit.getLogger().log(Level.SEVERE, "[LogBlock Updater] Error: ", ex);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
config.set("version", "1.27");
|
||||
}
|
||||
if (config.getString("version").compareTo("1.30") < 0) {
|
||||
getLogger().info("[LogBlock] Updating config to 1.30 ...");
|
||||
for (final String tool : config.getConfigurationSection("tools").getKeys(false))
|
||||
if (config.get("tools." + tool + ".permissionDefault") == null)
|
||||
config.set("tools." + tool + ".permissionDefault", "OP");
|
||||
config.set("version", "1.30");
|
||||
}
|
||||
if (config.getString("version").compareTo("1.31") < 0) {
|
||||
getLogger().info("[LogBlock] Updating tables to 1.31 ...");
|
||||
final Connection conn = logblock.getConnection();
|
||||
try {
|
||||
conn.setAutoCommit(true);
|
||||
final Statement st = conn.createStatement();
|
||||
st.execute("ALTER TABLE `lb-players` ADD COLUMN lastlogin DATETIME NOT NULL, ADD COLUMN onlinetime TIME NOT NULL, ADD COLUMN ip VARCHAR(255) NOT NULL");
|
||||
st.close();
|
||||
conn.close();
|
||||
} catch (final SQLException ex) {
|
||||
Bukkit.getLogger().log(Level.SEVERE, "[LogBlock Updater] Error: ", ex);
|
||||
return false;
|
||||
}
|
||||
config.set("version", "1.31");
|
||||
}
|
||||
if (config.getString("version").compareTo("1.32") < 0) {
|
||||
getLogger().info("[LogBlock] Updating tables to 1.32 ...");
|
||||
final Connection conn = logblock.getConnection();
|
||||
try {
|
||||
conn.setAutoCommit(true);
|
||||
final Statement st = conn.createStatement();
|
||||
st.execute("ALTER TABLE `lb-players` ADD COLUMN firstlogin DATETIME NOT NULL AFTER playername");
|
||||
st.close();
|
||||
conn.close();
|
||||
} catch (final SQLException ex) {
|
||||
Bukkit.getLogger().log(Level.SEVERE, "[LogBlock Updater] Error: ", ex);
|
||||
return false;
|
||||
}
|
||||
config.set("version", "1.32");
|
||||
}
|
||||
if (config.getString("version").compareTo("1.40") < 0) {
|
||||
getLogger().info("[LogBlock] Updating config to 1.40 ...");
|
||||
config.set("clearlog.keepLogDays", null);
|
||||
config.set("version", "1.40");
|
||||
}
|
||||
if (config.getString("version").compareTo("1.42") < 0) {
|
||||
getLogger().info("[LogBlock] Updating config to 1.42 ...");
|
||||
for (final String world : config.getStringList("loggedWorlds")) {
|
||||
final File file = new File(logblock.getDataFolder(), friendlyWorldname(world) + ".yml");
|
||||
final YamlConfiguration wcfg = YamlConfiguration.loadConfiguration(file);
|
||||
if (wcfg.contains("logBlockCreations"))
|
||||
wcfg.set("logging.BLOCKPLACE", wcfg.getBoolean("logBlockCreations"));
|
||||
if (wcfg.contains("logBlockDestroyings"))
|
||||
wcfg.set("logging.BLOCKBREAK", wcfg.getBoolean("logBlockDestroyings"));
|
||||
if (wcfg.contains("logSignTexts"))
|
||||
wcfg.set("logging.SIGNTEXT", wcfg.getBoolean("logSignTexts"));
|
||||
if (wcfg.contains("logFire"))
|
||||
wcfg.set("logging.FIRE", wcfg.getBoolean("logFire"));
|
||||
if (wcfg.contains("logLeavesDecay"))
|
||||
wcfg.set("logging.LEAVESDECAY", wcfg.getBoolean("logLeavesDecay"));
|
||||
if (wcfg.contains("logLavaFlow"))
|
||||
wcfg.set("logging.LAVAFLOW", wcfg.getBoolean("logLavaFlow"));
|
||||
if (wcfg.contains("logWaterFlow"))
|
||||
wcfg.set("logging.WATERFLOW", wcfg.getBoolean("logWaterFlow"));
|
||||
if (wcfg.contains("logChestAccess"))
|
||||
wcfg.set("logging.CHESTACCESS", wcfg.getBoolean("logChestAccess"));
|
||||
if (wcfg.contains("logButtonsAndLevers"))
|
||||
wcfg.set("logging.SWITCHINTERACT", wcfg.getBoolean("logButtonsAndLevers"));
|
||||
if (wcfg.contains("logKills"))
|
||||
wcfg.set("logging.KILL", wcfg.getBoolean("logKills"));
|
||||
if (wcfg.contains("logChat"))
|
||||
wcfg.set("logging.CHAT", wcfg.getBoolean("logChat"));
|
||||
if (wcfg.contains("logSnowForm"))
|
||||
wcfg.set("logging.SNOWFORM", wcfg.getBoolean("logSnowForm"));
|
||||
if (wcfg.contains("logSnowFade"))
|
||||
wcfg.set("logging.SNOWFADE", wcfg.getBoolean("logSnowFade"));
|
||||
if (wcfg.contains("logDoors"))
|
||||
wcfg.set("logging.DOORINTERACT", wcfg.getBoolean("logDoors"));
|
||||
if (wcfg.contains("logCakes"))
|
||||
wcfg.set("logging.CAKEEAT", wcfg.getBoolean("logCakes"));
|
||||
if (wcfg.contains("logEndermen"))
|
||||
wcfg.set("logging.ENDERMEN", wcfg.getBoolean("logEndermen"));
|
||||
if (wcfg.contains("logExplosions")) {
|
||||
final boolean logExplosions = wcfg.getBoolean("logExplosions");
|
||||
wcfg.set("logging.TNTEXPLOSION", logExplosions);
|
||||
wcfg.set("logging.MISCEXPLOSION", logExplosions);
|
||||
wcfg.set("logging.CREEPEREXPLOSION", logExplosions);
|
||||
wcfg.set("logging.GHASTFIREBALLEXPLOSION", logExplosions);
|
||||
}
|
||||
wcfg.set("logBlockCreations", null);
|
||||
wcfg.set("logBlockDestroyings", null);
|
||||
wcfg.set("logSignTexts", null);
|
||||
wcfg.set("logExplosions", null);
|
||||
wcfg.set("logFire", null);
|
||||
wcfg.set("logLeavesDecay", null);
|
||||
wcfg.set("logLavaFlow", null);
|
||||
wcfg.set("logWaterFlow", null);
|
||||
wcfg.set("logChestAccess", null);
|
||||
wcfg.set("logButtonsAndLevers", null);
|
||||
wcfg.set("logKills", null);
|
||||
wcfg.set("logChat", null);
|
||||
wcfg.set("logSnowForm", null);
|
||||
wcfg.set("logSnowFade", null);
|
||||
wcfg.set("logDoors", null);
|
||||
wcfg.set("logCakes", null);
|
||||
wcfg.set("logEndermen", null);
|
||||
try {
|
||||
wcfg.save(file);
|
||||
} catch (final IOException ex) {
|
||||
Bukkit.getLogger().log(Level.SEVERE, "[LogBlock Updater] Error: ", ex);
|
||||
}
|
||||
}
|
||||
config.set("clearlog.keepLogDays", null);
|
||||
config.set("version", "1.42");
|
||||
}
|
||||
if (config.getString("version").compareTo("1.51") < 0) {
|
||||
getLogger().info("[LogBlock] Updating tables to 1.51 ...");
|
||||
final Connection conn = logblock.getConnection();
|
||||
try {
|
||||
conn.setAutoCommit(true);
|
||||
final Statement st = conn.createStatement();
|
||||
for (final WorldConfig wcfg : getLoggedWorlds())
|
||||
if (wcfg.isLogging(Logging.KILL))
|
||||
st.execute("ALTER TABLE `" + wcfg.table + "-kills` ADD (x SMALLINT NOT NULL DEFAULT 0, y TINYINT UNSIGNED NOT NULL DEFAULT 0, z SMALLINT NOT NULL DEFAULT 0)");
|
||||
st.close();
|
||||
conn.close();
|
||||
} catch (final SQLException ex) {
|
||||
Bukkit.getLogger().log(Level.SEVERE, "[LogBlock Updater] Error: ", ex);
|
||||
return false;
|
||||
}
|
||||
config.set("version", "1.51");
|
||||
}
|
||||
if (config.getString("version").compareTo("1.52") < 0) {
|
||||
getLogger().info("[LogBlock] Updating tables to 1.52 ...");
|
||||
final Connection conn = logblock.getConnection();
|
||||
try {
|
||||
conn.setAutoCommit(true);
|
||||
final Statement st = conn.createStatement();
|
||||
final ResultSet rs = st.executeQuery("SHOW COLUMNS FROM `lb-players` WHERE field = 'onlinetime'");
|
||||
if (rs.next() && rs.getString("Type").equalsIgnoreCase("time")) {
|
||||
st.execute("ALTER TABLE `lb-players` ADD onlinetime2 INT UNSIGNED NOT NULL");
|
||||
st.execute("UPDATE `lb-players` SET onlinetime2 = HOUR(onlinetime) * 3600 + MINUTE(onlinetime) * 60 + SECOND(onlinetime)");
|
||||
st.execute("ALTER TABLE `lb-players` DROP onlinetime");
|
||||
st.execute("ALTER TABLE `lb-players` CHANGE onlinetime2 onlinetime INT UNSIGNED NOT NULL");
|
||||
} else
|
||||
getLogger().info("[LogBlock] Column lb-players was alredy modified, skipping it.");
|
||||
st.close();
|
||||
conn.close();
|
||||
} catch (final SQLException ex) {
|
||||
Bukkit.getLogger().log(Level.SEVERE, "[LogBlock Updater] Error: ", ex);
|
||||
return false;
|
||||
}
|
||||
config.set("version", "1.52");
|
||||
}
|
||||
logblock.saveConfig();
|
||||
return true;
|
||||
}
|
||||
|
||||
void checkTables() throws SQLException {
|
||||
final Connection conn = logblock.getConnection();
|
||||
if (conn == null)
|
||||
throw new SQLException("No connection");
|
||||
final Statement state = conn.createStatement();
|
||||
final DatabaseMetaData dbm = conn.getMetaData();
|
||||
conn.setAutoCommit(true);
|
||||
createTable(dbm, state, "lb-players", "(playerid SMALLINT UNSIGNED NOT NULL AUTO_INCREMENT, playername varchar(32) NOT NULL, firstlogin DATETIME NOT NULL, lastlogin DATETIME NOT NULL, onlinetime INT UNSIGNED NOT NULL, ip varchar(255) NOT NULL, PRIMARY KEY (playerid), UNIQUE (playername))");
|
||||
if (isLogging(Logging.CHAT))
|
||||
createTable(dbm, state, "lb-chat", "(id INT UNSIGNED NOT NULL AUTO_INCREMENT, date DATETIME NOT NULL, playerid SMALLINT UNSIGNED NOT NULL, message VARCHAR(255) NOT NULL, PRIMARY KEY (id), KEY playerid (playerid), FULLTEXT message (message)) ENGINE=MyISAM");
|
||||
for (final WorldConfig wcfg : getLoggedWorlds()) {
|
||||
createTable(dbm, state, wcfg.table, "(id INT UNSIGNED NOT NULL AUTO_INCREMENT, date DATETIME NOT NULL, playerid SMALLINT UNSIGNED NOT NULL, replaced TINYINT UNSIGNED NOT NULL, type TINYINT UNSIGNED NOT NULL, data TINYINT UNSIGNED NOT NULL, x SMALLINT NOT NULL, y TINYINT UNSIGNED NOT NULL, z SMALLINT NOT NULL, PRIMARY KEY (id), KEY coords (x, z, y), KEY date (date), KEY playerid (playerid))");
|
||||
createTable(dbm, state, wcfg.table + "-sign", "(id INT UNSIGNED NOT NULL, signtext VARCHAR(255) NOT NULL, PRIMARY KEY (id))");
|
||||
createTable(dbm, state, wcfg.table + "-chest", "(id INT UNSIGNED NOT NULL, itemtype SMALLINT UNSIGNED NOT NULL, itemamount SMALLINT NOT NULL, itemdata TINYINT UNSIGNED NOT NULL, PRIMARY KEY (id))");
|
||||
if (wcfg.isLogging(Logging.KILL))
|
||||
createTable(dbm, state, wcfg.table + "-kills", "(id INT UNSIGNED NOT NULL AUTO_INCREMENT, date DATETIME NOT NULL, killer SMALLINT UNSIGNED, victim SMALLINT UNSIGNED NOT NULL, weapon SMALLINT UNSIGNED NOT NULL, x SMALLINT NOT NULL, y TINYINT UNSIGNED NOT NULL, z SMALLINT NOT NULL, PRIMARY KEY (id))");
|
||||
}
|
||||
state.close();
|
||||
conn.close();
|
||||
}
|
||||
|
||||
private static void createTable(DatabaseMetaData dbm, Statement state, String table, String query) throws SQLException {
|
||||
if (!dbm.getTables(null, null, table, null).next()) {
|
||||
getLogger().log(Level.INFO, "[LogBlock] Creating table " + table + ".");
|
||||
state.execute("CREATE TABLE `" + table + "` " + query);
|
||||
if (!dbm.getTables(null, null, table, null).next())
|
||||
throw new SQLException("Table " + table + " not found and failed to create");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,230 @@
|
||||
package de.diddiz.LogBlock;
|
||||
|
||||
import static de.diddiz.LogBlock.config.Config.dontRollback;
|
||||
import static de.diddiz.LogBlock.config.Config.replaceAnyway;
|
||||
import static de.diddiz.util.BukkitUtils.equalTypes;
|
||||
import static de.diddiz.util.BukkitUtils.modifyContainer;
|
||||
import static de.diddiz.util.MaterialName.materialName;
|
||||
import static org.bukkit.Bukkit.getLogger;
|
||||
import java.io.File;
|
||||
import java.io.PrintWriter;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Queue;
|
||||
import java.util.concurrent.LinkedBlockingQueue;
|
||||
import java.util.logging.Level;
|
||||
import org.bukkit.Location;
|
||||
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.inventory.InventoryHolder;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.bukkit.material.Bed;
|
||||
import org.bukkit.material.PistonBaseMaterial;
|
||||
import org.bukkit.material.PistonExtensionMaterial;
|
||||
|
||||
public class WorldEditor implements Runnable
|
||||
{
|
||||
private final LogBlock logblock;
|
||||
private final Queue<Edit> edits = new LinkedBlockingQueue<Edit>();
|
||||
private final World world;
|
||||
private int taskID;
|
||||
private int successes = 0, blacklistCollisions = 0;
|
||||
private long elapsedTime = 0;
|
||||
public LookupCacheElement[] errors;
|
||||
|
||||
public WorldEditor(LogBlock logblock, World world) {
|
||||
this.logblock = logblock;
|
||||
this.world = world;
|
||||
}
|
||||
|
||||
public int getSize() {
|
||||
return edits.size();
|
||||
}
|
||||
|
||||
public int getSuccesses() {
|
||||
return successes;
|
||||
}
|
||||
|
||||
public int getErrors() {
|
||||
return errors.length;
|
||||
}
|
||||
|
||||
public int getBlacklistCollisions() {
|
||||
return blacklistCollisions;
|
||||
}
|
||||
|
||||
public void queueEdit(int x, int y, int z, int replaced, int type, byte data, String signtext, short itemType, short itemAmount, byte itemData) {
|
||||
edits.add(new Edit(0, new Location(world, x, y, z), null, replaced, type, data, signtext, new ChestAccess(itemType, itemAmount, itemData)));
|
||||
}
|
||||
|
||||
public long getElapsedTime() {
|
||||
return elapsedTime;
|
||||
}
|
||||
|
||||
synchronized public void start() throws Exception {
|
||||
final long start = System.currentTimeMillis();
|
||||
taskID = logblock.getServer().getScheduler().scheduleSyncRepeatingTask(logblock, this, 0, 1);
|
||||
if (taskID == -1)
|
||||
throw new Exception("Failed to schedule task");
|
||||
try {
|
||||
this.wait();
|
||||
} catch (final InterruptedException ex) {
|
||||
throw new Exception("Interrupted");
|
||||
}
|
||||
elapsedTime = System.currentTimeMillis() - start;
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void run() {
|
||||
final List<WorldEditorException> errorList = new ArrayList<WorldEditorException>();
|
||||
int counter = 0;
|
||||
while (!edits.isEmpty() && counter < 100) {
|
||||
try {
|
||||
switch (edits.poll().perform()) {
|
||||
case SUCCESS:
|
||||
successes++;
|
||||
break;
|
||||
case BLACKLISTED:
|
||||
blacklistCollisions++;
|
||||
break;
|
||||
}
|
||||
} catch (final WorldEditorException ex) {
|
||||
errorList.add(ex);
|
||||
} catch (final Exception ex) {
|
||||
getLogger().log(Level.WARNING, "[LogBlock WorldEditor] Exeption: ", ex);
|
||||
}
|
||||
counter++;
|
||||
}
|
||||
if (edits.isEmpty()) {
|
||||
logblock.getServer().getScheduler().cancelTask(taskID);
|
||||
if (errorList.size() > 0)
|
||||
try {
|
||||
final File file = new File("plugins/LogBlock/error/WorldEditor-" + new SimpleDateFormat("yy-MM-dd-HH-mm-ss").format(System.currentTimeMillis()) + ".log");
|
||||
file.getParentFile().mkdirs();
|
||||
final PrintWriter writer = new PrintWriter(file);
|
||||
for (final LookupCacheElement err : errorList)
|
||||
writer.println(err.getMessage());
|
||||
writer.close();
|
||||
} catch (final Exception ex) {
|
||||
}
|
||||
errors = errorList.toArray(new WorldEditorException[errorList.size()]);
|
||||
notify();
|
||||
}
|
||||
}
|
||||
|
||||
private static enum PerformResult
|
||||
{
|
||||
SUCCESS, BLACKLISTED, NO_ACTION
|
||||
}
|
||||
|
||||
private class Edit extends BlockChange
|
||||
{
|
||||
public Edit(long time, Location loc, String playerName, int replaced, int type, byte data, String signtext, ChestAccess ca) {
|
||||
super(time, loc, playerName, replaced, type, data, signtext, ca);
|
||||
}
|
||||
|
||||
PerformResult perform() throws WorldEditorException {
|
||||
if (dontRollback.contains(replaced))
|
||||
return PerformResult.BLACKLISTED;
|
||||
final Block block = loc.getBlock();
|
||||
if (replaced == 0 && block.getTypeId() == 0)
|
||||
return PerformResult.NO_ACTION;
|
||||
final BlockState state = block.getState();
|
||||
if (!world.isChunkLoaded(block.getChunk()))
|
||||
world.loadChunk(block.getChunk());
|
||||
if (type == replaced) {
|
||||
if (type == 0) {
|
||||
if (!block.setTypeId(0))
|
||||
throw new WorldEditorException(block.getTypeId(), 0, block.getLocation());
|
||||
} else if (ca != null && (type == 23 || type == 54 || type == 61 || type == 62)) {
|
||||
int leftover = 0;
|
||||
try {
|
||||
leftover = modifyContainer(state, new ItemStack(ca.itemType, -ca.itemAmount, (short)0, ca.itemData));
|
||||
if (leftover > 0)
|
||||
for (final BlockFace face : new BlockFace[]{BlockFace.NORTH, BlockFace.SOUTH, BlockFace.EAST, BlockFace.WEST})
|
||||
if (block.getRelative(face).getTypeId() == 54)
|
||||
leftover = modifyContainer(block.getRelative(face).getState(), new ItemStack(ca.itemType, ca.itemAmount < 0 ? leftover : -leftover, (short)0, ca.itemData));
|
||||
} catch (final Exception ex) {
|
||||
throw new WorldEditorException(ex.getMessage(), block.getLocation());
|
||||
}
|
||||
if (!state.update())
|
||||
throw new WorldEditorException("Failed to update inventory of " + materialName(block.getTypeId()), block.getLocation());
|
||||
if (leftover > 0 && ca.itemAmount < 0)
|
||||
throw new WorldEditorException("Not enough space left in " + materialName(block.getTypeId()), block.getLocation());
|
||||
} else
|
||||
return PerformResult.NO_ACTION;
|
||||
return PerformResult.SUCCESS;
|
||||
}
|
||||
if (!(equalTypes(block.getTypeId(), type) || replaceAnyway.contains(block.getTypeId())))
|
||||
return PerformResult.NO_ACTION;
|
||||
if (state instanceof InventoryHolder) {
|
||||
((InventoryHolder)state).getInventory().clear();
|
||||
state.update();
|
||||
}
|
||||
if (block.getTypeId() == replaced) {
|
||||
if (block.getData() != (type == 0 ? data : (byte)0))
|
||||
block.setData(type == 0 ? data : (byte)0, true);
|
||||
else
|
||||
return PerformResult.NO_ACTION;
|
||||
} else if (!block.setTypeIdAndData(replaced, type == 0 ? data : (byte)0, true))
|
||||
throw new WorldEditorException(block.getTypeId(), replaced, block.getLocation());
|
||||
final int curtype = block.getTypeId();
|
||||
if (signtext != null && (curtype == 63 || curtype == 68)) {
|
||||
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 " + materialName(block.getTypeId()), block.getLocation());
|
||||
} else if (curtype == 26) {
|
||||
final Bed bed = (Bed)block.getState().getData();
|
||||
final Block secBlock = bed.isHeadOfBed() ? block.getRelative(bed.getFacing().getOppositeFace()) : block.getRelative(bed.getFacing());
|
||||
if (secBlock.getTypeId() == 0 && !secBlock.setTypeIdAndData(26, (byte)(bed.getData() | 8), true))
|
||||
throw new WorldEditorException(secBlock.getTypeId(), 26, secBlock.getLocation());
|
||||
} else if (curtype == 64 || curtype == 71) {
|
||||
final byte blockData = block.getData();
|
||||
final Block secBlock = (blockData & 8) == 8 ? block.getRelative(BlockFace.DOWN) : block.getRelative(BlockFace.UP);
|
||||
if (secBlock.getTypeId() == 0 && !secBlock.setTypeIdAndData(curtype, (byte)(blockData | 8), true))
|
||||
throw new WorldEditorException(secBlock.getTypeId(), curtype, secBlock.getLocation());
|
||||
} else if ((curtype == 29 || curtype == 33) && (block.getData() & 8) > 0) {
|
||||
final PistonBaseMaterial piston = (PistonBaseMaterial)block.getState().getData();
|
||||
final Block secBlock = block.getRelative(piston.getFacing());
|
||||
if (secBlock.getTypeId() == 0 && !secBlock.setTypeIdAndData(34, curtype == 29 ? (byte)(block.getData() | 8) : (byte)(block.getData() & ~8), true))
|
||||
throw new WorldEditorException(secBlock.getTypeId(), 34, secBlock.getLocation());
|
||||
} else if (curtype == 34) {
|
||||
final PistonExtensionMaterial piston = (PistonExtensionMaterial)block.getState().getData();
|
||||
final Block secBlock = block.getRelative(piston.getFacing().getOppositeFace());
|
||||
if (secBlock.getTypeId() == 0 && !secBlock.setTypeIdAndData(piston.isSticky() ? 29 : 33, (byte)(block.getData() | 8), true))
|
||||
throw new WorldEditorException(secBlock.getTypeId(), piston.isSticky() ? 29 : 33, secBlock.getLocation());
|
||||
} else if (curtype == 18 && (block.getData() & 8) > 0)
|
||||
block.setData((byte)(block.getData() & 0xF7));
|
||||
return PerformResult.SUCCESS;
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("serial")
|
||||
public static class WorldEditorException extends Exception implements LookupCacheElement
|
||||
{
|
||||
private final Location loc;
|
||||
|
||||
public WorldEditorException(int typeBefore, int typeAfter, Location loc) {
|
||||
this("Failed to replace " + materialName(typeBefore) + " with " + materialName(typeAfter), loc);
|
||||
}
|
||||
|
||||
public WorldEditorException(String msg, Location loc) {
|
||||
super(msg + " at " + loc.getWorld().getName() + ":" + loc.getBlockX() + ":" + loc.getBlockY() + ":" + loc.getBlockZ());
|
||||
this.loc = loc;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Location getLocation() {
|
||||
return loc;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,261 @@
|
||||
package de.diddiz.LogBlock.config;
|
||||
|
||||
import static de.diddiz.util.BukkitUtils.friendlyWorldname;
|
||||
import static de.diddiz.util.Utils.parseTimeSpec;
|
||||
import static org.bukkit.Bukkit.getConsoleSender;
|
||||
import static org.bukkit.Bukkit.getLogger;
|
||||
import static org.bukkit.Bukkit.getWorlds;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Set;
|
||||
import java.util.logging.Level;
|
||||
import java.util.zip.DataFormatException;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.configuration.ConfigurationSection;
|
||||
import org.bukkit.permissions.PermissionDefault;
|
||||
import de.diddiz.LogBlock.LogBlock;
|
||||
import de.diddiz.LogBlock.Logging;
|
||||
import de.diddiz.LogBlock.QueryParams;
|
||||
import de.diddiz.LogBlock.Tool;
|
||||
import de.diddiz.LogBlock.ToolBehavior;
|
||||
import de.diddiz.LogBlock.ToolMode;
|
||||
|
||||
public class Config
|
||||
{
|
||||
private static LoggingEnabledMapping superWorldConfig;
|
||||
private static Map<String, WorldConfig> worldConfigs;
|
||||
public static String url, user, password;
|
||||
public static int delayBetweenRuns, forceToProcessAtLeast, timePerRun;
|
||||
public static boolean useBukkitScheduler;
|
||||
public static boolean enableAutoClearLog;
|
||||
public static List<String> autoClearLog;
|
||||
public static int autoClearLogDelay;
|
||||
public static boolean dumpDeletedLog;
|
||||
public static boolean logCreeperExplosionsAsPlayerWhoTriggeredThese, logPlayerInfo;
|
||||
public static LogKillsLevel logKillsLevel;
|
||||
public static Set<Integer> dontRollback, replaceAnyway;
|
||||
public static int rollbackMaxTime, rollbackMaxArea;
|
||||
public static Map<String, Tool> toolsByName;
|
||||
public static Map<Integer, Tool> toolsByType;
|
||||
public static int defaultDist, defaultTime;
|
||||
public static int linesPerPage, linesLimit;
|
||||
public static boolean askRollbacks, askRedos, askClearLogs, askClearLogAfterRollback, askRollbackAfterBan;
|
||||
public static String banPermission;
|
||||
public static Set<Integer> hiddenBlocks;
|
||||
public static Set<String> hiddenPlayers;
|
||||
|
||||
public static enum LogKillsLevel
|
||||
{
|
||||
PLAYERS, MONSTERS, ANIMALS;
|
||||
}
|
||||
|
||||
public static void load(LogBlock logblock) throws DataFormatException, IOException {
|
||||
final ConfigurationSection config = logblock.getConfig();
|
||||
final Map<String, Object> def = new HashMap<String, Object>();
|
||||
def.put("version", logblock.getDescription().getVersion());
|
||||
final List<String> worldNames = new ArrayList<String>();
|
||||
for (final World world : getWorlds())
|
||||
worldNames.add(world.getName());
|
||||
if (worldNames.isEmpty()) {
|
||||
worldNames.add("world");
|
||||
worldNames.add("world_nether");
|
||||
worldNames.add("world_the_end");
|
||||
}
|
||||
def.put("loggedWorlds", worldNames);
|
||||
def.put("mysql.host", "localhost");
|
||||
def.put("mysql.port", 3306);
|
||||
def.put("mysql.database", "minecraft");
|
||||
def.put("mysql.user", "username");
|
||||
def.put("mysql.password", "pass");
|
||||
def.put("consumer.delayBetweenRuns", 6);
|
||||
def.put("consumer.forceToProcessAtLeast", 20);
|
||||
def.put("consumer.timePerRun", 200);
|
||||
def.put("consumer.useBukkitScheduler", true);
|
||||
def.put("clearlog.dumpDeletedLog", false);
|
||||
def.put("clearlog.enableAutoClearLog", false);
|
||||
def.put("clearlog.auto", Arrays.asList("world \"world\" before 365 days all", "world \"world\" player lavaflow waterflow leavesdecay before 7 days all", "world world_nether before 365 days all", "world world_nether player lavaflow before 7 days all"));
|
||||
def.put("clearlog.autoClearLogDelay", "6h");
|
||||
def.put("logging.logCreeperExplosionsAsPlayerWhoTriggeredThese", false);
|
||||
def.put("logging.logKillsLevel", "PLAYERS");
|
||||
def.put("logging.logPlayerInfo", false);
|
||||
def.put("logging.hiddenPlayers", new ArrayList<String>());
|
||||
def.put("logging.hiddenBlocks", Arrays.asList(0));
|
||||
def.put("rollback.dontRollback", Arrays.asList(10, 11, 46, 51));
|
||||
def.put("rollback.replaceAnyway", Arrays.asList(8, 9, 10, 11, 51));
|
||||
def.put("rollback.maxTime", "2 days");
|
||||
def.put("rollback.maxArea", 50);
|
||||
def.put("lookup.defaultDist", 20);
|
||||
def.put("lookup.defaultTime", "30 minutes");
|
||||
def.put("lookup.linesPerPage", 15);
|
||||
def.put("lookup.linesLimit", 1500);
|
||||
def.put("questioner.askRollbacks", true);
|
||||
def.put("questioner.askRedos", true);
|
||||
def.put("questioner.askClearLogs", true);
|
||||
def.put("questioner.askClearLogAfterRollback", true);
|
||||
def.put("questioner.askRollbackAfterBan", false);
|
||||
def.put("questioner.banPermission", "mcbans.ban.local");
|
||||
def.put("tools.tool.aliases", Arrays.asList("t"));
|
||||
def.put("tools.tool.leftClickBehavior", "NONE");
|
||||
def.put("tools.tool.rightClickBehavior", "TOOL");
|
||||
def.put("tools.tool.defaultEnabled", true);
|
||||
def.put("tools.tool.item", 270);
|
||||
def.put("tools.tool.canDrop", true);
|
||||
def.put("tools.tool.params", "area 0 all sum none limit 15 desc silent");
|
||||
def.put("tools.tool.mode", "LOOKUP");
|
||||
def.put("tools.tool.permissionDefault", "TRUE");
|
||||
def.put("tools.toolblock.aliases", Arrays.asList("tb"));
|
||||
def.put("tools.toolblock.leftClickBehavior", "TOOL");
|
||||
def.put("tools.toolblock.rightClickBehavior", "BLOCK");
|
||||
def.put("tools.toolblock.defaultEnabled", true);
|
||||
def.put("tools.toolblock.item", 7);
|
||||
def.put("tools.toolblock.canDrop", false);
|
||||
def.put("tools.toolblock.params", "area 0 all sum none limit 15 desc silent");
|
||||
def.put("tools.toolblock.mode", "LOOKUP");
|
||||
def.put("tools.toolblock.permissionDefault", "TRUE");
|
||||
for (final Entry<String, Object> e : def.entrySet())
|
||||
if (!config.contains(e.getKey()))
|
||||
config.set(e.getKey(), e.getValue());
|
||||
logblock.saveConfig();
|
||||
url = "jdbc:mysql://" + config.getString("mysql.host") + ":" + config.getInt("mysql.port") + "/" + getStringIncludingInts(config, "mysql.database");
|
||||
String username = config.getString("mysql.user");
|
||||
if (username == null)
|
||||
username = String.valueOf(config.getInt("mysql.user"));
|
||||
user = getStringIncludingInts(config, "mysql.user");
|
||||
password = getStringIncludingInts(config, "mysql.password");
|
||||
delayBetweenRuns = config.getInt("consumer.delayBetweenRuns", 6);
|
||||
forceToProcessAtLeast = config.getInt("consumer.forceToProcessAtLeast", 0);
|
||||
timePerRun = config.getInt("consumer.timePerRun", 100);
|
||||
useBukkitScheduler = config.getBoolean("consumer.useBukkitScheduler", true);
|
||||
enableAutoClearLog = config.getBoolean("clearlog.enableAutoClearLog");
|
||||
autoClearLog = config.getStringList("clearlog.auto");
|
||||
dumpDeletedLog = config.getBoolean("clearlog.dumpDeletedLog", false);
|
||||
autoClearLogDelay = parseTimeSpec(config.getString("clearlog.autoClearLogDelay").split(" "));
|
||||
logCreeperExplosionsAsPlayerWhoTriggeredThese = config.getBoolean("logging.logCreeperExplosionsAsPlayerWhoTriggeredThese", false);
|
||||
logPlayerInfo = config.getBoolean("logging.logPlayerInfo", true);
|
||||
try {
|
||||
logKillsLevel = LogKillsLevel.valueOf(config.getString("logging.logKillsLevel").toUpperCase());
|
||||
} catch (final IllegalArgumentException ex) {
|
||||
throw new DataFormatException("lookup.toolblockID doesn't appear to be a valid log level. Allowed are 'PLAYERS', 'MONSTERS' and 'ANIMALS'");
|
||||
}
|
||||
hiddenPlayers = new HashSet<String>();
|
||||
for (final String playerName : config.getStringList("logging.hiddenPlayers"))
|
||||
hiddenPlayers.add(playerName);
|
||||
hiddenBlocks = new HashSet<Integer>();
|
||||
for (final Object blocktype : config.getList("logging.hiddenBlocks")) {
|
||||
final Material mat = Material.matchMaterial(String.valueOf(blocktype));
|
||||
if (mat != null)
|
||||
hiddenBlocks.add(mat.getId());
|
||||
else
|
||||
throw new DataFormatException("Not a valid material: '" + blocktype + "'");
|
||||
}
|
||||
dontRollback = new HashSet<Integer>(config.getIntegerList("rollback.dontRollback"));
|
||||
replaceAnyway = new HashSet<Integer>(config.getIntegerList("rollback.replaceAnyway"));
|
||||
rollbackMaxTime = parseTimeSpec(config.getString("rollback.maxTime").split(" "));
|
||||
rollbackMaxArea = config.getInt("rollback.maxArea", 50);
|
||||
defaultDist = config.getInt("lookup.defaultDist", 20);
|
||||
defaultTime = parseTimeSpec(config.getString("lookup.defaultTime").split(" "));
|
||||
linesPerPage = config.getInt("lookup.linesPerPage", 15);
|
||||
linesLimit = config.getInt("lookup.linesLimit", 1500);
|
||||
askRollbacks = config.getBoolean("questioner.askRollbacks", true);
|
||||
askRedos = config.getBoolean("questioner.askRedos", true);
|
||||
askClearLogs = config.getBoolean("questioner.askClearLogs", true);
|
||||
askClearLogAfterRollback = config.getBoolean("questioner.askClearLogAfterRollback", true);
|
||||
askRollbackAfterBan = config.getBoolean("questioner.askRollbackAfterBan", false);
|
||||
banPermission = config.getString("questioner.banPermission");
|
||||
final List<Tool> tools = new ArrayList<Tool>();
|
||||
final ConfigurationSection toolsSec = config.getConfigurationSection("tools");
|
||||
for (final String toolName : toolsSec.getKeys(false))
|
||||
try {
|
||||
final ConfigurationSection tSec = toolsSec.getConfigurationSection(toolName);
|
||||
final List<String> aliases = tSec.getStringList("aliases");
|
||||
final ToolBehavior leftClickBehavior = ToolBehavior.valueOf(tSec.getString("leftClickBehavior").toUpperCase());
|
||||
final ToolBehavior rightClickBehavior = ToolBehavior.valueOf(tSec.getString("rightClickBehavior").toUpperCase());
|
||||
final boolean defaultEnabled = tSec.getBoolean("defaultEnabled", false);
|
||||
final int item = tSec.getInt("item", 0);
|
||||
final boolean canDrop = tSec.getBoolean("canDrop", false);
|
||||
final QueryParams params = new QueryParams(logblock);
|
||||
params.prepareToolQuery = true;
|
||||
params.parseArgs(getConsoleSender(), Arrays.asList(tSec.getString("params").split(" ")));
|
||||
final ToolMode mode = ToolMode.valueOf(tSec.getString("mode").toUpperCase());
|
||||
final PermissionDefault pdef = PermissionDefault.valueOf(tSec.getString("permissionDefault").toUpperCase());
|
||||
tools.add(new Tool(toolName, aliases, leftClickBehavior, rightClickBehavior, defaultEnabled, item, canDrop, params, mode, pdef));
|
||||
} catch (final Exception ex) {
|
||||
getLogger().log(Level.WARNING, "Error at parsing tool '" + toolName + "': ", ex);
|
||||
}
|
||||
toolsByName = new HashMap<String, Tool>();
|
||||
toolsByType = new HashMap<Integer, Tool>();
|
||||
for (final Tool tool : tools) {
|
||||
toolsByType.put(tool.item, tool);
|
||||
toolsByName.put(tool.name.toLowerCase(), tool);
|
||||
for (final String alias : tool.aliases)
|
||||
toolsByName.put(alias, tool);
|
||||
}
|
||||
final List<String> loggedWorlds = config.getStringList("loggedWorlds");
|
||||
worldConfigs = new HashMap<String, WorldConfig>();
|
||||
if (loggedWorlds.size() == 0)
|
||||
throw new DataFormatException("No worlds configured");
|
||||
for (final String world : loggedWorlds)
|
||||
worldConfigs.put(world, new WorldConfig(new File(logblock.getDataFolder(), friendlyWorldname(world) + ".yml")));
|
||||
superWorldConfig = new LoggingEnabledMapping();
|
||||
for (final WorldConfig wcfg : worldConfigs.values())
|
||||
for (final Logging l : Logging.values())
|
||||
if (wcfg.isLogging(l))
|
||||
superWorldConfig.setLogging(l, true);
|
||||
}
|
||||
|
||||
private static String getStringIncludingInts(ConfigurationSection cfg, String key) {
|
||||
String str = cfg.getString(key);
|
||||
if (str == null)
|
||||
str = String.valueOf(cfg.getInt(key));
|
||||
if (str == null)
|
||||
str = "No value set for '" + key + "'";
|
||||
return str;
|
||||
}
|
||||
|
||||
public static boolean isLogging(World world, Logging l) {
|
||||
final WorldConfig wcfg = worldConfigs.get(world.getName());
|
||||
return wcfg != null && wcfg.isLogging(l);
|
||||
}
|
||||
|
||||
public static boolean isLogged(World world) {
|
||||
return worldConfigs.containsKey(world.getName());
|
||||
}
|
||||
|
||||
public static WorldConfig getWorldConfig(World world) {
|
||||
return worldConfigs.get(world.getName());
|
||||
}
|
||||
|
||||
public static WorldConfig getWorldConfig(String world) {
|
||||
return worldConfigs.get(world);
|
||||
}
|
||||
|
||||
public static boolean isLogging(Logging l) {
|
||||
return superWorldConfig.isLogging(l);
|
||||
}
|
||||
|
||||
public static Collection<WorldConfig> getLoggedWorlds() {
|
||||
return worldConfigs.values();
|
||||
}
|
||||
}
|
||||
|
||||
class LoggingEnabledMapping
|
||||
{
|
||||
private final boolean[] logging = new boolean[Logging.length];
|
||||
|
||||
public void setLogging(Logging l, boolean enabled) {
|
||||
logging[l.ordinal()] = enabled;
|
||||
}
|
||||
|
||||
public boolean isLogging(Logging l) {
|
||||
return logging[l.ordinal()];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
package de.diddiz.LogBlock.config;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import org.bukkit.configuration.file.YamlConfiguration;
|
||||
import de.diddiz.LogBlock.Logging;
|
||||
|
||||
public class WorldConfig extends LoggingEnabledMapping
|
||||
{
|
||||
public final String table;
|
||||
|
||||
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(' ', '_'));
|
||||
for (final Logging l : Logging.values())
|
||||
def.put("logging." + l.toString(), l.isDefaultEnabled());
|
||||
final YamlConfiguration config = YamlConfiguration.loadConfiguration(file);
|
||||
for (final Entry<String, Object> e : def.entrySet())
|
||||
if (config.get(e.getKey()) == null)
|
||||
config.set(e.getKey(), e.getValue());
|
||||
config.save(file);
|
||||
table = config.getString("table");
|
||||
for (final Logging l : Logging.values())
|
||||
setLogging(l, config.getBoolean("logging." + l.toString()));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
package de.diddiz.LogBlock.listeners;
|
||||
|
||||
import static de.diddiz.LogBlock.config.Config.banPermission;
|
||||
import static de.diddiz.LogBlock.config.Config.isLogged;
|
||||
import static org.bukkit.Bukkit.getScheduler;
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.Listener;
|
||||
import org.bukkit.event.player.PlayerCommandPreprocessEvent;
|
||||
import de.diddiz.LogBlock.CommandsHandler;
|
||||
import de.diddiz.LogBlock.LogBlock;
|
||||
import de.diddiz.LogBlock.QueryParams;
|
||||
|
||||
public class BanListener implements Listener
|
||||
{
|
||||
private final CommandsHandler handler;
|
||||
private final LogBlock logblock;
|
||||
|
||||
public BanListener(LogBlock logblock) {
|
||||
this.logblock = logblock;
|
||||
handler = logblock.getCommandsHandler();
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
public void onPlayerCommandPreprocess(final PlayerCommandPreprocessEvent event) {
|
||||
final String[] split = event.getMessage().split(" ");
|
||||
if (split.length > 1 && split[0].equalsIgnoreCase("/ban") && logblock.hasPermission(event.getPlayer(), banPermission)) {
|
||||
final QueryParams p = new QueryParams(logblock);
|
||||
p.setPlayer(split[1].equalsIgnoreCase("g") ? split[2] : split[1]);
|
||||
p.since = 0;
|
||||
p.silent = false;
|
||||
getScheduler().scheduleAsyncDelayedTask(logblock, new Runnable()
|
||||
{
|
||||
@Override
|
||||
public void run() {
|
||||
for (final World world : logblock.getServer().getWorlds())
|
||||
if (isLogged(world)) {
|
||||
p.world = world;
|
||||
try {
|
||||
handler.new CommandRollback(event.getPlayer(), p, false);
|
||||
} catch (final Exception ex) {
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
package de.diddiz.LogBlock.listeners;
|
||||
|
||||
import static de.diddiz.LogBlock.config.Config.getWorldConfig;
|
||||
import static de.diddiz.LogBlock.config.Config.isLogging;
|
||||
import org.bukkit.block.Sign;
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.EventPriority;
|
||||
import org.bukkit.event.block.BlockBreakEvent;
|
||||
import org.bukkit.event.player.PlayerBucketFillEvent;
|
||||
import de.diddiz.LogBlock.LogBlock;
|
||||
import de.diddiz.LogBlock.Logging;
|
||||
import de.diddiz.LogBlock.config.WorldConfig;
|
||||
|
||||
public class BlockBreakLogging extends LoggingListener
|
||||
{
|
||||
public BlockBreakLogging(LogBlock lb) {
|
||||
super(lb);
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
|
||||
public void onBlockBreak(BlockBreakEvent event) {
|
||||
final WorldConfig wcfg = getWorldConfig(event.getBlock().getWorld());
|
||||
if (wcfg != null && wcfg.isLogging(Logging.BLOCKBREAK)) {
|
||||
final int type = event.getBlock().getTypeId();
|
||||
if (wcfg.isLogging(Logging.SIGNTEXT) && (type == 63 || type == 68))
|
||||
consumer.queueSignBreak(event.getPlayer().getName(), (Sign)event.getBlock().getState());
|
||||
else if (wcfg.isLogging(Logging.CHESTACCESS) && (type == 23 || type == 54 || type == 61))
|
||||
consumer.queueContainerBreak(event.getPlayer().getName(), event.getBlock().getState());
|
||||
else if (type == 79)
|
||||
consumer.queueBlockReplace(event.getPlayer().getName(), event.getBlock().getState(), 9, (byte)0);
|
||||
else
|
||||
consumer.queueBlockBreak(event.getPlayer().getName(), event.getBlock().getState());
|
||||
}
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
|
||||
public void onPlayerBucketFill(PlayerBucketFillEvent event) {
|
||||
if (isLogging(event.getPlayer().getWorld(), Logging.BLOCKBREAK))
|
||||
consumer.queueBlockBreak(event.getPlayer().getName(), event.getBlockClicked().getState());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
package de.diddiz.LogBlock.listeners;
|
||||
|
||||
import static de.diddiz.LogBlock.config.Config.isLogging;
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.EventPriority;
|
||||
import org.bukkit.event.block.BlockBurnEvent;
|
||||
import de.diddiz.LogBlock.LogBlock;
|
||||
import de.diddiz.LogBlock.Logging;
|
||||
|
||||
public class BlockBurnLogging extends LoggingListener
|
||||
{
|
||||
public BlockBurnLogging(LogBlock lb) {
|
||||
super(lb);
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
|
||||
public void onBlockBurn(BlockBurnEvent event) {
|
||||
if (isLogging(event.getBlock().getWorld(), Logging.FIRE))
|
||||
consumer.queueBlockBreak("Fire", event.getBlock().getState());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
package de.diddiz.LogBlock.listeners;
|
||||
|
||||
import static de.diddiz.LogBlock.config.Config.getWorldConfig;
|
||||
import static de.diddiz.LogBlock.config.Config.isLogging;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.block.BlockState;
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.EventPriority;
|
||||
import org.bukkit.event.block.BlockPlaceEvent;
|
||||
import org.bukkit.event.player.PlayerBucketEmptyEvent;
|
||||
import org.bukkit.material.MaterialData;
|
||||
import de.diddiz.LogBlock.LogBlock;
|
||||
import de.diddiz.LogBlock.Logging;
|
||||
import de.diddiz.LogBlock.config.WorldConfig;
|
||||
|
||||
public class BlockPlaceLogging extends LoggingListener
|
||||
{
|
||||
public BlockPlaceLogging(LogBlock lb) {
|
||||
super(lb);
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
|
||||
public void onBlockPlace(BlockPlaceEvent event) {
|
||||
final WorldConfig wcfg = getWorldConfig(event.getBlock().getWorld());
|
||||
if (wcfg != null && wcfg.isLogging(Logging.BLOCKPLACE)) {
|
||||
final int type = event.getBlock().getTypeId();
|
||||
final BlockState before = event.getBlockReplacedState();
|
||||
final BlockState after = event.getBlockPlaced().getState();
|
||||
final String playerName = event.getPlayer().getName();
|
||||
if (type == 0 && event.getItemInHand() != null) {
|
||||
if (event.getItemInHand().getTypeId() == 51)
|
||||
return;
|
||||
after.setTypeId(event.getItemInHand().getTypeId());
|
||||
after.setData(new MaterialData(event.getItemInHand().getTypeId()));
|
||||
}
|
||||
// Delay queuing of stairs and blocks by 1 tick to allow the raw data to update
|
||||
if (type == 53 || type == 67 || type == 108 || type == 109 || type == 114 || type == 128 || type == 134 || type == 135 || type == 136 || type == 26) {
|
||||
LogBlock.getInstance().getServer().getScheduler().scheduleSyncDelayedTask(LogBlock.getInstance(), new Runnable()
|
||||
{
|
||||
@Override
|
||||
public void run() {
|
||||
if (before.getTypeId() == 0)
|
||||
consumer.queueBlockPlace(playerName, after);
|
||||
else
|
||||
consumer.queueBlockReplace(playerName, before, after);
|
||||
}
|
||||
}, 1L);
|
||||
return;
|
||||
}
|
||||
if (wcfg.isLogging(Logging.SIGNTEXT) && (type == 63 || type == 68))
|
||||
return;
|
||||
if (before.getTypeId() == 0)
|
||||
consumer.queueBlockPlace(event.getPlayer().getName(), after);
|
||||
else
|
||||
consumer.queueBlockReplace(event.getPlayer().getName(), before, after);
|
||||
}
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
|
||||
public void onPlayerBucketEmpty(PlayerBucketEmptyEvent event) {
|
||||
if (isLogging(event.getPlayer().getWorld(), Logging.BLOCKPLACE))
|
||||
consumer.queueBlockPlace(event.getPlayer().getName(), event.getBlockClicked().getRelative(event.getBlockFace()).getLocation(), event.getBucket() == Material.WATER_BUCKET ? 9 : 11, (byte)0);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
package de.diddiz.LogBlock.listeners;
|
||||
|
||||
import static de.diddiz.LogBlock.config.Config.isLogging;
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.EventPriority;
|
||||
import org.bukkit.event.player.PlayerChatEvent;
|
||||
import org.bukkit.event.player.PlayerCommandPreprocessEvent;
|
||||
import org.bukkit.event.server.ServerCommandEvent;
|
||||
import de.diddiz.LogBlock.LogBlock;
|
||||
import de.diddiz.LogBlock.Logging;
|
||||
|
||||
public class ChatLogging extends LoggingListener
|
||||
{
|
||||
public ChatLogging(LogBlock lb) {
|
||||
super(lb);
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.MONITOR)
|
||||
public void onPlayerCommandPreprocess(PlayerCommandPreprocessEvent event) {
|
||||
if (isLogging(event.getPlayer().getWorld(), Logging.CHAT))
|
||||
consumer.queueChat(event.getPlayer().getName(), event.getMessage());
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.MONITOR)
|
||||
public void onPlayerChat(PlayerChatEvent event) {
|
||||
if (isLogging(event.getPlayer().getWorld(), Logging.CHAT))
|
||||
consumer.queueChat(event.getPlayer().getName(), event.getMessage());
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.MONITOR)
|
||||
public void onServerCommand(ServerCommandEvent event) {
|
||||
consumer.queueChat("Console", "/" + event.getCommand());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
package de.diddiz.LogBlock.listeners;
|
||||
|
||||
import static de.diddiz.util.BukkitUtils.compareInventories;
|
||||
import static de.diddiz.util.BukkitUtils.compressInventory;
|
||||
import static de.diddiz.util.BukkitUtils.getInventoryHolderLocation;
|
||||
import static de.diddiz.util.BukkitUtils.getInventoryHolderType;
|
||||
import static de.diddiz.util.BukkitUtils.rawData;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.block.BlockState;
|
||||
import org.bukkit.block.DoubleChest;
|
||||
import org.bukkit.entity.HumanEntity;
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.EventPriority;
|
||||
import org.bukkit.event.inventory.InventoryCloseEvent;
|
||||
import org.bukkit.event.inventory.InventoryOpenEvent;
|
||||
import org.bukkit.inventory.InventoryHolder;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import de.diddiz.LogBlock.LogBlock;
|
||||
|
||||
public class ChestAccessLogging extends LoggingListener
|
||||
{
|
||||
private final Map<HumanEntity, ItemStack[]> containers = new HashMap<HumanEntity, ItemStack[]>();
|
||||
|
||||
public ChestAccessLogging(LogBlock lb) {
|
||||
super(lb);
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
|
||||
public void onInventoryClose(InventoryCloseEvent event) {
|
||||
InventoryHolder holder = event.getInventory().getHolder();
|
||||
if (holder instanceof BlockState || holder instanceof DoubleChest) {
|
||||
final HumanEntity player = event.getPlayer();
|
||||
final ItemStack[] before = containers.get(player);
|
||||
if (before != null) {
|
||||
final ItemStack[] after = compressInventory(event.getInventory().getContents());
|
||||
final ItemStack[] diff = compareInventories(before, after);
|
||||
final Location loc = getInventoryHolderLocation(holder);
|
||||
for (final ItemStack item : diff) {
|
||||
consumer.queueChestAccess(player.getName(), loc, loc.getWorld().getBlockTypeIdAt(loc), (short)item.getTypeId(), (short)item.getAmount(), rawData(item));
|
||||
}
|
||||
containers.remove(player);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
|
||||
public void onInventoryOpen(InventoryOpenEvent event) {
|
||||
if (event.getInventory() != null) {
|
||||
InventoryHolder holder = event.getInventory().getHolder();
|
||||
if (holder instanceof BlockState || holder instanceof DoubleChest) {
|
||||
if (getInventoryHolderType(holder) != 58) {
|
||||
containers.put(event.getPlayer(), compressInventory(event.getInventory().getContents()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package de.diddiz.LogBlock.listeners;
|
||||
|
||||
import static de.diddiz.LogBlock.config.Config.isLogging;
|
||||
import org.bukkit.entity.Enderman;
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.EventPriority;
|
||||
import org.bukkit.event.entity.EntityChangeBlockEvent;
|
||||
import de.diddiz.LogBlock.LogBlock;
|
||||
import de.diddiz.LogBlock.Logging;
|
||||
|
||||
public class EndermenLogging extends LoggingListener
|
||||
{
|
||||
public EndermenLogging(LogBlock lb) {
|
||||
super(lb);
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
|
||||
public void onEntityChangeBlock(EntityChangeBlockEvent event) {
|
||||
if (event.getEntity() instanceof Enderman && isLogging(event.getBlock().getWorld(), Logging.ENDERMEN))
|
||||
consumer.queueBlockReplace("Enderman", event.getBlock().getState(), event.getTo().getId(), (byte)0); // Figure out how to get the data of the placed block;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
package de.diddiz.LogBlock.listeners;
|
||||
|
||||
import static de.diddiz.LogBlock.config.Config.getWorldConfig;
|
||||
import static de.diddiz.LogBlock.config.Config.logCreeperExplosionsAsPlayerWhoTriggeredThese;
|
||||
import org.bukkit.block.Block;
|
||||
import org.bukkit.block.Sign;
|
||||
import org.bukkit.entity.Creeper;
|
||||
import org.bukkit.entity.EnderDragon;
|
||||
import org.bukkit.entity.Entity;
|
||||
import org.bukkit.entity.Fireball;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.entity.TNTPrimed;
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.EventPriority;
|
||||
import org.bukkit.event.entity.EntityExplodeEvent;
|
||||
import de.diddiz.LogBlock.LogBlock;
|
||||
import de.diddiz.LogBlock.Logging;
|
||||
import de.diddiz.LogBlock.config.WorldConfig;
|
||||
|
||||
public class ExplosionLogging extends LoggingListener
|
||||
{
|
||||
public ExplosionLogging(LogBlock lb) {
|
||||
super(lb);
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
|
||||
public void onEntityExplode(EntityExplodeEvent event) {
|
||||
final WorldConfig wcfg = getWorldConfig(event.getLocation().getWorld());
|
||||
if (wcfg != null) {
|
||||
final String name;
|
||||
if (event.getEntity() == null) {
|
||||
if (!wcfg.isLogging(Logging.MISCEXPLOSION))
|
||||
return;
|
||||
name = "Explosion";
|
||||
} else if (event.getEntity() instanceof TNTPrimed) {
|
||||
if (!wcfg.isLogging(Logging.TNTEXPLOSION))
|
||||
return;
|
||||
name = "TNT";
|
||||
} else if (event.getEntity() instanceof Creeper) {
|
||||
if (!wcfg.isLogging(Logging.CREEPEREXPLOSION))
|
||||
return;
|
||||
if (logCreeperExplosionsAsPlayerWhoTriggeredThese) {
|
||||
final Entity target = ((Creeper)event.getEntity()).getTarget();
|
||||
name = target instanceof Player ? ((Player)target).getName() : "Creeper";
|
||||
} else
|
||||
name = "Creeper";
|
||||
} else if (event.getEntity() instanceof Fireball) {
|
||||
if (!wcfg.isLogging(Logging.GHASTFIREBALLEXPLOSION))
|
||||
return;
|
||||
name = "Ghast";
|
||||
} else if (event.getEntity() instanceof EnderDragon) {
|
||||
if (!wcfg.isLogging(Logging.ENDERDRAGON))
|
||||
return;
|
||||
name = "EnderDragon";
|
||||
} else {
|
||||
if (!wcfg.isLogging(Logging.MISCEXPLOSION))
|
||||
return;
|
||||
name = "Explosion";
|
||||
}
|
||||
for (final Block block : event.blockList()) {
|
||||
final int type = block.getTypeId();
|
||||
if (wcfg.isLogging(Logging.SIGNTEXT) & (type == 63 || type == 68))
|
||||
consumer.queueSignBreak(name, (Sign)block.getState());
|
||||
else if (wcfg.isLogging(Logging.CHESTACCESS) && (type == 23 || type == 54 || type == 61))
|
||||
consumer.queueContainerBreak(name, block.getState());
|
||||
else
|
||||
consumer.queueBlockBreak(name, block.getState());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
package de.diddiz.LogBlock.listeners;
|
||||
|
||||
import static de.diddiz.LogBlock.config.Config.getWorldConfig;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import org.bukkit.block.Block;
|
||||
import org.bukkit.block.BlockFace;
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.EventPriority;
|
||||
import org.bukkit.event.block.BlockFromToEvent;
|
||||
import de.diddiz.LogBlock.LogBlock;
|
||||
import de.diddiz.LogBlock.Logging;
|
||||
import de.diddiz.LogBlock.config.WorldConfig;
|
||||
|
||||
public class FluidFlowLogging extends LoggingListener
|
||||
{
|
||||
private static final Set<Integer> nonFluidProofBlocks = new HashSet<Integer>(Arrays.asList(27, 28, 31, 32, 37, 38, 39, 40, 50, 51, 55, 59, 66, 69, 70, 75, 76, 78, 93, 94, 104, 105, 106));
|
||||
|
||||
public FluidFlowLogging(LogBlock lb) {
|
||||
super(lb);
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
|
||||
public void onBlockFromTo(BlockFromToEvent event) {
|
||||
final WorldConfig wcfg = getWorldConfig(event.getBlock().getWorld());
|
||||
if (wcfg != null) {
|
||||
final Block to = event.getToBlock();
|
||||
final int typeFrom = event.getBlock().getTypeId();
|
||||
final int typeTo = to.getTypeId();
|
||||
final boolean canFlow = typeTo == 0 || nonFluidProofBlocks.contains(typeTo);
|
||||
if (typeFrom == 10 || typeFrom == 11) {
|
||||
if (canFlow) {
|
||||
if (isSurroundedByWater(to) && event.getBlock().getData() <= 2)
|
||||
consumer.queueBlockReplace("LavaFlow", to.getState(), 4, (byte)0);
|
||||
else if (typeTo == 0) {
|
||||
if (wcfg.isLogging(Logging.LAVAFLOW))
|
||||
consumer.queueBlockPlace("LavaFlow", to.getLocation(), 10, (byte)(event.getBlock().getData() + 1));
|
||||
} else
|
||||
consumer.queueBlockReplace("LavaFlow", to.getState(), 10, (byte)(event.getBlock().getData() + 1));
|
||||
} else if (typeTo == 8 || typeTo == 9)
|
||||
if (event.getFace() == BlockFace.DOWN)
|
||||
consumer.queueBlockReplace("LavaFlow", to.getState(), 1, (byte)0);
|
||||
else
|
||||
consumer.queueBlockReplace("LavaFlow", to.getState(), 4, (byte)0);
|
||||
} else if (typeFrom == 8 || typeFrom == 9) {
|
||||
if (typeTo == 0) {
|
||||
if (wcfg.isLogging(Logging.WATERFLOW))
|
||||
consumer.queueBlockPlace("WaterFlow", to.getLocation(), 8, (byte)(event.getBlock().getData() + 1));
|
||||
} else if (nonFluidProofBlocks.contains(typeTo))
|
||||
consumer.queueBlockReplace("WaterFlow", to.getState(), 8, (byte)(event.getBlock().getData() + 1));
|
||||
else if (typeTo == 10 || typeTo == 11)
|
||||
if (to.getData() == 0)
|
||||
consumer.queueBlockReplace("WaterFlow", to.getState(), 49, (byte)0);
|
||||
else if (event.getFace() == BlockFace.DOWN)
|
||||
consumer.queueBlockReplace("LavaFlow", to.getState(), 1, (byte)0);
|
||||
if (typeTo == 0 || nonFluidProofBlocks.contains(typeTo))
|
||||
for (final BlockFace face : new BlockFace[]{BlockFace.DOWN, BlockFace.NORTH, BlockFace.WEST, BlockFace.EAST, BlockFace.SOUTH}) {
|
||||
final Block lower = to.getRelative(face);
|
||||
if (lower.getTypeId() == 10 || lower.getTypeId() == 11)
|
||||
consumer.queueBlockReplace("WaterFlow", lower.getState(), lower.getData() == 0 ? 49 : 4, (byte)0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean isSurroundedByWater(Block block) {
|
||||
for (final BlockFace face : new BlockFace[]{BlockFace.NORTH, BlockFace.WEST, BlockFace.EAST, BlockFace.SOUTH}) {
|
||||
final int type = block.getRelative(face).getTypeId();
|
||||
if (type == 8 || type == 9)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
package de.diddiz.LogBlock.listeners;
|
||||
|
||||
import static de.diddiz.LogBlock.config.Config.getWorldConfig;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.EventPriority;
|
||||
import org.bukkit.event.block.Action;
|
||||
import org.bukkit.event.player.PlayerInteractEvent;
|
||||
import de.diddiz.LogBlock.LogBlock;
|
||||
import de.diddiz.LogBlock.Logging;
|
||||
import de.diddiz.LogBlock.config.WorldConfig;
|
||||
|
||||
public class InteractLogging extends LoggingListener
|
||||
{
|
||||
public InteractLogging(LogBlock lb) {
|
||||
super(lb);
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
|
||||
public void onPlayerInteract(PlayerInteractEvent event) {
|
||||
final WorldConfig wcfg = getWorldConfig(event.getPlayer().getWorld());
|
||||
if (wcfg != null && (event.getAction() == Action.LEFT_CLICK_BLOCK || event.getAction() == Action.RIGHT_CLICK_BLOCK)) {
|
||||
final Material type = event.getClickedBlock().getType();
|
||||
final int typeId = type.getId();
|
||||
final byte blockData = event.getClickedBlock().getData();
|
||||
final Player player = event.getPlayer();
|
||||
final Location loc = event.getClickedBlock().getLocation();
|
||||
|
||||
switch (type) {
|
||||
case LEVER:
|
||||
case STONE_BUTTON:
|
||||
if (wcfg.isLogging(Logging.SWITCHINTERACT))
|
||||
consumer.queueBlock(player.getName(), loc, typeId, typeId, blockData);
|
||||
break;
|
||||
case FENCE_GATE:
|
||||
if (event.getAction() != Action.RIGHT_CLICK_BLOCK)
|
||||
break;
|
||||
case WOODEN_DOOR:
|
||||
case TRAP_DOOR:
|
||||
if (wcfg.isLogging(Logging.DOORINTERACT))
|
||||
consumer.queueBlock(player.getName(), loc, typeId, typeId, blockData);
|
||||
break;
|
||||
case CAKE_BLOCK:
|
||||
if (wcfg.isLogging(Logging.CAKEEAT) && player.getFoodLevel() < 20)
|
||||
consumer.queueBlock(player.getName(), loc, typeId, typeId, blockData);
|
||||
break;
|
||||
case NOTE_BLOCK:
|
||||
if (wcfg.isLogging(Logging.NOTEBLOCKINTERACT))
|
||||
consumer.queueBlock(player.getName(), loc, typeId, typeId, blockData);
|
||||
break;
|
||||
case DIODE_BLOCK_OFF:
|
||||
case DIODE_BLOCK_ON:
|
||||
if (wcfg.isLogging(Logging.DIODEINTERACT) && event.getAction() == Action.RIGHT_CLICK_BLOCK)
|
||||
consumer.queueBlock(player.getName(), loc, typeId, typeId, blockData);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
package de.diddiz.LogBlock.listeners;
|
||||
|
||||
import static de.diddiz.LogBlock.config.Config.isLogging;
|
||||
import static de.diddiz.LogBlock.config.Config.logKillsLevel;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import org.bukkit.entity.Entity;
|
||||
import org.bukkit.entity.LivingEntity;
|
||||
import org.bukkit.entity.Monster;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.EventPriority;
|
||||
import org.bukkit.event.entity.EntityDamageByEntityEvent;
|
||||
import org.bukkit.event.entity.EntityDamageEvent;
|
||||
import de.diddiz.LogBlock.LogBlock;
|
||||
import de.diddiz.LogBlock.Logging;
|
||||
import de.diddiz.LogBlock.config.Config.LogKillsLevel;
|
||||
|
||||
public class KillLogging extends LoggingListener
|
||||
{
|
||||
private final Map<Integer, Integer> lastAttackedEntity = new HashMap<Integer, Integer>();
|
||||
private final Map<Integer, Long> lastAttackTime = new HashMap<Integer, Long>();
|
||||
|
||||
public KillLogging(LogBlock lb) {
|
||||
super(lb);
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
|
||||
public void onEntityDamage(EntityDamageEvent event) {
|
||||
if (isLogging(event.getEntity().getWorld(), Logging.KILL) && event instanceof EntityDamageByEntityEvent && event.getEntity() instanceof LivingEntity) {
|
||||
final LivingEntity victim = (LivingEntity)event.getEntity();
|
||||
final Entity killer = ((EntityDamageByEntityEvent)event).getDamager();
|
||||
if (victim.getHealth() - event.getDamage() > 0 || victim.getHealth() <= 0)
|
||||
return;
|
||||
if (logKillsLevel == LogKillsLevel.PLAYERS && !(victim instanceof Player && killer instanceof Player))
|
||||
return;
|
||||
else if (logKillsLevel == LogKillsLevel.MONSTERS && !((victim instanceof Player || victim instanceof Monster) && killer instanceof Player || killer instanceof Monster))
|
||||
return;
|
||||
if (lastAttackedEntity.containsKey(killer.getEntityId()) && lastAttackedEntity.get(killer.getEntityId()) == victim.getEntityId() && System.currentTimeMillis() - lastAttackTime.get(killer.getEntityId()) < 5000)
|
||||
return;
|
||||
consumer.queueKill(killer, victim);
|
||||
lastAttackedEntity.put(killer.getEntityId(), victim.getEntityId());
|
||||
lastAttackTime.put(killer.getEntityId(), System.currentTimeMillis());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
package de.diddiz.LogBlock.listeners;
|
||||
|
||||
import static de.diddiz.LogBlock.config.Config.isLogging;
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.EventPriority;
|
||||
import org.bukkit.event.block.LeavesDecayEvent;
|
||||
import de.diddiz.LogBlock.LogBlock;
|
||||
import de.diddiz.LogBlock.Logging;
|
||||
|
||||
public class LeavesDecayLogging extends LoggingListener
|
||||
{
|
||||
public LeavesDecayLogging(LogBlock lb) {
|
||||
super(lb);
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
|
||||
public void onLeavesDecay(LeavesDecayEvent event) {
|
||||
if (isLogging(event.getBlock().getWorld(), Logging.LEAVESDECAY))
|
||||
consumer.queueBlockBreak("LeavesDecay", event.getBlock().getState());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
package de.diddiz.LogBlock.listeners;
|
||||
|
||||
import org.bukkit.event.Listener;
|
||||
import de.diddiz.LogBlock.Consumer;
|
||||
import de.diddiz.LogBlock.LogBlock;
|
||||
|
||||
public class LoggingListener implements Listener
|
||||
{
|
||||
protected final Consumer consumer;
|
||||
|
||||
public LoggingListener(LogBlock lb) {
|
||||
consumer = lb.getConsumer();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
package de.diddiz.LogBlock.listeners;
|
||||
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.EventPriority;
|
||||
import org.bukkit.event.player.PlayerJoinEvent;
|
||||
import org.bukkit.event.player.PlayerQuitEvent;
|
||||
import de.diddiz.LogBlock.LogBlock;
|
||||
|
||||
public class PlayerInfoLogging extends LoggingListener
|
||||
{
|
||||
public PlayerInfoLogging(LogBlock lb) {
|
||||
super(lb);
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.MONITOR)
|
||||
public void onPlayerJoin(PlayerJoinEvent event) {
|
||||
consumer.queueJoin(event.getPlayer());
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.MONITOR)
|
||||
public void onPlayerQuit(PlayerQuitEvent event) {
|
||||
consumer.queueLeave(event.getPlayer());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
package de.diddiz.LogBlock.listeners;
|
||||
|
||||
import static de.diddiz.LogBlock.config.Config.isLogging;
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.EventPriority;
|
||||
import org.bukkit.event.block.SignChangeEvent;
|
||||
import de.diddiz.LogBlock.LogBlock;
|
||||
import de.diddiz.LogBlock.Logging;
|
||||
|
||||
public class SignChangeLogging extends LoggingListener
|
||||
{
|
||||
public SignChangeLogging(LogBlock lb) {
|
||||
super(lb);
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
|
||||
public void onSignChange(SignChangeEvent event) {
|
||||
if (isLogging(event.getBlock().getWorld(), Logging.SIGNTEXT))
|
||||
consumer.queueSignPlace(event.getPlayer().getName(), event.getBlock().getLocation(), event.getBlock().getTypeId(), event.getBlock().getData(), event.getLines());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
package de.diddiz.LogBlock.listeners;
|
||||
|
||||
import static de.diddiz.LogBlock.config.Config.isLogging;
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.EventPriority;
|
||||
import org.bukkit.event.block.BlockFadeEvent;
|
||||
import de.diddiz.LogBlock.LogBlock;
|
||||
import de.diddiz.LogBlock.Logging;
|
||||
|
||||
public class SnowFadeLogging extends LoggingListener
|
||||
{
|
||||
public SnowFadeLogging(LogBlock lb) {
|
||||
super(lb);
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
|
||||
public void onBlockFade(BlockFadeEvent event) {
|
||||
if (isLogging(event.getBlock().getWorld(), Logging.SNOWFADE)) {
|
||||
final int type = event.getBlock().getTypeId();
|
||||
if (type == 78 || type == 79)
|
||||
consumer.queueBlockReplace("SnowFade", event.getBlock().getState(), event.getNewState());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
package de.diddiz.LogBlock.listeners;
|
||||
|
||||
import static de.diddiz.LogBlock.config.Config.isLogging;
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.EventPriority;
|
||||
import org.bukkit.event.block.BlockFormEvent;
|
||||
import org.bukkit.event.block.LeavesDecayEvent;
|
||||
import de.diddiz.LogBlock.LogBlock;
|
||||
import de.diddiz.LogBlock.Logging;
|
||||
|
||||
public class SnowFormLogging extends LoggingListener
|
||||
{
|
||||
public SnowFormLogging(LogBlock lb) {
|
||||
super(lb);
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
|
||||
public void onLeavesDecay(LeavesDecayEvent event) {
|
||||
if (isLogging(event.getBlock().getWorld(), Logging.SNOWFORM))
|
||||
consumer.queueBlockBreak("LeavesDecay", event.getBlock().getState());
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
|
||||
public void onBlockForm(BlockFormEvent event) {
|
||||
if (isLogging(event.getBlock().getWorld(), Logging.SNOWFORM)) {
|
||||
final int type = event.getNewState().getTypeId();
|
||||
if (type == 78 || type == 79)
|
||||
consumer.queueBlockReplace("SnowForm", event.getBlock().getState(), event.getNewState());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
package de.diddiz.LogBlock.listeners;
|
||||
|
||||
import static de.diddiz.LogBlock.config.Config.getWorldConfig;
|
||||
import org.bukkit.block.BlockState;
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.EventPriority;
|
||||
import org.bukkit.event.world.StructureGrowEvent;
|
||||
import de.diddiz.LogBlock.LogBlock;
|
||||
import de.diddiz.LogBlock.Logging;
|
||||
import de.diddiz.LogBlock.config.WorldConfig;
|
||||
|
||||
public class StructureGrowLogging extends LoggingListener
|
||||
{
|
||||
public StructureGrowLogging(LogBlock lb) {
|
||||
super(lb);
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
|
||||
public void onStructureGrow(StructureGrowEvent event) {
|
||||
final WorldConfig wcfg = getWorldConfig(event.getWorld());
|
||||
if (wcfg != null) {
|
||||
final String playerName;
|
||||
if (event.getPlayer() != null) {
|
||||
if (!wcfg.isLogging(Logging.BONEMEALSTRUCTUREGROW))
|
||||
return;
|
||||
playerName = event.getPlayer().getName();
|
||||
} else {
|
||||
if (!wcfg.isLogging(Logging.NATURALSTRUCTUREGROW))
|
||||
return;
|
||||
playerName = "NaturalGrow";
|
||||
}
|
||||
for (final BlockState state : event.getBlocks())
|
||||
consumer.queueBlockReplace(playerName, state.getBlock().getState(), state);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,118 @@
|
||||
package de.diddiz.LogBlock.listeners;
|
||||
|
||||
import static de.diddiz.LogBlock.Session.getSession;
|
||||
import static de.diddiz.LogBlock.Session.hasSession;
|
||||
import static de.diddiz.LogBlock.config.Config.isLogged;
|
||||
import static de.diddiz.LogBlock.config.Config.toolsByType;
|
||||
import com.sk89q.worldedit.bukkit.selections.CuboidSelection;
|
||||
import java.util.Map.Entry;
|
||||
import org.bukkit.ChatColor;
|
||||
import org.bukkit.block.Block;
|
||||
import org.bukkit.block.BlockFace;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.Listener;
|
||||
import org.bukkit.event.block.Action;
|
||||
import org.bukkit.event.player.PlayerChangedWorldEvent;
|
||||
import org.bukkit.event.player.PlayerDropItemEvent;
|
||||
import org.bukkit.event.player.PlayerInteractEvent;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import de.diddiz.LogBlock.CommandsHandler;
|
||||
import de.diddiz.LogBlock.LogBlock;
|
||||
import de.diddiz.LogBlock.QueryParams;
|
||||
import de.diddiz.LogBlock.Session;
|
||||
import de.diddiz.LogBlock.Tool;
|
||||
import de.diddiz.LogBlock.ToolBehavior;
|
||||
import de.diddiz.LogBlock.ToolData;
|
||||
import de.diddiz.LogBlock.ToolMode;
|
||||
|
||||
public class ToolListener implements Listener
|
||||
{
|
||||
private final CommandsHandler handler;
|
||||
private final LogBlock logblock;
|
||||
|
||||
public ToolListener(LogBlock logblock) {
|
||||
this.logblock = logblock;
|
||||
handler = logblock.getCommandsHandler();
|
||||
}
|
||||
|
||||
@EventHandler(ignoreCancelled = true)
|
||||
public void onPlayerInteract(PlayerInteractEvent event) {
|
||||
if (event.getMaterial() != null) {
|
||||
final Action action = event.getAction();
|
||||
final int type = event.getMaterial().getId();
|
||||
final Tool tool = toolsByType.get(type);
|
||||
final Player player = event.getPlayer();
|
||||
if (tool != null && (action == Action.RIGHT_CLICK_BLOCK || action == Action.LEFT_CLICK_BLOCK) && isLogged(player.getWorld()) && logblock.hasPermission(player, "logblock.tools." + tool.name)) {
|
||||
final ToolBehavior behavior = action == Action.RIGHT_CLICK_BLOCK ? tool.rightClickBehavior : tool.leftClickBehavior;
|
||||
final ToolData toolData = getSession(player).toolData.get(tool);
|
||||
if (behavior != ToolBehavior.NONE && toolData.enabled) {
|
||||
final Block block = event.getClickedBlock();
|
||||
final QueryParams params = toolData.params;
|
||||
params.loc = null;
|
||||
params.sel = null;
|
||||
if (behavior == ToolBehavior.BLOCK)
|
||||
params.setLocation(block.getRelative(event.getBlockFace()).getLocation());
|
||||
else if (block.getTypeId() != 54 || tool.params.radius != 0)
|
||||
params.setLocation(block.getLocation());
|
||||
else {
|
||||
for (final BlockFace face : new BlockFace[]{BlockFace.NORTH, BlockFace.SOUTH, BlockFace.EAST, BlockFace.WEST})
|
||||
if (block.getRelative(face).getTypeId() == 54)
|
||||
params.setSelection(new CuboidSelection(event.getPlayer().getWorld(), block.getLocation(), block.getRelative(face).getLocation()));
|
||||
if (params.sel == null)
|
||||
params.setLocation(block.getLocation());
|
||||
}
|
||||
try {
|
||||
if (toolData.mode == ToolMode.ROLLBACK)
|
||||
handler.new CommandRollback(player, params, true);
|
||||
else if (toolData.mode == ToolMode.REDO)
|
||||
handler.new CommandRedo(player, params, true);
|
||||
else if (toolData.mode == ToolMode.CLEARLOG)
|
||||
handler.new CommandClearLog(player, params, true);
|
||||
else if (toolData.mode == ToolMode.WRITELOGFILE)
|
||||
handler.new CommandWriteLogFile(player, params, true);
|
||||
else
|
||||
handler.new CommandLookup(player, params, true);
|
||||
} catch (final Exception ex) {
|
||||
player.sendMessage(ChatColor.RED + ex.getMessage());
|
||||
}
|
||||
event.setCancelled(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
public void onPlayerChangedWorld(PlayerChangedWorldEvent event) {
|
||||
final Player player = event.getPlayer();
|
||||
if (hasSession(player)) {
|
||||
final Session session = getSession(player);
|
||||
for (final Entry<Tool, ToolData> entry : session.toolData.entrySet()) {
|
||||
final Tool tool = entry.getKey();
|
||||
final ToolData toolData = entry.getValue();
|
||||
if (toolData.enabled && !logblock.hasPermission(player, "logblock.tools." + tool.name)) {
|
||||
toolData.enabled = false;
|
||||
player.getInventory().removeItem(new ItemStack(tool.item, 1));
|
||||
player.sendMessage(ChatColor.GREEN + "Tool disabled.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
public void onPlayerDropItem(PlayerDropItemEvent event) {
|
||||
final Player player = event.getPlayer();
|
||||
if (hasSession(player)) {
|
||||
final Session session = getSession(player);
|
||||
for (final Entry<Tool, ToolData> entry : session.toolData.entrySet()) {
|
||||
final Tool tool = entry.getKey();
|
||||
final ToolData toolData = entry.getValue();
|
||||
final int item = event.getItemDrop().getItemStack().getTypeId();
|
||||
if (item == tool.item && toolData.enabled && !tool.canDrop) {
|
||||
player.sendMessage(ChatColor.RED + "You cannot drop this tool.");
|
||||
event.setCancelled(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
package de.diddiz.util;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class Block
|
||||
{
|
||||
private int block;
|
||||
private int data;
|
||||
|
||||
/**
|
||||
* @param block The id of the block
|
||||
* @param data The data for the block, -1 for any data
|
||||
*
|
||||
*/
|
||||
public Block(int block, int data) {
|
||||
this.block = block;
|
||||
this.data = data;
|
||||
}
|
||||
|
||||
public int getBlock() {
|
||||
return this.block;
|
||||
}
|
||||
|
||||
public int getData() {
|
||||
return this.data;
|
||||
}
|
||||
|
||||
public static boolean inList(List<Block> types, int blockID) {
|
||||
for (Block block : types) {
|
||||
if (block.getBlock() == blockID) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,211 @@
|
||||
package de.diddiz.util;
|
||||
|
||||
import static de.diddiz.util.MaterialName.materialName;
|
||||
import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import org.bukkit.ChatColor;
|
||||
import org.bukkit.Chunk;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.block.BlockState;
|
||||
import org.bukkit.block.DoubleChest;
|
||||
import org.bukkit.entity.Entity;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.entity.TNTPrimed;
|
||||
import org.bukkit.inventory.Inventory;
|
||||
import org.bukkit.inventory.InventoryHolder;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
|
||||
public class BukkitUtils
|
||||
{
|
||||
private static final Set<Set<Integer>> blockEquivalents;
|
||||
|
||||
static {
|
||||
blockEquivalents = new HashSet<Set<Integer>>(7);
|
||||
blockEquivalents.add(new HashSet<Integer>(Arrays.asList(2, 3, 60)));
|
||||
blockEquivalents.add(new HashSet<Integer>(Arrays.asList(8, 9, 79)));
|
||||
blockEquivalents.add(new HashSet<Integer>(Arrays.asList(10, 11)));
|
||||
blockEquivalents.add(new HashSet<Integer>(Arrays.asList(61, 62)));
|
||||
blockEquivalents.add(new HashSet<Integer>(Arrays.asList(73, 74)));
|
||||
blockEquivalents.add(new HashSet<Integer>(Arrays.asList(75, 76)));
|
||||
blockEquivalents.add(new HashSet<Integer>(Arrays.asList(93, 94)));
|
||||
}
|
||||
|
||||
public static int getInventoryHolderType(InventoryHolder holder) {
|
||||
if (holder instanceof BlockState) {
|
||||
return ((BlockState)holder).getTypeId();
|
||||
} else if (holder instanceof DoubleChest) {
|
||||
return ((DoubleChest)holder).getLocation().getBlock().getTypeId();
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
public static Location getInventoryHolderLocation(InventoryHolder holder) {
|
||||
if (holder instanceof BlockState) {
|
||||
return ((BlockState)holder).getLocation();
|
||||
} else if (holder instanceof DoubleChest) {
|
||||
return ((DoubleChest)holder).getLocation();
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public static ItemStack[] compareInventories(ItemStack[] items1, ItemStack[] items2) {
|
||||
final ItemStackComparator comperator = new ItemStackComparator();
|
||||
final ArrayList<ItemStack> diff = new ArrayList<ItemStack>();
|
||||
final int l1 = items1.length, l2 = items2.length;
|
||||
int c1 = 0, c2 = 0;
|
||||
while (c1 < l1 || c2 < l2) {
|
||||
if (c1 >= l1) {
|
||||
diff.add(items2[c2]);
|
||||
c2++;
|
||||
continue;
|
||||
}
|
||||
if (c2 >= l2) {
|
||||
items1[c1].setAmount(items1[c1].getAmount() * -1);
|
||||
diff.add(items1[c1]);
|
||||
c1++;
|
||||
continue;
|
||||
}
|
||||
final int comp = comperator.compare(items1[c1], items2[c2]);
|
||||
if (comp < 0) {
|
||||
items1[c1].setAmount(items1[c1].getAmount() * -1);
|
||||
diff.add(items1[c1]);
|
||||
c1++;
|
||||
} else if (comp > 0) {
|
||||
diff.add(items2[c2]);
|
||||
c2++;
|
||||
} else {
|
||||
final int amount = items2[c2].getAmount() - items1[c1].getAmount();
|
||||
if (amount != 0) {
|
||||
items1[c1].setAmount(amount);
|
||||
diff.add(items1[c1]);
|
||||
}
|
||||
c1++;
|
||||
c2++;
|
||||
}
|
||||
}
|
||||
return diff.toArray(new ItemStack[diff.size()]);
|
||||
}
|
||||
|
||||
public static ItemStack[] compressInventory(ItemStack[] items) {
|
||||
final ArrayList<ItemStack> compressed = new ArrayList<ItemStack>();
|
||||
for (final ItemStack item : items)
|
||||
if (item != null) {
|
||||
final int type = item.getTypeId();
|
||||
final byte data = rawData(item);
|
||||
boolean found = false;
|
||||
for (final ItemStack item2 : compressed)
|
||||
if (type == item2.getTypeId() && data == rawData(item2)) {
|
||||
item2.setAmount(item2.getAmount() + item.getAmount());
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
if (!found)
|
||||
compressed.add(new ItemStack(type, item.getAmount(), (short)0, data));
|
||||
}
|
||||
Collections.sort(compressed, new ItemStackComparator());
|
||||
return compressed.toArray(new ItemStack[compressed.size()]);
|
||||
}
|
||||
|
||||
public static boolean equalTypes(int type1, int type2) {
|
||||
if (type1 == type2)
|
||||
return true;
|
||||
for (final Set<Integer> equivalent : blockEquivalents)
|
||||
if (equivalent.contains(type1) && equivalent.contains(type2))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
public static String friendlyWorldname(String worldName) {
|
||||
return new File(worldName).getName();
|
||||
}
|
||||
|
||||
public static Set<Set<Integer>> getBlockEquivalents() {
|
||||
return blockEquivalents;
|
||||
}
|
||||
|
||||
public static String entityName(Entity entity) {
|
||||
if (entity instanceof Player)
|
||||
return ((Player)entity).getName();
|
||||
if (entity instanceof TNTPrimed)
|
||||
return "TNT";
|
||||
return entity.getClass().getSimpleName().substring(5);
|
||||
}
|
||||
|
||||
public static void giveTool(Player player, int type) {
|
||||
final Inventory inv = player.getInventory();
|
||||
if (inv.contains(type))
|
||||
player.sendMessage(ChatColor.RED + "You have already a " + materialName(type));
|
||||
else {
|
||||
final int free = inv.firstEmpty();
|
||||
if (free >= 0) {
|
||||
if (player.getItemInHand() != null && player.getItemInHand().getTypeId() != 0)
|
||||
inv.setItem(free, player.getItemInHand());
|
||||
player.setItemInHand(new ItemStack(type, 1));
|
||||
player.sendMessage(ChatColor.GREEN + "Here's your " + materialName(type));
|
||||
} else
|
||||
player.sendMessage(ChatColor.RED + "You have no empty slot in your inventory");
|
||||
}
|
||||
}
|
||||
|
||||
public static byte rawData(ItemStack item) {
|
||||
return item.getType() != null ? item.getData() != null ? item.getData().getData() : 0 : 0;
|
||||
}
|
||||
|
||||
public static int saveSpawnHeight(Location loc) {
|
||||
final World world = loc.getWorld();
|
||||
final Chunk chunk = world.getChunkAt(loc);
|
||||
if (!world.isChunkLoaded(chunk))
|
||||
world.loadChunk(chunk);
|
||||
final int x = loc.getBlockX(), z = loc.getBlockZ();
|
||||
int y = loc.getBlockY();
|
||||
boolean lower = world.getBlockTypeIdAt(x, y, z) == 0, upper = world.getBlockTypeIdAt(x, y + 1, z) == 0;
|
||||
while ((!lower || !upper) && y != 127) {
|
||||
lower = upper;
|
||||
upper = world.getBlockTypeIdAt(x, ++y, z) == 0;
|
||||
}
|
||||
while (world.getBlockTypeIdAt(x, y - 1, z) == 0 && y != 0)
|
||||
y--;
|
||||
return y;
|
||||
}
|
||||
|
||||
public static int modifyContainer(BlockState b, ItemStack item) {
|
||||
if (b instanceof InventoryHolder) {
|
||||
final Inventory inv = ((InventoryHolder)b).getInventory();
|
||||
if (item.getAmount() < 0) {
|
||||
item.setAmount(-item.getAmount());
|
||||
final ItemStack tmp = inv.removeItem(item).get(0);
|
||||
return tmp != null ? tmp.getAmount() : 0;
|
||||
} else if (item.getAmount() > 0) {
|
||||
final ItemStack tmp = inv.addItem(item).get(0);
|
||||
return tmp != null ? tmp.getAmount() : 0;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
public static class ItemStackComparator implements Comparator<ItemStack>
|
||||
{
|
||||
@Override
|
||||
public int compare(ItemStack a, ItemStack b) {
|
||||
final int aType = a.getTypeId(), bType = b.getTypeId();
|
||||
if (aType < bType)
|
||||
return -1;
|
||||
if (aType > bType)
|
||||
return 1;
|
||||
final byte aData = rawData(a), bData = rawData(b);
|
||||
if (aData < bData)
|
||||
return -1;
|
||||
if (aData > bData)
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,98 @@
|
||||
package de.diddiz.util;
|
||||
|
||||
import static de.diddiz.util.Utils.isByte;
|
||||
import static de.diddiz.util.Utils.isInt;
|
||||
import static org.bukkit.Bukkit.getLogger;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.logging.Level;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.configuration.ConfigurationSection;
|
||||
import org.bukkit.configuration.file.YamlConfiguration;
|
||||
import org.bukkit.material.MaterialData;
|
||||
|
||||
public class MaterialName
|
||||
{
|
||||
private static final Map<Integer, String> materialNames = new HashMap<Integer, String>();
|
||||
private static final Map<Integer, Map<Byte, String>> materialDataNames = new HashMap<Integer, Map<Byte, String>>();
|
||||
|
||||
static {
|
||||
// Add all known materials
|
||||
for (final Material mat : Material.values())
|
||||
materialNames.put(mat.getId(), mat.toString().replace('_', ' ').toLowerCase());
|
||||
// Load config
|
||||
final File file = new File("plugins/LogBlock/materials.yml");
|
||||
final YamlConfiguration cfg = YamlConfiguration.loadConfiguration(file);
|
||||
if (cfg.getKeys(false).isEmpty()) {
|
||||
// Generate defaults
|
||||
cfg.options().header("Add block or item names you want to be overridden or also names for custom blocks");
|
||||
cfg.set("6.1", "redwood sapling");
|
||||
cfg.set("6.2", "birch sapling");
|
||||
cfg.set("9", "water");
|
||||
cfg.set("11", "lava");
|
||||
cfg.set("17.1", "redwood log");
|
||||
cfg.set("17.2", "birch log");
|
||||
cfg.set("18.1", "redwood leaves");
|
||||
cfg.set("18.2", "birch leaves");
|
||||
cfg.set("31.0", "dead long grass");
|
||||
cfg.set("31.2", "fern");
|
||||
for (byte i = 0; i < 7; i++) {
|
||||
cfg.set("35." + i, toReadable(Material.STEP.getNewData(i)));
|
||||
cfg.set("351." + i, toReadable(Material.DOUBLE_STEP.getNewData(i)));
|
||||
}
|
||||
for (byte i = 0; i < 16; i++) {
|
||||
cfg.set("35." + i, toReadable(Material.WOOL.getNewData(i)));
|
||||
cfg.set("351." + i, toReadable(Material.INK_SACK.getNewData(i)));
|
||||
}
|
||||
try {
|
||||
cfg.save(file);
|
||||
} catch (final IOException ex) {
|
||||
getLogger().log(Level.WARNING, "Unable to save material.yml: ", ex);
|
||||
}
|
||||
}
|
||||
for (final String entry : cfg.getKeys(false))
|
||||
if (isInt(entry)) {
|
||||
if (cfg.isString(entry))
|
||||
materialNames.put(Integer.valueOf(entry), cfg.getString(entry));
|
||||
else if (cfg.isConfigurationSection(entry)) {
|
||||
final Map<Byte, String> dataNames = new HashMap<Byte, String>();
|
||||
materialDataNames.put(Integer.valueOf(entry), dataNames);
|
||||
final ConfigurationSection sec = cfg.getConfigurationSection(entry);
|
||||
for (final String data : sec.getKeys(false))
|
||||
if (isByte(data)) {
|
||||
if (sec.isString(data))
|
||||
dataNames.put(Byte.valueOf(data), sec.getString(data));
|
||||
else
|
||||
getLogger().warning("Parsing materials.yml: '" + data + "' is not a string.");
|
||||
} else
|
||||
getLogger().warning("Parsing materials.yml: '" + data + "' is no valid material data");
|
||||
} else
|
||||
getLogger().warning("Parsing materials.yml: '" + entry + "' is neither a string nor a section.");
|
||||
} else
|
||||
getLogger().warning("Parsing materials.yml: '" + entry + "' is no valid material id");
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Name of the material, or if it's unknown, the id.
|
||||
*/
|
||||
public static String materialName(int type) {
|
||||
return materialNames.containsKey(type) ? materialNames.get(type) : String.valueOf(type);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Name of the material regarding it's data, or if it's unknown, the basic name.
|
||||
*/
|
||||
public static String materialName(int type, byte data) {
|
||||
final Map<Byte, String> dataNames = materialDataNames.get(type);
|
||||
if (dataNames != null)
|
||||
if (dataNames.containsKey(data))
|
||||
return dataNames.get(data);
|
||||
return materialName(type);
|
||||
}
|
||||
|
||||
private static String toReadable(MaterialData matData) {
|
||||
return matData.toString().toLowerCase().replace('_', ' ').replaceAll("[^a-z ]", "");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,436 @@
|
||||
package de.diddiz.util;
|
||||
|
||||
import java.io.Closeable;
|
||||
import java.sql.Array;
|
||||
import java.sql.Blob;
|
||||
import java.sql.CallableStatement;
|
||||
import java.sql.Clob;
|
||||
import java.sql.Connection;
|
||||
import java.sql.DatabaseMetaData;
|
||||
import java.sql.DriverManager;
|
||||
import java.sql.NClob;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.SQLClientInfoException;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.SQLWarning;
|
||||
import java.sql.SQLXML;
|
||||
import java.sql.Savepoint;
|
||||
import java.sql.Statement;
|
||||
import java.sql.Struct;
|
||||
import java.util.Enumeration;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
import java.util.Vector;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.concurrent.locks.Lock;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
|
||||
public class MySQLConnectionPool implements Closeable
|
||||
{
|
||||
private final static int poolsize = 10;
|
||||
private final static long timeToLive = 300000;
|
||||
private final Vector<JDCConnection> connections;
|
||||
private final ConnectionReaper reaper;
|
||||
private final String url, user, password;
|
||||
private final Lock lock = new ReentrantLock();
|
||||
|
||||
public MySQLConnectionPool(String url, String user, String password) throws ClassNotFoundException {
|
||||
Class.forName("com.mysql.jdbc.Driver");
|
||||
this.url = url;
|
||||
this.user = user;
|
||||
this.password = password;
|
||||
connections = new Vector<JDCConnection>(poolsize);
|
||||
reaper = new ConnectionReaper();
|
||||
reaper.start();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
lock.lock();
|
||||
final Enumeration<JDCConnection> conns = connections.elements();
|
||||
while (conns.hasMoreElements()) {
|
||||
final JDCConnection conn = conns.nextElement();
|
||||
connections.remove(conn);
|
||||
conn.terminate();
|
||||
}
|
||||
lock.unlock();
|
||||
}
|
||||
|
||||
public Connection getConnection() throws SQLException {
|
||||
lock.lock();
|
||||
try {
|
||||
final Enumeration<JDCConnection> conns = connections.elements();
|
||||
while (conns.hasMoreElements()) {
|
||||
final JDCConnection conn = conns.nextElement();
|
||||
if (conn.lease()) {
|
||||
if (conn.isValid())
|
||||
return conn;
|
||||
connections.remove(conn);
|
||||
conn.terminate();
|
||||
}
|
||||
}
|
||||
final JDCConnection conn = new JDCConnection(DriverManager.getConnection(url, user, password));
|
||||
conn.lease();
|
||||
if (!conn.isValid()) {
|
||||
conn.terminate();
|
||||
throw new SQLException("Failed to validate a brand new connection");
|
||||
}
|
||||
connections.add(conn);
|
||||
return conn;
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
private void reapConnections() {
|
||||
lock.lock();
|
||||
final long stale = System.currentTimeMillis() - timeToLive;
|
||||
final Iterator<JDCConnection> itr = connections.iterator();
|
||||
while (itr.hasNext()) {
|
||||
final JDCConnection conn = itr.next();
|
||||
if (conn.inUse() && stale > conn.getLastUse() && !conn.isValid())
|
||||
itr.remove();
|
||||
}
|
||||
lock.unlock();
|
||||
}
|
||||
|
||||
private class ConnectionReaper extends Thread
|
||||
{
|
||||
@Override
|
||||
public void run() {
|
||||
while (true) {
|
||||
try {
|
||||
Thread.sleep(300000);
|
||||
} catch (final InterruptedException e) {
|
||||
}
|
||||
reapConnections();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class JDCConnection implements Connection
|
||||
{
|
||||
private final Connection conn;
|
||||
private boolean inuse;
|
||||
private long timestamp;
|
||||
private int networkTimeout;
|
||||
private String schema;
|
||||
|
||||
JDCConnection(Connection conn) {
|
||||
this.conn = conn;
|
||||
inuse = false;
|
||||
timestamp = 0;
|
||||
networkTimeout = 30;
|
||||
schema = "default";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clearWarnings() throws SQLException {
|
||||
conn.clearWarnings();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
inuse = false;
|
||||
try {
|
||||
if (!conn.getAutoCommit())
|
||||
conn.setAutoCommit(true);
|
||||
} catch (final SQLException ex) {
|
||||
connections.remove(conn);
|
||||
terminate();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void commit() throws SQLException {
|
||||
conn.commit();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Array createArrayOf(String typeName, Object[] elements) throws SQLException {
|
||||
return conn.createArrayOf(typeName, elements);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Blob createBlob() throws SQLException {
|
||||
return conn.createBlob();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Clob createClob() throws SQLException {
|
||||
return conn.createClob();
|
||||
}
|
||||
|
||||
@Override
|
||||
public NClob createNClob() throws SQLException {
|
||||
return conn.createNClob();
|
||||
}
|
||||
|
||||
@Override
|
||||
public SQLXML createSQLXML() throws SQLException {
|
||||
return conn.createSQLXML();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Statement createStatement() throws SQLException {
|
||||
return conn.createStatement();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Statement createStatement(int resultSetType, int resultSetConcurrency) throws SQLException {
|
||||
return conn.createStatement(resultSetType, resultSetConcurrency);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Statement createStatement(int resultSetType, int resultSetConcurrency, int resultSetHoldability) throws SQLException {
|
||||
return conn.createStatement(resultSetType, resultSetConcurrency, resultSetHoldability);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Struct createStruct(String typeName, Object[] attributes) throws SQLException {
|
||||
return conn.createStruct(typeName, attributes);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getAutoCommit() throws SQLException {
|
||||
return conn.getAutoCommit();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getCatalog() throws SQLException {
|
||||
return conn.getCatalog();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Properties getClientInfo() throws SQLException {
|
||||
return conn.getClientInfo();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getClientInfo(String name) throws SQLException {
|
||||
return conn.getClientInfo(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getHoldability() throws SQLException {
|
||||
return conn.getHoldability();
|
||||
}
|
||||
|
||||
@Override
|
||||
public DatabaseMetaData getMetaData() throws SQLException {
|
||||
return conn.getMetaData();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getTransactionIsolation() throws SQLException {
|
||||
return conn.getTransactionIsolation();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Class<?>> getTypeMap() throws SQLException {
|
||||
return conn.getTypeMap();
|
||||
}
|
||||
|
||||
@Override
|
||||
public SQLWarning getWarnings() throws SQLException {
|
||||
return conn.getWarnings();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isClosed() throws SQLException {
|
||||
return conn.isClosed();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isReadOnly() throws SQLException {
|
||||
return conn.isReadOnly();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isValid(int timeout) throws SQLException {
|
||||
return conn.isValid(timeout);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isWrapperFor(Class<?> iface) throws SQLException {
|
||||
return conn.isWrapperFor(iface);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String nativeSQL(String sql) throws SQLException {
|
||||
return conn.nativeSQL(sql);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CallableStatement prepareCall(String sql) throws SQLException {
|
||||
return conn.prepareCall(sql);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CallableStatement prepareCall(String sql, int resultSetType, int resultSetConcurrency) throws SQLException {
|
||||
return conn.prepareCall(sql, resultSetType, resultSetConcurrency);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CallableStatement prepareCall(String sql, int resultSetType, int resultSetConcurrency, int resultSetHoldability) throws SQLException {
|
||||
return conn.prepareCall(sql, resultSetType, resultSetConcurrency, resultSetHoldability);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PreparedStatement prepareStatement(String sql) throws SQLException {
|
||||
return conn.prepareStatement(sql);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PreparedStatement prepareStatement(String sql, int autoGeneratedKeys) throws SQLException {
|
||||
return conn.prepareStatement(sql, autoGeneratedKeys);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PreparedStatement prepareStatement(String sql, int resultSetType, int resultSetConcurrency) throws SQLException {
|
||||
return conn.prepareStatement(sql, resultSetType, resultSetConcurrency);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PreparedStatement prepareStatement(String sql, int resultSetType, int resultSetConcurrency, int resultSetHoldability) throws SQLException {
|
||||
return conn.prepareStatement(sql, resultSetType, resultSetConcurrency, resultSetHoldability);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PreparedStatement prepareStatement(String sql, int[] columnIndexes) throws SQLException {
|
||||
return conn.prepareStatement(sql, columnIndexes);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PreparedStatement prepareStatement(String sql, String[] columnNames) throws SQLException {
|
||||
return conn.prepareStatement(sql, columnNames);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void releaseSavepoint(Savepoint savepoint) throws SQLException {
|
||||
conn.releaseSavepoint(savepoint);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void rollback() throws SQLException {
|
||||
conn.rollback();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void rollback(Savepoint savepoint) throws SQLException {
|
||||
conn.rollback(savepoint);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setAutoCommit(boolean autoCommit) throws SQLException {
|
||||
conn.setAutoCommit(autoCommit);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setCatalog(String catalog) throws SQLException {
|
||||
conn.setCatalog(catalog);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setClientInfo(Properties properties) throws SQLClientInfoException {
|
||||
conn.setClientInfo(properties);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setClientInfo(String name, String value) throws SQLClientInfoException {
|
||||
conn.setClientInfo(name, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setHoldability(int holdability) throws SQLException {
|
||||
conn.setHoldability(holdability);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setReadOnly(boolean readOnly) throws SQLException {
|
||||
conn.setReadOnly(readOnly);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Savepoint setSavepoint() throws SQLException {
|
||||
return conn.setSavepoint();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Savepoint setSavepoint(String name) throws SQLException {
|
||||
return conn.setSavepoint(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setTransactionIsolation(int level) throws SQLException {
|
||||
conn.setTransactionIsolation(level);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setTypeMap(Map<String, Class<?>> map) throws SQLException {
|
||||
conn.setTypeMap(map);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T unwrap(Class<T> iface) throws SQLException {
|
||||
return conn.unwrap(iface);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public int getNetworkTimeout() throws SQLException {
|
||||
return networkTimeout;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public void setNetworkTimeout(Executor exec, int timeout) throws SQLException {
|
||||
networkTimeout = timeout;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public void abort(Executor exec) throws SQLException {
|
||||
// Not implemented really...
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public String getSchema() throws SQLException {
|
||||
return schema;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public void setSchema(String str) throws SQLException {
|
||||
schema = str;
|
||||
}
|
||||
|
||||
long getLastUse() {
|
||||
return timestamp;
|
||||
}
|
||||
|
||||
boolean inUse() {
|
||||
return inuse;
|
||||
}
|
||||
|
||||
boolean isValid() {
|
||||
try {
|
||||
return conn.isValid(1);
|
||||
} catch (final SQLException ex) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
synchronized boolean lease() {
|
||||
if (inuse)
|
||||
return false;
|
||||
inuse = true;
|
||||
timestamp = System.currentTimeMillis();
|
||||
return true;
|
||||
}
|
||||
|
||||
void terminate() {
|
||||
try {
|
||||
conn.close();
|
||||
} catch (final SQLException ex) {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,185 @@
|
||||
package de.diddiz.util;
|
||||
|
||||
import java.io.BufferedOutputStream;
|
||||
import java.io.BufferedReader;
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.FilenameFilter;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.OutputStream;
|
||||
import java.net.URL;
|
||||
import java.text.ParseException;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.List;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
public class Utils
|
||||
{
|
||||
public static String newline = System.getProperty("line.separator");
|
||||
|
||||
public static void download(Logger log, URL url, File file) throws IOException {
|
||||
if (!file.getParentFile().exists())
|
||||
file.getParentFile().mkdir();
|
||||
if (file.exists())
|
||||
file.delete();
|
||||
file.createNewFile();
|
||||
final int size = url.openConnection().getContentLength();
|
||||
log.info("Downloading " + file.getName() + " (" + size / 1024 + "kb) ...");
|
||||
final InputStream in = url.openStream();
|
||||
final OutputStream out = new BufferedOutputStream(new FileOutputStream(file));
|
||||
final byte[] buffer = new byte[1024];
|
||||
int len, downloaded = 0, msgs = 0;
|
||||
final long start = System.currentTimeMillis();
|
||||
while ((len = in.read(buffer)) >= 0) {
|
||||
out.write(buffer, 0, len);
|
||||
downloaded += len;
|
||||
if ((int)((System.currentTimeMillis() - start) / 500) > msgs) {
|
||||
log.info((int)(downloaded / (double)size * 100d) + "%");
|
||||
msgs++;
|
||||
}
|
||||
}
|
||||
in.close();
|
||||
out.close();
|
||||
log.info("Download finished");
|
||||
}
|
||||
|
||||
public static String readURL(URL url) throws IOException {
|
||||
final StringBuilder content = new StringBuilder();
|
||||
final BufferedReader in = new BufferedReader(new InputStreamReader(url.openStream()));
|
||||
String inputLine;
|
||||
while ((inputLine = in.readLine()) != null)
|
||||
content.append(inputLine);
|
||||
in.close();
|
||||
return content.toString();
|
||||
}
|
||||
|
||||
public static boolean isInt(String str) {
|
||||
try {
|
||||
Integer.parseInt(str);
|
||||
return true;
|
||||
} catch (final NumberFormatException ex) {
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static boolean isByte(String str) {
|
||||
try {
|
||||
Byte.parseByte(str);
|
||||
return true;
|
||||
} catch (final NumberFormatException ex) {
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static String listing(String[] entries, String delimiter, String finalDelimiter) {
|
||||
final int len = entries.length;
|
||||
if (len == 0)
|
||||
return "";
|
||||
if (len == 1)
|
||||
return entries[0];
|
||||
final StringBuilder builder = new StringBuilder(entries[0]);
|
||||
for (int i = 1; i < len - 1; i++)
|
||||
builder.append(delimiter + entries[i]);
|
||||
builder.append(finalDelimiter + entries[len - 1]);
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
public static String listing(List<?> entries, String delimiter, String finalDelimiter) {
|
||||
final int len = entries.size();
|
||||
if (len == 0)
|
||||
return "";
|
||||
if (len == 1)
|
||||
return entries.get(0).toString();
|
||||
final StringBuilder builder = new StringBuilder(entries.get(0).toString());
|
||||
for (int i = 1; i < len - 1; i++)
|
||||
builder.append(delimiter + entries.get(i).toString());
|
||||
builder.append(finalDelimiter + entries.get(len - 1).toString());
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
public static int parseTimeSpec(String[] spec) {
|
||||
if (spec == null || spec.length < 1 || spec.length > 2)
|
||||
return -1;
|
||||
if (spec.length == 1 && isInt(spec[0]))
|
||||
return Integer.valueOf(spec[0]);
|
||||
if (!spec[0].contains(":") && !spec[0].contains("."))
|
||||
if (spec.length == 2) {
|
||||
if (!isInt(spec[0]))
|
||||
return -1;
|
||||
int min = Integer.parseInt(spec[0]);
|
||||
if (spec[1].startsWith("h"))
|
||||
min *= 60;
|
||||
else if (spec[1].startsWith("d"))
|
||||
min *= 1440;
|
||||
return min;
|
||||
} else if (spec.length == 1) {
|
||||
int days = 0, hours = 0, minutes = 0;
|
||||
int lastIndex = 0, currIndex = 1;
|
||||
while (currIndex <= spec[0].length()) {
|
||||
while (currIndex <= spec[0].length() && isInt(spec[0].substring(lastIndex, currIndex)))
|
||||
currIndex++;
|
||||
if (currIndex - 1 != lastIndex) {
|
||||
final String param = spec[0].substring(currIndex - 1, currIndex).toLowerCase();
|
||||
if (param.equals("d"))
|
||||
days = Integer.parseInt(spec[0].substring(lastIndex, currIndex - 1));
|
||||
else if (param.equals("h"))
|
||||
hours = Integer.parseInt(spec[0].substring(lastIndex, currIndex - 1));
|
||||
else if (param.equals("m"))
|
||||
minutes = Integer.parseInt(spec[0].substring(lastIndex, currIndex - 1));
|
||||
}
|
||||
lastIndex = currIndex;
|
||||
currIndex++;
|
||||
}
|
||||
if (days == 0 && hours == 0 && minutes == 0)
|
||||
return -1;
|
||||
return minutes + hours * 60 + days * 1440;
|
||||
} else
|
||||
return -1;
|
||||
final String timestamp;
|
||||
if (spec.length == 1) {
|
||||
if (spec[0].contains(":"))
|
||||
timestamp = new SimpleDateFormat("dd.MM.yyyy").format(System.currentTimeMillis()) + " " + spec[0];
|
||||
else
|
||||
timestamp = spec[0] + " 00:00:00";
|
||||
} else
|
||||
timestamp = spec[0] + " " + spec[1];
|
||||
try {
|
||||
return (int)((System.currentTimeMillis() - new SimpleDateFormat("dd.MM.yyyy HH:mm:ss").parse(timestamp).getTime()) / 60000);
|
||||
} catch (final ParseException ex) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
public static String spaces(int count) {
|
||||
final StringBuilder filled = new StringBuilder(count);
|
||||
for (int i = 0; i < count; i++)
|
||||
filled.append(' ');
|
||||
return filled.toString();
|
||||
}
|
||||
|
||||
public static String join(String[] s, String delimiter) {
|
||||
if (s == null || s.length == 0)
|
||||
return "";
|
||||
final int len = s.length;
|
||||
final StringBuffer buffer = new StringBuffer(s[0]);
|
||||
for (int i = 1; i < len; i++)
|
||||
buffer.append(delimiter).append(s[i]);
|
||||
return buffer.toString();
|
||||
}
|
||||
|
||||
public static class ExtensionFilenameFilter implements FilenameFilter
|
||||
{
|
||||
private final String ext;
|
||||
|
||||
public ExtensionFilenameFilter(String ext) {
|
||||
this.ext = ext;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean accept(File dir, String name) {
|
||||
return name.toLowerCase().endsWith(ext);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user