Browse Source

change data structure for player data + mysql support

MasterCake 1 year ago
parent
commit
31897a5504

+ 9 - 3
Plugin/pom.xml

@@ -7,7 +7,7 @@
 	<parent>
 		<groupId>de.Linus122.TimeIsMoney</groupId>
 		<artifactId>parent</artifactId>
-		<version>1.9.6.22</version>
+		<version>1.9.7</version>
 		<relativePath>../pom.xml</relativePath>
 	</parent>
 
@@ -104,7 +104,13 @@
 		<dependency>
 			<groupId>de.Linus122.TimeIsMoney</groupId>
 			<artifactId>Tools</artifactId>
-			<version>1.9.6.22</version>
+			<version>1.9.7</version>
 		</dependency>
-	</dependencies>
+        <dependency>
+            <groupId>org.jetbrains</groupId>
+            <artifactId>annotations</artifactId>
+            <version>RELEASE</version>
+            <scope>compile</scope>
+        </dependency>
+    </dependencies>
 </project>

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

@@ -0,0 +1,44 @@
+package de.Linus122.TimeIsMoney;
+
+import de.Linus122.TimeIsMoney.data.MySQLPluginData;
+import org.bukkit.event.EventHandler;
+import org.bukkit.event.Listener;
+import org.bukkit.event.player.PlayerJoinEvent;
+import org.bukkit.event.player.PlayerQuitEvent;
+
+import java.sql.Date;
+import java.util.List;
+import java.util.logging.Level;
+
+public class Listeners implements Listener {
+    Main main;
+    public Listeners(Main main) {
+        this.main = main;
+    }
+
+    @EventHandler
+    public void onJoin(PlayerJoinEvent event){
+        main.getPluginData().getPlayerData(event.getPlayer());
+
+        if(main.getPluginData() instanceof MySQLPluginData) {
+            Date lastPlayed = new Date(event.getPlayer().getLastPlayed());
+
+            MySQLPluginData pluginData = (MySQLPluginData) main.getPluginData();
+            List<Integer> pending = pluginData.getPendingPayouts(lastPlayed, event.getPlayer());
+
+            pending.forEach(k -> {
+                System.out.println("paying user from pending payout: " + k);
+                main.pay(event.getPlayer());
+            });
+
+        }
+    }
+    @EventHandler
+    public void onQuit(PlayerQuitEvent event) {
+        if(main.getPluginData() instanceof MySQLPluginData) {
+            ((MySQLPluginData) main.getPluginData()).savePlayerData(event.getPlayer().getUniqueId(), main.getPluginData().getPlayerData(event.getPlayer()));
+
+            main.getLogger().log(Level.ALL, "Updated payout data for player " + event.getPlayer().getName() + " in MySQL");
+        }
+    }
+}

+ 98 - 86
Plugin/src/main/java/de/Linus122/TimeIsMoney/Main.java

@@ -10,19 +10,23 @@ import java.io.PrintWriter;
 import java.sql.Timestamp;
 import java.util.ArrayList;
 import java.util.Calendar;
-import java.util.Date;
 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.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 org.bukkit.Bukkit;
 import org.bukkit.Location;
 import org.bukkit.Server;
-import org.bukkit.command.ConsoleCommandSender;
 import org.bukkit.configuration.file.FileConfiguration;
 import org.bukkit.configuration.file.YamlConfiguration;
 import org.bukkit.entity.Player;
