6 次代码提交 a7e51d7b59 ... 772b46a786

作者 SHA1 备注 提交日期
  mastercake10 772b46a786 implement individual intervals for payouts 1 年之前
  mastercake10 00ec36d9d7 Merge remote-tracking branch 'origin/master' 1 年之前
  mastercake10 648f6ec6c8 add support for RGB color codes (format: &#RRGGBB) 1 年之前
  mastercake10 a037f2ac43 add support for RGB color codes (format: &#RRGGBB) 1 年之前
  mastercake10 eba20df5f0 ATM: limit amount of money that can be deposited / stored 1 年之前
  mastercake10 9ed65c74cf multiple accounts check: still payout the first player 1 年之前

+ 4 - 0
Plugin/src/main/java/de/Linus122/TimeIsMoney/ATM.java

@@ -186,6 +186,10 @@ public class ATM implements Listener, CommandExecutor {
 	}
 	
 	private static void interactDeposit(Player p, double amount) {
+		if (ATM.getBankBalance(p) >= Main.finalconfig.getDouble("atm_balance_limit", Double.MAX_VALUE)) {
+			p.sendMessage(CC(Main.finalconfig.getString("message_atm_limit_reached")));
+			return;
+		}
 		if (Main.economy.has(p, amount)) {
 			ATM.depositBank(p, amount);
 			Main.economy.withdrawPlayer(p, amount);

+ 1 - 1
Plugin/src/main/java/de/Linus122/TimeIsMoney/Listeners.java

@@ -28,7 +28,7 @@ public class Listeners implements Listener {
 
             pending.forEach(k -> {
                 System.out.println("paying user from pending payout: " + k);
-                main.pay(event.getPlayer());
+                main.pay(event.getPlayer(), main.getPayouts().get(k), main.getPluginData().getPlayerData(event.getPlayer()).getPayoutData(k));
             });
 
         }

+ 77 - 52
Plugin/src/main/java/de/Linus122/TimeIsMoney/Main.java

@@ -8,23 +8,13 @@ import java.io.FileReader;
 import java.io.IOException;
 import java.io.PrintWriter;
 import java.sql.Timestamp;
-import java.util.ArrayList;
-import java.util.Calendar;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Random;
-import java.util.Set;
-import java.util.UUID;
+import java.util.*;
 import java.util.logging.Level;
 import java.util.logging.Logger;
 import java.util.stream.Collectors;
 
-import de.Linus122.TimeIsMoney.data.MySQLPluginData;
-import de.Linus122.TimeIsMoney.data.PlayerData;
-import de.Linus122.TimeIsMoney.data.PluginData;
-import de.Linus122.TimeIsMoney.data.YamlPluginData;
+import de.Linus122.TimeIsMoney.data.*;
+import de.Linus122.TimeIsMoney.tools.Utils;
 import org.bukkit.Bukkit;
 import org.bukkit.Location;
 import org.bukkit.Server;
@@ -179,22 +169,33 @@ public class Main extends JavaPlugin {
 	}
 	
 	public void startPlaytimeWatcher() {
+		String intervalString = getConfig().getString("global_interval", getConfig().getInt("give_money_every_second") + "s");
+		int globalTimerSeconds = Utils.parseTimeFormat(intervalString);
+
 		playtimeWatcherTask = Bukkit.getScheduler().runTaskTimer(this, () -> {
 			for (Player player : Bukkit.getOnlinePlayers()) {
 				if (disabledWorlds.contains(player.getWorld().getName())) continue;
 				PlayerData playerData = this.pluginData.getPlayerData(player);
 
-				playerData.setSecondsSinceLastPayout(playerData.getSecondsSinceLastPayout() + 1);
-				if (playerData.getSecondsSinceLastPayout() >= getConfig().getInt("give_money_every_second")) {
-					// new payout triggered, handling the payout
-					pay(player);
+				for(Payout payout : this.getApplicablePayoutsForPlayer(player)) {
+					PayoutData playerPayoutData = playerData.getPayoutData(payout.id);
+					playerPayoutData.setSecondsSinceLastPayout(playerPayoutData.getSecondsSinceLastPayout() + 1);
+
+					int intervalSeconds = payout.interval != 0 ? payout.interval : globalTimerSeconds;
 
-					if(this.pluginData instanceof MySQLPluginData) {
-						// let other servers know of this payout
-						((MySQLPluginData) this.pluginData).createPendingPayout(player);
+					if (playerPayoutData.getSecondsSinceLastPayout() >= intervalSeconds) {
+						// new payout triggered, handling the payout
+						pay(player, payout, playerPayoutData);
+
+						if(this.pluginData instanceof MySQLPluginData) {
+							// let other servers know of this payout
+							((MySQLPluginData) this.pluginData).createPendingPayout(player);
+						}
+						playerPayoutData.setSecondsSinceLastPayout(0);
 					}
-					playerData.setSecondsSinceLastPayout(0);
 				}
+
+
 			}
 		}, 20L, 20L);
 	}
@@ -228,6 +229,7 @@ public class Main extends JavaPlugin {
 			payouts.clear();
 			for (String key : finalconfig.getConfigurationSection("payouts").getKeys(false)) {
 				Payout payout = new Payout();
+				payout.id = Integer.parseInt(key);
 				payout.max_payout_per_day = finalconfig.getDouble("payouts." + key + ".max_payout_per_day");
 				payout.payout_amount = finalconfig.getDouble("payouts." + key + ".payout_amount");
 				if (finalconfig.isSet("payouts." + key + ".permission")) {
@@ -243,6 +245,10 @@ public class Main extends JavaPlugin {
 				if (finalconfig.isSet("payouts." + key + ".chance")) {
 					payout.chance = finalconfig.getDouble("payouts." + key + ".chance");
 				}
+				if (finalconfig.isSet("payouts." + key + ".interval")) {
+					// TODO: Add error message when parsing failed
+					payout.interval = Utils.parseTimeFormat(finalconfig.getString("payouts." + key + ".interval"));
+				}
 				payouts.add(payout);
 			}
 			logger.info("[TimeIsMoney] &aLoaded " + finalconfig.getConfigurationSection("payouts").getKeys(false).size() + " Payouts!");
@@ -274,7 +280,32 @@ public class Main extends JavaPlugin {
 	private List<Payout> getApplicablePayoutsForPlayer(Player player){
 		if (!this.getConfig().getBoolean("choose-payout-by-chance")) {
 			// Choose applicable payouts by permission
-			return payouts.stream().filter(payout -> player.hasPermission(payout.permission) || payout.permission.length() == 0).collect(Collectors.toList());
+			List<Payout> payouts_ = payouts.stream().filter(payout -> player.hasPermission(payout.permission) || payout.permission.length() == 0).collect(Collectors.toList());
+
+			if(finalconfig.getBoolean("choose-only-one-payout", true)) {
+				List<Payout> finalPayouts = new ArrayList<>();
+				// add payouts with a custom timer anyways
+				finalPayouts.addAll(payouts_.stream().filter(payout -> payout.interval != 0).collect(Collectors.toList()));
+
+				// choose the last element of the payouts that does not have a custom timer
+				List<Payout> payoutsWithoutInterval = payouts_.stream().filter(payout -> payout.interval == 0).collect(Collectors.toList());
+				finalPayouts.add(payoutsWithoutInterval.get(payoutsWithoutInterval.size() - 1));
+				return finalPayouts;
+			} else if(this.getConfig().getBoolean("merge-payouts")) {
+				// Mering multiple payouts to one
+				Payout payout = new Payout();
+				payout.id = 1;
+				for (Payout payout_ : payouts_) {
+					if(payout_.interval != 0) {
+						continue;
+					}
+					payout.commands.addAll(payout_.commands);
+					payout.commands_if_afk.addAll(payout_.commands_if_afk);
+					payout.payout_amount += payout_.payout_amount;
+					payout.max_payout_per_day += payout_.max_payout_per_day;
+				}
+			}
+			return payouts_;
 		}else {
 			// Get a random payout
 			Random rnd = new Random();
@@ -297,34 +328,11 @@ public class Main extends JavaPlugin {
 	 *
 	 * @param player The player to pay.
 	 */
-	public void pay(Player player) {
+	public void pay(Player player, Payout payout, PayoutData payoutPlayerData) {
 		if (player == null) return;
 
-		PlayerData playerData = this.pluginData.getPlayerData(player);
-		
-		//REACHED MAX PAYOUT CHECK
-
-		List<Payout> applicablePayouts = this.getApplicablePayoutsForPlayer(player);
-		if (applicablePayouts.size() == 0) {
-			return;
-		}
-		
-		Payout payout = new Payout();
-		
-		if(this.getConfig().getBoolean("merge-payouts")) {
-			// Mering multiple payouts to one
-			for (Payout payout_ : applicablePayouts) {
-				payout.commands.addAll(payout_.commands);
-				payout.commands_if_afk.addAll(payout_.commands_if_afk);
-				payout.payout_amount += payout_.payout_amount;
-				payout.max_payout_per_day += payout_.max_payout_per_day;
-			}	
-		}else {
-			payout = applicablePayouts.get(applicablePayouts.size() - 1);
-		}
-
 		if (payout.max_payout_per_day != -1) {
-			if (playerData.getReceivedToday() >= payout.max_payout_per_day) { //Reached max payout
+			if (payoutPlayerData.getReceivedToday() >= payout.max_payout_per_day) { //Reached max payout
 				
 				if(finalconfig.getBoolean("display-payout-limit-reached-message-once") && payoutLimitReached.contains(player.getUniqueId())) {
 					return;
@@ -341,12 +349,21 @@ public class Main extends JavaPlugin {
 				return;
 			}
 		}
-		
+
 		if (!finalconfig.getBoolean("allow-multiple-accounts") && !player.hasPermission("tim.multipleaccountsbypass")) {
-			int same_address_count = (int) Bukkit.getOnlinePlayers().stream().filter(p -> p.getAddress().getHostString().equals(p.getAddress().getHostString())).count();
+			Set<? extends Player> sameAddressPlayers = Bukkit.getOnlinePlayers().stream().filter(p -> p.getAddress().getHostString().equals(p.getAddress().getHostString())).collect(Collectors.toSet());
+			int same_address_count = sameAddressPlayers.size();
+
 			if (same_address_count > finalconfig.getInt("max-multiple-accounts")) {
-				sendMessage(player, finalconfig.getString("message_multiple_ips"));
-				return;
+				Optional<? extends Player> firstPlayer = sameAddressPlayers.stream().min(Comparator.comparing(Player::getName));
+
+				if (firstPlayer.isPresent()) {
+					// one of the players with multiple ips still should receive a payout
+					if (firstPlayer.get() != player) {
+						sendMessage(player, finalconfig.getString("message_multiple_ips"));
+						return;
+					}
+				}
 			}
 		}
 		
@@ -400,6 +417,10 @@ public class Main extends JavaPlugin {
 		}
 
 		if (finalconfig.getBoolean("store-money-in-bank")) {
+			if(ATM.getBankBalance(player) >= finalconfig.getDouble("atm_balance_limit", Double.MAX_VALUE)) {
+				sendMessage(player, CC(finalconfig.getString("message_atm_limit_reached")));
+				return;
+			}
 			ATM.depositBank(player, payout_amt);
 		} else {
 			double before = 0;
@@ -434,7 +455,7 @@ public class Main extends JavaPlugin {
 		}
 		
 		//ADD PAYED MONEY
-		playerData.setReceivedToday(playerData.getReceivedToday() + payout_amt);
+		payoutPlayerData.setReceivedToday(payoutPlayerData.getReceivedToday() + payout_amt);
 
 		
 		lastLocation.put(player.getUniqueId(), player.getLocation());
@@ -559,4 +580,8 @@ public class Main extends JavaPlugin {
 	public PluginData getPluginData() {
 		return pluginData;
 	}
+
+	public List<Payout> getPayouts() {
+		return payouts;
+	}
 }

+ 6 - 0
Plugin/src/main/java/de/Linus122/TimeIsMoney/Payout.java

@@ -10,6 +10,7 @@ import java.util.List;
  * @since 1.9.6.1
  */
 class Payout {
+	int id;
 	/**
 	 * The payout amount.
 	 */
@@ -34,4 +35,9 @@ class Payout {
 	 * The list of commands to execute if this payout is earned while afk.
 	 */
 	List<String> commands_if_afk = new ArrayList<>();
+
+	/**
+	 * Interval seconds
+	 */
+	int interval = 0;
 }

+ 31 - 25
Plugin/src/main/java/de/Linus122/TimeIsMoney/data/MySQLPluginData.java

@@ -31,11 +31,13 @@ public class MySQLPluginData extends PluginData{
             this.plugin.getLogger().info(String.format("MySQL connected!", host, port, database));
 
             // table for the player data
-            String sqlCreate = "CREATE TABLE IF NOT EXISTS playerData ("
-                    + "   uuid                     VARCHAR(36) PRIMARY KEY,"
+            String sqlCreate = "CREATE TABLE IF NOT EXISTS payoutData ("
+                    + "   uuid                     VARCHAR(36),"
+                    + "   id                       INT,"
                     + "   receivedToday            DOUBLE,"
                     + "   secondsSinceLastPayout   INTEGER,"
-                    + "   lastPayoutDate           DATE)";
+                    + "   lastPayoutDate           DATE,"
+                    + "   primary key (uuid, id))";
 
             Statement statement = connection.createStatement();
             statement.execute(sqlCreate);
@@ -73,19 +75,22 @@ public class MySQLPluginData extends PluginData{
     }
 
     public void savePlayerData(UUID uuid, PlayerData playerData) {
-        try {
-            PreparedStatement preparedStatement = connection
-                    .prepareStatement("REPLACE INTO playerData (uuid, receivedToday, secondsSinceLastPayout, lastPayoutDate) VALUES (?, ?, ? ,?)");
-            preparedStatement.setString(1, uuid.toString());
-            preparedStatement.setDouble(2, playerData.getReceivedToday());
-            preparedStatement.setInt(3, playerData.getSecondsSinceLastPayout());
-            preparedStatement.setDate(4, new java.sql.Date(playerData.getLastPayoutDate().getTime()));
-
-            preparedStatement.execute();
-
-        } catch (SQLException e) {
-            e.printStackTrace();
-        }
+        playerData.getPayoutDataMap().forEach((payoutID, payoutData) -> {
+            try {
+                PreparedStatement preparedStatement = connection
+                        .prepareStatement("REPLACE INTO playerData (uuid, payout_id receivedToday, secondsSinceLastPayout, lastPayoutDate) VALUES (?, ?, ?, ? ,?)");
+                preparedStatement.setString(1, uuid.toString());
+                preparedStatement.setInt(1, payoutID);
+                preparedStatement.setDouble(2, payoutData.getReceivedToday());
+                preparedStatement.setInt(3, payoutData.getSecondsSinceLastPayout());
+                preparedStatement.setDate(4, new java.sql.Date(payoutData.getLastPayoutDate().getTime()));
+
+                preparedStatement.execute();
+
+            } catch (SQLException e) {
+                e.printStackTrace();
+            }
+        });
     }
 
     @Override
@@ -93,13 +98,12 @@ public class MySQLPluginData extends PluginData{
 
     }
 
-    private AbstractMap.Entry<UUID, PlayerData> readPlayerData(ResultSet result) throws SQLException {
-        UUID uuid = UUID.fromString(result.getString("uuid"));
+    private PayoutData readPayoutData(ResultSet result) throws SQLException {
         double receivedToday = result.getDouble("receivedToday");
         int secondsOnline = result.getInt("secondsSinceLastPayout");
         Date date = new Date(result.getDate("lastPayoutDate").getTime());
 
-        return new AbstractMap.SimpleEntry<>(uuid, new PlayerData(receivedToday, date, secondsOnline));
+        return new PayoutData(receivedToday, date, secondsOnline);
     }
 
     @Blocking
@@ -108,14 +112,16 @@ public class MySQLPluginData extends PluginData{
             return playerDataMap.get(player.getUniqueId());
         }
         try{
+            PlayerData playerData = new PlayerData();
             // get data from DB
             ResultSet result = connection.prepareStatement("SELECT * FROM playerData WHERE uuid='" + player.getUniqueId() + "'").executeQuery();
-            if(result.next()) {
-                AbstractMap.Entry<UUID, PlayerData> data = this.readPlayerData(result);
-                playerDataMap.put(data.getKey(), data.getValue());
-            } else {
-                // no entry, create new object
-                playerDataMap.put(player.getUniqueId(), new PlayerData(0, new java.util.Date(), 0));
+            while(result.next()) {
+                UUID uuid = UUID.fromString(result.getString("uuid"));
+                int payoutID = result.getInt("payout_id");
+
+                PayoutData payoutData = this.readPayoutData(result);
+                playerData.getPayoutDataMap().put(payoutID, payoutData);
+                playerDataMap.put(uuid, playerData);
             }
         } catch (SQLException e) {
             e.printStackTrace();

+ 52 - 0
Plugin/src/main/java/de/Linus122/TimeIsMoney/data/PayoutData.java

@@ -0,0 +1,52 @@
+package de.Linus122.TimeIsMoney.data;
+
+import org.apache.commons.lang.time.DateUtils;
+
+import java.util.Date;
+
+public class PayoutData {
+    private double receivedToday = 0d;
+    private Date lastPayoutDate;
+    private int secondsSinceLastPayout = 0;
+
+    public PayoutData(double receivedToday, Date lastPayoutDate, int secondsSinceLastPayout) {
+        this.receivedToday = receivedToday;
+        this.lastPayoutDate = lastPayoutDate;
+        this.secondsSinceLastPayout = secondsSinceLastPayout;
+    }
+
+    public double getReceivedToday() {
+        if(lastPayoutDate == null || !DateUtils.isSameDay(lastPayoutDate, new Date())) {
+            // new day, reset total money received
+            receivedToday = 0d;
+        }
+        return receivedToday;
+    }
+
+
+    /**
+     * Sets the total amount of money received for today and updates the {@link #lastPayoutDate} variable to now.
+     * @param receivedToday     Amount of money received today
+     * @since 1.9.7
+     */
+    public void setReceivedToday(double receivedToday) {
+        this.receivedToday = receivedToday;
+        lastPayoutDate = new Date();
+    }
+
+    public int getSecondsSinceLastPayout() {
+        return secondsSinceLastPayout;
+    }
+
+    public void setSecondsSinceLastPayout(int secondsSinceLastPayout) {
+        this.secondsSinceLastPayout = secondsSinceLastPayout;
+    }
+
+    public Date getLastPayoutDate() {
+        return lastPayoutDate;
+    }
+
+    public void setLastPayoutDate(Date lastPayoutDate) {
+        this.lastPayoutDate = lastPayoutDate;
+    }
+}

+ 16 - 43
Plugin/src/main/java/de/Linus122/TimeIsMoney/data/PlayerData.java

@@ -1,55 +1,28 @@
 package de.Linus122.TimeIsMoney.data;
 
-import org.apache.commons.lang.time.DateUtils;
-
 
 import java.util.Date;
+import java.util.HashMap;
+import java.util.LinkedHashMap;
 
-public class PlayerData {
-
-    private double receivedToday = 0d;
-    private Date lastPayoutDate;
-    private int secondsSinceLastPayout = 0;
-
-    public PlayerData(double receivedToday, Date lastPayoutDate, int secondsSinceLastPayout) {
-        this.receivedToday = receivedToday;
-        this.lastPayoutDate = lastPayoutDate;
-        this.secondsSinceLastPayout = secondsSinceLastPayout;
-    }
 
-    public double getReceivedToday() {
-        if(lastPayoutDate == null || !DateUtils.isSameDay(lastPayoutDate, new Date())) {
-            // new day, reset total money received
-            receivedToday = 0d;
+public class PlayerData {
+    // stores per payout data (key as defined in config.yml)
+    private final HashMap<Integer, PayoutData> payoutData = new LinkedHashMap<>();
+    
+    public PayoutData getPayoutData(int payout_id) {
+        if(payoutData.containsKey(payout_id)) {
+            return payoutData.get(payout_id);
         }
-        return receivedToday;
-    }
-
-
-    /**
-     * Sets the total amount of money received for today and updates the {@link #lastPayoutDate} variable to now.
-     * @param receivedToday     Amount of money received today
-     * @since 1.9.7
-     */
-    public void setReceivedToday(double receivedToday) {
-        this.receivedToday = receivedToday;
-        lastPayoutDate = new Date();
-    }
-
-    public int getSecondsSinceLastPayout() {
-        return secondsSinceLastPayout;
-    }
-
-    public void setSecondsSinceLastPayout(int secondsSinceLastPayout) {
-        this.secondsSinceLastPayout = secondsSinceLastPayout;
-    }
-
-    public Date getLastPayoutDate() {
-        return lastPayoutDate;
+        PayoutData payoutDataEntry = new PayoutData(0, new Date(), 0);
+        
+        payoutData.put(payout_id, payoutDataEntry);
+        
+        return payoutDataEntry;
     }
 
-    public void setLastPayoutDate(Date lastPayoutDate) {
-        this.lastPayoutDate = lastPayoutDate;
+    public HashMap<Integer, PayoutData> getPayoutDataMap() {
+        return this.payoutData;
     }
 
 }

+ 20 - 10
Plugin/src/main/java/de/Linus122/TimeIsMoney/data/YamlPluginData.java

@@ -33,12 +33,20 @@ public class YamlPluginData extends PluginData{
 
             for(String key : yamlConfiguration.getKeys(false)) {
                 UUID uuid = UUID.fromString(key);
-
-                double receivedToday = yamlConfiguration.getDouble(key + ".receivedToday");
-                int secondsOnline = yamlConfiguration.getInt(key + ".secondsSinceLastPayout");
-                Date date = yamlConfiguration.getObject(key + ".lastPayoutDate", Date.class);
-
-                PlayerData playerData = new PlayerData(receivedToday, date, secondsOnline);
+                PlayerData playerData = new PlayerData();
+                for(String payout_id : yamlConfiguration.getConfigurationSection(key).getKeys(false)) {
+                    try {
+                        Integer.parseInt(payout_id);
+                    } catch(NumberFormatException e) {
+                        continue;
+                    }
+                    double receivedToday = yamlConfiguration.getDouble(key + "." + payout_id + ".receivedToday");
+                    int secondsOnline = yamlConfiguration.getInt(key + "." + payout_id + ".secondsSinceLastPayout");
+                    Date date = yamlConfiguration.getObject(key + "." + payout_id + ".lastPayoutDate", Date.class);
+
+                    PayoutData payoutData = new PayoutData(receivedToday, date, secondsOnline);
+                    playerData.getPayoutDataMap().put(Integer.parseInt(payout_id), payoutData);
+                }
 
                 playerDataMap.put(uuid, playerData);
             }
@@ -58,9 +66,11 @@ public class YamlPluginData extends PluginData{
         }
         YamlConfiguration yamlConfiguration = new YamlConfiguration();
         super.playerDataMap.forEach((uuid, playerData) -> {
-            yamlConfiguration.set(uuid + ".receivedToday" , playerData.getReceivedToday());
-            yamlConfiguration.set(uuid + ".secondsSinceLastPayout" , playerData.getSecondsSinceLastPayout());
-            yamlConfiguration.set(uuid + ".lastPayoutDate" , playerData.getLastPayoutDate());
+            playerData.getPayoutDataMap().forEach((payout_id, payoutData) -> {
+                yamlConfiguration.set(uuid + "." + payout_id + ".receivedToday" , payoutData.getReceivedToday());
+                yamlConfiguration.set(uuid + "." + payout_id + ".secondsSinceLastPayout" , payoutData.getSecondsSinceLastPayout());
+                yamlConfiguration.set(uuid + "." + payout_id + ".lastPayoutDate" , payoutData.getLastPayoutDate());
+            });
 
         });
         try {
@@ -73,7 +83,7 @@ public class YamlPluginData extends PluginData{
     public PlayerData getPlayerData(@NotNull Player player) {
         if(!this.playerDataMap.containsKey(player.getUniqueId())) {
             // create a new PlayerData object
-            PlayerData playerData = new PlayerData(0, new Date(), 0);
+            PlayerData playerData = new PlayerData();
 
             this.playerDataMap.put(player.getUniqueId(), playerData);
             return playerData;

+ 32 - 3
Plugin/src/main/resources/config.yml

@@ -1,3 +1,5 @@
+# Config generated for TimeIsMoney ${project.version}
+
 configuration-version: 12
 
 debug-log: false
@@ -17,12 +19,16 @@ afk_use_essentials: true
 display-messages-in-chat: true
 display-messages-in-actionbar: true
 display-messages-in-actionbar-time: 10
-give_money_every_second: 600
+
+# defines whether the money should be stored in the bank (access it using /atm or an [ATM] sign, see more below)
 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 accounts per ip.
+# Define if multiple accounts should get paid with the same ip address.
+# You may disable this if your players are using multiple accounts per ip on purpose.
 # Can also set "tim.multipleaccountsbypass" permission to bypass this for individual players.
+# Note that one of the players that share the same ip address will still receive their payout.
 allow-multiple-accounts: true
+
 # Option to increase the maximal amount of players of the same IP getting a payout. Only applies when allow-multiple-accounts is set to false.
 max-multiple-accounts: 1
 
@@ -33,8 +39,19 @@ bank-account: ""
 choose-payout-by-chance: false
 
 # Enable this if you want to add up all lower payouts to the final payout (only relevant when using permissions)
+# doesn't merge payouts with individual intervals. Also doesn't merge when choose-only-one-payout is enabled.
 merge-payouts: false
 
+# Should it choose the last payout a player has permission for from the list? In the payout configuration below,
+# this would mean that VIPs (players with the tim.vip perm) would receive the last payout, while normal players the
+# first in the list. Does not apply for payouts with individual intervals
+choose-only-one-payout: true
+
+# Interval for all payouts; how long should a player need to play on the server to receive one of the payouts?
+# (previously called give_money_every_second)
+# It accepts seconds (s), minutes (m), and hours (h)
+global_interval: "10m"
+
 # You can add as many payouts you want. You only can choose between "permission"
 # and "chance", not both.
 payouts:
@@ -42,7 +59,7 @@ payouts:
     payout_amount: 50
     max_payout_per_day: 1000
     # chance: 10
-    permission:
+    permission: ""
   2:
     payout_amount: 100
     max_payout_per_day: 10000
@@ -53,8 +70,16 @@ payouts:
     # chance: 90
     # You can use any permission name you want. e.g. myserver.donor
     permission: tim.vip
+#  3:
+#    payout_amount: 1000
+#    max_payout_per_day: 10000
+#    # you can define a custom interval for individual payouts
+#    interval: 10h
+#    permission: ""
+
 
 # Translations
+# Both legacy color codes, &c etc. and hex RGB color codes, #&FF00FF, are supported.
 message: "&aYou earned &c%money% &afor 10 minutes online time!"
 message_payoutlimit_reached: "&cYou have reached the payout limit today. You earned 0$"
 message_afk: "&cYou havn't earned money because you were afk!"
@@ -71,6 +96,7 @@ message_atm_nopermbuild: "&cYou don't have permissions to build ATM's (tim.atm.p
 message_atm_created: "&2ATM created! (You can also write something in the Lines 2-4)"
 message_atm_withdrew: "&cTook &a%s &cfrom your account."
 message_atm_deposited: "&2Added &a%s &2to your account."
+message_atm_limit_reached: "&cATM limit reached! Please withdraw some money before you can receive money again."
 
 # Set this to true to send the payout limit reached message only one time once reached
 display-payout-limit-reached-message-once: false
@@ -94,6 +120,9 @@ atm_worth_gradation:
   - 1000.0
   - 10000.0
 
+# Sets a limit for the max amount of money allowed inside a player's ATM.
+#atm_balance_limit: 100000000.0;
+
 # You can seperate the ATM balances for different worlds by group them. Just set group-atms to true and write atm_groups as described below.
 # Note: Existing bank accounts will be removed when enabling this feature. 
 group-atms: false

+ 50 - 1
Tools/src/main/java/de/Linus122/TimeIsMoney/tools/Utils.java

@@ -4,6 +4,9 @@ import org.bukkit.Bukkit;
 import org.bukkit.ChatColor;
 import org.bukkit.entity.Player;
 
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
 /**
  * Utility class.
  *
@@ -29,7 +32,7 @@ public class Utils {
 		if(s == null) {
 			return "";
 		}
-		return ChatColor.translateAlternateColorCodes('&', s);
+		return ChatColor.translateAlternateColorCodes('&', parseRGB(s));
 	}
 	
 	public static String applyPlaceholders(Player player, String s) {
@@ -38,4 +41,50 @@ public class Utils {
         }
         return s;
 	}
+
+	private static final Pattern rgbColor = Pattern.compile("(?<!\\\\)(&#[a-fA-F0-9]{6})");
+
+	public static String parseRGB(String msg) {
+		Matcher matcher = rgbColor.matcher(msg);
+		while (matcher.find()) {
+			String color = msg.substring(matcher.start(), matcher.end());
+			String hex = color.replace("&", "");
+			try {
+				msg = msg.replace(color, String.valueOf(net.md_5.bungee.api.ChatColor.of(hex)));
+			} catch(NoSuchMethodError e) {
+				// older spigot versions
+
+				msg = msg.replace(color, "");
+			}
+			matcher = rgbColor.matcher(msg);
+		}
+		return msg;
+	}
+
+	/**
+	 * Return the seconds of a time format, valid suffixes are s (seconds), m (minutes) and h (hours)
+	 * @param format the formatted time, examples: 10m, 8h, 30s.
+	 * @return converted seconds, invalid return -1
+	 */
+	public static int parseTimeFormat(String format) {
+		Pattern pattern = Pattern.compile("^(?:(\\d+)s|(\\d+)m|(\\d+)h)$");
+		Matcher matcher = pattern.matcher(format);
+
+		if (matcher.matches()) {
+			String seconds = matcher.group(1);
+			String minutes = matcher.group(2);
+			String hours = matcher.group(3);
+
+			if (seconds != null) {
+				return Integer.parseInt(seconds);
+			}
+			if (minutes != null) {
+				return Integer.parseInt(minutes) * 60;
+			}
+			if (hours != null) {
+				return Integer.parseInt(hours) * 60 * 60;
+			}
+		}
+		return -1;
+	}
 }