Browse Source

Merge pull request #13 from georgePadolsey/master

General cleanup + improving clarity of codebase
Linus 7 years ago
parent
commit
35acf728f0

+ 49 - 0
.gitignore

@@ -0,0 +1,49 @@
+# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm
+# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
+
+# User-specific stuff:
+.idea/**/workspace.xml
+.idea/**/tasks.xml
+.idea/dictionaries
+
+# Sensitive or high-churn files:
+.idea/**/dataSources/
+.idea/**/dataSources.ids
+.idea/**/dataSources.xml
+.idea/**/dataSources.local.xml
+.idea/**/sqlDataSources.xml
+.idea/**/dynamic.xml
+.idea/**/uiDesigner.xml
+
+# Gradle:
+.idea/**/gradle.xml
+.idea/**/libraries
+
+# CMake
+cmake-build-debug/
+
+# Mongo Explorer plugin:
+.idea/**/mongoSettings.xml
+
+## File-based project format:
+*.iws
+
+## Plugin-specific files:
+
+# IntelliJ
+out/
+
+# mpeltonen/sbt-idea plugin
+.idea_modules/
+
+# JIRA plugin
+atlassian-ide-plugin.xml
+
+# Cursive Clojure plugin
+.idea/replstate.xml
+
+# Crashlytics plugin (for Android Studio and IntelliJ)
+com_crashlytics_export_strings.xml
+crashlytics.properties
+crashlytics-build.properties
+fabric.properties

+ 2 - 2
TimeIsMoney2/src/config.yml

@@ -15,7 +15,7 @@ display-messages-in-actionbar-time: 10
 give_money_every_second: 600
 store-money-in-bank: false
 
-# Define if multiple accounts should get payed with the same ip-address. You may disable this if your players are using multiple acconts per ip. 
+# Define if multiple accounts should get payed with the same ip-address. You may disable this if your players are using multiple accounts per ip.
 allow-multiple-accounts: true
 
 # Payouts will be delivered by "chance" instead "permission".
@@ -45,7 +45,7 @@ message_afk: "&cYou havn't earned money because you were afk!"
 message_multiple_ips: "&cYou havn't earned money because you're playing with multiple accounts!"
 message_actionbar: "&aYou earned &c%money% &afor 10 minutes online time!"
 message_payoutlimit_reached_actionbar: "&cYou have reached the payout limit today. You got 0$"
-message_afk_actionbar: "&cYou havn't earned money because you were afk!"
+message_afk_actionbar: "&cYou haven't earned money because you were afk!"
 message_atm_noperms: "&cYou don't have the permission to use ATM's!"
 message_atm_nomoneyinbank: "&cYou don't have enough money in bank!"
 message_atm_nomoney: "&cYou don't have enough money!"

+ 7 - 0
TimeIsMoney2/src/de/Linus122/TimeIsMoney/ActionBarUtils.java

@@ -0,0 +1,7 @@
+package de.Linus122.TimeIsMoney;
+
+import org.bukkit.entity.Player;
+
+public interface ActionBarUtils {
+	void sendActionBarMessage(Player p, String message);
+}

+ 6 - 4
TimeIsMoney2/src/de/Linus122/TimeIsMoney/Cmd.java

@@ -4,9 +4,11 @@ import org.bukkit.command.Command;
 import org.bukkit.command.CommandExecutor;
 import org.bukkit.command.CommandSender;
 