@@ -76,23 +80,21 @@ public class Main extends JavaPlugin {
 	 * Players that have already reached the payout limit for today
 	 */
 	private Set<UUID> payoutLimitReached = new HashSet<>();
-	/**
-	 * The time online in seconds of each player by UUID.
-	 */
-	private final HashMap<UUID, Integer> onlineSeconds = new HashMap<>();
 	/**
 	 * The last location of each player by UUID.
 	 */
 	private final HashMap<UUID, Location> lastLocation = new HashMap<>();
 	/**
-	 * The console logger.
+	 * The plugin logger.
 	 */
-	private final ConsoleCommandSender clogger = this.getServer().getConsoleSender();
+	private final Logger logger = this.getLogger();
 	
 	/**
 	 * Main task for keeping track of player's online time
 	 */
 	private BukkitTask playtimeWatcherTask;
+
+	private PluginData pluginData;
 	
 	/**
 	 * {@inheritDoc}
@@ -100,6 +102,7 @@ public class Main extends JavaPlugin {
 	@SuppressWarnings({"deprecation"})
 	@Override
 	public void onEnable() {
+
 		this.getCommand("timeismoney").setExecutor(new Cmd(this));
 		PL_VERSION = this.getDescription().getVersion();
 		
@@ -114,8 +117,8 @@ public class Main extends JavaPlugin {
 			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."));
+					logger.warning("[TimeIsMoney] &cYOU ARE USING AN OLD CONFIG-VERSION. The plugin CANT work with this.");
+					logger.warning("[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));
 				}
 			}
@@ -130,11 +133,16 @@ public class Main extends JavaPlugin {
 		}
 		
 		finalconfig = this.getConfig();
+
+		if (this.getConfig().getBoolean("debug-log")) {
+			// enable debug level
+			getLogger().setLevel(Level.ALL);
+		}
 		
 		disabledWorlds = getConfig().getStringList("disabled_in_worlds");
-		
+
 		if (getConfig().getBoolean("enable_atm")) new ATM(this);
-		
+
 		startPlaytimeWatcher();
 		
 		// Placeholder API
@@ -142,46 +150,49 @@ public class Main extends JavaPlugin {
         if(Bukkit.getPluginManager().getPlugin("PlaceholderAPI") != null) {
         	new NamePlaceholder(this).register();
         }
-		
-		Bukkit.getScheduler().scheduleSyncRepeatingTask(this, () -> {
-			if (PluginData.getLastRefreshDay() != new Date().getDay() && PluginData.getPayedMoney().size() > 0) { //Next day, clear payouts!
-				log("Cleared all payouts for last day");
-				PluginData.getPayedMoney().clear();
-				PluginData.setLastRefreshDay(new Date().getDay());
-			}
-		}, 20L * 60, 20L * 60 * 15);
+
 		setupEconomy();
-		
-		PluginData.loadData();
+
+		if (getConfig().contains("mysql")) {
+			pluginData = new MySQLPluginData(this, getConfig().getString("mysql.host"),
+					getConfig().getInt("mysql.port"), getConfig().getString("mysql.user"),
+					getConfig().getString("mysql.database"), getConfig().getString("mysql.password"));
+
+			pluginData.loadData();
+		} else {
+			pluginData = new YamlPluginData(this);
+			pluginData.loadData();
+		}
+
+		this.getServer().getPluginManager().registerEvents(new Listeners(this), this);
 		
 		loadPayouts();
 		
 		if (Bukkit.getPluginManager().isPluginEnabled("Essentials") && this.getConfig().getBoolean("afk_use_essentials")) {
-			clogger.sendMessage("Time is Money: Essentials found. Hook in it -> Will use Essentials's AFK feature if afk is enabled.");
+			logger.info("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."));
+		logger.info(CC("&aTime is Money &2v" + PL_VERSION + " &astarted."));
 	}
 	
 	public void startPlaytimeWatcher() {
 		playtimeWatcherTask = Bukkit.getScheduler().runTaskTimer(this, () -> {
-			try {
-				for (Player p : Bukkit.getOnlinePlayers()) {
-					if (disabledWorlds.contains(p.getWorld().getName())) continue;
-					
-					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()) >=  getConfig().getInt("give_money_every_second")) {
-						pay(p);
-						onlineSeconds.remove(p.getUniqueId());
+			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);
+
+					if(this.pluginData instanceof MySQLPluginData) {
+						// let other servers know of this payout
+						((MySQLPluginData) this.pluginData).createPendingPayout(player);
 					}
+					playerData.setSecondsSinceLastPayout(0);
 				}
-			} catch (NullPointerException ignored) {
 			}
 		}, 20L, 20L);
 	}
@@ -192,11 +203,13 @@ public class Main extends JavaPlugin {
 	@Override
 	public void onDisable() {
 		playtimeWatcherTask.cancel();
-		PluginData.saveData();
+
+		this.pluginData.saveData();
+
 	}
 
 	/**
-	 * Reloads TimeIsMoney.
+	 * Reloads TimeIsMoney
 	 */
 	void reload() {
 		this.reloadConfig();
@@ -230,9 +243,9 @@ public class Main extends JavaPlugin {
 				}
 				payouts.add(payout);
 			}
-			clogger.sendMessage(CC("[TimeIsMoney] &aLoaded " + finalconfig.getConfigurationSection("payouts").getKeys(false).size() + " Payouts!"));
+			logger.info("[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?)"));
+			logger.info("[TimeIsMoney] &aFailed to load Payouts! (May made a mistake in config.yml?)");
 		}
 	}
 	
