Mavenize this project so we can use Jenkins and easily enable developers to compile from source.

This commit is contained in:
md_5
2012-07-28 14:52:38 +10:00
parent 9eb65ba133
commit 61e9643b90
53 changed files with 112 additions and 25 deletions
@@ -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);
}
}
}
}
}
+36
View File
@@ -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) {
}
}
}
}
+185
View File
@@ -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);
}
}
}