-public class Cmd implements CommandExecutor {
-	Main main;
-	public Cmd(Main main) {
+import static de.Linus122.TimeIsMoney.Utils.CC;
+
+class Cmd implements CommandExecutor {
+	private final Main main;
+	Cmd(Main main) {
 		this.main = main;
 	}
 
@@ -14,7 +16,7 @@ public class Cmd implements CommandExecutor {
 	public boolean onCommand(CommandSender cs, Command arg1, String arg2, String[] arg3) {
 		if(cs.hasPermission("tim.reload")){
 			main.reload();
-			cs.sendMessage("§aTime is Money §cv" + main.PL_VERSION + " §areloaded!");
+			cs.sendMessage(CC("&aTime is Money &cv" + Main.PL_VERSION + " &areloaded!"));
 			
 		}
 		return true;

+ 431 - 463
TimeIsMoney2/src/de/Linus122/TimeIsMoney/Main.java

@@ -9,7 +9,6 @@ import java.io.ObjectInputStream;
 import java.io.ObjectOutputStream;
 import java.io.PrintWriter;
 import java.lang.reflect.Field;
-import java.net.URL;
 import java.sql.Timestamp;
 import java.util.ArrayList;
 import java.util.Calendar;
@@ -19,9 +18,9 @@ import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
 import java.util.Random;
-import java.util.Set;
 import java.util.UUID;
 
+import com.earth2me.essentials.Essentials;
 import org.bukkit.Bukkit;
 import org.bukkit.Location;
 import org.bukkit.Server;
@@ -40,123 +39,110 @@ import org.bukkit.plugin.java.JavaPlugin;
 import modules.atm.ATM;
 import net.milkbowl.vault.economy.Economy;
 
-public class Main extends JavaPlugin{
-	
-	List<Payout> payouts = new ArrayList<Payout>();
-	HashMap<String, Double> payedMoney = new HashMap<String, Double>();
-	
-	HashMap<UUID, Integer> onlineSeconds = new HashMap<UUID, Integer>();
-	
-	HashMap<UUID, Location> lastLocation = new HashMap<UUID, Location>();
-	
-	public static Economy economy = null;
-	public static Utils utils = null;
-	String message;
-	
-	ConsoleCommandSender clogger = this.getServer().getConsoleSender();
-	
-	public static int CFG_VERSION = 12;
-	public static int PL_VERSION;
-
-	int currentDay = 0;
-	
-	public static YamlConfiguration finalconfig;
-	
-	boolean use18Features = true;
-	
-	public static List<String> disabledWorlds;
-	public static HashMap<String, UUID> boundIPs = new HashMap<String, UUID>();
-	
-	@SuppressWarnings({ "deprecation", "unchecked" })
-	@Override
-	public void onEnable(){
-		PL_VERSION = Integer.parseInt(((Plugin)this).getDescription().getVersion());
-		this.getCommand("timeismoney").setExecutor(new Cmd(this));
-		
-		currentDay = (new Date()).getDay();
-	
-		File config = new File("plugins/TimeIsMoney/config.yml");
-		if(config.exists()){
-			YamlConfiguration cfg = YamlConfiguration.loadConfiguration(config);	
-			String old_config = "config_old " + cfg.getInt("configuration-version") + ".yml";
-			if(cfg.contains("configuration-version")){
-				if(cfg.getInt("configuration-version") < CFG_VERSION){
-					clogger.sendMessage("[TimeIsMoney] §cYOU ARE USING AN OLD CONFIG-VERSION. The plugin CANT work with this.");
-					clogger.sendMessage("[TimeIsMoney] §cI have created an new config for you. The old one is saved as config_old.yml.");
-					config.renameTo(new File("plugins/TimeIsMoney/" + old_config));
-				}
-			}
-			this.saveDefaultConfig();
-			for(String key : cfg.getConfigurationSection("").getKeys(true)){
-				if(!this.getConfig().contains(key)){
-					this.getConfig().set(key, cfg.get(key));
-				}
-			}
-		}else{
-			this.saveDefaultConfig();
-		}
-		
-		
-	    
-		finalconfig = YamlConfiguration.loadConfiguration(config);
-		disabledWorlds = getConfig().getStringList("disabled_in_worlds");
-		
-		if(getConfig().getBoolean("enable_atm")) new ATM(this);
-		
-		final int seconds = getConfig().getInt("give_money_every_second");
-		Bukkit.getScheduler().runTaskTimerAsynchronously(this, new Runnable(){
-			public void run(){
-				try{
-					for(Player p : Bukkit.getOnlinePlayers()){
-						if(disabledWorlds.contains(p.getWorld().getName())) continue;
-						if(!boundIPs.containsKey(p.getAddress().getHostName())){
-							boundIPs.put(p.getAddress().getHostName(), p.getUniqueId());
-						}
-						if(onlineSeconds.containsKey(p.getUniqueId())){
-							
-							onlineSeconds.put(p.getUniqueId(), onlineSeconds.get(p.getUniqueId()) + 1);
-						}else{
-							onlineSeconds.put(p.getUniqueId(), 1);
-						}
-						if(onlineSeconds.get(p.getUniqueId()) > seconds){
-							pay(p);
-							onlineSeconds.remove(p.getUniqueId());
-						}
-					}
-				}catch(NullPointerException e){
-					// 
-				}
-			}
-		}, 20L, 20L);
-		Bukkit.getScheduler().scheduleSyncRepeatingTask(this, new Runnable(){
-			public void run(){
-				if(currentDay != new Date().getDay()){ //Next day, clear payouts!
-					log("Cleared all payouts");
-					payedMoney.clear();
-					currentDay = new Date().getDay();
-				}
-			}
-		}, 20L * 60, 20L * 60 * 15);
-		setupEconomy();
-		
-		message = finalconfig.getString("message");
-		message = message.replace('&', '§');
-
-		try{
-		    FileInputStream fis = new FileInputStream(new File("plugins/TimeIsMoney/payed_today.data"));
-		    ObjectInputStream ois = new ObjectInputStream(fis);
-		    payedMoney = (HashMap<String, Double>) ((HashMap<String, Double>) ois.readObject()).clone();
-
-		    ois.close();	
-		}catch(Exception e){
-			
-		}
-		
-		loadPayouts();
-		
-		
-		
-		String packageName = this.getServer().getClass().getPackage().getName();
+import static de.Linus122.TimeIsMoney.Utils.CC;
+
+public class Main extends JavaPlugin {
+
+    public static Economy economy = null;
+    private static ActionBarUtils actionBarUtils = null;
+    private static final int CFG_VERSION = 12;
+    public static int PL_VERSION;
+    public static YamlConfiguration finalconfig;
+    private static List<String> disabledWorlds;
+    private static final HashMap<String, UUID> boundIPs = new HashMap<>();
+    private final List<Payout> payouts = new ArrayList<>();
+    private HashMap<String, Double> payedMoney = new HashMap<>();
+    private final HashMap<UUID, Integer> onlineSeconds = new HashMap<>();
+    private final HashMap<UUID, Location> lastLocation = new HashMap<>();
+    private String message;
+    private final ConsoleCommandSender clogger = this.getServer().getConsoleSender();
+    private int currentDay = 0;
+    private boolean use18Features = true;
+
+    @SuppressWarnings({"deprecation", "unchecked"})
+    @Override
+    public void onEnable() {
+        PL_VERSION = Integer.parseInt(this.getDescription().getVersion());
+        this.getCommand("timeismoney").setExecutor(new Cmd(this));
+
+        currentDay = (new Date()).getDay();
+
+        File config = new File("plugins/TimeIsMoney/config.yml");
+        if (config.exists()) {
+            YamlConfiguration cfg = YamlConfiguration.loadConfiguration(config);
+            String old_config = "config_old " + cfg.getInt("configuration-version") + ".yml";
+            if (cfg.contains("configuration-version")) {
+                if (cfg.getInt("configuration-version") < CFG_VERSION) {
+                    clogger.sendMessage(CC("[TimeIsMoney] &cYOU ARE USING AN OLD CONFIG-VERSION. The plugin CANT work with this."));
+                    clogger.sendMessage(CC("[TimeIsMoney] &cI have created an new config for you. The old one is saved as config_old.yml."));
+                    config.renameTo(new File("plugins/TimeIsMoney/" + old_config));
+                }
+            }
+            this.saveDefaultConfig();
+            for (String key : cfg.getConfigurationSection("").getKeys(true)) {
+                if (!this.getConfig().contains(key)) {
+                    this.getConfig().set(key, cfg.get(key));
+                }
+            }
+        } else {
+            this.saveDefaultConfig();
+        }
+
+
+        finalconfig = YamlConfiguration.loadConfiguration(config);
+        disabledWorlds = getConfig().getStringList("disabled_in_worlds");
+
+        if (getConfig().getBoolean("enable_atm")) new ATM(this);
+
+        final int seconds = getConfig().getInt("give_money_every_second");
+        Bukkit.getScheduler().runTaskTimerAsynchronously(this, () -> {
+            try {
+                for (Player p : Bukkit.getOnlinePlayers()) {
+                    if (disabledWorlds.contains(p.getWorld().getName())) continue;
+                    if (!boundIPs.containsKey(p.getAddress().getHostName())) {
+                        boundIPs.put(p.getAddress().getHostName(), p.getUniqueId());
+                    }
+                    if (onlineSeconds.containsKey(p.getUniqueId())) {
+
+                        onlineSeconds.put(p.getUniqueId(), onlineSeconds.get(p.getUniqueId()) + 1);
+                    } else {
+                        onlineSeconds.put(p.getUniqueId(), 1);
+                    }
+                    if (onlineSeconds.get(p.getUniqueId()) > seconds) {
+                        pay(p);
+                        onlineSeconds.remove(p.getUniqueId());
+                    }
+                }
+            } catch (NullPointerException e) {
+                //
+            }
+        }, 20L, 20L);
+        Bukkit.getScheduler().scheduleSyncRepeatingTask(this, () -> {
+            if (currentDay != new Date().getDay()) { //Next day, clear payouts!
+                log("Cleared all payouts");
+                payedMoney.clear();
+                currentDay = new Date().getDay();
+            }
+        }, 20L * 60, 20L * 60 * 15);
+        setupEconomy();
+
+        message = finalconfig.getString("message");
+        message = CC(message);
+
+        try {
+            FileInputStream fis = new FileInputStream(new File("plugins/TimeIsMoney/payed_today.data"));
+            ObjectInputStream ois = new ObjectInputStream(fis);
+            payedMoney = (HashMap<String, Double>) ((HashMap<String, Double>) ois.readObject()).clone();
+
+            ois.close();
+        } catch (Exception ignored) {
+
+        }
+
+        loadPayouts();
+
+
+        String packageName = this.getServer().getClass().getPackage().getName();
         // Get full package string of CraftServer.
         // org.bukkit.craftbukkit.version
         String Bukkitversion = packageName.substring(packageName.lastIndexOf('.') + 1);
@@ -165,70 +151,73 @@ public class Main extends JavaPlugin{
             final Class<?> clazz = Class.forName(Bukkitversion + ".NBTUtils");
             // Check if we have a NMSHandler class at that location.
             if (Utils.class.isAssignableFrom(clazz)) { // Make sure it actually implements NMS
-                utils = (Utils) clazz.getConstructor().newInstance(); // Set our handler
-     
+                actionBarUtils = (ActionBarUtils) clazz.getConstructor().newInstance(); // Set our handler
+
             }
-            
+
         } catch (final Exception e) {
             this.getLogger().severe("Actionbars are not supported on your spigot version, sorry.");
             use18Features = false;
             return;
         }
 
-		if(Bukkit.getPluginManager().isPluginEnabled("Essentials")){
-			clogger.sendMessage("Time is Money: Essentials found. Hook in it -> Will use Essentials's AFK feature if afk is enabled.");
-		}
-		new Metrics(this);
-
-		clogger.sendMessage("§aTime is Money §2v" + PL_VERSION + " §astarted.");
-	}
-	@Override
-	public void onDisable(){
-	    FileOutputStream fos;
-	    try {
-	    	fos = new FileOutputStream(new File("plugins/TimeIsMoney/payed_today.data"));
-	        ObjectOutputStream oos = new ObjectOutputStream(fos);
-	        oos.writeObject(payedMoney);
-	        oos.close();
-	    }catch(Exception e){
-	    	
-	    }
-	}
-	public void reload(){
-		//File config = new File("plugins/TimeIsMoney/config.yml");
-		//finalconfig = YamlConfiguration.loadConfiguration(config);
-		Bukkit.getPluginManager().disablePlugin(this);
-		Bukkit.getPluginManager().enablePlugin(this);
-		//this.onDisable();
-		//this.onEnable();
-		//loadPayouts();
-	}
-	public void loadPayouts(){
-		try{
-			payouts.clear();
-			for(String key : finalconfig.getConfigurationSection("payouts").getKeys(false)){
-				Payout payout = new Payout();
-				payout.max_payout_per_day = finalconfig.getDouble("payouts." + key + ".max_payout_per_day");
-				payout.payout_amount = finalconfig.getDouble("payouts." + key + ".payout_amount");
-				if(finalconfig.getString("payouts." + key + ".permission") != null){
-					payout.permission = finalconfig.getString("payouts." + key + ".permission");	
-				}
-				if(finalconfig.getString("payouts." + key + ".commands") != null){
-					payout.commands = finalconfig.getStringList("payouts." + key + ".commands");
-				}
-				
-				if(finalconfig.getString("payouts." + key + ".chance") != null){
-					payout.chance = finalconfig.getInt("payouts." + key + ".chance");
-				}
-				payouts.add(payout);
-			}
-			clogger.sendMessage("[TimeIsMoney] §aLoaded " + finalconfig.getConfigurationSection("payouts").getKeys(false).size() + " Payouts!");
-		}catch(Exception e){
-			clogger.sendMessage("[TimeIsMoney] §aFailed to load Payouts! (May made a mistake in config.yml?)");
-		}
-	}
-    boolean setupEconomy()
-    {
+        if (Bukkit.getPluginManager().isPluginEnabled("Essentials")) {
+            clogger.sendMessage("Time is Money: Essentials found. Hook in it -> Will use Essentials's AFK feature if afk is enabled.");
+        }
+        new Metrics(this);
+
+        clogger.sendMessage(CC("&aTime is Money &2v" + PL_VERSION + " &astarted."));
+    }
+
+    @Override
+    public void onDisable() {
+        FileOutputStream fos;
+        try {
+            fos = new FileOutputStream(new File("plugins/TimeIsMoney/payed_today.data"));
+            ObjectOutputStream oos = new ObjectOutputStream(fos);
+            oos.writeObject(payedMoney);
+            oos.close();
+        } catch (Exception ignored) {
+
+        }
+    }
+
+    public void reload() {
+        //File config = new File("plugins/TimeIsMoney/config.yml");
+        //finalconfig = YamlConfiguration.loadConfiguration(config);
+        Bukkit.getPluginManager().disablePlugin(this);
+        Bukkit.getPluginManager().enablePlugin(this);
+        //this.onDisable();
+        //this.onEnable();
+        //loadPayouts();
+    }
+
+    private void loadPayouts() {
+        try {
+            payouts.clear();
+            for (String key : finalconfig.getConfigurationSection("payouts").getKeys(false)) {
+                Payout payout = new Payout();
+                payout.max_payout_per_day = finalconfig.getDouble("payouts." + key + ".max_payout_per_day");
+                payout.payout_amount = finalconfig.getDouble("payouts." + key + ".payout_amount");
+                if (finalconfig.getString("payouts." + key + ".permission") != null) {
+                    payout.permission = finalconfig.getString("payouts." + key + ".permission");
+                }
+                if (finalconfig.getString("payouts." + key + ".commands") != null) {
+                    payout.commands = finalconfig.getStringList("payouts." + key + ".commands");
+                }
+
+                if (finalconfig.getString("payouts." + key + ".chance") != null) {
+                    payout.chance = finalconfig.getInt("payouts." + key + ".chance");
+                }
+                payouts.add(payout);
+            }
+            clogger.sendMessage(CC("[TimeIsMoney] &aLoaded " + finalconfig.getConfigurationSection("payouts").getKeys(false).size() + " Payouts!"));
+        } catch (Exception e) {
+            clogger.sendMessage(CC("[TimeIsMoney] &aFailed to load Payouts! (May made a mistake in config.yml?)"));
+        }
+    }
+
+    private boolean setupEconomy() {
         RegisteredServiceProvider<Economy> economyProvider = getServer().getServicesManager().getRegistration(net.milkbowl.vault.economy.Economy.class);
         if (economyProvider != null) {
             economy = economyProvider.getProvider();
@@ -236,292 +225,271 @@ public class Main extends JavaPlugin{
 
         return (economy != null);
     }
-    public Payout getPayOutForPlayer(Player p){
-    	Payout finalpayout = null;
-    	if(!this.getConfig().getBoolean("choose-payout-by-chance")){
-    		//by Permission
-    		for(Payout payout: payouts){
-    			if(payout.permission == "") finalpayout = payout;
-    			if(p.hasPermission(payout.permission)){
-    				finalpayout = payout;
-    			}
-    		}	
-    	}else{
-    		//by Chance
-    		Random rnd = new Random();
-    		List<Payout> list = new ArrayList<Payout>();
-    		for(Payout payout: payouts){
-    			for(int i = 0; i < payout.chance; i++) list.add(payout);
-    		}
-    		finalpayout = list.get(rnd.nextInt(list.size() - 1));
-    	}
-    	return finalpayout;
+
+    private Payout getPayOutForPlayer(Player p) {
+        Payout finalpayout = null;
+        if (!this.getConfig().getBoolean("choose-payout-by-chance")) {
+            //by Permission
+            for (Payout payout : payouts) {
+                if (payout.permission.equalsIgnoreCase("")) finalpayout = payout;
+                if (p.hasPermission(payout.permission)) {
+                    finalpayout = payout;
+                }
+            }
+        } else {
+            //by Chance
+            Random rnd = new Random();
+            List<Payout> list = new ArrayList<>();
+            for (Payout payout : payouts) {
+                for (int i = 0; i < payout.chance; i++) list.add(payout);
+            }
+            finalpayout = list.get(rnd.nextInt(list.size() - 1));
+        }
+        return finalpayout;
+    }
+
+    @SuppressWarnings("deprecation")
+    private void pay(Player p) {
+        if (p == null) return;
+
+        //REACHED MAX PAYOUT CHECK
+        double payed = 0;
+        if (payedMoney.containsKey(p.getName())) {
+            payed = payedMoney.get(p.getName());
+        }
+        Payout payout = getPayOutForPlayer(p);
+        if (payout == null) return;
+        if (payout.max_payout_per_day != -1) {
+            if (payed >= payout.max_payout_per_day) { //Reached max payout
+                if (finalconfig.getBoolean("display-messages-in-chat")) {
+                    sendMessage(p, finalconfig.getString("message_payoutlimit_reached"));
+                }
+                if (finalconfig.getBoolean("display-messages-in-actionbar") && use18Features) {
+                    sendActionbar(p, finalconfig.getString("message_payoutlimit_reached_actionbar"));
+                }
+                return;
+            }
+        }
+
+        if (!finalconfig.getBoolean("allow-multiple-accounts")) {
+            if (boundIPs.containsKey(p.getAddress().getHostName())) {
+                if (!boundIPs.get(p.getAddress().getHostName()).equals(p.getUniqueId())) {
+                    sendMessage(p, finalconfig.getString("message_multiple_ips"));
+                    return;
+                }
+            }
+        }
+
+        //AFK CHECK
+        if (!finalconfig.getBoolean("afk_payout") && !p.hasPermission("tim.afkbypass")) {
+            //ESENTIALS_AFK_FEATURE
+            if (Bukkit.getServer().getPluginManager().isPluginEnabled("Essentials")) {
+                Essentials essentials = (com.earth2me.essentials.Essentials) Bukkit.getServer().getPluginManager().getPlugin("Essentials");
+                if (essentials.getUser(p).isAfk()) {
+                    //AFK
+                    if (finalconfig.getBoolean("display-messages-in-chat")) {
+                        sendMessage(p, finalconfig.getString("message_afk"));
+                    }
+                    if (finalconfig.getBoolean("display-messages-in-actionbar") && use18Features) {
+                        sendActionbar(p, finalconfig.getString("message_afk_actionbar"));
+                    }
+                    return;
+                }
+            } else
+                //PLUGIN_AFK_FEATURE
+                if (lastLocation.containsKey(p.getUniqueId())) { //AntiAFK
+                    if (lastLocation.get(p.getUniqueId()).getX() == p.getLocation().getX() && lastLocation.get(p.getUniqueId()).getY() == p.getLocation().getY() && lastLocation.get(p.getUniqueId()).getZ() == p.getLocation().getZ() || lastLocation.get(p.getUniqueId()).getYaw() == p.getLocation().getYaw()) {
+                        //AFK
+                        if (finalconfig.getBoolean("display-messages-in-chat")) {
+                            sendMessage(p, finalconfig.getString("message_afk"));
+                        }
+                        if (finalconfig.getBoolean("display-messages-in-actionbar") && use18Features) {
+                            sendActionbar(p, finalconfig.getString("message_afk_actionbar"));
+                        }
+                        return;
+                    }
+                }
+        }
+
+        //DEPOSIT
+        if (finalconfig.getBoolean("store-money-in-bank")) {
+            ATM.depositBank(p, payout.payout_amount);
+        } else {
+            double before = 0;
+            if (economy.hasAccount(p)) {
+                before = economy.getBalance(p);
+            }
+
+            economy.depositPlayer(p, payout.payout_amount);
+            log(p.getName() + ": Deposited: " + payout.payout_amount + " Balance-before: " + before + " Balance-now: " + economy.getBalance(p));
+
+        }
+        if (finalconfig.getBoolean("display-messages-in-chat")) {
+            sendMessage(p, message.replace("%money%", economy.format(payout.payout_amount)));
+        }
+        if (finalconfig.getBoolean("display-messages-in-actionbar") && use18Features) {
+            sendActionbar(p, message.replace("%money%", economy.format(payout.payout_amount)));
+        }
+        for (String cmd : payout.commands) {
+            dispatchCommandSync(cmd.replace("/", "").replaceAll("%player%", p.getName()));
+        }
+
+        //ADD PAYED MONEY
+        if (payedMoney.containsKey(p.getName())) {
+            payedMoney.put(p.getName(), payedMoney.get(p.getName()) + payout.payout_amount);
+        } else {
+            payedMoney.put(p.getName(), payout.payout_amount);
+        }
+
+        lastLocation.put(p.getUniqueId(), p.getLocation());
+
+    }
+
+    private void dispatchCommandSync(final String cmd) {
+        final Server server = this.getServer();
+
+        this.getServer().getScheduler().runTask(this, () -> server.dispatchCommand(server.getConsoleSender(), cmd));
+    }
+
+    @SuppressWarnings("deprecation")
+    private void log(String msg) {
+        if (!this.getConfig().getBoolean("debug-log")) {
+            return;
+        }
+        Timestamp currentTimestamp = new Timestamp(Calendar.getInstance().getTime().getTime());
+
+        File file = new File("plugins/TimeIsMoney/log.txt");
+        try {
+            if (!file.exists()) {
+                file.createNewFile();
+            }
+            FileReader pr = new FileReader(file);
+            int number;
+            StringBuffer text = new StringBuffer();
+            while ((number = pr.read()) != -1) {
+
+                text.append((char) number);
+            }
+            text.append(currentTimestamp.toGMTString()).append(":").append(msg).append("\n");
+            PrintWriter pw = new PrintWriter(file);
+            pw.print(text);
+
+            pw.close();
+        } catch (IOException e) {
+            e.printStackTrace();
+        }
+    }
+
+    private void sendMessage(Player p, String msg) {
+        if (msg == null) return;
+        if (msg.length() == 0) return;
+        p.sendMessage(CC(msg));
+    }
+
+    private void sendActionbar(final Player p, final String msg) {
+        if (msg.length() == 0) return;
+        int times = finalconfig.getInt("display-messages-in-actionbar-time");
+        if (times == 1) {
+            if (actionBarUtils != null) {
+                actionBarUtils.sendActionBarMessage(p, msg);
+            }
+        } else if (times > 1) {
+            if (actionBarUtils != null) {
+                actionBarUtils.sendActionBarMessage(p, msg);
+            }
+            times--;
+            for (int i = 0; i < times; i++) {
+                Bukkit.getScheduler().scheduleSyncDelayedTask(this, () -> actionBarUtils.sendActionBarMessage(p, CC(msg)), 20L * i);
+            }
+        }
+    }
+
+    private boolean unloadPlugin(String pluginName)
+            throws Exception {
+        PluginManager manager = getServer().getPluginManager();
+        SimplePluginManager spmanager = (SimplePluginManager) manager;
+        if (spmanager != null) {
+            Field pluginsField = spmanager.getClass().getDeclaredField("plugins");
+            pluginsField.setAccessible(true);
+            List<Plugin> plugins = (List) pluginsField.get(spmanager);
+
+            Field lookupNamesField = spmanager.getClass().getDeclaredField("lookupNames");
+            lookupNamesField.setAccessible(true);
+            Map<String, Plugin> lookupNames = (Map) lookupNamesField.get(spmanager);
+
+            Field commandMapField = spmanager.getClass().getDeclaredField("commandMap");
+            commandMapField.setAccessible(true);
+            SimpleCommandMap commandMap = (SimpleCommandMap) commandMapField.get(spmanager);
+
+            Field knownCommandsField;
+            Map<String, Command> knownCommands = null;
+            if (commandMap != null) {
+                knownCommandsField = commandMap.getClass().getDeclaredField("knownCommands");
+                knownCommandsField.setAccessible(true);
+                knownCommands = (Map) knownCommandsField.get(commandMap);
+            }
+            Plugin plugin;
+            Iterator<Map.Entry<String, Command>> it;
+            for (Plugin plugin1 : manager.getPlugins()) {
+                if (plugin1.getDescription().getName().equalsIgnoreCase(pluginName)) {
+                    manager.disablePlugin(plugin1);
+                    if ((plugins != null) && (plugins.contains(plugin1))) {
+                        plugins.remove(plugin1);
+                    }
+                    if ((lookupNames != null) && (lookupNames.containsKey(pluginName))) {
+                        lookupNames.remove(pluginName);
+                    }
+                    if (commandMap != null) {
+                        for (it = knownCommands.entrySet().iterator(); it.hasNext(); ) {
+                            Map.Entry<String, Command> entry = it.next();
+                            if ((entry.getValue() instanceof PluginCommand)) {
+                                PluginCommand command = (PluginCommand) entry.getValue();
+                                if (command.getPlugin() == plugin1) {
+                                    command.unregister(commandMap);
+                                    it.remove();
+                                }
+                            }
+                        }
+                    }
+                }
+            }
+        } else {
+
+            return true;
+        }
+
+
+        return true;
+    }
+
+    private boolean loadPlugin(String pluginName) {
+        try {
+            PluginManager manager = getServer().getPluginManager();
+            Plugin plugin = manager.loadPlugin(new File("plugins", pluginName + ".jar"));
+            if (plugin == null) {
+                return false;
+            }
+            plugin.onLoad();
+            manager.enablePlugin(plugin);
+        } catch (Exception e) {
+
+            return false;
+        }
+
+        return true;
+    }
+
+    private boolean reloadPlugin(String pluginName)
+            throws Exception {
+        boolean unload = unloadPlugin(pluginName);
+        boolean load = loadPlugin(pluginName);
+
+        if ((unload) && (load)) {
+
+        } else {
+
+            return false;
+        }
+        return true;
     }
-	@SuppressWarnings("deprecation")
-	public void pay(Player p){
-		if(p == null) return;
-		
-		//REACHED MAX PAYOUT CHECK
-		double payed = 0;
-		if(payedMoney.containsKey(p.getName())){
-			payed = payedMoney.get(p.getName());
-		}
-		Payout payout = getPayOutForPlayer(p);
-		if(payout == null) return;
-		if(payout.max_payout_per_day != -1){
-			if(payed >= payout.max_payout_per_day){ //Reached max payout
-				if(finalconfig.getBoolean("display-messages-in-chat")){
-					sendMessage(p, finalconfig.getString("message_payoutlimit_reached"));
-				}
-				if(finalconfig.getBoolean("display-messages-in-actionbar") && use18Features){
-					sendActionbar(p, finalconfig.getString("message_payoutlimit_reached_actionbar"));
-				}
-				return;
-			}	
-		}
-		
-		if(!finalconfig.getBoolean("allow-multiple-accounts")){
-			if(boundIPs.containsKey(p.getAddress().getHostName())){
-				if(!boundIPs.get(p.getAddress().getHostName()).equals(p.getUniqueId())){
-					sendMessage(p, finalconfig.getString("message_multiple_ips"));
-					return;
-				}
-			}
-		}
-		
-		//AFK CHECK
-		if(!finalconfig.getBoolean("afk_payout") && !p.hasPermission("tim.afkbypass")){
-			//ESENTIALS_AFK_FEATURE
-			if(Bukkit.getServer().getPluginManager().isPluginEnabled("Essentials")){
-				com.earth2me.essentials.Essentials essentials = (com.earth2me.essentials.Essentials) Bukkit.getServer().getPluginManager().getPlugin("Essentials");
-			    if(essentials.getUser(p).isAfk()){
-			    	//AFK
-					if(finalconfig.getBoolean("display-messages-in-chat")){
-						sendMessage(p, finalconfig.getString("message_afk"));	
-					}
-					if(finalconfig.getBoolean("display-messages-in-actionbar") && use18Features){
-						sendActionbar(p, finalconfig.getString("message_afk_actionbar"));
-					}
-					return;
-			    }
-			}else
-			//PLUGIN_AFK_FEATURE
-			if(lastLocation.containsKey(p.getUniqueId())){ //AntiAFK
-				if(lastLocation.get(p.getUniqueId()).getX() == p.getLocation().getX() && lastLocation.get(p.getUniqueId()).getY() == p.getLocation().getY() && lastLocation.get(p.getUniqueId()).getZ() == p.getLocation().getZ() || lastLocation.get(p.getUniqueId()).getYaw() == p.getLocation().getYaw()){
-					//AFK
-					if(finalconfig.getBoolean("display-messages-in-chat")){
-						sendMessage(p, finalconfig.getString("message_afk"));	
-					}
-					if(finalconfig.getBoolean("display-messages-in-actionbar") && use18Features){
-						sendActionbar(p, finalconfig.getString("message_afk_actionbar"));
-					}
-					return;
-				}
-			}	
-		}
-		
-		//DEPOSIT
-		if(finalconfig.getBoolean("store-money-in-bank")){
-			ATM.depositBank(p, payout.payout_amount);
-		}else{
-			double before = 0;
-			if(economy.hasAccount(p)){
-				before = economy.getBalance(p);
-			}
-		
-			economy.depositPlayer(p, payout.payout_amount);
-			log(p.getName() + ": Deposited: " + payout.payout_amount + " Balance-before: " + before + " Balance-now: " + economy.getBalance(p));
-			
-		}
-		if(finalconfig.getBoolean("display-messages-in-chat")){
-			sendMessage(p, message.replace("%money%", economy.format(payout.payout_amount)));	
-		}
-		if(finalconfig.getBoolean("display-messages-in-actionbar") && use18Features){
-			sendActionbar(p, message.replace("%money%", economy.format(payout.payout_amount)));
-		}
-		for(String cmd : payout.commands){
-			dispatchCommandSync(cmd.replace("/", "").replaceAll("%player%", p.getName()));
-		}
-		
-		//ADD PAYED MONEY
-		if(payedMoney.containsKey(p.getName())){
-			payedMoney.put(p.getName(), payedMoney.get(p.getName()) + payout.payout_amount);
-		}else{
-			payedMoney.put(p.getName(), payout.payout_amount);
-		}
-
-		lastLocation.put(p.getUniqueId(), p.getLocation());
-	
-	}
-	public void dispatchCommandSync(final String cmd){
-		final Server server = this.getServer();
-		
-		this.getServer().getScheduler().runTask(this, new Runnable(){
-			
-			public void run(){
-				server.dispatchCommand(server.getConsoleSender(), cmd);
-			}
-		});
-	}
-	@SuppressWarnings("deprecation")
-	public void log(String msg){
-		if(!this.getConfig().getBoolean("debug-log")){
-			return;	
-		}
-		Timestamp currentTimestamp = new Timestamp(Calendar.getInstance().getTime().getTime());
-
-		File file = new File("plugins/TimeIsMoney/log.txt");
-			try {
-				if(!file.exists()){
-					file.createNewFile();
-				}
-				FileReader pr = new FileReader(file);
-				int number = 0;
-				StringBuffer text = new StringBuffer();
-				while((number = pr.read()) != -1){
-					
-					text.append((char) number);
-				}
-				text.append(currentTimestamp.toGMTString() + ":" + msg + "\n");
-				PrintWriter pw = new PrintWriter(file);
-				pw.print(text);
-				
-				pw.close();
-			} catch (IOException e) {
-				// TODO Auto-generated catch block
-				e.printStackTrace();
-			}
-	}
-	public void sendMessage(Player p, String msg){
-		if(msg == null) return;
-		if(msg.length() == 0) return;
-		p.sendMessage(msg.replace('&', '§'));
-	}
-	public void sendActionbar(final Player p, final String msg){
-		if(msg.length() == 0) return;
-		int times = finalconfig.getInt("display-messages-in-actionbar-time");
-		if(times == 1){
-			if(utils != null){
-				utils.sendActionBarMessage(p, msg);	
-			}
-		}else if(times > 1){
-			if(utils != null){
-				utils.sendActionBarMessage(p, msg);	
-			}
-			times--;
-			for(int i = 0; i < times; i++){
-				Bukkit.getScheduler().scheduleSyncDelayedTask(this, new Runnable(){
-					public void run(){
-						utils.sendActionBarMessage(p, msg.replace('&', '§'));
-					}
-				}, 20L * i);
-			}
-		}
-	}
-	private boolean unloadPlugin(String pluginName)
-		    throws Exception
-		  {
-		    PluginManager manager = getServer().getPluginManager();
-		    SimplePluginManager spmanager = (SimplePluginManager)manager;
-		    if (spmanager != null)
-		    {
-		      Field pluginsField = spmanager.getClass().getDeclaredField("plugins");
-		      pluginsField.setAccessible(true);
-		      List<Plugin> plugins = (List)pluginsField.get(spmanager);
-		      
-		      Field lookupNamesField = spmanager.getClass().getDeclaredField("lookupNames");
-		      lookupNamesField.setAccessible(true);
-		      Map<String, Plugin> lookupNames = (Map)lookupNamesField.get(spmanager);
-		      
-		      Field commandMapField = spmanager.getClass().getDeclaredField("commandMap");
-		      commandMapField.setAccessible(true);
-		      SimpleCommandMap commandMap = (SimpleCommandMap)commandMapField.get(spmanager);
-		      
-		      Field knownCommandsField = null;
-		      Map<String, Command> knownCommands = null;
-		      if (commandMap != null)
-		      {
-		        knownCommandsField = commandMap.getClass().getDeclaredField("knownCommands");
-		        knownCommandsField.setAccessible(true);
-		        knownCommands = (Map)knownCommandsField.get(commandMap);
-		      }
-		      Plugin plugin;
-		      Iterator<Map.Entry<String, Command>> it;
-		      for (Plugin plugin1 : manager.getPlugins()) {
-		        if (plugin1.getDescription().getName().equalsIgnoreCase(pluginName))
-		        {
-		          manager.disablePlugin(plugin1);
-		          if ((plugins != null) && (plugins.contains(plugin1))) {
-		            plugins.remove(plugin1);
-		          }
-		          if ((lookupNames != null) && (lookupNames.containsKey(pluginName))) {
-		            lookupNames.remove(pluginName);
-		          }
-		          if (commandMap != null) {
-		            for (it = knownCommands.entrySet().iterator(); it.hasNext();)
-		            {
-		              Map.Entry<String, Command> entry = (Map.Entry)it.next();
-		              if ((entry.getValue() instanceof PluginCommand))
-		              {
-		                PluginCommand command = (PluginCommand)entry.getValue();
-		                if (command.getPlugin() == plugin1)
-		                {
-		                  command.unregister(commandMap);
-		                  it.remove();
-		                }
-		              }
-		            }
-		          }
-		        }
-		      }
-		    }
-		    else
-		    {
-
-		      return true;
-		    }
-
-
-		    return true;
-		  }
-		  
-		  private boolean loadPlugin(String pluginName)
-		  {
-		    try
-		    {
-		      PluginManager manager = getServer().getPluginManager();
-		      Plugin plugin = manager.loadPlugin(new File("plugins", pluginName + ".jar"));
-		      if (plugin == null)
-		      {
-		        return false;
-		      }
-		      plugin.onLoad();
-		      manager.enablePlugin(plugin);
-		    }
-		    catch (Exception e)
-		    {
-
-		      return false;
-		    }
-
-		    return true;
-		  }
-		  
-		  private boolean reloadPlugin(String pluginName)
-		    throws Exception
-		  {
-		    boolean unload = unloadPlugin(pluginName);
-		    boolean load = loadPlugin(pluginName);
-
-		    if ((unload) && (load))
-		    {
-
-		    }
-		    else
-		    {
-
-		      return false;
-		    }
-		    return true;
-		  }
 }

+ 130 - 491
TimeIsMoney2/src/de/Linus122/TimeIsMoney/Metrics.java

@@ -2,6 +2,8 @@ package de.Linus122.TimeIsMoney;
 
 import org.bukkit.Bukkit;
 import org.bukkit.configuration.file.YamlConfiguration;
+import org.bukkit.entity.Player;
+import org.bukkit.plugin.RegisteredServiceProvider;
 import org.bukkit.plugin.ServicePriority;
 import org.bukkit.plugin.java.JavaPlugin;
 import org.json.simple.JSONArray;
@@ -13,15 +15,16 @@ import java.io.DataOutputStream;
 import java.io.File;
 import java.io.IOException;
 import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
 import java.net.URL;
 import java.util.ArrayList;
-import java.util.HashMap;
+import java.util.Collection;
 import java.util.List;
-import java.util.Locale;
 import java.util.Map;
 import java.util.Timer;
 import java.util.TimerTask;
 import java.util.UUID;
+import java.util.concurrent.Callable;
 import java.util.logging.Level;
 import java.util.zip.GZIPOutputStream;
 
@@ -29,16 +32,21 @@ import java.util.zip.GZIPOutputStream;
  * bStats collects some data for plugin authors.
  *
  * Check out https://bStats.org/ to learn more about bStats!
+ * @link https://github.com/BtoBastian/bStats-Metrics/blob/master/bstats-bukkit/src/main/java/org/bstats/bukkit/Metrics.java
  */
 public class Metrics {
 
     static {
-        // Maven's Relocate is clever and changes strings, too. So we have to use this little "trick" ... :D
-        final String defaultPackage = new String(new byte[] { 'o', 'r', 'g', '.', 'b', 's', 't', 'a', 't', 's' });
-        final String examplePackage = new String(new byte[] { 'y', 'o', 'u', 'r', '.', 'p', 'a', 'c', 'k', 'a', 'g', 'e' });
-        // We want to make sure nobody just copy & pastes the example and use the wrong package names
-        if (Metrics.class.getPackage().getName().equals(defaultPackage) || Metrics.class.getPackage().getName().equals(examplePackage)) {
-            throw new IllegalStateException("bStats Metrics class has not been relocated correctly!");
+        // You can use the property to disable the check in your test environment
+        if (System.getProperty("bstats.relocatecheck") == null || !System.getProperty("bstats.relocatecheck").equals("false")) {
+            // Maven's Relocate is clever and changes strings, too. So we have to use this little "trick" ... :D
+            final String defaultPackage = new String(
+                    new byte[]{'o', 'r', 'g', '.', 'b', 's', 't', 'a', 't', 's', '.', 'b', 'u', 'k', 'k', 'i', 't'});
+            final String examplePackage = new String(new byte[]{'y', 'o', 'u', 'r', '.', 'p', 'a', 'c', 'k', 'a', 'g', 'e'});
+            // We want to make sure nobody just copy & pastes the example and use the wrong package names
+            if (Metrics.class.getPackage().getName().equals(defaultPackage) || Metrics.class.getPackage().getName().equals(examplePackage)) {
+                throw new IllegalStateException("bStats Metrics class has not been relocated correctly!");
+            }
         }
     }
 
@@ -194,7 +202,17 @@ public class Metrics {
      */
     private JSONObject getServerData() {
         // Minecraft specific data
-        int playerAmount = Bukkit.getOnlinePlayers().size();
+        int playerAmount;
+        try {
+            // Around MC 1.8 the return type was changed to a collection from an array,
+            // This fixes java.lang.NoSuchMethodError: org.bukkit.Bukkit.getOnlinePlayers()Ljava/util/Collection;
+            Method onlinePlayersMethod = Class.forName("org.bukkit.Server").getMethod("getOnlinePlayers");
+            playerAmount = onlinePlayersMethod.getReturnType().equals(Collection.class)
+                    ? ((Collection<?>) onlinePlayersMethod.invoke(Bukkit.getServer())).size()
+                    : ((Player[]) onlinePlayersMethod.invoke(Bukkit.getServer())).length;
+        } catch (Exception e) {
+            playerAmount = Bukkit.getOnlinePlayers().size(); // Just use the new method if the Reflection failed
+        }
         int onlineMode = Bukkit.getOnlineMode() ? 1 : 0;
         String bukkitVersion = org.bukkit.Bukkit.getVersion();
         bukkitVersion = bukkitVersion.substring(bukkitVersion.indexOf("MC: ") + 4, bukkitVersion.length() - 1);
@@ -234,13 +252,13 @@ public class Metrics {
         for (Class<?> service : Bukkit.getServicesManager().getKnownServices()) {
             try {
                 service.getField("B_STATS_VERSION"); // Our identifier :)
-            } catch (NoSuchFieldException ignored) {
-                continue; // Continue "searching"
-            }
-            // Found one!
-            try {
-                pluginData.add(service.getMethod("getPluginData").invoke(Bukkit.getServicesManager().load(service)));
-            } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException ignored) { }
+
+                for (RegisteredServiceProvider<?> provider : Bukkit.getServicesManager().getRegistrations(service)) {
+                    try {
+                        pluginData.add(provider.getService().getMethod("getPluginData").invoke(provider.getProvider()));
+                    } catch (NullPointerException | NoSuchMethodException | IllegalAccessException | InvocationTargetException ignored) { }
+                }
+            } catch (NoSuchFieldException ignored) { }
         }
 
         data.put("plugins", pluginData);
@@ -323,21 +341,21 @@ public class Metrics {
     public static abstract class CustomChart {
 
         // The id of the chart
-        protected final String chartId;
+        final String chartId;
 
         /**
          * Class constructor.
          *
          * @param chartId The id of the chart.
          */
-        public CustomChart(String chartId) {
+        CustomChart(String chartId) {
             if (chartId == null || chartId.isEmpty()) {
                 throw new IllegalArgumentException("ChartId cannot be null or empty!");
             }
             this.chartId = chartId;
         }
 
-        protected JSONObject getRequestJsonObject() {
+        private JSONObject getRequestJsonObject() {
             JSONObject chart = new JSONObject();
             chart.put("chartId", chartId);
             try {
@@ -356,35 +374,32 @@ public class Metrics {
             return chart;
         }
 
-        protected abstract JSONObject getChartData();
+        protected abstract JSONObject getChartData() throws Exception;
 
     }
 
     /**
      * Represents a custom simple pie.
      */
-    public static abstract class SimplePie extends CustomChart {
+    public static class SimplePie extends CustomChart {
+
+        private final Callable<String> callable;
 
         /**
          * Class constructor.
          *
          * @param chartId The id of the chart.
+         * @param callable The callable which is used to request the chart data.
          */
-        public SimplePie(String chartId) {
+        public SimplePie(String chartId, Callable<String> callable) {
             super(chartId);
+            this.callable = callable;
         }
 
-        /**
-         * Gets the value of the pie.
-         *
-         * @return The value of the pie.
-         */
-        public abstract String getValue();
-
         @Override
-        protected JSONObject getChartData() {
+        protected JSONObject getChartData() throws Exception {
             JSONObject data = new JSONObject();
-            String value = getValue();
+            String value = callable.call();
             if (value == null || value.isEmpty()) {
                 // Null = skip the chart
                 return null;
@@ -397,31 +412,26 @@ public class Metrics {
     /**
      * Represents a custom advanced pie.
      */
-    public static abstract class AdvancedPie extends CustomChart {
+    public static class AdvancedPie extends CustomChart {
+
+        private final Callable<Map<String, Integer>> callable;
 
         /**
          * Class constructor.
          *
          * @param chartId The id of the chart.
+         * @param callable The callable which is used to request the chart data.
          */
-        public AdvancedPie(String chartId) {
+        public AdvancedPie(String chartId, Callable<Map<String, Integer>> callable) {
             super(chartId);
+            this.callable = callable;
         }
 
-        /**
-         * Gets the values of the pie.
-         *
-         * @param valueMap Just an empty map. The only reason it exists is to make your life easier.
-         *                 You don't have to create a map yourself!
-         * @return The values of the pie.
-         */
-        public abstract HashMap<String, Integer> getValues(HashMap<String, Integer> valueMap);
-
         @Override
-        protected JSONObject getChartData() {
+        protected JSONObject getChartData() throws Exception {
             JSONObject data = new JSONObject();
             JSONObject values = new JSONObject();
-            HashMap<String, Integer> map = getValues(new HashMap<String, Integer>());
+            Map<String, Integer> map = callable.call();
             if (map == null || map.isEmpty()) {
                 // Null = skip the chart
                 return null;
@@ -444,176 +454,120 @@ public class Metrics {
     }
 
     /**
-     * Represents a custom single line chart.
+     * Represents a custom drilldown pie.
      */
-    public static abstract class SingleLineChart extends CustomChart {
+    public static class DrilldownPie extends CustomChart {
 
-        /**
-         * Class constructor.
-         *
-         * @param chartId The id of the chart.
-         */
-        public SingleLineChart(String chartId) {
-            super(chartId);
-        }
-
-        /**
-         * Gets the value of the chart.
-         *
-         * @return The value of the chart.
-         */
-        public abstract int getValue();
-
-        @Override
-        protected JSONObject getChartData() {
-            JSONObject data = new JSONObject();
-            int value = getValue();
-            if (value == 0) {
-                // Null = skip the chart
-                return null;
-            }
-            data.put("value", value);
-            return data;
-        }
-
-    }
-
-    /**
-     * Represents a custom multi line chart.
-     */
-    public static abstract class MultiLineChart extends CustomChart {
+        private final Callable<Map<String, Map<String, Integer>>> callable;
 
         /**
          * Class constructor.
          *
          * @param chartId The id of the chart.
+         * @param callable The callable which is used to request the chart data.
          */
-        public MultiLineChart(String chartId) {
+        public DrilldownPie(String chartId, Callable<Map<String, Map<String, Integer>>> callable) {
             super(chartId);
+            this.callable = callable;
         }
 
-        /**
-         * Gets the values of the chart.
-         *
-         * @param valueMap Just an empty map. The only reason it exists is to make your life easier.
-         *                 You don't have to create a map yourself!
-         * @return The values of the chart.
-         */
-        public abstract HashMap<String, Integer> getValues(HashMap<String, Integer> valueMap);
-
         @Override
-        protected JSONObject getChartData() {
+        public JSONObject getChartData() throws Exception {
             JSONObject data = new JSONObject();
             JSONObject values = new JSONObject();
-            HashMap<String, Integer> map = getValues(new HashMap<String, Integer>());
+            Map<String, Map<String, Integer>> map = callable.call();
             if (map == null || map.isEmpty()) {
                 // Null = skip the chart
                 return null;
             }
-            boolean allSkipped = true;
-            for (Map.Entry<String, Integer> entry : map.entrySet()) {
-                if (entry.getValue() == 0) {
-                    continue; // Skip this invalid
+            boolean reallyAllSkipped = true;
+            for (Map.Entry<String, Map<String, Integer>> entryValues : map.entrySet()) {
+                JSONObject value = new JSONObject();
+                boolean allSkipped = true;
+                for (Map.Entry<String, Integer> valueEntry : map.get(entryValues.getKey()).entrySet()) {
+                    value.put(valueEntry.getKey(), valueEntry.getValue());
+                    allSkipped = false;
+                }
+                if (!allSkipped) {
+                    reallyAllSkipped = false;
+                    values.put(entryValues.getKey(), value);
                 }
-                allSkipped = false;
-                values.put(entry.getKey(), entry.getValue());
             }
-            if (allSkipped) {
+            if (reallyAllSkipped) {
                 // Null = skip the chart
                 return null;
             }
             data.put("values", values);
             return data;
         }
-
     }
 
     /**
-     * Represents a custom simple bar chart.
+     * Represents a custom single line chart.
      */
-    public static abstract class SimpleBarChart extends CustomChart {
+    public static class SingleLineChart extends CustomChart {
+
+        private final Callable<Integer> callable;
 
         /**
          * Class constructor.
          *
          * @param chartId The id of the chart.
+         * @param callable The callable which is used to request the chart data.
          */
-        public SimpleBarChart(String chartId) {
+        public SingleLineChart(String chartId, Callable<Integer> callable) {
             super(chartId);
+            this.callable = callable;
         }
 
-        /**
-         * Gets the value of the chart.
-         *
-         * @param valueMap Just an empty map. The only reason it exists is to make your life easier.
-         *                 You don't have to create a map yourself!
-         * @return The value of the chart.
-         */
-        public abstract HashMap<String, Integer> getValues(HashMap<String, Integer> valueMap);
-
         @Override
-        protected JSONObject getChartData() {
+        protected JSONObject getChartData() throws Exception {
             JSONObject data = new JSONObject();
-            JSONObject values = new JSONObject();
-            HashMap<String, Integer> map = getValues(new HashMap<String, Integer>());
-            if (map == null || map.isEmpty()) {
+            int value = callable.call();
+            if (value == 0) {
                 // Null = skip the chart
                 return null;
             }
-            for (Map.Entry<String, Integer> entry : map.entrySet()) {
-                JSONArray categoryValues = new JSONArray();
-                categoryValues.add(entry.getValue());
-                values.put(entry.getKey(), categoryValues);
-            }
-            data.put("values", values);
+            data.put("value", value);
             return data;
         }
 
     }
 
     /**
-     * Represents a custom advanced bar chart.
+     * Represents a custom multi line chart.
      */
-    public static abstract class AdvancedBarChart extends CustomChart {
+    public static class MultiLineChart extends CustomChart {
+
+        private final Callable<Map<String, Integer>> callable;
 
         /**
          * Class constructor.
          *
          * @param chartId The id of the chart.
+         * @param callable The callable which is used to request the chart data.
          */
-        public AdvancedBarChart(String chartId) {
+        public MultiLineChart(String chartId, Callable<Map<String, Integer>> callable) {
             super(chartId);
+            this.callable = callable;
         }
 
-        /**
-         * Gets the value of the chart.
-         *
-         * @param valueMap Just an empty map. The only reason it exists is to make your life easier.
-         *                 You don't have to create a map yourself!
-         * @return The value of the chart.
-         */
-        public abstract HashMap<String, int[]> getValues(HashMap<String, int[]> valueMap);
-
         @Override
-        protected JSONObject getChartData() {
+        protected JSONObject getChartData() throws Exception {
             JSONObject data = new JSONObject();
             JSONObject values = new JSONObject();
-            HashMap<String, int[]> map = getValues(new HashMap<String, int[]>());
+            Map<String, Integer> map = callable.call();
             if (map == null || map.isEmpty()) {
                 // Null = skip the chart
                 return null;
             }
             boolean allSkipped = true;
-            for (Map.Entry<String, int[]> entry : map.entrySet()) {
-                if (entry.getValue().length == 0) {
+            for (Map.Entry<String, Integer> entry : map.entrySet()) {
+                if (entry.getValue() == 0) {
                     continue; // Skip this invalid
                 }
                 allSkipped = false;
-                JSONArray categoryValues = new JSONArray();
-                for (int categoryValue : entry.getValue()) {
-                    categoryValues.add(categoryValue);
-                }
-                values.put(entry.getKey(), categoryValues);
+                values.put(entry.getKey(), entry.getValue());
             }
             if (allSkipped) {
                 // Null = skip the chart
@@ -626,80 +580,81 @@ public class Metrics {
     }
 
     /**
-     * Represents a custom simple map chart.
+     * Represents a custom simple bar chart.
      */
-    public static abstract class SimpleMapChart extends CustomChart {
+    public static class SimpleBarChart extends CustomChart {
+
+        private final Callable<Map<String, Integer>> callable;
 
         /**
          * Class constructor.
          *
          * @param chartId The id of the chart.
+         * @param callable The callable which is used to request the chart data.
          */
-        public SimpleMapChart(String chartId) {
+        public SimpleBarChart(String chartId, Callable<Map<String, Integer>> callable) {
             super(chartId);
+            this.callable = callable;
         }
 
-        /**
-         * Gets the value of the chart.
-         *
-         * @return The value of the chart.
-         */
-        public abstract Country getValue();
-
         @Override
-        protected JSONObject getChartData() {
+        protected JSONObject getChartData() throws Exception {
             JSONObject data = new JSONObject();
-            Country value = getValue();
-
-            if (value == null) {
+            JSONObject values = new JSONObject();
+            Map<String, Integer> map = callable.call();
+            if (map == null || map.isEmpty()) {
                 // Null = skip the chart
                 return null;
             }
-            data.put("value", value.getCountryIsoTag());
+            for (Map.Entry<String, Integer> entry : map.entrySet()) {
+                JSONArray categoryValues = new JSONArray();
+                categoryValues.add(entry.getValue());
+                values.put(entry.getKey(), categoryValues);
+            }
+            data.put("values", values);
             return data;
         }
 
     }
 
     /**
-     * Represents a custom advanced map chart.
+     * Represents a custom advanced bar chart.
      */
-    public static abstract class AdvancedMapChart extends CustomChart {
+    public static class AdvancedBarChart extends CustomChart {
+
+        private final Callable<Map<String, int[]>> callable;
 
         /**
          * Class constructor.
          *
          * @param chartId The id of the chart.
+         * @param callable The callable which is used to request the chart data.
          */
-        public AdvancedMapChart(String chartId) {
+        public AdvancedBarChart(String chartId, Callable<Map<String, int[]>> callable) {
             super(chartId);
+            this.callable = callable;
         }
 
-        /**
-         * Gets the value of the chart.
-         *
-         * @param valueMap Just an empty map. The only reason it exists is to make your life easier.
-         *                 You don't have to create a map yourself!
-         * @return The value of the chart.
-         */
-        public abstract HashMap<Country, Integer> getValues(HashMap<Country, Integer> valueMap);
-
         @Override
-        protected JSONObject getChartData() {
+        protected JSONObject getChartData() throws Exception {
             JSONObject data = new JSONObject();
             JSONObject values = new JSONObject();
-            HashMap<Country, Integer> map = getValues(new HashMap<Country, Integer>());
+            Map<String, int[]> map = callable.call();
             if (map == null || map.isEmpty()) {
                 // Null = skip the chart
                 return null;
             }
             boolean allSkipped = true;
-            for (Map.Entry<Country, Integer> entry : map.entrySet()) {
-                if (entry.getValue() == 0) {
+            for (Map.Entry<String, int[]> entry : map.entrySet()) {
+                if (entry.getValue().length == 0) {
                     continue; // Skip this invalid
                 }
                 allSkipped = false;
-                values.put(entry.getKey().getCountryIsoTag(), entry.getValue());
+                JSONArray categoryValues = new JSONArray();
+                for (int categoryValue : entry.getValue()) {
+                    categoryValues.add(categoryValue);
+                }
+                values.put(entry.getKey(), categoryValues);
             }
             if (allSkipped) {
                 // Null = skip the chart
@@ -710,320 +665,4 @@ public class Metrics {
         }
 
     }
-
-    /**
-     * A enum which is used for custom maps.
-     */
-    public enum Country {
-
-        /**
-         * bStats will use the country of the server.
-         */
-        AUTO_DETECT("AUTO", "Auto Detected"),
-
-        ANDORRA("AD", "Andorra"),
-        UNITED_ARAB_EMIRATES("AE", "United Arab Emirates"),
-        AFGHANISTAN("AF", "Afghanistan"),
-        ANTIGUA_AND_BARBUDA("AG", "Antigua and Barbuda"),
-        ANGUILLA("AI", "Anguilla"),
-        ALBANIA("AL", "Albania"),
-        ARMENIA("AM", "Armenia"),
-        NETHERLANDS_ANTILLES("AN", "Netherlands Antilles"),
-        ANGOLA("AO", "Angola"),
-        ANTARCTICA("AQ", "Antarctica"),
-        ARGENTINA("AR", "Argentina"),
-        AMERICAN_SAMOA("AS", "American Samoa"),
-        AUSTRIA("AT", "Austria"),
-        AUSTRALIA("AU", "Australia"),
-        ARUBA("AW", "Aruba"),
-        ALAND_ISLANDS("AX", "Åland Islands"),
-        AZERBAIJAN("AZ", "Azerbaijan"),
-        BOSNIA_AND_HERZEGOVINA("BA", "Bosnia and Herzegovina"),
-        BARBADOS("BB", "Barbados"),
-        BANGLADESH("BD", "Bangladesh"),
-        BELGIUM("BE", "Belgium"),
-        BURKINA_FASO("BF", "Burkina Faso"),
-        BULGARIA("BG", "Bulgaria"),
-        BAHRAIN("BH", "Bahrain"),
-        BURUNDI("BI", "Burundi"),
-        BENIN("BJ", "Benin"),
-        SAINT_BARTHELEMY("BL", "Saint Barthélemy"),
-        BERMUDA("BM", "Bermuda"),
-        BRUNEI("BN", "Brunei"),
-        BOLIVIA("BO", "Bolivia"),
-        BONAIRE_SINT_EUSTATIUS_AND_SABA("BQ", "Bonaire, Sint Eustatius and Saba"),
-        BRAZIL("BR", "Brazil"),
-        BAHAMAS("BS", "Bahamas"),
-        BHUTAN("BT", "Bhutan"),
-        BOUVET_ISLAND("BV", "Bouvet Island"),
-        BOTSWANA("BW", "Botswana"),
-        BELARUS("BY", "Belarus"),
-        BELIZE("BZ", "Belize"),
-        CANADA("CA", "Canada"),
-        COCOS_ISLANDS("CC", "Cocos Islands"),
-        THE_DEMOCRATIC_REPUBLIC_OF_CONGO("CD", "The Democratic Republic Of Congo"),
-        CENTRAL_AFRICAN_REPUBLIC("CF", "Central African Republic"),
-        CONGO("CG", "Congo"),
-        SWITZERLAND("CH", "Switzerland"),
-        COTE_D_IVOIRE("CI", "Côte d'Ivoire"),
-        COOK_ISLANDS("CK", "Cook Islands"),
-        CHILE("CL", "Chile"),
-        CAMEROON("CM", "Cameroon"),
-        CHINA("CN", "China"),
-        COLOMBIA("CO", "Colombia"),
-        COSTA_RICA("CR", "Costa Rica"),
-        CUBA("CU", "Cuba"),
-        CAPE_VERDE("CV", "Cape Verde"),
-        CURACAO("CW", "Curaçao"),
-        CHRISTMAS_ISLAND("CX", "Christmas Island"),
-        CYPRUS("CY", "Cyprus"),
-        CZECH_REPUBLIC("CZ", "Czech Republic"),
-        GERMANY("DE", "Germany"),
-        DJIBOUTI("DJ", "Djibouti"),
-        DENMARK("DK", "Denmark"),
-        DOMINICA("DM", "Dominica"),
-        DOMINICAN_REPUBLIC("DO", "Dominican Republic"),
-        ALGERIA("DZ", "Algeria"),
-        ECUADOR("EC", "Ecuador"),
-        ESTONIA("EE", "Estonia"),
-        EGYPT("EG", "Egypt"),
-        WESTERN_SAHARA("EH", "Western Sahara"),
-        ERITREA("ER", "Eritrea"),
-        SPAIN("ES", "Spain"),
-        ETHIOPIA("ET", "Ethiopia"),
-        FINLAND("FI", "Finland"),
-        FIJI("FJ", "Fiji"),
-        FALKLAND_ISLANDS("FK", "Falkland Islands"),
-        MICRONESIA("FM", "Micronesia"),
-        FAROE_ISLANDS("FO", "Faroe Islands"),
-        FRANCE("FR", "France"),
-        GABON("GA", "Gabon"),
-        UNITED_KINGDOM("GB", "United Kingdom"),
-        GRENADA("GD", "Grenada"),
-        GEORGIA("GE", "Georgia"),
-        FRENCH_GUIANA("GF", "French Guiana"),
-        GUERNSEY("GG", "Guernsey"),
-        GHANA("GH", "Ghana"),
-        GIBRALTAR("GI", "Gibraltar"),
-        GREENLAND("GL", "Greenland"),
-        GAMBIA("GM", "Gambia"),
-        GUINEA("GN", "Guinea"),
-        GUADELOUPE("GP", "Guadeloupe"),
-        EQUATORIAL_GUINEA("GQ", "Equatorial Guinea"),
-        GREECE("GR", "Greece"),
-        SOUTH_GEORGIA_AND_THE_SOUTH_SANDWICH_ISLANDS("GS", "South Georgia And The South Sandwich Islands"),
-        GUATEMALA("GT", "Guatemala"),
-        GUAM("GU", "Guam"),
-        GUINEA_BISSAU("GW", "Guinea-Bissau"),
-        GUYANA("GY", "Guyana"),
-        HONG_KONG("HK", "Hong Kong"),
-        HEARD_ISLAND_AND_MCDONALD_ISLANDS("HM", "Heard Island And McDonald Islands"),
-        HONDURAS("HN", "Honduras"),
-        CROATIA("HR", "Croatia"),
-        HAITI("HT", "Haiti"),
-        HUNGARY("HU", "Hungary"),
-        INDONESIA("ID", "Indonesia"),
-        IRELAND("IE", "Ireland"),
-        ISRAEL("IL", "Israel"),
-        ISLE_OF_MAN("IM", "Isle Of Man"),
-        INDIA("IN", "India"),
-        BRITISH_INDIAN_OCEAN_TERRITORY("IO", "British Indian Ocean Territory"),
-        IRAQ("IQ", "Iraq"),
-        IRAN("IR", "Iran"),
-        ICELAND("IS", "Iceland"),
-        ITALY("IT", "Italy"),
-        JERSEY("JE", "Jersey"),
-        JAMAICA("JM", "Jamaica"),
-        JORDAN("JO", "Jordan"),
-        JAPAN("JP", "Japan"),
-        KENYA("KE", "Kenya"),
-        KYRGYZSTAN("KG", "Kyrgyzstan"),
-        CAMBODIA("KH", "Cambodia"),
-        KIRIBATI("KI", "Kiribati"),
-        COMOROS("KM", "Comoros"),
-        SAINT_KITTS_AND_NEVIS("KN", "Saint Kitts And Nevis"),
-        NORTH_KOREA("KP", "North Korea"),
-        SOUTH_KOREA("KR", "South Korea"),
-        KUWAIT("KW", "Kuwait"),
-        CAYMAN_ISLANDS("KY", "Cayman Islands"),
-        KAZAKHSTAN("KZ", "Kazakhstan"),
-        LAOS("LA", "Laos"),
-        LEBANON("LB", "Lebanon"),
-        SAINT_LUCIA("LC", "Saint Lucia"),
-        LIECHTENSTEIN("LI", "Liechtenstein"),
-        SRI_LANKA("LK", "Sri Lanka"),
-        LIBERIA("LR", "Liberia"),
-        LESOTHO("LS", "Lesotho"),
-        LITHUANIA("LT", "Lithuania"),
-        LUXEMBOURG("LU", "Luxembourg"),
-        LATVIA("LV", "Latvia"),
-        LIBYA("LY", "Libya"),
-        MOROCCO("MA", "Morocco"),
-        MONACO("MC", "Monaco"),
-        MOLDOVA("MD", "Moldova"),
-        MONTENEGRO("ME", "Montenegro"),
-        SAINT_MARTIN("MF", "Saint Martin"),
-        MADAGASCAR("MG", "Madagascar"),
-        MARSHALL_ISLANDS("MH", "Marshall Islands"),
-        MACEDONIA("MK", "Macedonia"),
-        MALI("ML", "Mali"),
-        MYANMAR("MM", "Myanmar"),
-        MONGOLIA("MN", "Mongolia"),
-        MACAO("MO", "Macao"),
-        NORTHERN_MARIANA_ISLANDS("MP", "Northern Mariana Islands"),
-        MARTINIQUE("MQ", "Martinique"),
-        MAURITANIA("MR", "Mauritania"),
-        MONTSERRAT("MS", "Montserrat"),
-        MALTA("MT", "Malta"),
-        MAURITIUS("MU", "Mauritius"),
-        MALDIVES("MV", "Maldives"),
-        MALAWI("MW", "Malawi"),
-        MEXICO("MX", "Mexico"),
-        MALAYSIA("MY", "Malaysia"),
-        MOZAMBIQUE("MZ", "Mozambique"),
-        NAMIBIA("NA", "Namibia"),
-        NEW_CALEDONIA("NC", "New Caledonia"),
-        NIGER("NE", "Niger"),
-        NORFOLK_ISLAND("NF", "Norfolk Island"),
-        NIGERIA("NG", "Nigeria"),
-        NICARAGUA("NI", "Nicaragua"),
-        NETHERLANDS("NL", "Netherlands"),
-        NORWAY("NO", "Norway"),
-        NEPAL("NP", "Nepal"),
-        NAURU("NR", "Nauru"),
-        NIUE("NU", "Niue"),
-        NEW_ZEALAND("NZ", "New Zealand"),
-        OMAN("OM", "Oman"),
-        PANAMA("PA", "Panama"),
-        PERU("PE", "Peru"),
-        FRENCH_POLYNESIA("PF", "French Polynesia"),
-        PAPUA_NEW_GUINEA("PG", "Papua New Guinea"),
-        PHILIPPINES("PH", "Philippines"),
-        PAKISTAN("PK", "Pakistan"),
-        POLAND("PL", "Poland"),
-        SAINT_PIERRE_AND_MIQUELON("PM", "Saint Pierre And Miquelon"),
-        PITCAIRN("PN", "Pitcairn"),
-        PUERTO_RICO("PR", "Puerto Rico"),
-        PALESTINE("PS", "Palestine"),
-        PORTUGAL("PT", "Portugal"),
-        PALAU("PW", "Palau"),
-        PARAGUAY("PY", "Paraguay"),
-        QATAR("QA", "Qatar"),
-        REUNION("RE", "Reunion"),
-        ROMANIA("RO", "Romania"),
-        SERBIA("RS", "Serbia"),
-        RUSSIA("RU", "Russia"),
-        RWANDA("RW", "Rwanda"),
-        SAUDI_ARABIA("SA", "Saudi Arabia"),
-        SOLOMON_ISLANDS("SB", "Solomon Islands"),
-        SEYCHELLES("SC", "Seychelles"),
-        SUDAN("SD", "Sudan"),
-        SWEDEN("SE", "Sweden"),
-        SINGAPORE("SG", "Singapore"),
-        SAINT_HELENA("SH", "Saint Helena"),
-        SLOVENIA("SI", "Slovenia"),
-        SVALBARD_AND_JAN_MAYEN("SJ", "Svalbard And Jan Mayen"),
-        SLOVAKIA("SK", "Slovakia"),
-        SIERRA_LEONE("SL", "Sierra Leone"),
-        SAN_MARINO("SM", "San Marino"),
-        SENEGAL("SN", "Senegal"),
-        SOMALIA("SO", "Somalia"),
-        SURINAME("SR", "Suriname"),
-        SOUTH_SUDAN("SS", "South Sudan"),
-        SAO_TOME_AND_PRINCIPE("ST", "Sao Tome And Principe"),
-        EL_SALVADOR("SV", "El Salvador"),
-        SINT_MAARTEN_DUTCH_PART("SX", "Sint Maarten (Dutch part)"),
-        SYRIA("SY", "Syria"),
-        SWAZILAND("SZ", "Swaziland"),
-        TURKS_AND_CAICOS_ISLANDS("TC", "Turks And Caicos Islands"),
-        CHAD("TD", "Chad"),
-        FRENCH_SOUTHERN_TERRITORIES("TF", "French Southern Territories"),
-        TOGO("TG", "Togo"),
-        THAILAND("TH", "Thailand"),
-        TAJIKISTAN("TJ", "Tajikistan"),
-        TOKELAU("TK", "Tokelau"),
-        TIMOR_LESTE("TL", "Timor-Leste"),
-        TURKMENISTAN("TM", "Turkmenistan"),
-        TUNISIA("TN", "Tunisia"),
-        TONGA("TO", "Tonga"),
-        TURKEY("TR", "Turkey"),
-        TRINIDAD_AND_TOBAGO("TT", "Trinidad and Tobago"),
-        TUVALU("TV", "Tuvalu"),
-        TAIWAN("TW", "Taiwan"),
-        TANZANIA("TZ", "Tanzania"),
-        UKRAINE("UA", "Ukraine"),
-        UGANDA("UG", "Uganda"),
-        UNITED_STATES_MINOR_OUTLYING_ISLANDS("UM", "United States Minor Outlying Islands"),
-        UNITED_STATES("US", "United States"),
-        URUGUAY("UY", "Uruguay"),
-        UZBEKISTAN("UZ", "Uzbekistan"),
-        VATICAN("VA", "Vatican"),
-        SAINT_VINCENT_AND_THE_GRENADINES("VC", "Saint Vincent And The Grenadines"),
-        VENEZUELA("VE", "Venezuela"),
-        BRITISH_VIRGIN_ISLANDS("VG", "British Virgin Islands"),
-        U_S__VIRGIN_ISLANDS("VI", "U.S. Virgin Islands"),
-        VIETNAM("VN", "Vietnam"),
-        VANUATU("VU", "Vanuatu"),
-        WALLIS_AND_FUTUNA("WF", "Wallis And Futuna"),
-        SAMOA("WS", "Samoa"),
-        YEMEN("YE", "Yemen"),
-        MAYOTTE("YT", "Mayotte"),
-        SOUTH_AFRICA("ZA", "South Africa"),
-        ZAMBIA("ZM", "Zambia"),
-        ZIMBABWE("ZW", "Zimbabwe");
-
-        private String isoTag;
-        private String name;
-
-        Country(String isoTag, String name) {
-            this.isoTag = isoTag;
-            this.name = name;
-        }
-
-        /**
-         * Gets the name of the country.
-         *
-         * @return The name of the country.
-         */
-        public String getCountryName() {
-            return name;
-        }
-
-        /**
-         * Gets the iso tag of the country.
-         *
-         * @return The iso tag of the country.
-         */
-        public String getCountryIsoTag() {
-            return isoTag;
-        }
-
-        /**
-         * Gets a country by it's iso tag.
-         *
-         * @param isoTag The iso tag of the county.
-         * @return The country with the given iso tag or <code>null</code> if unknown.
-         */
-        public static Country byIsoTag(String isoTag) {
-            for (Country country : Country.values()) {
-                if (country.getCountryIsoTag().equals(isoTag)) {
-                    return country;
-                }
-            }
-            return null;
-        }
-
-        /**
-         * Gets a country by a locale.
-         *
-         * @param locale The locale.
-         * @return The country from the giben locale or <code>null</code> if unknown country or
-         *         if the locale does not contain a country.
-         */
-        public static Country byLocale(Locale locale) {
-            return byIsoTag(locale.getCountry());
-        }
-
-    }
-
 }

+ 3 - 3
TimeIsMoney2/src/de/Linus122/TimeIsMoney/Payout.java

@@ -6,7 +6,7 @@ import java.util.List;
 public class Payout {
 	double payout_amount = 0;
 	double max_payout_per_day = 0;
-	String permission = "";
-	int chance = 0;
-	List<String> commands = new ArrayList<String>();
+	String permission = "";
+	int chance = 0;
+	List<String> commands = new ArrayList<>();
 }

+ 15 - 3
TimeIsMoney2/src/de/Linus122/TimeIsMoney/Utils.java

@@ -1,7 +1,19 @@
 package de.Linus122.TimeIsMoney;
 
-import org.bukkit.entity.Player;
+import org.bukkit.ChatColor;
 
-public interface Utils {
-	public void sendActionBarMessage(Player p, String message);
+public class Utils {
+
+    public Utils() {
+        throw new RuntimeException("Utils class should not be instantiated!");
+    }
+
+    /**
+     * Utility method which converts &<color> bukkit colors to real bukkit colors which correct symbol
+     * @param s string to convert
+     * @return converted string
+     */
+    public static String CC(String s) {
+        return ChatColor.translateAlternateColorCodes('&', s);
+    }
 }

+ 53 - 61
TimeIsMoney2/src/modules/atm/ATM.java

@@ -2,7 +2,6 @@ package modules.atm;
 
 import java.io.File;
 import java.io.IOException;
-import java.util.ArrayList;
 import java.util.List;
 
 import org.bukkit.Bukkit;
@@ -33,13 +32,15 @@ import com.google.common.primitives.Doubles;
 
 import de.Linus122.TimeIsMoney.Main;
 
+import static de.Linus122.TimeIsMoney.Utils.CC;
+
 public class ATM implements Listener, CommandExecutor {
-	Plugin pl;
+	private final Plugin pl;
 	
-	public static YamlConfiguration cfg;
-	public static File fileBankAccounts = new File("plugins/TimeIsMoney/data.dat");
+	private static YamlConfiguration cfg;
+	private static final File fileBankAccounts = new File("plugins/TimeIsMoney/data.dat");
 	
-	double[] worths = new double[4];
+	private double[] worths = new double[4];
 	
 	public ATM(Main pl){
 		this.pl = pl;
@@ -49,7 +50,6 @@ public class ATM implements Listener, CommandExecutor {
 			try {
 				fileBankAccounts.createNewFile();
 			} catch (IOException e) {
-				// TODO Auto-generated catch block
 				e.printStackTrace();
 			}	
 		}
@@ -57,7 +57,7 @@ public class ATM implements Listener, CommandExecutor {
 		
 		worths = Doubles.toArray(Main.finalconfig.getDoubleList("atm_worth_gradation"));
 	}
-	public static void withdrawBank(Player p, double amount){
+	private static void withdrawBank(Player p, double amount){
 		String bankString = getBankString(p);
 		if(!cfg.contains(bankString)) cfg.set(bankString, 0.0);
 		cfg.set(bankString, getBankBalance(p) - amount);
@@ -69,41 +69,36 @@ public class ATM implements Listener, CommandExecutor {
 		cfg.set(bankString, getBankBalance(p) + amount);
 		saveBanks();
 	}
-	public static boolean bankHas(Player p, double amount){
+	private static boolean bankHas(Player p, double amount){
 		String bankString = getBankString(p);
 		if(!cfg.contains(bankString)) cfg.set(bankString, 0.0);
-		if(getBankBalance(p) >= amount){
-			return true;
-		}else{
-			return false;
-		}
+		return getBankBalance(p) >= amount;
 		
 	}
 	//Doesn't support groups 
-	public static double getBankBalance(OfflinePlayer p){
+	private static double getBankBalance(OfflinePlayer p){
 		String bankString =  p.getName() + "_TimBANK";
 		if(!cfg.contains(bankString)) cfg.set(bankString, 0.0);
 		return cfg.getDouble(bankString);
 	}
-	public static double getBankBalance(Player p){
+	private static double getBankBalance(Player p){
 		String bankString = getBankString(p);
 		if(!cfg.contains(bankString)) cfg.set(bankString, 0.0);
 		return cfg.getDouble(bankString);
 	}
-	public static void saveBanks(){
+	private static void saveBanks(){
 		try {
 			cfg.save(fileBankAccounts);
 		} catch (IOException e) {
-			// TODO Auto-generated catch block
 			e.printStackTrace();
 		}
 	}
 	//Converts old tim bank
-	public static void convertOldBank(Player p){
+	private static void convertOldBank(Player p){
 		String bankString = getBankString(p);
 		if(Main.economy.hasAccount(bankString)){
 			if(Main.economy.getBalance(bankString) > 0){
-				p.sendMessage(aSuccessfully converted your old TIM-Bank to new version!");
+				p.sendMessage(CC("&aSuccessfully converted your old TIM-Bank to new version!"));
 				depositBank(p, Main.economy.getBalance(bankString));
 				Main.economy.withdrawPlayer(bankString, Main.economy.getBalance(bankString));	
 			}
@@ -138,9 +133,9 @@ public class ATM implements Listener, CommandExecutor {
 		if(e.getClickedBlock() != null){
 			if(e.getClickedBlock().getType() == Material.WALL_SIGN || e.getClickedBlock().getType() == Material.SIGN || e.getClickedBlock().getType() == Material.SIGN_POST){
 				Sign sign = (Sign) e.getClickedBlock().getState();	
-				if(sign.getLine(0).equalsIgnoreCase("§cATM")){
+				if(sign.getLine(0).equalsIgnoreCase(CC("&cATM"))){
 					if(!e.getPlayer().hasPermission("tim.atm.use")){
-						e.getPlayer().sendMessage(Main.finalconfig.getString("message_atm_noperms").replace('&', '§'));
+						e.getPlayer().sendMessage(CC(Main.finalconfig.getString("message_atm_noperms")));
 					}else{
 						this.openGUI(e.getPlayer());
 					}
@@ -152,7 +147,7 @@ public class ATM implements Listener, CommandExecutor {
 	public void onMove(InventoryMoveItemEvent e){
 		if(e.getSource() == null) return;
 		if(e.getSource().getTitle() == null) return;
-		if(e.getSource().getTitle().equals(Main.finalconfig.getString("atm_title").replace('&', '§'))){
+		if(e.getSource().getTitle().equals(CC(Main.finalconfig.getString("atm_title")))) {
 			e.setCancelled(true);
 		}
 	}
@@ -163,7 +158,7 @@ public class ATM implements Listener, CommandExecutor {
 			if(e == null) return;
 			if(e.getInventory() == null) return;
 			if(e.getInventory().getTitle() == null) return;
-			if(e.getInventory().getTitle().equals(Main.finalconfig.getString("atm_title").replace('&', '§'))){
+			if(e.getInventory().getTitle().equals(CC(Main.finalconfig.getString("atm_title")))){
 				e.setResult(Result.DENY);
 				Player p = (Player) e.getWhoClicked();
 				//e.setCancelled(true);
@@ -176,9 +171,9 @@ public class ATM implements Listener, CommandExecutor {
 						if(ATM.bankHas(p, amount)){
 							ATM.withdrawBank(p, amount);
 							Main.economy.depositPlayer(p, amount);
-							e.getWhoClicked().sendMessage(Main.finalconfig.getString("atm_withdraw").replace('&', '§') + " " + Main.economy.format(amount));
+							e.getWhoClicked().sendMessage(CC(Main.finalconfig.getString("atm_withdraw")) + " " + Main.economy.format(amount));
 						}else{
-							e.getWhoClicked().sendMessage(Main.finalconfig.getString("message_atm_nomoneyinbank").replace('&', '§'));
+							e.getWhoClicked().sendMessage(CC(Main.finalconfig.getString("message_atm_nomoneyinbank")));
 						}
 					}else
 					// right side
@@ -189,30 +184,30 @@ public class ATM implements Listener, CommandExecutor {
 						if(Main.economy.has((Player) e.getWhoClicked(), amount)){
 							ATM.depositBank(p, amount);
 							Main.economy.withdrawPlayer((Player) e.getWhoClicked(), amount);
-							e.getWhoClicked().sendMessage(Main.finalconfig.getString("atm_deposit").replace('&', '§') + " " + Main.economy.format(amount));
+							e.getWhoClicked().sendMessage(CC(Main.finalconfig.getString("atm_deposit")) + " " + Main.economy.format(amount));
 						}else{
-							e.getWhoClicked().sendMessage(Main.finalconfig.getString("message_atm_nomoney").replace('&', '§'));
+							e.getWhoClicked().sendMessage(CC(Main.finalconfig.getString("message_atm_nomoney")));
 						}
 					}
 					ItemStack is = new ItemStack(Material.GOLD_NUGGET, 1);
 					ItemMeta im = is.getItemMeta();
-					im.setDisplayName(Main.finalconfig.getString("atm_balance").replace('&', '§') + " " + Main.economy.format(ATM.getBankBalance(p)));
+					im.setDisplayName(CC(Main.finalconfig.getString("atm_balance")) + " " + Main.economy.format(ATM.getBankBalance(p)));
 					is.setItemMeta(im);
 					e.getInventory().setItem(4, is);
 				}
 			}
-		}catch(Exception e2){
+		}catch(Exception ignored){
 			
 		}
 	}
 	private void openGUI(Player player) {
 		convertOldBank(player);
-		Inventory atm_gui = Bukkit.createInventory(null, 9, Main.finalconfig.getString("atm_title").replace('&', '§'));
+		Inventory atm_gui = Bukkit.createInventory(null, 9, CC(Main.finalconfig.getString("atm_title")));
 		
 		//
 		ItemStack is = new ItemStack(Material.GOLD_NUGGET, 1);
 		ItemMeta im = is.getItemMeta();
-		im.setDisplayName(Main.finalconfig.getString("atm_balance").replace('&', '§') + " " + Main.economy.format(ATM.getBankBalance(player)));
+		im.setDisplayName(CC(Main.finalconfig.getString("atm_balance")) + " " + Main.economy.format(ATM.getBankBalance(player)));
 		is.setItemMeta(im);
 		atm_gui.setItem(4, is);
 		
@@ -220,28 +215,28 @@ public class ATM implements Listener, CommandExecutor {
 		//
 		is = new ItemStack(Material.CLAY_BRICK, 1);
 		im = is.getItemMeta();
-		im.setDisplayName(Main.finalconfig.getString("atm_withdraw").replace('&', '§') + " §a" + Main.economy.format(worths[0]));
+		im.setDisplayName(CC(Main.finalconfig.getString("atm_withdraw") + " &a") + Main.economy.format(worths[0]));
 		is.setItemMeta(im);
 		atm_gui.setItem(3, is);
 		
 		//
 		is = new ItemStack(Material.IRON_INGOT, 1);
 		im = is.getItemMeta();
-		im.setDisplayName(Main.finalconfig.getString("atm_withdraw").replace('&', '§') +  " §a" + Main.economy.format(worths[1]));
+		im.setDisplayName(CC(Main.finalconfig.getString("atm_withdraw") +  " &a") + Main.economy.format(worths[1]));
 		is.setItemMeta(im);
 		atm_gui.setItem(2, is);
 		
 		//
 		is = new ItemStack(Material.GOLD_INGOT, 1);
 		im = is.getItemMeta();
-		im.setDisplayName(Main.finalconfig.getString("atm_withdraw").replace('&', '§') + " §a" + Main.economy.format(worths[2]));
+		im.setDisplayName(CC(Main.finalconfig.getString("atm_withdraw") + " &a") + Main.economy.format(worths[2]));
 		is.setItemMeta(im);
 		atm_gui.setItem(1, is);
 		
 		//
 		is = new ItemStack(Material.DIAMOND, 1);
 		im = is.getItemMeta();
-		im.setDisplayName(Main.finalconfig.getString("atm_withdraw").replace('&', '§') + " §a" + Main.economy.format(worths[3]));
+		im.setDisplayName(CC(Main.finalconfig.getString("atm_withdraw") + " &a") + Main.economy.format(worths[3]));
 		is.setItemMeta(im);
 		atm_gui.setItem(0, is);
 		
@@ -249,28 +244,28 @@ public class ATM implements Listener, CommandExecutor {
 		//
 		is = new ItemStack(Material.CLAY_BRICK, 1);
 		im = is.getItemMeta();
-		im.setDisplayName(Main.finalconfig.getString("atm_deposit").replace('&', '§') + " §4" + Main.economy.format(worths[0]));
+		im.setDisplayName(CC(Main.finalconfig.getString("atm_deposit") + " &4") + Main.economy.format(worths[0]));
 		is.setItemMeta(im);
 		atm_gui.setItem(5, is);
 		
 		//
 		is = new ItemStack(Material.IRON_INGOT, 1);
 		im = is.getItemMeta();
-		im.setDisplayName(Main.finalconfig.getString("atm_deposit").replace('&', '§') + " §4" + Main.economy.format(worths[1]));
+		im.setDisplayName(CC(Main.finalconfig.getString("atm_deposit") + " &4") + Main.economy.format(worths[1]));
 		is.setItemMeta(im);
 		atm_gui.setItem(6, is);
 		
 		//
 		is = new ItemStack(Material.GOLD_INGOT, 1);
 		im = is.getItemMeta();
-		im.setDisplayName(Main.finalconfig.getString("atm_deposit").replace('&', '§') + " §4" + Main.economy.format(worths[2]));
+		im.setDisplayName(CC(Main.finalconfig.getString("atm_deposit") + " &4" )+ Main.economy.format(worths[2]));
 		is.setItemMeta(im);
 		atm_gui.setItem(7, is);
 		
 		//
 		is = new ItemStack(Material.DIAMOND, 1);
 		im = is.getItemMeta();
-		im.setDisplayName(Main.finalconfig.getString("atm_deposit").replace('&', '§') + " §4" + Main.economy.format(worths[3]));
+		im.setDisplayName(CC(Main.finalconfig.getString("atm_deposit") + " &4") + Main.economy.format(worths[3]));
 		is.setItemMeta(im);
 		atm_gui.setItem(8, is);
 		
@@ -285,7 +280,7 @@ public class ATM implements Listener, CommandExecutor {
 		if(e == null) return;
 		if(e.getInventory() == null) return;
 		if(e.getInventory().getTitle() == null) return;
-		if(e.getInventory().getTitle().equals(Main.finalconfig.getString("atm_title").replace('&', '§'))){
+		if(e.getInventory().getTitle().equals(CC(Main.finalconfig.getString("atm_title")))){
 			e.setResult(Result.DENY);
 		}
 	}
@@ -293,24 +288,21 @@ public class ATM implements Listener, CommandExecutor {
 	public void onSign(final SignChangeEvent e){
 		final Block b = e.getBlock();
 		if(b.getType() == Material.WALL_SIGN || b.getType() == Material.SIGN || b.getType() == Material.SIGN_POST){
-			pl.getServer().getScheduler().scheduleSyncDelayedTask(pl, new Runnable(){
-				public void run(){
-					if(b.getType() == Material.WALL_SIGN || b.getType() == Material.SIGN || b.getType() == Material.SIGN_POST){
-						Sign sign = (Sign) e.getBlock().getState();
-						if(sign.getLine(0).equalsIgnoreCase("[atm]")){
-							if(!e.getPlayer().hasPermission("tim.atm.place")){
-								e.getPlayer().sendMessage("§cYou dont have permissions to build ATM's!");
-								sign.setLine(0, "");
-								return;
-							}else{
-								sign.setLine(0, "§cATM");
-								sign.update();
-								e.getPlayer().sendMessage("§2ATM created! (You can also write something in the Lins 2-4)");
-							}
-						}
-					}
-				}
-			}, 10L);
+			pl.getServer().getScheduler().scheduleSyncDelayedTask(pl, () -> {
+                if(b.getType() == Material.WALL_SIGN || b.getType() == Material.SIGN || b.getType() == Material.SIGN_POST){
+                    Sign sign = (Sign) e.getBlock().getState();
+                    if(sign.getLine(0).equalsIgnoreCase("[atm]")){
+                        if(!e.getPlayer().hasPermission("tim.atm.place")){
+                            e.getPlayer().sendMessage(CC("&cYou dont have permissions to build ATM's!"));
+                            sign.setLine(0, "");
+                        }else{
+                            sign.setLine(0, CC("&cATM"));
+                            sign.update();
+                            e.getPlayer().sendMessage(CC("&2ATM created! (You can also write something in the Lines 2-4)"));
+                        }
+                    }
+                }
+            }, 10L);
 		}
 	}
 
@@ -327,7 +319,7 @@ public class ATM implements Listener, CommandExecutor {
 				switch(args[0]){
 					case "balance":
 						if(args.length > 1){		
-							cs.sendMessage("§2ATM-Balance of§c " + args[1] + "§2: §c" + this.getBankBalance(Bukkit.getOfflinePlayer(args[1])));
+							cs.sendMessage(CC("&2ATM-Balance of&c " + args[1] + "&2: &c") + getBankBalance(Bukkit.getOfflinePlayer(args[1])));
 						}else{
 							cs.sendMessage("/atm balance <player>");
 						}
@@ -348,8 +340,8 @@ public class ATM implements Listener, CommandExecutor {
 				}
 			
 			}else{
-				cs.sendMessage("§c/atm <player> §a- opens atm for player");
-				cs.sendMessage("§c/atm balance <player> §a- gets balance of player");
+				cs.sendMessage(CC("&c/atm <player> &a- opens atm for player"));
+				cs.sendMessage(CC("&c/atm balance <player> &a- gets balance of player"));
 				return true;
 			}
 		}

+ 5 - 3
TimeIsMoney2/src/v1_10_R1/NBTUtils.java

@@ -1,16 +1,18 @@
 package v1_10_R1;
 
+import de.Linus122.TimeIsMoney.ActionBarUtils;
 import org.bukkit.craftbukkit.v1_10_R1.entity.CraftPlayer;
 import org.bukkit.entity.Player;
 
-import de.Linus122.TimeIsMoney.Utils;
 import net.minecraft.server.v1_10_R1.IChatBaseComponent;
 import net.minecraft.server.v1_10_R1.PacketPlayOutChat;
 
-public class NBTUtils implements Utils{
+import static de.Linus122.TimeIsMoney.Utils.CC;
+
+public class NBTUtils implements ActionBarUtils {
   public void sendActionBarMessage(Player p, String message)
   {
-	    IChatBaseComponent icbc = IChatBaseComponent.ChatSerializer.a("{\"text\": \"" + message.replace('&', '§') + "\"}");
+	    IChatBaseComponent icbc = IChatBaseComponent.ChatSerializer.a("{\"text\": \"" + CC(message)  + "\"}");
 	    PacketPlayOutChat bar = new PacketPlayOutChat(icbc, (byte)2);
 	    ((CraftPlayer)p).getHandle().playerConnection.sendPacket(bar);
   }

+ 5 - 3
TimeIsMoney2/src/v1_11_R1/NBTUtils.java

@@ -1,16 +1,18 @@
 package v1_11_R1;
 
+import de.Linus122.TimeIsMoney.ActionBarUtils;
 import org.bukkit.craftbukkit.v1_11_R1.entity.CraftPlayer;
 import org.bukkit.entity.Player;
 
-import de.Linus122.TimeIsMoney.Utils;
 import net.minecraft.server.v1_11_R1.IChatBaseComponent;
 import net.minecraft.server.v1_11_R1.PacketPlayOutChat;
 
-public class NBTUtils implements Utils{
+import static de.Linus122.TimeIsMoney.Utils.CC;
+
+public class NBTUtils implements ActionBarUtils {
   public void sendActionBarMessage(Player p, String message)
   {
-	    IChatBaseComponent icbc = IChatBaseComponent.ChatSerializer.a("{\"text\": \"" + message.replace('&', '§') + "\"}");
+	    IChatBaseComponent icbc = IChatBaseComponent.ChatSerializer.a("{\"text\": \"" + CC(message)  + "\"}");
 	    PacketPlayOutChat bar = new PacketPlayOutChat(icbc, (byte)2);
 	    ((CraftPlayer)p).getHandle().playerConnection.sendPacket(bar);
   }

+ 7 - 6
TimeIsMoney2/src/v1_12_R1/NBTUtils.java

@@ -1,17 +1,18 @@
 package v1_12_R1;
 
-import org.bukkit.craftbukkit.v1_12_R1.entity.CraftPlayer;
-import org.bukkit.entity.Player;
-
-import de.Linus122.TimeIsMoney.Utils;
+import de.Linus122.TimeIsMoney.ActionBarUtils;
 import net.minecraft.server.v1_12_R1.ChatMessageType;
 import net.minecraft.server.v1_12_R1.IChatBaseComponent;
 import net.minecraft.server.v1_12_R1.PacketPlayOutChat;
+import org.bukkit.craftbukkit.v1_12_R1.entity.CraftPlayer;
+import org.bukkit.entity.Player;
+
+import static de.Linus122.TimeIsMoney.Utils.CC;
 
-public class NBTUtils implements Utils{
+public class NBTUtils implements ActionBarUtils{
   public void sendActionBarMessage(Player p, String message)
   {
-	    IChatBaseComponent icbc = IChatBaseComponent.ChatSerializer.a("{\"text\": \"" + message.replace('&', '§') + "\"}");
+	    IChatBaseComponent icbc = IChatBaseComponent.ChatSerializer.a("{\"text\": \"" + CC(message)  + "\"}");
 	    PacketPlayOutChat bar = new PacketPlayOutChat(icbc, ChatMessageType.GAME_INFO);
 	    ((CraftPlayer)p).getHandle().playerConnection.sendPacket(bar);
   }

+ 7 - 6
TimeIsMoney2/src/v1_8_R1/NBTUtils.java

@@ -1,18 +1,19 @@
 package v1_8_R1;
 
-import org.bukkit.craftbukkit.v1_8_R1.entity.CraftPlayer;
-import org.bukkit.entity.Player;
-
-import de.Linus122.TimeIsMoney.Utils;
+import de.Linus122.TimeIsMoney.ActionBarUtils;
 import net.minecraft.server.v1_8_R1.ChatSerializer;
 import net.minecraft.server.v1_8_R1.IChatBaseComponent;
 import net.minecraft.server.v1_8_R1.PacketPlayOutChat;
+import org.bukkit.craftbukkit.v1_8_R1.entity.CraftPlayer;
+import org.bukkit.entity.Player;
+
+import static de.Linus122.TimeIsMoney.Utils.CC;
 
-public class NBTUtils implements Utils{
+public class NBTUtils implements ActionBarUtils {
   @Override
   public void sendActionBarMessage(Player p, String message)
   {
-	    IChatBaseComponent icbc = ChatSerializer.a("{\"text\": \"" + message.replace('&', '§') + "\"}");
+	    IChatBaseComponent icbc = ChatSerializer.a("{\"text\": \"" + CC(message) + "\"}");
 	    PacketPlayOutChat bar = new PacketPlayOutChat(icbc, (byte)2);
 	    ((CraftPlayer)p).getHandle().playerConnection.sendPacket(bar);
   }

+ 6 - 5
TimeIsMoney2/src/v1_8_R2/NBTUtils.java

@@ -1,17 +1,18 @@
 package v1_8_R2;
 
+import de.Linus122.TimeIsMoney.ActionBarUtils;
+import net.minecraft.server.v1_8_R2.IChatBaseComponent;
+import net.minecraft.server.v1_8_R2.PacketPlayOutChat;
 import org.bukkit.craftbukkit.v1_8_R2.entity.CraftPlayer;
 import org.bukkit.entity.Player;
 
-import de.Linus122.TimeIsMoney.Utils;
-import net.minecraft.server.v1_8_R2.IChatBaseComponent;
-import net.minecraft.server.v1_8_R2.PacketPlayOutChat;
+import static de.Linus122.TimeIsMoney.Utils.CC;
 
-public class NBTUtils implements Utils{
+public class NBTUtils implements ActionBarUtils {
   @Override
   public void sendActionBarMessage(Player p, String message)
   {
-	    IChatBaseComponent icbc = IChatBaseComponent.ChatSerializer.a("{\"text\": \"" + message.replace('&', '§') + "\"}");
+	    IChatBaseComponent icbc = IChatBaseComponent.ChatSerializer.a("{\"text\": \"" + CC(message) + "\"}");
 	    PacketPlayOutChat bar = new PacketPlayOutChat(icbc, (byte)2);
 	    ((CraftPlayer)p).getHandle().playerConnection.sendPacket(bar);
   }

+ 6 - 5
TimeIsMoney2/src/v1_8_R3/NBTUtils.java

@@ -1,16 +1,17 @@
 package v1_8_R3;
 
+import de.Linus122.TimeIsMoney.ActionBarUtils;
+import net.minecraft.server.v1_8_R3.IChatBaseComponent;
+import net.minecraft.server.v1_8_R3.PacketPlayOutChat;
 import org.bukkit.craftbukkit.v1_8_R3.entity.CraftPlayer;
 import org.bukkit.entity.Player;
 
-import de.Linus122.TimeIsMoney.Utils;
-import net.minecraft.server.v1_8_R3.IChatBaseComponent;
-import net.minecraft.server.v1_8_R3.PacketPlayOutChat;
+import static de.Linus122.TimeIsMoney.Utils.CC;
 
-public class NBTUtils implements Utils{
+public class NBTUtils implements ActionBarUtils {
   public void sendActionBarMessage(Player p, String message)
   {
-	    IChatBaseComponent icbc = IChatBaseComponent.ChatSerializer.a("{\"text\": \"" + message.replace('&', '§') + "\"}");
+	    IChatBaseComponent icbc = IChatBaseComponent.ChatSerializer.a("{\"text\": \"" + CC(message)  + "\"}");
 	    PacketPlayOutChat bar = new PacketPlayOutChat(icbc, (byte)2);
 	    ((CraftPlayer)p).getHandle().playerConnection.sendPacket(bar);
   }

+ 6 - 5
TimeIsMoney2/src/v1_9_R1/NBTUtils.java

@@ -1,16 +1,17 @@
 package v1_9_R1;
 
+import de.Linus122.TimeIsMoney.ActionBarUtils;
+import net.minecraft.server.v1_9_R1.IChatBaseComponent;
+import net.minecraft.server.v1_9_R1.PacketPlayOutChat;
 import org.bukkit.craftbukkit.v1_9_R1.entity.CraftPlayer;
 import org.bukkit.entity.Player;
 
-import de.Linus122.TimeIsMoney.Utils;
-import net.minecraft.server.v1_9_R1.IChatBaseComponent;
-import net.minecraft.server.v1_9_R1.PacketPlayOutChat;
+import static de.Linus122.TimeIsMoney.Utils.CC;
 
-public class NBTUtils implements Utils{
+public class NBTUtils implements ActionBarUtils {
   public void sendActionBarMessage(Player p, String message)
   {
-	    IChatBaseComponent icbc = IChatBaseComponent.ChatSerializer.a("{\"text\": \"" + message.replace('&', '§') + "\"}");
+	    IChatBaseComponent icbc = IChatBaseComponent.ChatSerializer.a("{\"text\": \"" + CC(message) + "\"}");
 	    PacketPlayOutChat bar = new PacketPlayOutChat(icbc, (byte)2);
 	    ((CraftPlayer)p).getHandle().playerConnection.sendPacket(bar);
   }

+ 5 - 3
TimeIsMoney2/src/v1_9_R2/NBTUtils.java

@@ -1,16 +1,18 @@
 package v1_9_R2;
 
+import de.Linus122.TimeIsMoney.ActionBarUtils;
 import org.bukkit.craftbukkit.v1_9_R2.entity.CraftPlayer;
 import org.bukkit.entity.Player;
 
-import de.Linus122.TimeIsMoney.Utils;
 import net.minecraft.server.v1_9_R2.IChatBaseComponent;
 import net.minecraft.server.v1_9_R2.PacketPlayOutChat;
 
-public class NBTUtils implements Utils{
+import static de.Linus122.TimeIsMoney.Utils.CC;
+
+public class NBTUtils implements ActionBarUtils {
   public void sendActionBarMessage(Player p, String message)
   {
-	    IChatBaseComponent icbc = IChatBaseComponent.ChatSerializer.a("{\"text\": \"" + message.replace('&', '§') + "\"}");
+	    IChatBaseComponent icbc = IChatBaseComponent.ChatSerializer.a("{\"text\": \"" + CC(message)  + "\"}");
 	    PacketPlayOutChat bar = new PacketPlayOutChat(icbc, (byte)2);
 	    ((CraftPlayer)p).getHandle().playerConnection.sendPacket(bar);
   }