diff --git a/appinfo.properties b/appinfo.properties index 7b5eacee..713fee60 100644 --- a/appinfo.properties +++ b/appinfo.properties @@ -1,6 +1,6 @@ -#Sun, 21 Jul 2013 22:39:51 -0400 +#Sat, 27 Jul 2013 16:48:23 -0400 program.VERSION=2.21 -program.BUILDNUM=329 -program.BUILDDATE=07/21/2013 10\:39 PM +program.BUILDNUM=345 +program.BUILDDATE=07/27/2013 04\:48 PM diff --git a/buildnumber.properties b/buildnumber.properties index 64033172..6bb47401 100644 --- a/buildnumber.properties +++ b/buildnumber.properties @@ -1,3 +1,3 @@ #Build Number for ANT. Do not edit! -#Sun Jul 21 22:39:51 EDT 2013 -build.number=330 +#Sat Jul 27 16:48:23 EDT 2013 +build.number=346 diff --git a/src/config.yml b/src/config.yml index 79fdd9b2..2c9b9abe 100644 --- a/src/config.yml +++ b/src/config.yml @@ -130,3 +130,7 @@ twitterbot_secret: # Pet Protect - Prevent tamed pets from being killed. pet_protect_enabled: true + +# Logs Registration +logs_register_password: +logs_register_url: diff --git a/src/me/StevenLawson/TotalFreedomMod/Commands/Command_deafen.java b/src/me/StevenLawson/TotalFreedomMod/Commands/Command_deafen.java index ab7ba047..05e8290f 100644 --- a/src/me/StevenLawson/TotalFreedomMod/Commands/Command_deafen.java +++ b/src/me/StevenLawson/TotalFreedomMod/Commands/Command_deafen.java @@ -6,6 +6,7 @@ import org.bukkit.Sound; import org.bukkit.command.Command; import org.bukkit.command.CommandSender; import org.bukkit.entity.Player; +import org.bukkit.scheduler.BukkitRunnable; @CommandPermissions(level = AdminLevel.SENIOR, source = SourceType.BOTH, block_host_console = true) @CommandParameters(description = "Make some noise.", usage = "/") @@ -23,14 +24,14 @@ public class Command_deafen extends TFM_Command { final float pitch = (float) (percent * 2.0); - server.getScheduler().scheduleSyncDelayedTask(plugin, new Runnable() + new BukkitRunnable() { @Override public void run() { p.playSound(randomOffset(p.getLocation(), 5.0), Sound.values()[random.nextInt(Sound.values().length)], 100.0f, pitch); } - }, Math.round(20.0 * percent * 2.0)); + }.runTaskLater(plugin, Math.round(20.0 * percent * 2.0)); } } diff --git a/src/me/StevenLawson/TotalFreedomMod/Commands/Command_doom.java b/src/me/StevenLawson/TotalFreedomMod/Commands/Command_doom.java index 28000c87..1aedc4a8 100644 --- a/src/me/StevenLawson/TotalFreedomMod/Commands/Command_doom.java +++ b/src/me/StevenLawson/TotalFreedomMod/Commands/Command_doom.java @@ -8,6 +8,7 @@ import org.bukkit.GameMode; import org.bukkit.command.Command; import org.bukkit.command.CommandSender; import org.bukkit.entity.Player; +import org.bukkit.scheduler.BukkitRunnable; @CommandPermissions(level = AdminLevel.SENIOR, source = SourceType.ONLY_CONSOLE) @CommandParameters(description = "For the bad Superadmins.", usage = "/ ") @@ -69,7 +70,7 @@ public class Command_doom extends TFM_Command // generate explosion p.getWorld().createExplosion(p.getLocation(), 4F); - server.getScheduler().scheduleSyncDelayedTask(plugin, new Runnable() + new BukkitRunnable() { @Override public void run() @@ -80,9 +81,9 @@ public class Command_doom extends TFM_Command // kill (if not done already) p.setHealth(0.0); } - }, 40L); // 2 seconds + }.runTaskLater(plugin, 20L * 2L); - server.getScheduler().scheduleSyncDelayedTask(plugin, new Runnable() + new BukkitRunnable() { @Override public void run() @@ -96,7 +97,7 @@ public class Command_doom extends TFM_Command // kick player p.kickPlayer(ChatColor.RED + "FUCKOFF, and get your shit together!"); } - }, 60L); // 3 seconds + }.runTaskLater(plugin, 20L * 3L); return true; } diff --git a/src/me/StevenLawson/TotalFreedomMod/Commands/Command_fr.java b/src/me/StevenLawson/TotalFreedomMod/Commands/Command_fr.java index 7f46f811..527df1ce 100644 --- a/src/me/StevenLawson/TotalFreedomMod/Commands/Command_fr.java +++ b/src/me/StevenLawson/TotalFreedomMod/Commands/Command_fr.java @@ -1,6 +1,5 @@ package me.StevenLawson.TotalFreedomMod.Commands; -import me.StevenLawson.TotalFreedomMod.TFM_Log; import me.StevenLawson.TotalFreedomMod.TFM_PlayerData; import me.StevenLawson.TotalFreedomMod.TFM_Util; import me.StevenLawson.TotalFreedomMod.TotalFreedomMod; @@ -8,6 +7,7 @@ import org.bukkit.ChatColor; import org.bukkit.command.Command; import org.bukkit.command.CommandSender; import org.bukkit.entity.Player; +import org.bukkit.scheduler.BukkitRunnable; @CommandPermissions(level = AdminLevel.SUPER, source = SourceType.BOTH) @CommandParameters(description = "Freeze players (toggles on and off).", usage = "/ [target | purge]") @@ -24,33 +24,30 @@ public class Command_fr extends TFM_Command { TFM_Util.adminAction(sender.getName(), "Freezing all players", false); TotalFreedomMod.allPlayersFrozen = true; - TotalFreedomMod.freezePurgeEventId = server.getScheduler().scheduleSyncDelayedTask(plugin, new Runnable() + + if (TotalFreedomMod.freezePurgeTask != null) + { + TotalFreedomMod.freezePurgeTask.cancel(); + } + TotalFreedomMod.freezePurgeTask = new BukkitRunnable() { @Override public void run() { - if (TotalFreedomMod.freezePurgeEventId == 0) - { - TFM_Log.warning("Freeze autopurge task was improperly cancelled!"); - return; - } - TFM_Util.adminAction("FreezeTimer", "Unfreezing all players", false); - TotalFreedomMod.allPlayersFrozen = false; - TotalFreedomMod.freezePurgeEventId = 0; } - }, 6000L); // five minutes in ticks: 20*60*5 + }.runTaskLater(plugin, 20L * 60L * 5L); + playerMsg("Players are now frozen."); } else { TFM_Util.adminAction(sender.getName(), "Unfreezing all players", false); TotalFreedomMod.allPlayersFrozen = false; - if (TotalFreedomMod.freezePurgeEventId != 0) + if (TotalFreedomMod.freezePurgeTask != null) { - server.getScheduler().cancelTask(TotalFreedomMod.freezePurgeEventId); - TotalFreedomMod.freezePurgeEventId = 0; + TotalFreedomMod.freezePurgeTask.cancel(); } playerMsg("Players are now free to move."); } diff --git a/src/me/StevenLawson/TotalFreedomMod/Commands/Command_health.java b/src/me/StevenLawson/TotalFreedomMod/Commands/Command_health.java index 6cabb41e..2ebc7ab1 100644 --- a/src/me/StevenLawson/TotalFreedomMod/Commands/Command_health.java +++ b/src/me/StevenLawson/TotalFreedomMod/Commands/Command_health.java @@ -1,10 +1,5 @@ package me.StevenLawson.TotalFreedomMod.Commands; -import java.text.DecimalFormat; -import me.StevenLawson.TotalFreedomMod.TFM_Log; -import me.StevenLawson.TotalFreedomMod.TFM_TickMeter; -import org.apache.commons.lang.exception.ExceptionUtils; -import org.bukkit.ChatColor; import org.bukkit.command.Command; import org.bukkit.command.CommandSender; import org.bukkit.entity.Player; @@ -16,38 +11,39 @@ public class Command_health extends TFM_Command @Override public boolean run(final CommandSender sender, Player sender_p, Command cmd, String commandLabel, String[] args, boolean senderIsConsole) { - - Runnable task; - task = new Runnable() // async - { - @Override - public void run() - { - try - { - TFM_TickMeter meter = new TFM_TickMeter(plugin); - meter.startTicking(); - Thread.sleep(1000); // per second - meter.stopTicking(); - - Runtime runtime = Runtime.getRuntime(); - int mb = 1048576; // 1024 * 1024 - - float usedMem = runtime.totalMemory() - runtime.freeMemory(); - - playerMsg("Reserved Memory: " + runtime.totalMemory() / mb + "mb"); - playerMsg("Used Memory: " + new DecimalFormat("#").format(usedMem / mb) + "mb (" + new DecimalFormat("#").format(usedMem / runtime.totalMemory() * 100) + "%)"); - playerMsg("Max Memory: " + runtime.maxMemory() / mb + "mb"); - playerMsg("Ticks per second: " + (meter.getTicks() == 20 ? ChatColor.GREEN : ChatColor.RED) + meter.getTicks()); - } - catch (Exception iex) - { - TFM_Log.warning("Exception in TFM_TickMeter: Thread was interupted in sleeping process."); - TFM_Log.warning(ExceptionUtils.getStackTrace(iex)); - } - } - }; - new Thread(task, "TickMeter").start(); +// -- MADGEEK: This is not thread safe. It needs to be rewritten using proper Bukkit scheduler API. +// +// Runnable task; +// task = new Runnable() // async +// { +// @Override +// public void run() +// { +// try +// { +// TFM_TickMeter meter = new TFM_TickMeter(plugin); +// meter.startTicking(); +// Thread.sleep(1000); // per second +// meter.stopTicking(); +// +// Runtime runtime = Runtime.getRuntime(); +// int mb = 1048576; // 1024 * 1024 +// +// float usedMem = runtime.totalMemory() - runtime.freeMemory(); +// +// playerMsg("Reserved Memory: " + runtime.totalMemory() / mb + "mb"); +// playerMsg("Used Memory: " + new DecimalFormat("#").format(usedMem / mb) + "mb (" + new DecimalFormat("#").format(usedMem / runtime.totalMemory() * 100) + "%)"); +// playerMsg("Max Memory: " + runtime.maxMemory() / mb + "mb"); +// playerMsg("Ticks per second: " + (meter.getTicks() == 20 ? ChatColor.GREEN : ChatColor.RED) + meter.getTicks()); +// } +// catch (Exception iex) +// { +// TFM_Log.warning("Exception in TFM_TickMeter: Thread was interupted in sleeping process."); +// TFM_Log.warning(ExceptionUtils.getStackTrace(iex)); +// } +// } +// }; +// new Thread(task, "TickMeter").start(); return true; } } diff --git a/src/me/StevenLawson/TotalFreedomMod/Commands/Command_lockup.java b/src/me/StevenLawson/TotalFreedomMod/Commands/Command_lockup.java index 5b024e49..33d36a29 100644 --- a/src/me/StevenLawson/TotalFreedomMod/Commands/Command_lockup.java +++ b/src/me/StevenLawson/TotalFreedomMod/Commands/Command_lockup.java @@ -5,6 +5,7 @@ import me.StevenLawson.TotalFreedomMod.TFM_Util; import org.bukkit.command.Command; import org.bukkit.command.CommandSender; import org.bukkit.entity.Player; +import org.bukkit.scheduler.BukkitRunnable; import org.bukkit.scheduler.BukkitTask; @CommandPermissions(level = AdminLevel.SENIOR, source = SourceType.ONLY_CONSOLE, block_host_console = true) @@ -111,7 +112,7 @@ public class Command_lockup extends TFM_Command cancelLockup(playerdata); - playerdata.setLockupScheduleID(server.getScheduler().runTaskTimerAsynchronously(plugin, new Runnable() + playerdata.setLockupScheduleID(new BukkitRunnable() { @Override public void run() @@ -125,6 +126,6 @@ public class Command_lockup extends TFM_Command cancelLockup(playerdata); } } - }, 0L, 5L)); + }.runTaskTimer(plugin, 0L, 5L)); } } diff --git a/src/me/StevenLawson/TotalFreedomMod/Commands/Command_logs.java b/src/me/StevenLawson/TotalFreedomMod/Commands/Command_logs.java new file mode 100644 index 00000000..73cb7eae --- /dev/null +++ b/src/me/StevenLawson/TotalFreedomMod/Commands/Command_logs.java @@ -0,0 +1,164 @@ +package me.StevenLawson.TotalFreedomMod.Commands; + +import java.net.HttpURLConnection; +import java.net.MalformedURLException; +import java.net.URL; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import me.StevenLawson.TotalFreedomMod.TFM_Log; +import me.StevenLawson.TotalFreedomMod.TFM_Superadmin; +import me.StevenLawson.TotalFreedomMod.TotalFreedomMod; +import org.apache.commons.lang3.StringUtils; +import org.bukkit.ChatColor; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; +import org.bukkit.scheduler.BukkitRunnable; + +@CommandPermissions(level = AdminLevel.SUPER, source = SourceType.ONLY_IN_GAME) +@CommandParameters(description = "Register your connection with the TFM logviewer.", usage = "/") +public class Command_logs extends TFM_Command +{ + @Override + public boolean run(final CommandSender sender, final Player sender_p, Command cmd, String commandLabel, String[] args, boolean senderIsConsole) + { + if (sender_p == null) + { + return true; + } + + updateLogsRegistration(sender, sender_p, LogsRegistrationMode.UPDATE); + + return true; + } + + public static void updateLogsRegistration(final CommandSender sender, final Player target, final LogsRegistrationMode mode) + { + updateLogsRegistration(sender, target.getName(), target.getAddress().getAddress().getHostAddress().trim(), mode); + } + + public static void updateLogsRegistration(final CommandSender sender, final String targetName, final String targetIP, final LogsRegistrationMode mode) + { + if (TotalFreedomMod.logsRegisterURL == null || TotalFreedomMod.logsRegisterPassword == null) + { + return; + } + + if (TotalFreedomMod.logsRegisterPassword.isEmpty() || TotalFreedomMod.logsRegisterURL.isEmpty()) + { + return; + } + + new BukkitRunnable() + { + @Override + public void run() + { + try + { + if (sender != null) + { + sender.sendMessage(ChatColor.YELLOW + "Connecting..."); + } + + URL url = new URLBuilder(TotalFreedomMod.logsRegisterURL) + .addQueryParameter("mode", mode.toString()) + .addQueryParameter("password", TotalFreedomMod.logsRegisterPassword) + .addQueryParameter("name", targetName) + .addQueryParameter("ip", targetIP) + .getURL(); + + HttpURLConnection connection = (HttpURLConnection) url.openConnection(); + connection.setConnectTimeout(1000 * 5); + connection.setReadTimeout(1000 * 5); + connection.setUseCaches(false); + connection.setRequestMethod("HEAD"); + + final int responseCode = connection.getResponseCode(); + + if (sender != null) + { + new BukkitRunnable() + { + @Override + public void run() + { + if (responseCode == 200) + { + sender.sendMessage(ChatColor.GREEN + "Registration " + mode.toString() + "d."); + } + else + { + sender.sendMessage(ChatColor.RED + "Error contacting logs registration server."); + } + } + }.runTask(TotalFreedomMod.plugin); + } + } + catch (Exception ex) + { + TFM_Log.severe(ex); + } + } + }.runTaskAsynchronously(TotalFreedomMod.plugin); + } + + public static void deactivateSuperadmin(TFM_Superadmin superadmin) + { + for (String ip : superadmin.getIps()) + { + updateLogsRegistration(null, superadmin.getName(), ip, Command_logs.LogsRegistrationMode.DELETE); + } + } + + public static enum LogsRegistrationMode + { + UPDATE("update"), DELETE("delete"); + private final String mode; + + private LogsRegistrationMode(String mode) + { + this.mode = mode; + } + + @Override + public String toString() + { + return mode; + } + } + + private static class URLBuilder + { + private final String requestPath; + private final Map queryStringMap = new HashMap(); + + public URLBuilder(String requestPath) + { + this.requestPath = requestPath; + } + + public URLBuilder addQueryParameter(String key, String value) + { + queryStringMap.put(key, value); + return this; + } + + public URL getURL() throws MalformedURLException + { + List pairs = new ArrayList(); + Iterator> it = queryStringMap.entrySet().iterator(); + while (it.hasNext()) + { + Entry pair = it.next(); + pairs.add(pair.getKey() + "=" + pair.getValue()); + } + + return new URL(requestPath + "?" + StringUtils.join(pairs, "&")); + } + } +} diff --git a/src/me/StevenLawson/TotalFreedomMod/Commands/Command_mp.java b/src/me/StevenLawson/TotalFreedomMod/Commands/Command_mp.java index 467e2b72..22a228e8 100644 --- a/src/me/StevenLawson/TotalFreedomMod/Commands/Command_mp.java +++ b/src/me/StevenLawson/TotalFreedomMod/Commands/Command_mp.java @@ -1,6 +1,6 @@ package me.StevenLawson.TotalFreedomMod.Commands; -import org.bukkit.ChatColor; +import org.bukkit.Bukkit; import org.bukkit.World; import org.bukkit.command.Command; import org.bukkit.command.CommandSender; @@ -13,10 +13,15 @@ public class Command_mp extends TFM_Command @Override public boolean run(CommandSender sender, Player sender_p, Command cmd, String commandLabel, String[] args, boolean senderIsConsole) { - sender.sendMessage(ChatColor.GRAY + "Purging all mobs..."); + playerMsg("Purging all mobs..."); + playerMsg(purgeMobs() + " mobs removed."); + return true; + } + public static int purgeMobs() + { int removed = 0; - for (World world : server.getWorlds()) + for (World world : Bukkit.getWorlds()) { for (Entity ent : world.getLivingEntities()) { @@ -28,8 +33,6 @@ public class Command_mp extends TFM_Command } } - playerMsg(removed + " mobs removed."); - - return true; + return removed; } } diff --git a/src/me/StevenLawson/TotalFreedomMod/Commands/Command_purgeall.java b/src/me/StevenLawson/TotalFreedomMod/Commands/Command_purgeall.java index b5f83959..ff0ed949 100644 --- a/src/me/StevenLawson/TotalFreedomMod/Commands/Command_purgeall.java +++ b/src/me/StevenLawson/TotalFreedomMod/Commands/Command_purgeall.java @@ -4,16 +4,9 @@ import me.StevenLawson.TotalFreedomMod.TFM_DisguiseCraftBridge; import me.StevenLawson.TotalFreedomMod.TFM_PlayerData; import me.StevenLawson.TotalFreedomMod.TFM_Util; import me.StevenLawson.TotalFreedomMod.TotalFreedomMod; -import org.bukkit.World; import org.bukkit.command.Command; import org.bukkit.command.CommandSender; -import org.bukkit.entity.Ambient; -import org.bukkit.entity.Creature; -import org.bukkit.entity.EnderDragon; -import org.bukkit.entity.Entity; -import org.bukkit.entity.Ghast; import org.bukkit.entity.Player; -import org.bukkit.entity.Slime; import org.bukkit.potion.PotionEffect; @CommandPermissions(level = AdminLevel.SUPER, source = SourceType.BOTH) @@ -23,7 +16,6 @@ public class Command_purgeall extends TFM_Command @Override public boolean run(CommandSender sender, Player sender_p, Command cmd, String commandLabel, String[] args, boolean senderIsConsole) { - TFM_Util.adminAction(sender.getName(), "Purging all player data", true); // Purge entities @@ -71,32 +63,30 @@ public class Command_purgeall extends TFM_Command { p.removePotionEffect(potion_effect.getType()); } + + // Uncage + if (playerdata.isCaged()) + { + playerdata.setCaged(false); + playerdata.regenerateHistory(); + playerdata.clearHistory(); + } } // Clear auto-unmute and auto-unfreeze tasks - if (TotalFreedomMod.mutePurgeEventId != 0) + if (TotalFreedomMod.mutePurgeTask != null) { - server.getScheduler().cancelTask(TotalFreedomMod.mutePurgeEventId); - TotalFreedomMod.mutePurgeEventId = 0; - } - if (TotalFreedomMod.freezePurgeEventId != 0) - { - server.getScheduler().cancelTask(TotalFreedomMod.freezePurgeEventId); - TotalFreedomMod.freezePurgeEventId = 0; + TotalFreedomMod.mutePurgeTask.cancel(); } + TotalFreedomMod.allPlayersFrozen = false; + if (TotalFreedomMod.freezePurgeTask != null) + { + TotalFreedomMod.freezePurgeTask.cancel(); + } // Remove all mobs - for (World world : server.getWorlds()) - { - for (Entity ent : world.getLivingEntities()) - { - if (ent instanceof Creature || ent instanceof Ghast || ent instanceof Slime || ent instanceof EnderDragon || ent instanceof Ambient) - { - ent.remove(); - } - } - } + Command_mp.purgeMobs(); return true; } diff --git a/src/me/StevenLawson/TotalFreedomMod/Commands/Command_status.java b/src/me/StevenLawson/TotalFreedomMod/Commands/Command_status.java index b964f18d..672a0a97 100644 --- a/src/me/StevenLawson/TotalFreedomMod/Commands/Command_status.java +++ b/src/me/StevenLawson/TotalFreedomMod/Commands/Command_status.java @@ -10,13 +10,13 @@ import java.util.List; import java.util.Map; import java.util.Map.Entry; import me.StevenLawson.TotalFreedomMod.TFM_Log; -import me.StevenLawson.TotalFreedomMod.TotalFreedomMod; import org.apache.commons.lang.StringUtils; import org.bukkit.ChatColor; import org.bukkit.World; import org.bukkit.command.Command; import org.bukkit.command.CommandSender; import org.bukkit.entity.Player; +import org.bukkit.scheduler.BukkitRunnable; import org.json.simple.JSONArray; import org.json.simple.JSONObject; import org.json.simple.JSONValue; @@ -50,7 +50,7 @@ public class Command_status extends TFM_Command playerMsg(String.format("World %d: %s - %d players.", i++, world.getName(), world.getPlayers().size()), ChatColor.BLUE); } - server.getScheduler().runTaskAsynchronously(plugin, new Runnable() + new BukkitRunnable() { @SuppressWarnings("unchecked") @Override @@ -77,7 +77,7 @@ public class Command_status extends TFM_Command } } - List status_output = new ArrayList(); + final List status_output = new ArrayList(); Iterator> output_it = service_status.entrySet().iterator(); while (output_it.hasNext()) @@ -94,14 +94,21 @@ public class Command_status extends TFM_Command status_output.add(String.format("%s is %s", service_name, (service_online ? ChatColor.GREEN + "ONLINE" + ChatColor.GRAY : ChatColor.RED + "OFFLINE" + ChatColor.GRAY))); } - playerMsg(String.format("Mojang Service Status: %s.", StringUtils.join(status_output, ", ")), ChatColor.GRAY); + new BukkitRunnable() + { + @Override + public void run() + { + playerMsg(String.format("Mojang Service Status: %s.", StringUtils.join(status_output, ", ")), ChatColor.GRAY); + } + }.runTask(plugin); } catch (Exception ex) { TFM_Log.severe(ex); } } - }); + }.runTaskAsynchronously(plugin); return true; } diff --git a/src/me/StevenLawson/TotalFreedomMod/Commands/Command_stfu.java b/src/me/StevenLawson/TotalFreedomMod/Commands/Command_stfu.java index a48939b0..89b43ca7 100644 --- a/src/me/StevenLawson/TotalFreedomMod/Commands/Command_stfu.java +++ b/src/me/StevenLawson/TotalFreedomMod/Commands/Command_stfu.java @@ -1,13 +1,13 @@ package me.StevenLawson.TotalFreedomMod.Commands; -import me.StevenLawson.TotalFreedomMod.TFM_Log; -import me.StevenLawson.TotalFreedomMod.TFM_SuperadminList; import me.StevenLawson.TotalFreedomMod.TFM_PlayerData; +import me.StevenLawson.TotalFreedomMod.TFM_SuperadminList; import me.StevenLawson.TotalFreedomMod.TFM_Util; import me.StevenLawson.TotalFreedomMod.TotalFreedomMod; import org.bukkit.command.Command; import org.bukkit.command.CommandSender; import org.bukkit.entity.Player; +import org.bukkit.scheduler.BukkitRunnable; @CommandPermissions(level = AdminLevel.SUPER, source = SourceType.BOTH) @CommandParameters(description = "Mutes a player with brute force.", usage = "/ [ | list | purge | all]", aliases = "mute") @@ -54,10 +54,9 @@ public class Command_stfu extends TFM_Command count++; } } - if (TotalFreedomMod.mutePurgeEventId != 0) + if (TotalFreedomMod.mutePurgeTask != null) { - server.getScheduler().cancelTask(TotalFreedomMod.mutePurgeEventId); - TotalFreedomMod.mutePurgeEventId = 0; + TotalFreedomMod.mutePurgeTask.cancel(); } playerMsg("Unmuted " + count + " players."); } @@ -77,26 +76,24 @@ public class Command_stfu extends TFM_Command } } - TotalFreedomMod.mutePurgeEventId = server.getScheduler().scheduleSyncDelayedTask(plugin, new Runnable() + if (TotalFreedomMod.mutePurgeTask != null) + { + TotalFreedomMod.mutePurgeTask.cancel(); + } + + TotalFreedomMod.mutePurgeTask = new BukkitRunnable() { @Override public void run() { - if (TotalFreedomMod.mutePurgeEventId == 0) - { - TFM_Log.warning("Mute autopurge task was improperly cancelled!"); - return; - } - TFM_Util.adminAction("MuteTimer", "Unmuting all players", false); for (Player p : server.getOnlinePlayers()) { TFM_PlayerData.getPlayerData(p).setMuted(false); } - - TotalFreedomMod.mutePurgeEventId = 0; } - }, 6000L); // five minutes in ticks: 20*60*5 + }.runTaskLater(plugin, 20L * 60L * 5L); + playerMsg("Muted " + counter + " players."); } else diff --git a/src/me/StevenLawson/TotalFreedomMod/Commands/Command_tfupdate.java b/src/me/StevenLawson/TotalFreedomMod/Commands/Command_tfupdate.java index 60f05895..c03619eb 100644 --- a/src/me/StevenLawson/TotalFreedomMod/Commands/Command_tfupdate.java +++ b/src/me/StevenLawson/TotalFreedomMod/Commands/Command_tfupdate.java @@ -7,6 +7,7 @@ import me.StevenLawson.TotalFreedomMod.TotalFreedomMod; import org.bukkit.command.Command; import org.bukkit.command.CommandSender; import org.bukkit.entity.Player; +import org.bukkit.scheduler.BukkitRunnable; @CommandPermissions(level = AdminLevel.SENIOR, source = SourceType.ONLY_CONSOLE, block_host_console = true) @CommandParameters(description = "Update server files.", usage = "/") @@ -34,7 +35,7 @@ public class Command_tfupdate extends TFM_Command for (final String url : FILES) { - server.getScheduler().runTaskAsynchronously(plugin, new Runnable() + new BukkitRunnable() { @Override public void run() @@ -60,7 +61,7 @@ public class Command_tfupdate extends TFM_Command TFM_Log.severe(ex); } } - }); + }.runTaskAsynchronously(plugin); } return true; diff --git a/src/me/StevenLawson/TotalFreedomMod/Listener/TFM_PlayerListener.java b/src/me/StevenLawson/TotalFreedomMod/Listener/TFM_PlayerListener.java index 0e29bb87..f96110cc 100644 --- a/src/me/StevenLawson/TotalFreedomMod/Listener/TFM_PlayerListener.java +++ b/src/me/StevenLawson/TotalFreedomMod/Listener/TFM_PlayerListener.java @@ -24,6 +24,7 @@ import org.bukkit.event.block.Action; import org.bukkit.event.block.LeavesDecayEvent; import org.bukkit.event.player.*; import org.bukkit.inventory.ItemStack; +import org.bukkit.scheduler.BukkitRunnable; import org.bukkit.util.Vector; public class TFM_PlayerListener implements Listener @@ -186,7 +187,7 @@ public class TFM_PlayerListener implements Listener last_block = test_block; } - Bukkit.getScheduler().scheduleSyncDelayedTask(TotalFreedomMod.plugin, new Runnable() + new BukkitRunnable() { @Override public void run() @@ -198,7 +199,7 @@ public class TFM_PlayerListener implements Listener tnt_block.setType(Material.AIR); } } - }, 30L); + }.runTaskLater(TotalFreedomMod.plugin, 30L); event.setCancelled(true); } @@ -617,14 +618,14 @@ public class TFM_PlayerListener implements Listener if (TotalFreedomMod.adminOnlyMode) { - TotalFreedomMod.plugin.getServer().getScheduler().scheduleSyncDelayedTask(TotalFreedomMod.plugin, new Runnable() + new BukkitRunnable() { @Override public void run() { p.sendMessage(ChatColor.RED + "Server is currently closed to non-superadmins."); } - }, 60L); + }.runTaskLater(TotalFreedomMod.plugin, 20L * 3L); } } catch (Throwable ex) diff --git a/src/me/StevenLawson/TotalFreedomMod/TFM_Heartbeat.java b/src/me/StevenLawson/TotalFreedomMod/TFM_Heartbeat.java index f2aaa0e4..d29c1aa6 100644 --- a/src/me/StevenLawson/TotalFreedomMod/TFM_Heartbeat.java +++ b/src/me/StevenLawson/TotalFreedomMod/TFM_Heartbeat.java @@ -3,8 +3,9 @@ package me.StevenLawson.TotalFreedomMod; import org.bukkit.Server; import org.bukkit.World; import org.bukkit.entity.Player; +import org.bukkit.scheduler.BukkitRunnable; -public class TFM_Heartbeat implements Runnable +public class TFM_Heartbeat extends BukkitRunnable { private final TotalFreedomMod plugin; private final Server server; diff --git a/src/me/StevenLawson/TotalFreedomMod/TFM_PlayerData.java b/src/me/StevenLawson/TotalFreedomMod/TFM_PlayerData.java index 054afe05..f7f8584b 100644 --- a/src/me/StevenLawson/TotalFreedomMod/TFM_PlayerData.java +++ b/src/me/StevenLawson/TotalFreedomMod/TFM_PlayerData.java @@ -15,6 +15,7 @@ import org.bukkit.entity.Arrow; import org.bukkit.entity.EntityType; import org.bukkit.entity.LivingEntity; import org.bukkit.entity.Player; +import org.bukkit.scheduler.BukkitRunnable; import org.bukkit.scheduler.BukkitTask; public class TFM_PlayerData @@ -330,7 +331,7 @@ public class TFM_PlayerData public void startArrowShooter(TotalFreedomMod plugin) { this.stopArrowShooter(); - this.mp44_schedule_id = plugin.getServer().getScheduler().runTaskTimerAsynchronously(plugin, new ArrowShooter(this.player), 1L, 1L); + new ArrowShooter(this.player).runTaskTimer(plugin, 1L, 1L); mp44_firing = true; } @@ -344,7 +345,7 @@ public class TFM_PlayerData mp44_firing = false; } - class ArrowShooter implements Runnable + private class ArrowShooter extends BukkitRunnable { private Player _player; diff --git a/src/me/StevenLawson/TotalFreedomMod/TFM_RunSystemCommand.java b/src/me/StevenLawson/TotalFreedomMod/TFM_RunSystemCommand.java deleted file mode 100644 index 6ec91d00..00000000 --- a/src/me/StevenLawson/TotalFreedomMod/TFM_RunSystemCommand.java +++ /dev/null @@ -1,60 +0,0 @@ -package me.StevenLawson.TotalFreedomMod; - -import java.io.BufferedReader; -import java.io.IOException; -import java.io.InputStreamReader; - -public class TFM_RunSystemCommand implements Runnable -{ - private final String command; - private final TotalFreedomMod plugin; - - public TFM_RunSystemCommand(String command, TotalFreedomMod plugin) - { - this.command = command; - this.plugin = plugin; - } - - @Override - public void run() - { - try - { - final ProcessBuilder childBuilder = new ProcessBuilder(command); - childBuilder.redirectErrorStream(true); - childBuilder.directory(plugin.getDataFolder().getParentFile().getParentFile()); - final Process child = childBuilder.start(); - final BufferedReader reader = new BufferedReader(new InputStreamReader(child.getInputStream())); - try - { - child.waitFor(); - String line; - do - { - line = reader.readLine(); - if (line != null) - { - TFM_Log.info(line); - } - } - while (line != null); - } - finally - { - reader.close(); - } - } - catch (InterruptedException ex) - { - TFM_Log.severe(ex.getMessage()); - } - catch (IOException ex) - { - TFM_Log.severe(ex.getMessage()); - } - catch (Throwable ex) - { - TFM_Log.severe(ex); - } - } -} diff --git a/src/me/StevenLawson/TotalFreedomMod/TFM_SuperadminList.java b/src/me/StevenLawson/TotalFreedomMod/TFM_SuperadminList.java index 0670d18f..3152bf83 100644 --- a/src/me/StevenLawson/TotalFreedomMod/TFM_SuperadminList.java +++ b/src/me/StevenLawson/TotalFreedomMod/TFM_SuperadminList.java @@ -10,6 +10,7 @@ import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.concurrent.TimeUnit; +import me.StevenLawson.TotalFreedomMod.Commands.Command_logs; import org.bukkit.Bukkit; import org.bukkit.command.CommandSender; import org.bukkit.configuration.ConfigurationSection; @@ -386,6 +387,7 @@ public class TFM_SuperadminList { TFM_Superadmin superadmin = superadminList.get(admin_name); superadmin.setActivated(false); + Command_logs.deactivateSuperadmin(superadmin); saveSuperadminList(); } } @@ -423,6 +425,7 @@ public class TFM_SuperadminList } superadmin.setActivated(false); + Command_logs.deactivateSuperadmin(superadmin); } } } diff --git a/src/me/StevenLawson/TotalFreedomMod/TFM_TickMeter.java b/src/me/StevenLawson/TotalFreedomMod/TFM_TickMeter.java index 1d28cad1..3e68b223 100644 --- a/src/me/StevenLawson/TotalFreedomMod/TFM_TickMeter.java +++ b/src/me/StevenLawson/TotalFreedomMod/TFM_TickMeter.java @@ -2,37 +2,37 @@ package me.StevenLawson.TotalFreedomMod; public class TFM_TickMeter { - int ticks; - int taskId; - final TotalFreedomMod plugin; - - public TFM_TickMeter(TotalFreedomMod plugin) - { - this.plugin = plugin; - } - - public int startTicking() - { - int tId = plugin.getServer().getScheduler().scheduleSyncRepeatingTask(plugin, new Runnable() - { - @Override - public void run() - { - ticks += 1; - } - }, 1L, 1L); // ticks (20 in 1 second) - - taskId = tId; - return tId; - } - - public void stopTicking() - { - plugin.getServer().getScheduler().cancelTask(taskId); - } - - public int getTicks() - { - return ticks; - } +// int ticks; +// int taskId; +// final TotalFreedomMod plugin; +// +// public TFM_TickMeter(TotalFreedomMod plugin) +// { +// this.plugin = plugin; +// } +// +// public int startTicking() +// { +// int tId = plugin.getServer().getScheduler().scheduleSyncRepeatingTask(plugin, new Runnable() +// { +// @Override +// public void run() +// { +// ticks += 1; +// } +// }, 1L, 1L); // ticks (20 in 1 second) +// +// taskId = tId; +// return tId; +// } +// +// public void stopTicking() +// { +// plugin.getServer().getScheduler().cancelTask(taskId); +// } +// +// public int getTicks() +// { +// return ticks; +// } } diff --git a/src/me/StevenLawson/TotalFreedomMod/TotalFreedomMod.java b/src/me/StevenLawson/TotalFreedomMod/TotalFreedomMod.java index c741b63e..636fa21a 100644 --- a/src/me/StevenLawson/TotalFreedomMod/TotalFreedomMod.java +++ b/src/me/StevenLawson/TotalFreedomMod/TotalFreedomMod.java @@ -24,6 +24,8 @@ import org.bukkit.configuration.file.YamlConfiguration; import org.bukkit.entity.Player; import org.bukkit.plugin.PluginManager; import org.bukkit.plugin.java.JavaPlugin; +import org.bukkit.scheduler.BukkitRunnable; +import org.bukkit.scheduler.BukkitTask; import org.mcstats.Metrics; public class TotalFreedomMod extends JavaPlugin @@ -49,8 +51,8 @@ public class TotalFreedomMod extends JavaPlugin public static final String NOT_FROM_CONSOLE = "This command may not be used from the console."; // public static boolean allPlayersFrozen = false; - public static int freezePurgeEventId = 0; - public static int mutePurgeEventId = 0; + public static BukkitTask freezePurgeTask = null; + public static BukkitTask mutePurgeTask = null; public static Map fuckoffEnabledFor = new HashMap(); // public static String pluginVersion = ""; @@ -104,8 +106,23 @@ public class TotalFreedomMod extends JavaPlugin TFM_Util.deleteFolder(new File("./_deleteme")); + File[] coreDumps = new File(".").listFiles(new java.io.FileFilter() + { + @Override + public boolean accept(File file) + { + return file.getName().startsWith("java.core"); + } + }); + + for (File dump : coreDumps) + { + TFM_Log.info("Removing core dump file: " + dump.getName()); + dump.delete(); + } + // Heartbeat - server.getScheduler().scheduleSyncRepeatingTask(this, new TFM_Heartbeat(this), HEARTBEAT_RATE * 20L, HEARTBEAT_RATE * 20L); + new TFM_Heartbeat(this).runTaskTimer(plugin, HEARTBEAT_RATE * 20L, HEARTBEAT_RATE * 20L); // metrics @ http://mcstats.org/plugin/TotalFreedomMod try @@ -120,7 +137,7 @@ public class TotalFreedomMod extends JavaPlugin TFM_Log.info("Plugin Enabled - Version: " + TotalFreedomMod.pluginVersion + "." + TotalFreedomMod.buildNumber + " by Madgeek1450 and DarthSalamon"); - server.getScheduler().runTaskLater(this, new Runnable() + new BukkitRunnable() { @Override public void run() @@ -128,7 +145,7 @@ public class TotalFreedomMod extends JavaPlugin TFM_CommandLoader.getInstance().scan(); TFM_CommandBlockerNew.getInstance().parseBlockingRules(); } - }, 20L); + }.runTaskLater(this, 20L); } @Override @@ -242,6 +259,8 @@ public class TotalFreedomMod extends JavaPlugin public static String twitterbotUrl = ""; public static String twitterbotSecret = ""; public static boolean petProtectEnabled = true; + public static String logsRegisterPassword = ""; + public static String logsRegisterURL = ""; public static void loadMainConfig() { @@ -289,6 +308,8 @@ public class TotalFreedomMod extends JavaPlugin twitterbotUrl = config.getString("twitterbot_url", twitterbotUrl); twitterbotSecret = config.getString("twitterbot_secret", twitterbotSecret); petProtectEnabled = config.getBoolean("pet_protect_enabled", petProtectEnabled); + logsRegisterPassword = config.getString("logs_register_password", logsRegisterPassword); + logsRegisterURL = config.getString("logs_register_url", logsRegisterURL); } catch (Exception ex) { diff --git a/src/org/mcstats/Metrics.java b/src/org/mcstats/Metrics.java index 9d1e0328..8fbb3acc 100644 --- a/src/org/mcstats/Metrics.java +++ b/src/org/mcstats/Metrics.java @@ -1,5 +1,5 @@ /* - * Copyright 2011 Tyler Blair. All rights reserved. + * Copyright 2011-2013 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: @@ -25,21 +25,21 @@ * authors and contributors and should not be interpreted as representing official policies, * either expressed or implied, of anybody else. */ - package org.mcstats; import org.bukkit.Bukkit; -import org.bukkit.configuration.file.YamlConfiguration; import org.bukkit.configuration.InvalidConfigurationException; +import org.bukkit.configuration.file.YamlConfiguration; import org.bukkit.plugin.Plugin; import org.bukkit.plugin.PluginDescriptionFile; import org.bukkit.scheduler.BukkitTask; import java.io.BufferedReader; +import java.io.ByteArrayOutputStream; import java.io.File; import java.io.IOException; import java.io.InputStreamReader; -import java.io.OutputStreamWriter; +import java.io.OutputStream; import java.io.UnsupportedEncodingException; import java.net.Proxy; import java.net.URL; @@ -52,47 +52,29 @@ import java.util.LinkedHashSet; import java.util.Set; import java.util.UUID; import java.util.logging.Level; +import java.util.zip.GZIPOutputStream; -/** - *