@@ -267,27 +280,25 @@ public class Main extends JavaPlugin {
 			for (Payout payout : payouts) {
 				for (int i = 0; i < payout.chance; i++) list.add(payout);
 			}
-			List<Payout> returnlist = new ArrayList<>();
-			returnlist.add(list.get(rnd.nextInt(list.size() - 1)));
-			return returnlist;
+			List<Payout> payoutList = new ArrayList<>();
+			payoutList.add(list.get(rnd.nextInt(list.size() - 1)));
+			return payoutList;
 		}
 	}
 	
 	/**
 	 * Pays the specified player.
 	 *
-	 * @param p The player to pay.
+	 * @param player The player to pay.
 	 */
-	private void pay(Player p) {
-		if (p == null) return;
+	public void pay(Player player) {
+		if (player == null) return;
+
+		PlayerData playerData = this.pluginData.getPlayerData(player);
 		
 		//REACHED MAX PAYOUT CHECK
-		double payed = 0;
-		if (PluginData.getPayedMoney().containsKey(p.getName())) {
-			payed = PluginData.getPayedMoney().get(p.getName());
-		}
-		
-		List<Payout> applicablePayouts = this.getApplicablePayoutsForPlayer(p);
+
+		List<Payout> applicablePayouts = this.getApplicablePayoutsForPlayer(player);
 		if (applicablePayouts.size() == 0) {
 			return;
 		}
@@ -307,28 +318,28 @@ public class Main extends JavaPlugin {
 		}
 
 		if (payout.max_payout_per_day != -1) {
-			if (payed >= payout.max_payout_per_day) { //Reached max payout
+			if (playerData.getReceivedToday() >= payout.max_payout_per_day) { //Reached max payout
 				
-				if(finalconfig.getBoolean("display-payout-limit-reached-message-once") && payoutLimitReached.contains(p.getUniqueId())) {
+				if(finalconfig.getBoolean("display-payout-limit-reached-message-once") && payoutLimitReached.contains(player.getUniqueId())) {
 					return;
 				}
 				
 				if (finalconfig.getBoolean("display-messages-in-chat")) {
-					sendMessage(p, finalconfig.getString("message_payoutlimit_reached"));
+					sendMessage(player, finalconfig.getString("message_payoutlimit_reached"));
 				}
 				if (finalconfig.getBoolean("display-messages-in-actionbar")) {
-					sendActionbar(p, finalconfig.getString("message_payoutlimit_reached_actionbar"));
+					sendActionbar(player, finalconfig.getString("message_payoutlimit_reached_actionbar"));
 				}
 				if(finalconfig.getBoolean("display-payout-limit-reached-message-once"))
-					payoutLimitReached.add(p.getUniqueId());
+					payoutLimitReached.add(player.getUniqueId());
 				return;
 			}
 		}
 		
-		if (!finalconfig.getBoolean("allow-multiple-accounts") && !p.hasPermission("tim.multipleaccountsbypass")) {
-			int same_address_count = (int) Bukkit.getOnlinePlayers().stream().filter(player -> player.getAddress().getHostString().equals(p.getAddress().getHostString())).count();
+		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();
 			if (same_address_count > finalconfig.getInt("max-multiple-accounts")) {
-				sendMessage(p, finalconfig.getString("message_multiple_ips"));
+				sendMessage(player, finalconfig.getString("message_multiple_ips"));
 				return;
 			}
 		}
@@ -336,17 +347,17 @@ public class Main extends JavaPlugin {
 		//AFK CHECK
 		boolean afk = false;
 		double afkPercent = 0.0D;
-		if (!p.hasPermission("tim.afkbypass")) {
+		if (!player.hasPermission("tim.afkbypass")) {
 			//ESENTIALS_AFK_FEATURE
 			if (Bukkit.getServer().getPluginManager().isPluginEnabled("Essentials") && this.getConfig().getBoolean("afk_use_essentials")) {
 				Essentials essentials = (com.earth2me.essentials.Essentials) Bukkit.getServer().getPluginManager().getPlugin("Essentials");
-				if (essentials.getUser(p).isAfk()) {
+				if (essentials.getUser(player).isAfk()) {
 					afk = true;
 				}
 			} 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()) {
+				if (lastLocation.containsKey(player.getUniqueId())) { //AntiAFK
+					if (lastLocation.get(player.getUniqueId()).getX() == player.getLocation().getX() && lastLocation.get(player.getUniqueId()).getY() == player.getLocation().getY() && lastLocation.get(player.getUniqueId()).getZ() == player.getLocation().getZ() || lastLocation.get(player.getUniqueId()).getYaw() == player.getLocation().getYaw()) {
 						afk = true;
 					}
 				}
@@ -354,10 +365,10 @@ public class Main extends JavaPlugin {
 			if (afk) {
 				if (!finalconfig.getBoolean("afk_payout")) { // Payout is disabled
 					if (finalconfig.getBoolean("display-messages-in-chat")) {
-						sendMessage(p, finalconfig.getString("message_afk"));
+						sendMessage(player, finalconfig.getString("message_afk"));
 					}
 					if (finalconfig.getBoolean("display-messages-in-actionbar")) {
-						sendActionbar(p, finalconfig.getString("message_afk_actionbar"));
+						sendActionbar(player, finalconfig.getString("message_afk_actionbar"));
 					}
 					return;
 				} else { // Payout is enabled
@@ -383,51 +394,48 @@ public class Main extends JavaPlugin {
 		}
 		
 		if (finalconfig.getBoolean("store-money-in-bank")) {
-			ATM.depositBank(p, payout_amt);
+			ATM.depositBank(player, payout_amt);
 		} else {
 			double before = 0;
-			if (economy.hasAccount(p)) {
-				before = economy.getBalance(p);
+			if (economy.hasAccount(player)) {
+				before = economy.getBalance(player);
 			}
 			
-			economy.depositPlayer(p, payout_amt);
-			log(p.getName() + ": Deposited: " + payout_amt + " Balance-before: " + before + " Balance-now: " + economy.getBalance(p));
+			economy.depositPlayer(player, payout_amt);
+			log(player.getName() + ": Deposited: " + payout_amt + " Balance-before: " + before + " Balance-now: " + economy.getBalance(player));
 		}
 		
 		if (!afk) {
 			if (finalconfig.getBoolean("display-messages-in-chat")) {
-				sendMessage(p, CC(finalconfig.getString("message")).replace("%money%", economy.format(payout_amt)));
+				sendMessage(player, CC(finalconfig.getString("message")).replace("%money%", economy.format(payout_amt)));
 			}
 			if (finalconfig.getBoolean("display-messages-in-actionbar")) {
-				sendActionbar(p, CC(finalconfig.getString("message_actionbar")).replace("%money%", economy.format(payout_amt)));
+				sendActionbar(player, CC(finalconfig.getString("message_actionbar")).replace("%money%", economy.format(payout_amt)));
 			}
 			for (String cmd : payout.commands) {
-				dispatchCommandSync(applyPlaceholders(p, cmd.replace("/", "").replaceAll("%player%", p.getName())));
+				dispatchCommandSync(applyPlaceholders(player, cmd.replace("/", "").replaceAll("%player%", player.getName())));
 			}
 		} else {
 			if (finalconfig.getBoolean("display-messages-in-chat") && finalconfig.isSet("message_afk_payout")) {
-				sendMessage(p, CC(finalconfig.getString("message_afk_payout").replace("%money%", economy.format(payout_amt)).replace("%percent%", "" + afkPercent)));
+				sendMessage(player, CC(finalconfig.getString("message_afk_payout").replace("%money%", economy.format(payout_amt)).replace("%percent%", "" + afkPercent)));
 			}
 			if (finalconfig.getBoolean("display-messages-in-actionbar") && finalconfig.isSet("message_afk_actionbar_payout")) {
-				sendActionbar(p, CC(finalconfig.getString("message_afk_actionbar_payout").replace("%money%", economy.format(payout_amt)).replace("%percent%", "" + afkPercent)));
+				sendActionbar(player, CC(finalconfig.getString("message_afk_actionbar_payout").replace("%money%", economy.format(payout_amt)).replace("%percent%", "" + afkPercent)));
 			}
 			for (String cmd : payout.commands_if_afk) {
-				dispatchCommandSync(applyPlaceholders(p, cmd.replace("/", "").replaceAll("%player%", p.getName())));
+				dispatchCommandSync(applyPlaceholders(player, cmd.replace("/", "").replaceAll("%player%", player.getName())));
 			}
 		}
 		
 		//ADD PAYED MONEY
-		if (PluginData.getPayedMoney().containsKey(p.getName())) {
-			PluginData.getPayedMoney().put(p.getName(), PluginData.getPayedMoney().get(p.getName()) + payout_amt);
-		} else {
-			PluginData.getPayedMoney().put(p.getName(), payout_amt);
-		}
+		playerData.setReceivedToday(playerData.getReceivedToday() + payout_amt);
+
 		
-		lastLocation.put(p.getUniqueId(), p.getLocation());
+		lastLocation.put(player.getUniqueId(), player.getLocation());
 		
 		// clear payout limit reached message
 		if(finalconfig.getBoolean("display-payout-limit-reached-message-once"))
-			payoutLimitReached.remove(p.getUniqueId());
+			payoutLimitReached.remove(player.getUniqueId());
 	}
 	
 	/**
@@ -541,4 +549,8 @@ public class Main extends JavaPlugin {
 			e.printStackTrace();
 		}
 	}
+
+	public PluginData getPluginData() {
+		return pluginData;
+	}
 }

+ 0 - 90
Plugin/src/main/java/de/Linus122/TimeIsMoney/PluginData.java

@@ -1,90 +0,0 @@
-package de.Linus122.TimeIsMoney;
-
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileNotFoundException;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.ObjectInputStream;
-import java.io.ObjectOutputStream;
-import java.util.HashMap;
-
-public class PluginData {
-	/**
-	 * The payouts for the day.
-	 */
-	private static HashMap<String, Double> payedMoney = new HashMap<>();
-	/**
-	 * Day since last refresh
-	 */
-	private static int lastRefreshDay = 0;
-	
-
-	private final static String filePath = "plugins/TimeIsMoney/data/";
-	private final static File dataFile = new File(filePath + "payed_today.data");
-	
-	/**
-	 * Loads data from file if {@link #dataFile} exists.
-	 */
-	public static void loadData() {
-		if(!dataFile.exists()) return;
-		try {
-			FileInputStream fis = new FileInputStream(dataFile);
-			ObjectInputStream ois = new ObjectInputStream(fis);
-			Object payedMoneyObj = ois.readObject();
-			payedMoney = (HashMap<String, Double>) ((HashMap<String, Double>) payedMoneyObj).clone();
-			lastRefreshDay = ois.readInt();
-			ois.close();
-		} catch (IOException e1) {
-			// TODO Auto-generated catch block
-			e1.printStackTrace();
-		} catch (ClassNotFoundException e) {
-			// TODO Auto-generated catch block
-			e.printStackTrace();
-		}
-
-	}
-	
-	/**
-	 * Saves the data on disc to file {@link #dataFile}
-	 */
-	public static void saveData() {
-		(new File(filePath)).mkdirs();
-		
-		try {
-			FileOutputStream fos = new FileOutputStream(dataFile);
-			ObjectOutputStream oos = new ObjectOutputStream(fos);
-			oos.writeObject(payedMoney);
-			oos.writeInt(lastRefreshDay);
-			oos.close();
-		} catch (FileNotFoundException e) {
-			// TODO Auto-generated catch block
-			e.printStackTrace();
-		} catch (IOException e) {
-			// TODO Auto-generated catch block
-			e.printStackTrace();
-		}
-	}
-	
-	/**
-	 * @return the lastRefreshDay
-	 */
-	public static int getLastRefreshDay() {
-		return lastRefreshDay;
-	}
-	
-	/**
-	 * @param lastRefreshDay the lastRefreshDay to set
-	 */
-	public static void setLastRefreshDay(int lastRefreshDay) {
-		PluginData.lastRefreshDay = lastRefreshDay;
-	}
-	
-	/**
-	 * @return the payedMoney
-	 */
-	public static HashMap<String, Double> getPayedMoney() {
-		return payedMoney;
-	}
-
-}

+ 169 - 0
Plugin/src/main/java/de/Linus122/TimeIsMoney/data/MySQLPluginData.java

@@ -0,0 +1,169 @@
+package de.Linus122.TimeIsMoney.data;
+
+import de.Linus122.TimeIsMoney.Main;
+import org.bukkit.Bukkit;
+import org.bukkit.entity.Player;
+import org.jetbrains.annotations.Blocking;
+import org.jetbrains.annotations.NotNull;
+
+import java.sql.*;
+import java.sql.Date;
+import java.util.*;
+
+public class MySQLPluginData extends PluginData{
+
+    private Connection connection;
+
+    public class PendingPayout {
+
+    }
+
+    /**
+     * Loads data from file if {@link #dataFile} exists.
+     */
+    public MySQLPluginData(Main main, String host, int port, String username, String database, String password) {
+        super(main);
+
+        try {
+            this.plugin.getLogger().info(String.format("Trying to connect to jdbc:mysql://%s:%d/%s", host, port, database));
+            connection = DriverManager.getConnection(String.format("jdbc:mysql://%s:%d/%s", host, port, database), username, password);
+
+            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,"
+                    + "   receivedToday            DOUBLE,"
+                    + "   secondsSinceLastPayout   INTEGER,"
+                    + "   lastPayoutDate           DATE)";
+
+            Statement statement = connection.createStatement();
+            statement.execute(sqlCreate);
+
+            // table for tracking pending payouts for other servers
+            String sqlCreate2 = "CREATE TABLE IF NOT EXISTS pendingPayouts ("
+                    + "   id              INT NOT NULL AUTO_INCREMENT,"
+                    + "   uuid            VARCHAR(36),"
+                    + "   date            DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,"
+                    + "   PRIMARY KEY (id))";
+
+            Statement statement2 = connection.createStatement();
+            statement2.execute(sqlCreate2);
+
+            // table for paid payouts
+            String sqlCreate3 = "CREATE TABLE IF NOT EXISTS paidPayouts ("
+                    + "   payout_id        INT NOT NULL,"
+                    + "   server           VARCHAR(24),"
+                    + "   FOREIGN KEY (payout_id) REFERENCES pendingPayouts(id) ON DELETE CASCADE)";
+
+            Statement statement3 = connection.createStatement();
+            statement3.execute(sqlCreate3);
+
+        } catch (SQLException e) {
+            e.printStackTrace();
+        }
+
+    }
+
+    /**
+     * Saves the data on disc to file {@link #dataFile}
+     */
+    public void saveData() {
+        playerDataMap.forEach(this::savePlayerData);
+    }
+
+    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();
+        }
+    }
+
+    @Override
+    public void loadData() {
+
+    }
+
+    private AbstractMap.Entry<UUID, PlayerData> readPlayerData(ResultSet result) throws SQLException {
+        UUID uuid = UUID.fromString(result.getString("uuid"));
+        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));
+    }
+
+    @Blocking
+    public PlayerData getPlayerData(@NotNull Player player) {
+        if(playerDataMap.containsKey(player.getUniqueId())) {
+            return playerDataMap.get(player.getUniqueId());
+        }
+        try{
+            // 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));
+            }
+        } catch (SQLException e) {
+            e.printStackTrace();
+        }
+
+        return playerDataMap.get(player.getUniqueId());
+    }
+
+    public void createPendingPayout(Player player) {
+        try {
+            PreparedStatement preparedStatement = connection
+                    .prepareStatement("INSERT INTO pendingPayouts (uuid) VALUES (?)");
+            preparedStatement.setString(1, player.getUniqueId().toString());
+
+            preparedStatement.execute();
+
+
+        } catch (SQLException e) {
+            e.printStackTrace();
+        }
+    }
+
+    private String getServerName() {
+        return Bukkit.getServer().getWorlds().get(0).getUID().toString().substring(0,16) + ":" + Bukkit.getServer().getPort();
+    }
+
+    public List<Integer> getPendingPayouts(Date dateFrom, Player player) {
+        List<Integer> pendingPayouts = new ArrayList<>();
+
+        try{
+            //ResultSet result = connection.prepareStatement("SELECT * FROM `pendingPayouts` WHERE NOT EXISTS (SELECT * FROM paidPayouts WHERE server='" + this.getServerName() + "' AND pendingPayouts.id = paidPayouts.payout_id) AND uuid='" + player.getUniqueId() + "';").executeQuery();
+            PreparedStatement preparedStatement = connection.prepareStatement("SELECT * FROM `pendingPayouts` WHERE uuid=? AND date>?");
+            preparedStatement.setString(1, player.getUniqueId().toString());
+            preparedStatement.setTimestamp(2, new Timestamp(dateFrom.getTime()));
+
+            ResultSet result = preparedStatement.executeQuery();
+
+            while(result.next()) {
+                int id = result.getInt("id");
+
+                pendingPayouts.add(id);
+            }
+
+        } catch (SQLException e) {
+            e.printStackTrace();
+        }
+
+        return pendingPayouts;
+    }
+
+}

+ 55 - 0
Plugin/src/main/java/de/Linus122/TimeIsMoney/data/PlayerData.java

@@ -0,0 +1,55 @@
+package de.Linus122.TimeIsMoney.data;
+
+import org.apache.commons.lang.time.DateUtils;
+
+
+import java.util.Date;
+
+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;
+        }
+        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;
+    }
+
+}

+ 31 - 0
Plugin/src/main/java/de/Linus122/TimeIsMoney/data/PluginData.java

@@ -0,0 +1,31 @@
+package de.Linus122.TimeIsMoney.data;
+
+import de.Linus122.TimeIsMoney.Main;
+import org.bukkit.configuration.file.YamlConfiguration;
+import org.bukkit.entity.Player;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.io.File;
+
+import java.io.IOException;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.UUID;
+
+public abstract class PluginData {
+	/**
+	 * Data for each player
+	 */
+	protected HashMap<UUID, PlayerData> playerDataMap = new HashMap<>();
+	protected Main plugin;
+
+	public PluginData(Main main) {
+		this.plugin = main;
+	}
+
+	public abstract PlayerData getPlayerData(Player player);
+	public abstract void saveData();
+	public abstract void loadData();
+
+}

+ 83 - 0
Plugin/src/main/java/de/Linus122/TimeIsMoney/data/YamlPluginData.java

@@ -0,0 +1,83 @@
+package de.Linus122.TimeIsMoney.data;
+
+import de.Linus122.TimeIsMoney.Main;
+import org.bukkit.configuration.file.YamlConfiguration;
+import org.bukkit.entity.Player;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.UUID;
+
+public class YamlPluginData extends PluginData{
+    final String filePath = "plugins/TimeIsMoney/data/";
+    final File dataFile = new File(filePath + "data.yml");
+
+    public YamlPluginData(Main main) {
+        super(main);
+    }
+
+    /**
+     * Loads data from file if {@link #dataFile} exists.
+     */
+
+    @SuppressWarnings("unchecked")
+    public void loadData() {
+
+        if(dataFile.exists()) {
+            YamlConfiguration yamlConfiguration = YamlConfiguration.loadConfiguration(dataFile);
+            playerDataMap = new HashMap<>();
+
+            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);
+
+                playerDataMap.put(uuid, playerData);
+            }
+        }
+
+        if(playerDataMap == null)
+            playerDataMap = new HashMap<>();
+
+    }
+
+    /**
+     * Saves the data on disc to file {@link #dataFile}
+     */
+    public void saveData() {
+        if(!dataFile.exists()) {
+            dataFile.getParentFile().mkdirs();
+        }
+        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());
+
+        });
+        try {
+            yamlConfiguration.save(dataFile);
+        } catch (IOException exception) {
+            exception.printStackTrace();
+        }
+    }
+
+    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);
+
+            this.playerDataMap.put(player.getUniqueId(), playerData);
+            return playerData;
+        }
+        return playerDataMap.get(player.getUniqueId());
+    }
+}

