diff --git a/build.xml b/build.xml
new file mode 100644
index 000000000..0ca80f539
--- /dev/null
+++ b/build.xml
@@ -0,0 +1,73 @@
+
+
+
+
+
+
+
+
+
+
+ Builds, tests, and runs the project Plan.
+
+
+
diff --git a/manifest.mf b/manifest.mf
new file mode 100644
index 000000000..8e407f0a5
--- /dev/null
+++ b/manifest.mf
@@ -0,0 +1,2 @@
+Manifest-Version: 1.0
+X-COMMENT: Main-Class will be added automatically by build
diff --git a/nbproject/build-impl.xml b/nbproject/build-impl.xml
new file mode 100644
index 000000000..25e9bb8ff
--- /dev/null
+++ b/nbproject/build-impl.xml
@@ -0,0 +1,1419 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Must set src.dir
+ Must set test.src.dir
+ Must set build.dir
+ Must set dist.dir
+ Must set build.classes.dir
+ Must set dist.javadoc.dir
+ Must set build.test.classes.dir
+ Must set build.test.results.dir
+ Must set build.classes.excludes
+ Must set dist.jar
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Must set javac.includes
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ No tests executed.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Must set JVM to use for profiling in profiler.info.jvm
+ Must set profiler agent JVM arguments in profiler.info.jvmargs.agent
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Must select some files in the IDE or set javac.includes
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ To run this application from the command line without Ant, try:
+
+ java -jar "${dist.jar.resolved}"
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Must select one file in the IDE or set run.class
+
+
+
+ Must select one file in the IDE or set run.class
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Must select one file in the IDE or set debug.class
+
+
+
+
+ Must select one file in the IDE or set debug.class
+
+
+
+
+ Must set fix.includes
+
+
+
+
+
+
+
+
+
+ This target only works when run from inside the NetBeans IDE.
+
+
+
+
+
+
+
+
+ Must select one file in the IDE or set profile.class
+ This target only works when run from inside the NetBeans IDE.
+
+
+
+
+
+
+
+
+ This target only works when run from inside the NetBeans IDE.
+
+
+
+
+
+
+
+
+
+
+
+
+ This target only works when run from inside the NetBeans IDE.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Must select one file in the IDE or set run.class
+
+
+
+
+
+ Must select some files in the IDE or set test.includes
+
+
+
+
+ Must select one file in the IDE or set run.class
+
+
+
+
+ Must select one file in the IDE or set applet.url
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Must select some files in the IDE or set javac.includes
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Some tests failed; see details above.
+
+
+
+
+
+
+
+
+ Must select some files in the IDE or set test.includes
+
+
+
+ Some tests failed; see details above.
+
+
+
+ Must select some files in the IDE or set test.class
+ Must select some method in the IDE or set test.method
+
+
+
+ Some tests failed; see details above.
+
+
+
+
+ Must select one file in the IDE or set test.class
+
+
+
+ Must select one file in the IDE or set test.class
+ Must select some method in the IDE or set test.method
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Must select one file in the IDE or set applet.url
+
+
+
+
+
+
+
+
+ Must select one file in the IDE or set applet.url
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/nbproject/genfiles.properties b/nbproject/genfiles.properties
new file mode 100644
index 000000000..65f9e2e02
--- /dev/null
+++ b/nbproject/genfiles.properties
@@ -0,0 +1,8 @@
+build.xml.data.CRC32=4835e233
+build.xml.script.CRC32=f09b6a05
+build.xml.stylesheet.CRC32=8064a381@1.79.1.48
+# This file is used by a NetBeans-based IDE to track changes in generated files such as build-impl.xml.
+# Do not edit this file. You may delete it but then the IDE will never regenerate such files for you.
+nbproject/build-impl.xml.data.CRC32=4835e233
+nbproject/build-impl.xml.script.CRC32=8797a4da
+nbproject/build-impl.xml.stylesheet.CRC32=05530350@1.79.1.48
diff --git a/nbproject/private/config.properties b/nbproject/private/config.properties
new file mode 100644
index 000000000..e69de29bb
diff --git a/nbproject/private/private.properties b/nbproject/private/private.properties
new file mode 100644
index 000000000..a11705098
--- /dev/null
+++ b/nbproject/private/private.properties
@@ -0,0 +1,10 @@
+compile.on.save=true
+do.depend=false
+do.jar=true
+file.reference.craftbukkit-1.10.2.jar=D:\\Minecraft Servers\\Buildtools\\craftbukkit-1.10.2.jar
+file.reference.EssentialsX-2.0.1.jar=D:\\Downloads\\EssentialsX-2.0.1.jar
+file.reference.OnTime.jar=D:\\Downloads\\OnTime.jar
+file.reference.Vault.jar=D:\\Minecraft Servers\\TestServer\\plugins\\Uusi kansio\\Vault.jar
+javac.debug=true
+javadoc.preview=true
+user.properties.file=C:\\Users\\Risto\\AppData\\Roaming\\.tmcbeans\\8.1.0\\build.properties
diff --git a/nbproject/private/private.xml b/nbproject/private/private.xml
new file mode 100644
index 000000000..800132876
--- /dev/null
+++ b/nbproject/private/private.xml
@@ -0,0 +1,22 @@
+
+
+
+
+
+ file:/C:/Users/Risto/Documents/NetBeansProjects/PlayerAnalyticsPlugin-0.0.1/src/com/djrapitops/plan/Plan.java
+ file:/C:/Users/Risto/Documents/NetBeansProjects/PlayerAnalyticsPlugin-0.0.1/src/plugin.yml
+ file:/C:/Users/Risto/Documents/NetBeansProjects/PlayerAnalyticsPlugin-0.0.1/src/com/djrapitops/plan/command/hooks/Hook.java
+ file:/C:/Users/Risto/Documents/NetBeansProjects/PlayerAnalyticsPlugin-0.0.1/src/com/djrapitops/plan/command/hooks/VaultHook.java
+ file:/C:/Users/Risto/Documents/NetBeansProjects/PlayerAnalyticsPlugin-0.0.1/src/com/djrapitops/plan/command/utils/DataFormatUtils.java
+ file:/C:/Users/Risto/Documents/NetBeansProjects/PlayerAnalyticsPlugin-0.0.1/src/com/djrapitops/plan/command/hooks/FactionsHook.java
+ file:/C:/Users/Risto/Documents/NetBeansProjects/PlayerAnalyticsPlugin-0.0.1/src/com/djrapitops/plan/command/commands/InspectCommand.java
+ file:/C:/Users/Risto/Documents/NetBeansProjects/PlayerAnalyticsPlugin-0.0.1/src/config.yml
+ file:/C:/Users/Risto/Documents/NetBeansProjects/PlayerAnalyticsPlugin-0.0.1/src/com/djrapitops/plan/API.java
+ file:/C:/Users/Risto/Documents/NetBeansProjects/PlayerAnalyticsPlugin-0.0.1/src/com/djrapitops/plan/PlanCommand.java
+ file:/C:/Users/Risto/Documents/NetBeansProjects/PlayerAnalyticsPlugin-0.0.1/src/com/djrapitops/plan/command/hooks/PlaceholderAPIHook.java
+ file:/C:/Users/Risto/Documents/NetBeansProjects/PlayerAnalyticsPlugin-0.0.1/src/com/djrapitops/plan/command/utils/DataUtils.java
+ file:/C:/Users/Risto/Documents/NetBeansProjects/PlayerAnalyticsPlugin-0.0.1/src/com/djrapitops/plan/command/commands/AnalyzeCommand.java
+ file:/C:/Users/Risto/Documents/NetBeansProjects/PlayerAnalyticsPlugin-0.0.1/src/com/djrapitops/plan/command/hooks/AdvancedAchievementsHook.java
+
+
+
diff --git a/nbproject/project.properties b/nbproject/project.properties
new file mode 100644
index 000000000..d46472440
--- /dev/null
+++ b/nbproject/project.properties
@@ -0,0 +1,95 @@
+annotation.processing.enabled=true
+annotation.processing.enabled.in.editor=false
+annotation.processing.processors.list=
+annotation.processing.run.all.processors=true
+annotation.processing.source.output=${build.generated.sources.dir}/ap-source-output
+application.title=Plan
+application.vendor=Risto
+build.classes.dir=${build.dir}/classes
+build.classes.excludes=**/*.java,**/*.form
+# This directory is removed when the project is cleaned:
+build.dir=build
+build.generated.dir=${build.dir}/generated
+build.generated.sources.dir=${build.dir}/generated-sources
+# Only compile against the classpath explicitly listed here:
+build.sysclasspath=ignore
+build.test.classes.dir=${build.dir}/test/classes
+build.test.results.dir=${build.dir}/test/results
+# Uncomment to specify the preferred debugger connection transport:
+#debug.transport=dt_socket
+debug.classpath=\
+ ${run.classpath}
+debug.test.classpath=\
+ ${run.test.classpath}
+# Files in build.classes.dir which should be excluded from distribution jar
+dist.archive.excludes=
+# This directory is removed when the project is cleaned:
+dist.dir=dist
+dist.jar=${dist.dir}/Plan.jar
+dist.javadoc.dir=${dist.dir}/javadoc
+endorsed.classpath=
+excludes=
+file.reference.AdvancedAchievements.jar=D:\\Minecraft Servers\\TestServer\\plugins\\Uusi kansio\\AdvancedAchievements.jar
+file.reference.Factions.jar=D:\\Minecraft Servers\\TestServer\\plugins\\Uusi kansio\\Factions.jar
+file.reference.MassiveCore.jar=D:\\Minecraft Servers\\TestServer\\plugins\\Uusi kansio\\MassiveCore.jar
+file.reference.mcMMO.jar=D:\\Minecraft Servers\\TestServer\\plugins\\Uusi kansio\\mcMMO.jar
+file.reference.PlaceholderAPI.jar=D:\\Minecraft Servers\\TestServer\\plugins\\Uusi kansio\\PlaceholderAPI.jar
+file.reference.SuperbVote-0.3.2.jar=D:\\Minecraft Servers\\TestServer\\plugins\\Uusi kansio\\SuperbVote-0.3.2.jar
+file.reference.Towny.jar=D:\\Downloads\\Towny.jar
+file.reference.Vault.jar=D:\\Minecraft Servers\\TestServer\\plugins\\Vault.jar
+includes=**
+jar.compress=true
+javac.classpath=\
+ ${file.reference.craftbukkit-1.10.2.jar}:\
+ ${file.reference.OnTime.jar}:\
+ ${file.reference.EssentialsX-2.0.1.jar}:\
+ ${file.reference.Towny.jar}:\
+ ${file.reference.Vault.jar}:\
+ ${file.reference.Factions.jar}:\
+ ${file.reference.MassiveCore.jar}:\
+ ${file.reference.mcMMO.jar}:\
+ ${file.reference.SuperbVote-0.3.2.jar}:\
+ ${file.reference.PlaceholderAPI.jar}:\
+ ${file.reference.AdvancedAchievements.jar}
+# Space-separated list of extra javac options
+javac.compilerargs=
+javac.deprecation=false
+javac.external.vm=true
+javac.processorpath=\
+ ${javac.classpath}
+javac.source=1.8
+javac.target=1.8
+javac.test.classpath=\
+ ${javac.classpath}:\
+ ${build.classes.dir}
+javac.test.processorpath=\
+ ${javac.test.classpath}
+javadoc.additionalparam=
+javadoc.author=false
+javadoc.encoding=${source.encoding}
+javadoc.noindex=false
+javadoc.nonavbar=false
+javadoc.notree=false
+javadoc.private=false
+javadoc.splitindex=true
+javadoc.use=true
+javadoc.version=false
+javadoc.windowtitle=
+main.class=player.analytics.main.Main
+manifest.file=manifest.mf
+meta.inf.dir=${src.dir}/META-INF
+mkdist.disabled=false
+platform.active=default_platform
+run.classpath=\
+ ${javac.classpath}:\
+ ${build.classes.dir}
+# Space-separated list of JVM arguments used when running the project.
+# You may also define separate properties like run-sys-prop.name=value instead of -Dname=value.
+# To set system properties for unit tests define test-sys-prop.name=value:
+run.jvmargs=
+run.test.classpath=\
+ ${javac.test.classpath}:\
+ ${build.test.classes.dir}
+source.encoding=UTF-8
+src.dir=src
+test.src.dir=test
diff --git a/nbproject/project.xml b/nbproject/project.xml
new file mode 100644
index 000000000..6451c434d
--- /dev/null
+++ b/nbproject/project.xml
@@ -0,0 +1,15 @@
+
+
+ org.netbeans.modules.java.j2seproject
+
+
+ Plan
+
+
+
+
+
+
+
+
+
diff --git a/src/com/djrapitops/plan/API.java b/src/com/djrapitops/plan/API.java
new file mode 100644
index 000000000..c296c67af
--- /dev/null
+++ b/src/com/djrapitops/plan/API.java
@@ -0,0 +1,63 @@
+package com.djrapitops.plan;
+
+import com.djrapitops.plan.command.hooks.Hook;
+import com.djrapitops.plan.command.utils.DataFormatUtils;
+import com.djrapitops.plan.command.utils.DataUtils;
+import java.util.HashMap;
+
+public class API {
+
+ private Plan plugin;
+
+ public API(Plan plugin) {
+ this.plugin = plugin;
+ }
+
+ public boolean getDebug() {
+ return plugin.getConfig().getBoolean("debug");
+ }
+
+ public boolean getVisibleEssentials() {
+ return plugin.getConfig().getBoolean("visible.essentials");
+ }
+
+ public boolean getVisibleOnTime() {
+ return plugin.getConfig().getBoolean("visible.ontime");
+ }
+
+ public boolean getVisibleFactions() {
+ return plugin.getConfig().getBoolean("visible.factions");
+ }
+
+ public boolean getVisibleSuperbVote() {
+ return plugin.getConfig().getBoolean("visible.superbvote");
+ }
+
+ public boolean getVisibleTowny() {
+ return plugin.getConfig().getBoolean("visible.towny");
+ }
+
+ public boolean getVisibleVault() {
+ return plugin.getConfig().getBoolean("visible.vault");
+ }
+
+ public boolean getVisibleAdvancedAchievements() {
+ return plugin.getConfig().getBoolean("visible.advancedachievements");
+ }
+
+ public boolean getVisiblePlaceholderAPI() {
+ return plugin.getConfig().getBoolean("visible.placeholderapi");
+ }
+
+ public HashMap getData(String playerName) {
+ return DataFormatUtils.removeExtraDataPoints(DataUtils.getData(false, playerName));
+ }
+
+ public HashMap getAllData(String playerName) {
+ return DataFormatUtils.removeExtraDataPoints(DataUtils.getData(true, playerName));
+ }
+
+ public void addExtraHook(String name, Hook hook) {
+ plugin.addExtraHook(name, hook);
+ }
+}
diff --git a/src/com/djrapitops/plan/Plan.java b/src/com/djrapitops/plan/Plan.java
new file mode 100644
index 000000000..a74d1194e
--- /dev/null
+++ b/src/com/djrapitops/plan/Plan.java
@@ -0,0 +1,183 @@
+package com.djrapitops.plan;
+
+import com.djrapitops.plan.command.hooks.EssentialsHook;
+import com.djrapitops.plan.command.hooks.FactionsHook;
+import com.djrapitops.plan.command.hooks.OnTimeHook;
+import com.djrapitops.plan.command.hooks.Hook;
+import com.djrapitops.plan.command.hooks.PlaceholderAPIHook;
+import com.djrapitops.plan.command.hooks.SuperbVoteHook;
+//import com.djrapitops.plan.command.hooks.McMMOHook;
+import com.djrapitops.plan.command.hooks.TownyHook;
+import com.djrapitops.plan.command.hooks.VaultHook;
+import com.djrapitops.plan.command.hooks.AdvancedAchievementsHook;
+import com.djrapitops.plan.command.utils.DataUtils;
+import org.bukkit.plugin.java.JavaPlugin;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.PrintWriter;
+import org.bukkit.Bukkit;
+import org.bukkit.ChatColor;
+
+public class Plan extends JavaPlugin {
+
+ private final Map hooks;
+ private Hook placeholderAPIHook;
+ private API api;
+ private final Map extraHooks;
+
+ public Plan() {
+ this.hooks = new HashMap<>();
+ this.extraHooks = new HashMap<>();
+ }
+
+ public Map getHooks() {
+ return this.hooks;
+ }
+
+ @Override
+ public void onEnable() {
+ getDataFolder().mkdirs();
+
+ getConfig().options().copyDefaults(true);
+
+ getConfig().options().header("Plan Config\n"
+ + "debug - Errors are saved in errorlog.txt when they occur\n"
+ + "visible - Plugin's data is accessable with /plan inspect command"
+ );
+
+ saveConfig();
+
+ try {
+ if (Bukkit.getPluginManager().isPluginEnabled("PlaceholderAPI")) {
+ String[] placeholders = DataUtils.getPlaceholdersFileData();
+ if (placeholders != null) {
+ this.placeholderAPIHook = new PlaceholderAPIHook(this, placeholders);
+ PlaceholderAPIHook phAHook = (PlaceholderAPIHook) placeholderAPIHook;
+ phAHook.hook();
+ } else {
+ logToFile("Failed to read placeholders.yml\n");
+ }
+ }
+ } catch (Exception e) {
+ logError("Failed to create placeholders.yml");
+ logToFile("Failed to create placeholders.yml\n" + e);
+ }
+
+ List hookFail = hookInit();
+ if (this.hooks.isEmpty()) {
+ logError("Found no plugins to get data (or config set to false). Disabling plugin..");
+ logToFile("MAIN\nNo Hooks found. Plugin Disabled.");
+ getServer().getPluginManager().disablePlugin(this);
+ return;
+ }
+
+ this.api = new API(this);
+
+ String loadedMsg = "Hooked into: ";
+ for (String key : this.hooks.keySet()) {
+ loadedMsg += ChatColor.GREEN + key + " ";
+ }
+ String failedMsg = "Not Hooked: ";
+ for (String string : hookFail) {
+ failedMsg += ChatColor.RED + string + " ";
+ }
+ Bukkit.getServer().getConsoleSender().sendMessage("[Plan] " + loadedMsg);
+ if (!hookFail.isEmpty()) {
+ Bukkit.getServer().getConsoleSender().sendMessage("[Plan] " + failedMsg);
+ }
+
+ getCommand("plan").setExecutor(new PlanCommand(this));
+
+ log("Player Analytics Enabled.");
+ }
+
+ public List hookInit() {
+ this.hooks.clear();
+ List hookFail = new ArrayList<>();
+ String[] plugins = {"OnTime", "Essentials", "Towny", "Vault", "Factions", "SuperbVote", "AdvancedAchievements"};
+ for (String pluginName : plugins) {
+
+ if (getConfig().getBoolean("visible." + pluginName.toLowerCase())) {
+ try {
+ String className = "com.djrapitops.plan.command.hooks." + pluginName + "Hook";
+ Class clazz = (Class) Hook.class.forName(className);
+ this.hooks.put(pluginName, clazz.getConstructor(Plan.class).newInstance(this));
+ } catch (Exception | NoClassDefFoundError e) {
+ hookFail.add(pluginName);
+ String toLog = "MAIN-HOOKINIT\nFailed to hook " + pluginName + "\n" + e;
+ toLog += "\n" + e.getCause();
+ logToFile(toLog);
+
+ }
+ } else {
+ hookFail.add(ChatColor.YELLOW + pluginName);
+ }
+ }
+ for (String extraHook : this.extraHooks.keySet()) {
+ this.hooks.put(extraHook, this.extraHooks.get(extraHook));
+ }
+ if (getConfig().getBoolean("visible.placeholderapi")) {
+ if (this.placeholderAPIHook != null) {
+ this.hooks.put("PlaceholderAPI", this.placeholderAPIHook);
+ } else {
+ hookFail.add("PlaceholderAPI");
+ }
+ } else {
+ hookFail.add(ChatColor.YELLOW + "PlaceholderAPI");
+ }
+ return hookFail;
+ }
+
+ @Override
+ public void onDisable() {
+ log("Player Analytics Disabled.");
+ }
+
+ public void log(String message) {
+ getLogger().info(message);
+ }
+
+ public void logError(String message) {
+ getLogger().severe(message);
+ }
+
+ public void logToFile(String message) {
+ if (getConfig().getBoolean("debug")) {
+ File folder = getDataFolder();
+ if (!folder.exists()) {
+ folder.mkdir();
+ }
+ File log = new File(getDataFolder(), "errorlog.txt");
+ try {
+ if (!log.exists()) {
+ log.createNewFile();
+ }
+ FileWriter fw = new FileWriter(log, true);
+ try (PrintWriter pw = new PrintWriter(fw)) {
+ pw.println(message + "\n");
+ pw.flush();
+ }
+ } catch (IOException e) {
+ logError("Failed to create log.txt file");
+ }
+ }
+ }
+
+ public Hook getPlaceholderAPIHook() {
+ return this.placeholderAPIHook;
+ }
+
+ public API getAPI() {
+ return api;
+ }
+
+ public void addExtraHook(String name, Hook hook) {
+ this.extraHooks.put(name, hook);
+ }
+}
diff --git a/src/com/djrapitops/plan/PlanCommand.java b/src/com/djrapitops/plan/PlanCommand.java
new file mode 100644
index 000000000..cd5342425
--- /dev/null
+++ b/src/com/djrapitops/plan/PlanCommand.java
@@ -0,0 +1,109 @@
+package com.djrapitops.plan;
+
+import com.djrapitops.plan.command.CommandType;
+import com.djrapitops.plan.command.SubCommand;
+import com.djrapitops.plan.command.commands.AnalyzeCommand;
+import com.djrapitops.plan.command.commands.HelpCommand;
+import com.djrapitops.plan.command.commands.InspectCommand;
+import com.djrapitops.plan.command.commands.ReloadCommand;
+import com.djrapitops.plan.javaTools.Editor;
+import org.bukkit.ChatColor;
+
+import org.bukkit.command.Command;
+import org.bukkit.command.CommandExecutor;
+import org.bukkit.command.CommandSender;
+
+import java.util.List;
+import java.util.ArrayList;
+import org.bukkit.entity.Player;
+
+public class PlanCommand implements CommandExecutor {
+
+ private final List commands;
+
+ public PlanCommand(Plan plugin) {
+ commands = new ArrayList<>();
+
+ commands.add(new HelpCommand(plugin, this));
+ commands.add(new InspectCommand(plugin));
+ if (plugin.getConfig().getBoolean("analysis")) {
+ commands.add(new AnalyzeCommand(plugin));
+ }
+ commands.add(new ReloadCommand(plugin));
+ }
+
+ public List getCommands() {
+ return this.commands;
+ }
+
+ public SubCommand getCommand(String name) {
+ for (SubCommand command : commands) {
+ String[] aliases = command.getName().split(",");
+
+ for (String alias : aliases) {
+ if (alias.equalsIgnoreCase(name)) {
+ return command;
+ }
+ }
+ }
+ return null;
+ }
+
+ private void sendDefaultCommand(CommandSender sender, Command cmd, String commandLabel, String[] args) {
+ String command = "inspect";
+ if (args.length < 1) {
+ command = "help";
+ }
+ Editor edit = new Editor();
+ onCommand(sender, cmd, commandLabel, edit.mergeArrays(new String[]{command}, args));
+ }
+
+ @Override
+ public boolean onCommand(CommandSender sender, Command cmd, String commandLabel, String[] args) {
+ if (args.length < 1) {
+ sendDefaultCommand(sender, cmd, commandLabel, args);
+ return true;
+ }
+
+ SubCommand command = getCommand(args[0]);
+
+ if (command == null) {
+ sendDefaultCommand(sender, cmd, commandLabel, args);
+ return true;
+ }
+
+ if (!sender.hasPermission(command.getPermission())) {
+// Phrase.NO_PERMISSION_FOR_COMMAND.sendWithPrefix( sender );
+ sender.sendMessage(ChatColor.RED + "[PLAN] You do not have the required permmission.");
+ return true;
+ }
+
+ boolean console = !(sender instanceof Player);
+
+ if (console && args.length < 2 && command.getCommandType() == CommandType.CONSOLE_WITH_ARGUMENTS) {
+// Phrase.COMMAND_NEEDS_ARGUMENTS.sendWithPrefix( sender );
+ sender.sendMessage(ChatColor.RED + "[PLAN] Command requires arguments.");
+
+ return true;
+ }
+
+ if (console && command.getCommandType() == CommandType.PLAYER) {
+// Phrase.COMMAND_NOT_CONSOLE.sendWithPrefix( sender, commandLabel );
+ sender.sendMessage(ChatColor.RED + "[PLAN] This command can be only used as a player.");
+
+ return true;
+ }
+
+ String[] realArgs = new String[args.length - 1];
+
+ for (int i = 1; i < args.length; i++) {
+ realArgs[i - 1] = args[i];
+ }
+
+ if (!command.onCommand(sender, cmd, commandLabel, realArgs)) {
+// Phrase.TRY_COMMAND.sendWithPrefix( sender, parse( commandLabel, command ) );
+ }
+ return true;
+ }
+
+}
diff --git a/src/com/djrapitops/plan/UUIDFetcher.java b/src/com/djrapitops/plan/UUIDFetcher.java
new file mode 100644
index 000000000..cf26ecba7
--- /dev/null
+++ b/src/com/djrapitops/plan/UUIDFetcher.java
@@ -0,0 +1,133 @@
+/*
+ * Copyright (c) 2015 Nate Mortensen
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.djrapitops.plan;
+
+import com.google.common.collect.ImmutableList;
+import org.json.simple.JSONArray;
+import org.json.simple.JSONObject;
+import org.json.simple.parser.JSONParser;
+
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.net.HttpURLConnection;
+import java.net.URL;
+import java.nio.ByteBuffer;
+import java.util.*;
+import java.util.concurrent.Callable;
+
+public class UUIDFetcher implements Callable