- * The metrics class obtains data about a plugin and submits statistics about it to the metrics backend. - *

- *

- * Public methods provided by this class: - *

- * - * Graph createGraph(String name);
- * void addCustomData(Metrics.Plotter plotter);
- * void start();
- *
- */ public class Metrics { /** * The current revision number */ - private final static int REVISION = 6; + private final static int REVISION = 7; /** * The base url of the metrics domain */ - private static final String BASE_URL = "http://mcstats.org"; + private static final String BASE_URL = "http://report.mcstats.org"; /** * The url used to report a server's status */ - private static final String REPORT_URL = "/report/%s"; - - /** - * The separator to use for custom data. This MUST NOT change unless you are hosting your own - * version of metrics and want to change it. - */ - private static final String CUSTOM_DATA_SEPARATOR = "~~"; + private static final String REPORT_URL = "/plugin/%s"; /** * Interval of time to ping (in minutes) */ - private static final int PING_INTERVAL = 10; + private static final int PING_INTERVAL = 15; /** * The plugin this metrics submits for @@ -104,16 +86,11 @@ public class Metrics { */ private final Set graphs = Collections.synchronizedSet(new HashSet()); - /** - * The default graph, used for addCustomData when you don't want a specific graph - */ - private final Graph defaultGraph = new Graph("Default"); - /** * The plugin configuration file */ private final YamlConfiguration configuration; - + /** * The plugin configuration file */ @@ -167,8 +144,8 @@ public class Metrics { } /** - * Construct and create a Graph that can be used to separate specific plotters to their own graphs - * on the metrics website. Plotters can be added to the graph object returned. + * Construct and create a Graph that can be used to separate specific plotters to their own graphs on the metrics + * website. Plotters can be added to the graph object returned. * * @param name The name of the graph * @return Graph object created. Will never return NULL under normal circumstances unless bad parameters are given @@ -189,7 +166,7 @@ public class Metrics { } /** - * Add a Graph object to Metrics that represents data for the plugin that should be sent to the backend + * Add a Graph object to BukkitMetrics that represents data for the plugin that should be sent to the backend * * @param graph The name of the graph */ @@ -202,26 +179,9 @@ public class Metrics { } /** - * Adds a custom data plotter to the default graph - * - * @param plotter The plotter to use to plot custom data - */ - public void addCustomData(final Plotter plotter) { - if (plotter == null) { - throw new IllegalArgumentException("Plotter cannot be null"); - } - - // Add the plotter to the graph o/ - defaultGraph.addPlotter(plotter); - - // Ensure the default graph is included in the submitted graphs - graphs.add(defaultGraph); - } - - /** - * 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. + * 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. */ @@ -251,7 +211,7 @@ public class Metrics { task.cancel(); task = null; // Tell all plotters to stop gathering information. - for (Graph graph : graphs){ + for (Graph graph : graphs) { graph.onOptOut(); } } @@ -283,7 +243,7 @@ public class Metrics { * @return true if metrics should be opted out of it */ public boolean isOptOut() { - synchronized(optOutLock) { + synchronized (optOutLock) { try { // Reload the metrics file configuration.load(getConfigFile()); @@ -303,30 +263,30 @@ public class Metrics { } /** - * Enables metrics for the server by setting "opt-out" to false in the config file and starting the metrics task. - * - * @throws IOException - */ + * Enables metrics for the server by setting "opt-out" to false in the config file and starting the metrics task. + * + * @throws java.io.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); - } + // 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 (task == null) { - start(); - } + // Enable Task, if it is not running + if (task == null) { + start(); + } } } /** * Disables metrics for the server by setting "opt-out" to true in the config file and canceling the metrics task. * - * @throws IOException + * @throws java.io.IOException */ public void disable() throws IOException { // This has to be synchronized or it can collide with the check in the task. @@ -377,14 +337,14 @@ public class Metrics { // END server software specific section -- all code below does not use any code outside of this class / Java // Construct the post data - final StringBuilder data = new StringBuilder(); + StringBuilder json = new StringBuilder(1024); + json.append('{'); // The plugin's description file containg all of the plugin data such as name, version, author, etc - data.append(encode("guid")).append('=').append(encode(guid)); - encodeDataPair(data, "version", pluginVersion); - encodeDataPair(data, "server", serverVersion); - encodeDataPair(data, "players", Integer.toString(playersOnline)); - encodeDataPair(data, "revision", String.valueOf(REVISION)); + appendJSONPair(json, "guid", guid); + appendJSONPair(json, "plugin_version", pluginVersion); + appendJSONPair(json, "server_version", serverVersion); + appendJSONPair(json, "players_online", Integer.toString(playersOnline)); // New data as of R6 String osname = System.getProperty("os.name"); @@ -398,44 +358,63 @@ public class Metrics { osarch = "x86_64"; } - encodeDataPair(data, "osname", osname); - encodeDataPair(data, "osarch", osarch); - encodeDataPair(data, "osversion", osversion); - encodeDataPair(data, "cores", Integer.toString(coreCount)); - encodeDataPair(data, "online-mode", Boolean.toString(onlineMode)); - encodeDataPair(data, "java_version", java_version); + appendJSONPair(json, "osname", osname); + appendJSONPair(json, "osarch", osarch); + appendJSONPair(json, "osversion", osversion); + appendJSONPair(json, "cores", Integer.toString(coreCount)); + appendJSONPair(json, "auth_mode", onlineMode ? "1" : "0"); + appendJSONPair(json, "java_version", java_version); // If we're pinging, append it if (isPing) { - encodeDataPair(data, "ping", "true"); + appendJSONPair(json, "ping", "1"); } - // Acquire a lock on the graphs, which lets us make the assumption we also lock everything - // inside of the graph (e.g plotters) - synchronized (graphs) { - final Iterator iter = graphs.iterator(); + if (graphs.size() > 0) { + synchronized (graphs) { + json.append(','); + json.append('"'); + json.append("graphs"); + json.append('"'); + json.append(':'); + json.append('{'); - while (iter.hasNext()) { - final Graph graph = iter.next(); + boolean firstGraph = true; - for (Plotter plotter : graph.getPlotters()) { - // The key name to send to the metrics server - // The format is C-GRAPHNAME-PLOTTERNAME where separator - is defined at the top - // Legacy (R4) submitters use the format Custom%s, or CustomPLOTTERNAME - final String key = String.format("C%s%s%s%s", CUSTOM_DATA_SEPARATOR, graph.getName(), CUSTOM_DATA_SEPARATOR, plotter.getColumnName()); + final Iterator iter = graphs.iterator(); - // The value to send, which for the foreseeable future is just the string - // value of plotter.getValue() - final String value = Integer.toString(plotter.getValue()); + while (iter.hasNext()) { + Graph graph = iter.next(); - // Add it to the http post data :) - encodeDataPair(data, key, value); + StringBuilder graphJson = new StringBuilder(); + graphJson.append('{'); + + for (Plotter plotter : graph.getPlotters()) { + appendJSONPair(graphJson, plotter.getColumnName(), Integer.toString(plotter.getValue())); + } + + graphJson.append('}'); + + if (!firstGraph) { + json.append(','); + } + + json.append(escapeJSON(graph.getName())); + json.append(':'); + json.append(graphJson); + + firstGraph = false; } + + json.append('}'); } } + // close json + json.append('}'); + // Create the url - URL url = new URL(BASE_URL + String.format(REPORT_URL, encode(pluginName))); + URL url = new URL(BASE_URL + String.format(REPORT_URL, urlEncode(pluginName))); // Connect to the website URLConnection connection; @@ -448,26 +427,48 @@ public class Metrics { connection = url.openConnection(); } + + byte[] uncompressed = json.toString().getBytes(); + byte[] compressed = gzip(json.toString()); + + // Headers + connection.addRequestProperty("User-Agent", "MCStats/" + REVISION); + connection.addRequestProperty("Content-Type", "application/json"); + connection.addRequestProperty("Content-Encoding", "gzip"); + connection.addRequestProperty("Content-Length", Integer.toString(compressed.length)); + connection.addRequestProperty("Accept", "application/json"); + connection.addRequestProperty("Connection", "close"); + connection.setDoOutput(true); + if (debug) { + System.out.println("[Metrics] Prepared request for " + pluginName + " uncompressed=" + uncompressed.length + " compressed=" + compressed.length); + } + // Write the data - final OutputStreamWriter writer = new OutputStreamWriter(connection.getOutputStream()); - writer.write(data.toString()); - writer.flush(); + OutputStream os = connection.getOutputStream(); + os.write(compressed); + os.flush(); // Now read the response final BufferedReader reader = new BufferedReader(new InputStreamReader(connection.getInputStream())); - final String response = reader.readLine(); + String response = reader.readLine(); // close resources - writer.close(); + os.close(); reader.close(); - if (response == null || response.startsWith("ERR")) { - throw new IOException(response); //Throw the exception + if (response == null || response.startsWith("ERR") || response.startsWith("7")) { + if (response == null) { + response = "null"; + } else if (response.startsWith("7")) { + response = response.substring(response.startsWith("7,") ? 2 : 1); + } + + throw new IOException(response); } else { // Is this the first update this hour? - if (response.contains("OK This is your first update this hour")) { + if (response.equals("1") || response.contains("This is your first update this hour")) { synchronized (graphs) { final Iterator iter = graphs.iterator(); @@ -483,6 +484,31 @@ public class Metrics { } } + /** + * GZip compress a string of bytes + * + * @param input + * @return + */ + public static byte[] gzip(String input) { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + GZIPOutputStream gzos = null; + + try { + gzos = new GZIPOutputStream(baos); + gzos.write(input.getBytes("UTF-8")); + } catch (IOException e) { + e.printStackTrace(); + } finally { + if (gzos != null) try { + gzos.close(); + } catch (IOException ignore) { + } + } + + return baos.toByteArray(); + } + /** * Check if mineshafter is present. If it is, we need to bypass it to send POST requests * @@ -498,20 +524,83 @@ public class Metrics { } /** - *

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:

- * - * StringBuffer data = new StringBuffer(); - * data.append(encode("guid")).append('=').append(encode(guid)); - * encodeDataPair(data, "version", description.getVersion()); - * + * Appends a json encoded key/value pair to the given string builder. * - * @param buffer the stringbuilder to append the data pair onto - * @param key the key value - * @param value the value + * @param json + * @param key + * @param value + * @throws UnsupportedEncodingException */ - private static void encodeDataPair(final StringBuilder buffer, final String key, final String value) throws UnsupportedEncodingException { - buffer.append('&').append(encode(key)).append('=').append(encode(value)); + private static void appendJSONPair(StringBuilder json, String key, String value) throws UnsupportedEncodingException { + boolean isValueNumeric = false; + + try { + if (value.equals("0") || !value.endsWith("0")) { + Double.parseDouble(value); + isValueNumeric = true; + } + } catch (NumberFormatException e) { + isValueNumeric = false; + } + + if (json.charAt(json.length() - 1) != '{') { + json.append(','); + } + + json.append(escapeJSON(key)); + json.append(':'); + + if (isValueNumeric) { + json.append(value); + } else { + json.append(escapeJSON(value)); + } + } + + /** + * Escape a string to create a valid JSON string + * + * @param text + * @return + */ + private static String escapeJSON(String text) { + StringBuilder builder = new StringBuilder(); + + builder.append('"'); + for (int index = 0; index < text.length(); index++) { + char chr = text.charAt(index); + + switch (chr) { + case '"': + case '\\': + builder.append('\\'); + builder.append(chr); + break; + case '\b': + builder.append("\\b"); + break; + case '\t': + builder.append("\\t"); + break; + case '\n': + builder.append("\\n"); + break; + case '\r': + builder.append("\\r"); + break; + default: + if (chr < ' ') { + String t = "000" + Integer.toHexString(chr); + builder.append("\\u" + t.substring(t.length() - 4)); + } else { + builder.append(chr); + } + break; + } + } + builder.append('"'); + + return builder.toString(); } /** @@ -520,7 +609,7 @@ public class Metrics { * @param text the text to encode * @return the encoded text, as UTF-8 */ - private static String encode(final String text) throws UnsupportedEncodingException { + private static String urlEncode(final String text) throws UnsupportedEncodingException { return URLEncoder.encode(text, "UTF-8"); } @@ -530,8 +619,8 @@ public class Metrics { public static class Graph { /** - * The graph's name, alphanumeric and spaces only :) - * If it does not comply to the above when submitted, it is rejected + * The graph's name, alphanumeric and spaces only :) If it does not comply to the above when submitted, it is + * rejected */ private final String name; @@ -574,7 +663,7 @@ public class Metrics { /** * Gets an unmodifiable set of the plotter objects in the graph * - * @return an unmodifiable {@link Set} of the plotter objects + * @return an unmodifiable {@link java.util.Set} of the plotter objects */ public Set getPlotters() { return Collections.unmodifiableSet(plotters); @@ -596,11 +685,10 @@ public class Metrics { } /** - * Called when the server owner decides to opt-out of Metrics while the server is running. + * Called when the server owner decides to opt-out of BukkitMetrics while the server is running. */ protected void onOptOut() { } - } /** @@ -630,10 +718,9 @@ public class Metrics { } /** - * Get the current value for the plotted point. Since this function defers to an external function - * it may or may not return immediately thus cannot be guaranteed to be thread friendly or safe. - * This function can be called from any thread so care should be taken when accessing resources - * that need to be synchronized. + * Get the current value for the plotted point. Since this function defers to an external function it may or may + * not return immediately thus cannot be guaranteed to be thread friendly or safe. This function can be called + * from any thread so care should be taken when accessing resources that need to be synchronized. * * @return the current value for the point to be plotted. */ @@ -668,7 +755,5 @@ public class Metrics { final Plotter plotter = (Plotter) object; return plotter.name.equals(name) && plotter.getValue() == getValue(); } - } - } \ No newline at end of file