+ 9 - 0
Plugin/src/main/resources/config.yml

@@ -75,6 +75,15 @@ message_atm_deposited: "&2Added &a%s &2to your account."
 # Set this to true to send the payout limit reached message only one time once reached
 display-payout-limit-reached-message-once: false
 
+# uncomment to use database instead of data file
+
+#mysql:
+#  host: "127.0.0.1"
+#  user: "user"
+#  password: "pass"
+#  port: 3306
+#  database: "tim_db"
+
 # ATM -> Place down a sign with [atm] on the first line to use it!
 enable_atm: true
 # The label that is drawn on the sign upon placement (keep in mind that ATM signs are tracked using this tag, so choose something "unique")

+ 1 - 1
Tools/pom.xml

@@ -7,7 +7,7 @@
     <parent>
         <groupId>de.Linus122.TimeIsMoney</groupId>
         <artifactId>parent</artifactId>
-        <version>1.9.6.22</version>
+        <version>1.9.7</version>
         <relativePath>../pom.xml</relativePath>
     </parent>
 

+ 1 - 1
pom.xml

@@ -6,7 +6,7 @@
 
     <groupId>de.Linus122.TimeIsMoney</groupId>
     <artifactId>parent</artifactId>
-    <version>1.9.6.22</version>
+    <version>1.9.7</version>
     <name>TimeIsMoney</name>
     <url>https://www.spigotmc.org/resources/time-is-money.12409/</url>
     <packaging>pom</packaging>