From 7da6a99dd79d16df8d34565f2bf937c478d5ffa7 Mon Sep 17 00:00:00 2001 From: Rsl1122 Date: Sun, 29 Jan 2017 22:43:57 +0200 Subject: [PATCH] Added Database Managment command, Small improvements [2.3.0-SNAPSHOT] - Added database managment command: - Copy & Overwrite data from one database to another (moving from one db to another) - Copy & Combine data from one database to another (in case both were used) - Remove player's data from the active database - Clear a database - Check what database is in use, also on info command. Also now told on enable. - Hotswap to another database, reloads plugin with new database set in config - Reload command now restarts the plugin, DOES NOT LOAD NEWLY PLACED JAR - Fixed typo in search command arguments - Import command for OnTime in PlanLite - Recent Players - Lists of players - Location heatmap - Investigate Bungee support possibility - Make Analysis.java readable - Move the data combine methods from MiscUtils to new class - Database Cleaning - Change database initiation message to include "connection" - If you feel like it make better html --- .../main/java/com/djrapitops/plan/Phrase.java | 15 +- .../main/java/com/djrapitops/plan/Plan.java | 15 +- .../djrapitops/plan/command/PlanCommand.java | 2 + .../plan/command/commands/InfoCommand.java | 1 + .../plan/command/commands/ManageCommand.java | 133 ++++++++++++++ .../plan/command/commands/ReloadCommand.java | 5 +- .../plan/command/commands/SearchCommand.java | 2 +- .../commands/manage/ManageClearCommand.java | 86 +++++++++ .../commands/manage/ManageCombineCommand.java | 172 ++++++++++++++++++ .../commands/manage/ManageHelpCommand.java | 66 +++++++ .../commands/manage/ManageHotswapCommand.java | 76 ++++++++ .../commands/manage/ManageMoveCommand.java | 149 +++++++++++++++ .../commands/manage/ManageRemoveCommand.java | 98 ++++++++++ .../commands/manage/ManageStatusCommand.java | 61 +++++++ .../djrapitops/plan/database/Database.java | 5 + .../plan/database/databases/SQLDB.java | 54 ++++-- .../djrapitops/plan/utilities/MiscUtils.java | 110 +++++++++++ 17 files changed, 1027 insertions(+), 23 deletions(-) create mode 100644 Plan/src/main/java/com/djrapitops/plan/command/commands/ManageCommand.java create mode 100644 Plan/src/main/java/com/djrapitops/plan/command/commands/manage/ManageClearCommand.java create mode 100644 Plan/src/main/java/com/djrapitops/plan/command/commands/manage/ManageCombineCommand.java create mode 100644 Plan/src/main/java/com/djrapitops/plan/command/commands/manage/ManageHelpCommand.java create mode 100644 Plan/src/main/java/com/djrapitops/plan/command/commands/manage/ManageHotswapCommand.java create mode 100644 Plan/src/main/java/com/djrapitops/plan/command/commands/manage/ManageMoveCommand.java create mode 100644 Plan/src/main/java/com/djrapitops/plan/command/commands/manage/ManageRemoveCommand.java create mode 100644 Plan/src/main/java/com/djrapitops/plan/command/commands/manage/ManageStatusCommand.java diff --git a/Plan/src/main/java/com/djrapitops/plan/Phrase.java b/Plan/src/main/java/com/djrapitops/plan/Phrase.java index 01a4d77f9..b87dafebd 100644 --- a/Plan/src/main/java/com/djrapitops/plan/Phrase.java +++ b/Plan/src/main/java/com/djrapitops/plan/Phrase.java @@ -12,9 +12,9 @@ public enum Phrase { DATABASE_TYPE_DOES_NOT_EXIST("That database type doesn't exist."), DATABASE_FAILURE_DISABLE("Database initialization has failed, disabling Plan."), PLANLITE_REG_HOOK("Registered additional hook, passed on to PlanLite: "), - USERNAME_NOT_VALID(ChatColor.RED + "This Player doesn't exist."), - USERNAME_NOT_SEEN(ChatColor.RED + "This Player has not played on this server."), - USERNAME_NOT_KNOWN(ChatColor.RED + "Player not found from the database."), + USERNAME_NOT_VALID(ChatColor.RED + "[Plan] This Player doesn't exist."), + USERNAME_NOT_SEEN(ChatColor.RED + "[Plan] This Player has not played on this server."), + USERNAME_NOT_KNOWN(ChatColor.RED + "[Plan] Player not found from the database."), COLOR_MAIN(ChatColor.getByChar(getPlugin(Plan.class).getConfig().getString("Customization.Colors.Commands.Main").charAt(1))), COLOR_SEC(ChatColor.getByChar(getPlugin(Plan.class).getConfig().getString("Customization.Colors.Commands.Secondary").charAt(1))), COLOR_TER(ChatColor.getByChar(getPlugin(Plan.class).getConfig().getString("Customization.Colors.Commands.Highlight").charAt(1))), @@ -34,9 +34,16 @@ public enum Phrase { ERROR_NO_DATA_VIEW(ChatColor.YELLOW + "Webserver disabled but Alternative IP/PlanLite not used, no way to view data!"), ERROR_WEBSERVER_OFF_ANALYSIS(ChatColor.YELLOW + "[Plan] This command can be only used if the webserver is running on this server."), ERROR_WEBSERVER_OFF_INSPECT(ChatColor.YELLOW + "[Plan] This command can be only used if webserver/planlite is enabled on this server."), - + MANAGE_ERROR_INCORRECT_DB(ChatColor.RED+"[Plan] Incorrect database! (sqlite/mysql accepted): "), + MANAGE_ERROR_SAME_DB(ChatColor.RED+"[Plan] Can't move to the same database!"), + MANAGE_DATABASE_FAILURE(ChatColor.RED+"[Plan] One of the databases was not initialized properly."), + MANAGE_DB_CONFIG_REMINDER(ChatColor.YELLOW+"[Plan] Remember to swap to the new database and reload plugin"), + MANAGE_ERROR_NO_PLAYERS(ChatColor.RED+"[Plan] Database has no player data!"), + MANAGE_MOVE_SUCCESS(ChatColor.GREEN+"[Plan] All data moved successfully!"), + MANAGE_CLEAR_SUCCESS(ChatColor.GREEN+"[Plan] All data cleared successfully!"), COMMAND_SENDER_NOT_PLAYER(ChatColor.RED + "[Plan] This command can be only used as a player."), COMMAND_REQUIRES_ARGUMENTS(ChatColor.RED + "[Plan] Command requires arguments."), + COMMAND_ADD_CONFIRMATION_ARGUMENT(ChatColor.RED + "[Plan] Add -a to confirm execution!"), COMMAND_REQUIRES_ARGUMENTS_ONE(ChatColor.RED + "[Plan] Command requires one argument."), COMMAND_NO_PERMISSION(ChatColor.RED + "[Plan] You do not have the required permmission."); diff --git a/Plan/src/main/java/com/djrapitops/plan/Plan.java b/Plan/src/main/java/com/djrapitops/plan/Plan.java index ed940a2ce..63732a52e 100644 --- a/Plan/src/main/java/com/djrapitops/plan/Plan.java +++ b/Plan/src/main/java/com/djrapitops/plan/Plan.java @@ -24,8 +24,11 @@ import org.bukkit.scheduler.BukkitRunnable; Placeholder API Immutable InspectCache ? Recent players 25% - Optimize db with batch processing (commanduse, ips, nicks) -Manage command + Manage command +- Import data from sqlite > mysql and other way around +- Move data from __ + - Remove player's data +- Import data with PlanLite Database cleaning PlanLite Top 20 richest 25% PlanLite Top 20 most votes 25% @@ -72,7 +75,7 @@ public class Plan extends JavaPlugin { log(MiscUtils.checkVersion()); log("Database init.."); if (initDatabase()) { - log("Database initiated."); + log(db.getConfigName()+" Database initiated."); } else { logError(Phrase.DATABASE_FAILURE_DISABLE.toString()); getServer().getPluginManager().disablePlugin(this); @@ -181,7 +184,7 @@ public class Plan extends JavaPlugin { } } - private boolean initDatabase() { + public boolean initDatabase() { String type = getConfig().getString("database.type"); db = null; @@ -251,4 +254,8 @@ public class Plan extends JavaPlugin { public WebSocketServer getUiServer() { return uiServer; } + + public HashSet getDatabases() { + return databases; + } } diff --git a/Plan/src/main/java/com/djrapitops/plan/command/PlanCommand.java b/Plan/src/main/java/com/djrapitops/plan/command/PlanCommand.java index 686a65a79..b2b904b93 100644 --- a/Plan/src/main/java/com/djrapitops/plan/command/PlanCommand.java +++ b/Plan/src/main/java/com/djrapitops/plan/command/PlanCommand.java @@ -12,6 +12,7 @@ import org.bukkit.command.CommandSender; import java.util.List; import java.util.ArrayList; +import main.java.com.djrapitops.plan.command.commands.ManageCommand; import org.bukkit.entity.Player; /** @@ -38,6 +39,7 @@ public class PlanCommand implements CommandExecutor { commands.add(new SearchCommand(plugin)); commands.add(new InfoCommand(plugin)); commands.add(new ReloadCommand(plugin)); + commands.add(new ManageCommand(plugin)); PlanLiteHook planLiteHook = plugin.getPlanLiteHook(); if (planLiteHook != null) { if (planLiteHook.isEnabled()) { diff --git a/Plan/src/main/java/com/djrapitops/plan/command/commands/InfoCommand.java b/Plan/src/main/java/com/djrapitops/plan/command/commands/InfoCommand.java index 7f8e38e01..07c475355 100644 --- a/Plan/src/main/java/com/djrapitops/plan/command/commands/InfoCommand.java +++ b/Plan/src/main/java/com/djrapitops/plan/command/commands/InfoCommand.java @@ -38,6 +38,7 @@ public class InfoCommand extends SubCommand { hColor + Phrase.ARROWS_RIGHT.toString() + oColor + " Player Analytics - Info", tColor + " " + Phrase.BALL.toString() + oColor + " Version: " + tColor + plugin.getDescription().getVersion(), tColor + " " + Phrase.BALL.toString() + tColor + " " + MiscUtils.checkVersion(), + tColor + " " + Phrase.BALL.toString() + oColor +" Active Database: "+ tColor + plugin.getDB().getConfigName(), hColor + Phrase.ARROWS_RIGHT.toString() }; sender.sendMessage(messages); diff --git a/Plan/src/main/java/com/djrapitops/plan/command/commands/ManageCommand.java b/Plan/src/main/java/com/djrapitops/plan/command/commands/ManageCommand.java new file mode 100644 index 000000000..215f611de --- /dev/null +++ b/Plan/src/main/java/com/djrapitops/plan/command/commands/ManageCommand.java @@ -0,0 +1,133 @@ +package main.java.com.djrapitops.plan.command.commands; + +import com.djrapitops.plan.Phrase; +import com.djrapitops.plan.Plan; +import com.djrapitops.plan.command.CommandType; +import com.djrapitops.plan.command.SubCommand; +import com.djrapitops.plan.utilities.FormatUtils; + +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; + +import java.util.List; +import java.util.ArrayList; +import main.java.com.djrapitops.plan.command.commands.manage.*; +import org.bukkit.entity.Player; + +/** + * + * @author Rsl1122 + */ +public class ManageCommand extends SubCommand { + + private final List commands; + private Plan plugin; + + /** + * Subcommand Constructor. + * + * @param plugin Current instance of Plan + */ + public ManageCommand(Plan plugin) { + super("manage", "plan.manage", "Database managment command", CommandType.CONSOLE, ""); + this.plugin = plugin; + commands = new ArrayList<>(); + commands.add(new ManageHelpCommand(plugin, this)); + commands.add(new ManageMoveCommand(plugin)); + commands.add(new ManageCombineCommand(plugin)); + commands.add(new ManageHotswapCommand(plugin)); + commands.add(new ManageStatusCommand(plugin)); + commands.add(new ManageRemoveCommand(plugin)); + commands.add(new ManageClearCommand(plugin)); + } + + /** + * @return Initialized SubCommands + */ + public List getCommands() { + return this.commands; + } + + /** + * Checks SubCommands for matching aliases. + * + * @param name SubCommand in text form that might match alias. + * @return SubCommand, null if no match. + */ + public SubCommand getCommand(String name) { + for (SubCommand command : commands) { + String[] aliases = command.getName().split(","); + + for (String alias : aliases) { + if (alias.equalsIgnoreCase(name)) { + return command; + } + } + } + return null; + } + + private void sendDefaultCommand(CommandSender sender, Command cmd, String commandLabel, String[] args) { + String command = "inspect"; + if (args.length < 1) { + command = "help"; + } + onCommand(sender, cmd, commandLabel, FormatUtils.mergeArrays(new String[]{command}, args)); + } + + /** + * Checks if Sender has rights to run the command and executes matching + * subcommand. + * + * @param sender + * @param cmd + * @param commandLabel + * @param args + * @return true in all cases. + */ + @Override + public boolean onCommand(CommandSender sender, Command cmd, String commandLabel, String[] args) { + if (args.length < 1) { + sendDefaultCommand(sender, cmd, commandLabel, args); + return true; + } + + SubCommand command = getCommand(args[0]); + + if (command == null) { + sendDefaultCommand(sender, cmd, commandLabel, args); + return true; + } + + boolean console = !(sender instanceof Player); + + if (!sender.hasPermission(command.getPermission())) { + sender.sendMessage("" + Phrase.COMMAND_NO_PERMISSION); + return true; + } + + if (console && args.length < 2 && command.getCommandType() == CommandType.CONSOLE_WITH_ARGUMENTS) { + sender.sendMessage("" + Phrase.COMMAND_REQUIRES_ARGUMENTS); + + return true; + } + + if (console && command.getCommandType() == CommandType.PLAYER) {; + sender.sendMessage("" + Phrase.COMMAND_SENDER_NOT_PLAYER); + + return true; + } + + String[] realArgs = new String[args.length - 1]; + + for (int i = 1; i < args.length; i++) { + realArgs[i - 1] = args[i]; + } + + if (!command.onCommand(sender, cmd, commandLabel, realArgs)) { +// Phrase.TRY_COMMAND.sendWithPrefix( sender, parse( commandLabel, command ) ); + } + return true; + } + +} diff --git a/Plan/src/main/java/com/djrapitops/plan/command/commands/ReloadCommand.java b/Plan/src/main/java/com/djrapitops/plan/command/commands/ReloadCommand.java index 5622ff7e7..1e9ce4503 100644 --- a/Plan/src/main/java/com/djrapitops/plan/command/commands/ReloadCommand.java +++ b/Plan/src/main/java/com/djrapitops/plan/command/commands/ReloadCommand.java @@ -28,9 +28,8 @@ public class ReloadCommand extends SubCommand { @Override public boolean onCommand(CommandSender sender, Command cmd, String commandLabel, String[] args) { - plugin.reloadConfig(); - plugin.getHandler().saveCachedUserData(); - plugin.hookPlanLite(); + plugin.onDisable(); + plugin.onEnable(); sender.sendMessage(ChatColor.GREEN + "[Plan] Reload complete."); return true; diff --git a/Plan/src/main/java/com/djrapitops/plan/command/commands/SearchCommand.java b/Plan/src/main/java/com/djrapitops/plan/command/commands/SearchCommand.java index 18ef8d0bf..3ce9e2dc6 100644 --- a/Plan/src/main/java/com/djrapitops/plan/command/commands/SearchCommand.java +++ b/Plan/src/main/java/com/djrapitops/plan/command/commands/SearchCommand.java @@ -33,7 +33,7 @@ public class SearchCommand extends SubCommand { * @param plugin Current instance of Plan */ public SearchCommand(Plan plugin) { - super("search", "plan.search", "Search for player", CommandType.CONSOLE_WITH_ARGUMENTS, ""); this.plugin = plugin; inspectCache = plugin.getInspectCache(); } diff --git a/Plan/src/main/java/com/djrapitops/plan/command/commands/manage/ManageClearCommand.java b/Plan/src/main/java/com/djrapitops/plan/command/commands/manage/ManageClearCommand.java new file mode 100644 index 000000000..38ea56a36 --- /dev/null +++ b/Plan/src/main/java/com/djrapitops/plan/command/commands/manage/ManageClearCommand.java @@ -0,0 +1,86 @@ +package main.java.com.djrapitops.plan.command.commands.manage; + +import com.djrapitops.plan.Phrase; +import com.djrapitops.plan.Plan; +import com.djrapitops.plan.command.CommandType; +import com.djrapitops.plan.command.SubCommand; + +import com.djrapitops.plan.database.Database; +import java.util.Arrays; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.scheduler.BukkitRunnable; + +/** + * + * @author Rsl1122 + */ +public class ManageClearCommand extends SubCommand { + + private Plan plugin; + + /** + * Class Constructor. + * + * @param plugin Current instance of Plan + */ + public ManageClearCommand(Plan plugin) { + super("clear", "plan.manage", "Clear data from one database", CommandType.CONSOLE_WITH_ARGUMENTS, " [-a]"); + + this.plugin = plugin; + } + + /** + * Subcommand inspect. + * + * Adds player's data from DataCache/DB to the InspectCache for amount of + * time specified in the config, and clears the data from Cache with a timer + * task. + * + * @param sender + * @param cmd + * @param commandLabel + * @param args Player's name or nothing - if empty sender's name is used. + * @return true in all cases. + */ + @Override + public boolean onCommand(CommandSender sender, Command cmd, String commandLabel, String[] args) { + if (args.length == 0) { + sender.sendMessage(Phrase.COMMAND_REQUIRES_ARGUMENTS_ONE + ""); + return true; + } + String dbToClear = args[0].toLowerCase(); + if (!dbToClear.equals("mysql") && !dbToClear.equals("sqlite")) { + sender.sendMessage(Phrase.MANAGE_ERROR_INCORRECT_DB + dbToClear); + return true; + } + if (!Arrays.asList(args).contains("-a")) { + sender.sendMessage(Phrase.COMMAND_ADD_CONFIRMATION_ARGUMENT.toString() + " Data in " + args[0] + "-database will be removed!"); + return true; + } + + Database clearDB = null; + for (Database database : plugin.getDatabases()) { + if (dbToClear.equalsIgnoreCase(database.getConfigName())) { + clearDB = database; + clearDB.init(); + } + } + if (clearDB == null) { + sender.sendMessage(Phrase.MANAGE_DATABASE_FAILURE + ""); + plugin.logError(dbToClear + " was null!"); + return true; + } + + final Database clearThisDB = clearDB; + (new BukkitRunnable() { + @Override + public void run() { + clearThisDB.removeAllData(); + sender.sendMessage(Phrase.MANAGE_CLEAR_SUCCESS+""); + this.cancel(); + } + }).runTaskAsynchronously(plugin); + return true; + } +} diff --git a/Plan/src/main/java/com/djrapitops/plan/command/commands/manage/ManageCombineCommand.java b/Plan/src/main/java/com/djrapitops/plan/command/commands/manage/ManageCombineCommand.java new file mode 100644 index 000000000..90ef568cb --- /dev/null +++ b/Plan/src/main/java/com/djrapitops/plan/command/commands/manage/ManageCombineCommand.java @@ -0,0 +1,172 @@ +package main.java.com.djrapitops.plan.command.commands.manage; + +import com.djrapitops.plan.Phrase; +import com.djrapitops.plan.Plan; +import com.djrapitops.plan.command.CommandType; +import com.djrapitops.plan.command.SubCommand; +import com.djrapitops.plan.data.ServerData; +import com.djrapitops.plan.data.UserData; + +import com.djrapitops.plan.database.Database; +import com.djrapitops.plan.utilities.MiscUtils; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.UUID; + +import org.bukkit.OfflinePlayer; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.scheduler.BukkitRunnable; + +/** + * + * @author Rsl1122 + */ +public class ManageCombineCommand extends SubCommand { + + private Plan plugin; + + /** + * Class Constructor. + * + * @param plugin Current instance of Plan + */ + public ManageCombineCommand(Plan plugin) { + super("combine", "plan.manage", "Copy data from one database to another & combine values", CommandType.CONSOLE_WITH_ARGUMENTS, " [-a]"); + this.plugin = plugin; + } + + /** + * Subcommand inspect. + * + * Adds player's data from DataCache/DB to the InspectCache for amount of + * time specified in the config, and clears the data from Cache with a timer + * task. + * + * @param sender + * @param cmd + * @param commandLabel + * @param args Player's name or nothing - if empty sender's name is used. + * @return true in all cases. + */ + @Override + public boolean onCommand(CommandSender sender, Command cmd, String commandLabel, String[] args) { + if (args.length < 2) { + sender.sendMessage(Phrase.COMMAND_REQUIRES_ARGUMENTS.toString() + " Use /plan manage combine [-a]"); + return true; + } + String fromDB = args[0].toLowerCase(); + String toDB = args[1].toLowerCase(); + if (!fromDB.equals("mysql") && !fromDB.equals("sqlite")) { + sender.sendMessage(Phrase.MANAGE_ERROR_INCORRECT_DB + fromDB); + return true; + } + if (!toDB.equals("mysql") && !toDB.equals("sqlite")) { + sender.sendMessage(Phrase.MANAGE_ERROR_INCORRECT_DB + toDB); + return true; + } + if (fromDB.equals(toDB)) { + sender.sendMessage(Phrase.MANAGE_ERROR_SAME_DB + ""); + return true; + } + if (!Arrays.asList(args).contains("-a")) { + sender.sendMessage(Phrase.COMMAND_ADD_CONFIRMATION_ARGUMENT.toString() + " Data in " + args[1] + "-database will be removed!"); + return true; + } + + Database fromDatabase = null; + Database toDatabase = null; + for (Database database : plugin.getDatabases()) { + if (fromDB.equalsIgnoreCase(database.getConfigName())) { + fromDatabase = database; + fromDatabase.init(); + } + if (toDB.equalsIgnoreCase(database.getConfigName())) { + toDatabase = database; + toDatabase.init(); + } + } + if (fromDatabase == null) { + sender.sendMessage(Phrase.MANAGE_DATABASE_FAILURE + ""); + plugin.logError(fromDB + " was null!"); + return true; + } + if (toDatabase == null) { + sender.sendMessage(Phrase.MANAGE_DATABASE_FAILURE + ""); + plugin.logError(toDB + " was null!"); + return true; + } + + OfflinePlayer[] offlinePlayers; + try { + offlinePlayers = plugin.getServer().getOfflinePlayers(); + } catch (IndexOutOfBoundsException e) { + sender.sendMessage(Phrase.MANAGE_ERROR_NO_PLAYERS + " (" + fromDB + ")"); + return true; + } + final List fromUUIDS = new ArrayList<>(); + final List toUUIDS = new ArrayList<>(); + for (OfflinePlayer p : offlinePlayers) { + UUID uuid = p.getUniqueId(); + if (fromDatabase.wasSeenBefore(uuid)) { + fromUUIDS.add(uuid); + } + if (toDatabase.wasSeenBefore(uuid)) { + toUUIDS.add(uuid); + } + } + if (fromUUIDS.isEmpty() && toUUIDS.isEmpty()) { + sender.sendMessage(Phrase.MANAGE_ERROR_NO_PLAYERS + " (" + fromDB + ")"); + return true; + } + + final Database moveFromDB = fromDatabase; + final Database moveToDB = toDatabase; + (new BukkitRunnable() { + @Override + public void run() { + HashMap allFromUserData = new HashMap<>(); + HashMap allToUserData = new HashMap<>(); + for (UUID uuid : fromUUIDS) { + allFromUserData.put(uuid, moveFromDB.getUserData(uuid)); + } + for (UUID uuid : toUUIDS) { + allToUserData.put(uuid, moveToDB.getUserData(uuid)); + } + Set uuids = new HashSet<>(); + uuids.addAll(toUUIDS); + uuids.addAll(fromUUIDS); + List combinedUserData = MiscUtils.combineUserDatas(allFromUserData, allToUserData, uuids); + HashMap fromServerData = moveFromDB.getServerDataHashMap(); + HashMap toServerData = moveToDB.getServerDataHashMap(); + HashMap combinedServerData = MiscUtils.combineServerDatas(fromServerData, toServerData); + HashMap commandUse = MiscUtils.combineCommandUses(getCommandUse(fromServerData), getCommandUse(toServerData)); + moveToDB.removeAllData(); + moveToDB.saveServerDataHashMap(combinedServerData); + moveToDB.saveMultipleUserData(combinedUserData); + moveToDB.saveCommandUse(commandUse); + sender.sendMessage(Phrase.MANAGE_MOVE_SUCCESS+""); + sender.sendMessage(Phrase.MANAGE_DB_CONFIG_REMINDER+""); + this.cancel(); + } + + public HashMap getCommandUse(HashMap fromServerData) { + ServerData sData = null; + for (long sDataKey : fromServerData.keySet()) { + sData = fromServerData.get(sDataKey); + break; + } + HashMap fromCommandUse = null; + if (sData != null) { + fromCommandUse = sData.getCommandUsage(); + } + return fromCommandUse; + } + }).runTaskAsynchronously(plugin); + return true; + } +} diff --git a/Plan/src/main/java/com/djrapitops/plan/command/commands/manage/ManageHelpCommand.java b/Plan/src/main/java/com/djrapitops/plan/command/commands/manage/ManageHelpCommand.java new file mode 100644 index 000000000..1b147c188 --- /dev/null +++ b/Plan/src/main/java/com/djrapitops/plan/command/commands/manage/ManageHelpCommand.java @@ -0,0 +1,66 @@ +package main.java.com.djrapitops.plan.command.commands.manage; + +import com.djrapitops.plan.Phrase; +import com.djrapitops.plan.Plan; +import com.djrapitops.plan.command.CommandType; +import com.djrapitops.plan.command.SubCommand; +import main.java.com.djrapitops.plan.command.commands.ManageCommand; +import org.bukkit.ChatColor; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; + +/** + * + * @author Rsl1122 + */ +public class ManageHelpCommand extends SubCommand { + + private final Plan plugin; + private final ManageCommand command; + + /** + * Subcommand Constructor. + * + * @param plugin Current instance of Plan + * @param command Current instance of PlanCommand + */ + public ManageHelpCommand(Plan plugin, ManageCommand command) { + super("help,?", "plan.manage", "Show managment help.", CommandType.CONSOLE, ""); + + this.plugin = plugin; + this.command = command; + } + + @Override + public boolean onCommand(CommandSender sender, Command cmd, String commandLabel, String[] args) { + + ChatColor oColor = Phrase.COLOR_MAIN.color(); + ChatColor tColor = Phrase.COLOR_SEC.color(); + ChatColor hColor = Phrase.COLOR_TER.color(); + + // Header + sender.sendMessage(hColor + Phrase.ARROWS_RIGHT.toString() + oColor + " Player Analytics - Managment Help"); + // Help results + for (SubCommand command : this.command.getCommands()) { + if (command.getName().equalsIgnoreCase(getName())) { + continue; + } + + if (!sender.hasPermission(command.getPermission())) { + continue; + } + + if (!(sender instanceof Player) && command.getCommandType() == CommandType.PLAYER) { + continue; + } + + sender.sendMessage(tColor + " " + Phrase.BALL.toString() + oColor + + " /plan manage " + command.getFirstName() + " " + command.getArguments() + tColor + " - " + command.getUsage()); + } + // Footer + sender.sendMessage(hColor + Phrase.ARROWS_RIGHT.toString()); + return true; + } + +} diff --git a/Plan/src/main/java/com/djrapitops/plan/command/commands/manage/ManageHotswapCommand.java b/Plan/src/main/java/com/djrapitops/plan/command/commands/manage/ManageHotswapCommand.java new file mode 100644 index 000000000..5e5f43e4d --- /dev/null +++ b/Plan/src/main/java/com/djrapitops/plan/command/commands/manage/ManageHotswapCommand.java @@ -0,0 +1,76 @@ +package main.java.com.djrapitops.plan.command.commands.manage; + +import com.djrapitops.plan.Phrase; +import com.djrapitops.plan.Plan; +import com.djrapitops.plan.command.CommandType; +import com.djrapitops.plan.command.SubCommand; +import com.djrapitops.plan.data.ServerData; +import com.djrapitops.plan.data.UserData; + +import com.djrapitops.plan.database.Database; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.UUID; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.logging.Level; +import java.util.logging.Logger; + +import org.bukkit.ChatColor; +import org.bukkit.OfflinePlayer; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.scheduler.BukkitRunnable; + +/** + * + * @author Rsl1122 + */ +public class ManageHotswapCommand extends SubCommand { + + private Plan plugin; + + /** + * Class Constructor. + * + * @param plugin Current instance of Plan + */ + public ManageHotswapCommand(Plan plugin) { + super("hotswap", "plan.manage", "Hotswap to another database & restart the plugin", CommandType.CONSOLE_WITH_ARGUMENTS, ""); + + this.plugin = plugin; + } + + /** + * Subcommand inspect. + * + * Adds player's data from DataCache/DB to the InspectCache for amount of + * time specified in the config, and clears the data from Cache with a timer + * task. + * + * @param sender + * @param cmd + * @param commandLabel + * @param args Player's name or nothing - if empty sender's name is used. + * @return true in all cases. + */ + @Override + public boolean onCommand(CommandSender sender, Command cmd, String commandLabel, String[] args) { + if (args.length == 0) { + sender.sendMessage(Phrase.COMMAND_REQUIRES_ARGUMENTS_ONE + ""); + return true; + } + String dbToSwapTo = args[0].toLowerCase(); + if (!dbToSwapTo.equals("mysql") && !dbToSwapTo.equals("sqlite")) { + sender.sendMessage(Phrase.MANAGE_ERROR_INCORRECT_DB + dbToSwapTo); + return true; + } + plugin.getConfig().set("database.type", dbToSwapTo); + plugin.saveConfig(); + plugin.onDisable(); + plugin.onEnable(); + return true; + } +} diff --git a/Plan/src/main/java/com/djrapitops/plan/command/commands/manage/ManageMoveCommand.java b/Plan/src/main/java/com/djrapitops/plan/command/commands/manage/ManageMoveCommand.java new file mode 100644 index 000000000..e6bbc0929 --- /dev/null +++ b/Plan/src/main/java/com/djrapitops/plan/command/commands/manage/ManageMoveCommand.java @@ -0,0 +1,149 @@ +package main.java.com.djrapitops.plan.command.commands.manage; + +import com.djrapitops.plan.Phrase; +import com.djrapitops.plan.Plan; +import com.djrapitops.plan.command.CommandType; +import com.djrapitops.plan.command.SubCommand; +import com.djrapitops.plan.data.ServerData; +import com.djrapitops.plan.data.UserData; + +import com.djrapitops.plan.database.Database; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.UUID; + +import org.bukkit.OfflinePlayer; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.scheduler.BukkitRunnable; + +/** + * + * @author Rsl1122 + */ +public class ManageMoveCommand extends SubCommand { + + private Plan plugin; + + /** + * Class Constructor. + * + * @param plugin Current instance of Plan + */ + public ManageMoveCommand(Plan plugin) { + super("move", "plan.manage", "Copy data from one database to another & overwrite values", CommandType.CONSOLE_WITH_ARGUMENTS, " [-a]"); + + this.plugin = plugin; + } + + /** + * Subcommand inspect. + * + * Adds player's data from DataCache/DB to the InspectCache for amount of + * time specified in the config, and clears the data from Cache with a timer + * task. + * + * @param sender + * @param cmd + * @param commandLabel + * @param args Player's name or nothing - if empty sender's name is used. + * @return true in all cases. + */ + @Override + public boolean onCommand(CommandSender sender, Command cmd, String commandLabel, String[] args) { + if (args.length < 2) { + sender.sendMessage(Phrase.COMMAND_REQUIRES_ARGUMENTS.toString() + " Use /plan manage move [-a]"); + return true; + } + String fromDB = args[0].toLowerCase(); + String toDB = args[1].toLowerCase(); + if (!fromDB.equals("mysql") && !fromDB.equals("sqlite")) { + sender.sendMessage(Phrase.MANAGE_ERROR_INCORRECT_DB + fromDB); + return true; + } + if (!toDB.equals("mysql") && !toDB.equals("sqlite")) { + sender.sendMessage(Phrase.MANAGE_ERROR_INCORRECT_DB + toDB); + return true; + } + if (fromDB.equals(toDB)) { + sender.sendMessage(Phrase.MANAGE_ERROR_SAME_DB + ""); + return true; + } + if (!Arrays.asList(args).contains("-a")) { + sender.sendMessage(Phrase.COMMAND_ADD_CONFIRMATION_ARGUMENT.toString() + " Data in " + args[1] + "-database will be removed!"); + return true; + } + + Database fromDatabase = null; + Database toDatabase = null; + for (Database database : plugin.getDatabases()) { + if (fromDB.equalsIgnoreCase(database.getConfigName())) { + fromDatabase = database; + fromDatabase.init(); + } + if (toDB.equalsIgnoreCase(database.getConfigName())) { + toDatabase = database; + toDatabase.init(); + } + } + if (fromDatabase == null) { + sender.sendMessage(Phrase.MANAGE_DATABASE_FAILURE + ""); + plugin.logError(fromDB + " was null!"); + return true; + } + if (toDatabase == null) { + sender.sendMessage(Phrase.MANAGE_DATABASE_FAILURE + ""); + plugin.logError(toDB + " was null!"); + return true; + } + + OfflinePlayer[] offlinePlayers; + try { + offlinePlayers = plugin.getServer().getOfflinePlayers(); + } catch (IndexOutOfBoundsException e) { + sender.sendMessage(Phrase.MANAGE_ERROR_NO_PLAYERS + " (" + fromDB + ")"); + return true; + } + final List uuids = new ArrayList<>(); + for (OfflinePlayer p : offlinePlayers) { + UUID uuid = p.getUniqueId(); + if (fromDatabase.wasSeenBefore(uuid)) { + uuids.add(uuid); + } + } + if (uuids.isEmpty()) { + sender.sendMessage(Phrase.MANAGE_ERROR_NO_PLAYERS + " (" + fromDB + ")"); + return true; + } + + final Database moveFromDB = fromDatabase; + final Database moveToDB = toDatabase; + (new BukkitRunnable() { + @Override + public void run() { + moveToDB.removeAllData(); + List allUserData = new ArrayList<>(); + for (UUID uuid : uuids) { + allUserData.add(moveFromDB.getUserData(uuid)); + } + moveToDB.saveMultipleUserData(allUserData); + HashMap serverData = moveFromDB.getServerDataHashMap(); + moveToDB.saveServerDataHashMap(serverData); + ServerData sData = null; + for (long sDataKey : serverData.keySet()) { + sData = serverData.get(sDataKey); + break; + } + if (sData != null) { + moveToDB.saveCommandUse(sData.getCommandUsage()); + } + sender.sendMessage(Phrase.MANAGE_MOVE_SUCCESS+""); + sender.sendMessage(Phrase.MANAGE_DB_CONFIG_REMINDER+""); + this.cancel(); + } + }).runTaskAsynchronously(plugin); + return true; + } +} diff --git a/Plan/src/main/java/com/djrapitops/plan/command/commands/manage/ManageRemoveCommand.java b/Plan/src/main/java/com/djrapitops/plan/command/commands/manage/ManageRemoveCommand.java new file mode 100644 index 000000000..a2926bb16 --- /dev/null +++ b/Plan/src/main/java/com/djrapitops/plan/command/commands/manage/ManageRemoveCommand.java @@ -0,0 +1,98 @@ +package main.java.com.djrapitops.plan.command.commands.manage; + +import com.djrapitops.plan.Phrase; +import com.djrapitops.plan.Plan; +import com.djrapitops.plan.utilities.UUIDFetcher; +import com.djrapitops.plan.command.CommandType; +import com.djrapitops.plan.command.SubCommand; + +import com.djrapitops.plan.utilities.MiscUtils; +import java.util.Arrays; +import java.util.UUID; + +import org.bukkit.ChatColor; +import org.bukkit.OfflinePlayer; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.scheduler.BukkitRunnable; +import static org.bukkit.Bukkit.getOfflinePlayer; + +/** + * + * @author Rsl1122 + */ +public class ManageRemoveCommand extends SubCommand { + + private Plan plugin; + + /** + * Class Constructor. + * + * @param plugin Current instance of Plan + */ + public ManageRemoveCommand(Plan plugin) { + super("remove", "plan.manage", "Remove players's data from the Active Database.", CommandType.CONSOLE_WITH_ARGUMENTS, " [-a]"); + + this.plugin = plugin; + } + + /** + * Subcommand inspect. + * + * Adds player's data from DataCache/DB to the InspectCache for amount of + * time specified in the config, and clears the data from Cache with a timer + * task. + * + * @param sender + * @param cmd + * @param commandLabel + * @param args Player's name or nothing - if empty sender's name is used. + * @return true in all cases. + */ + @Override + public boolean onCommand(CommandSender sender, Command cmd, String commandLabel, String[] args) { + if (args.length == 0) { + sender.sendMessage(Phrase.COMMAND_REQUIRES_ARGUMENTS_ONE.toString()); + return true; + } + + String playerName = MiscUtils.getPlayerDisplayname(args, sender); + + UUID uuid; + try { + uuid = UUIDFetcher.getUUIDOf(playerName); + if (uuid == null) { + throw new Exception("Username doesn't exist."); + } + } catch (Exception e) { + sender.sendMessage(Phrase.USERNAME_NOT_VALID.toString()); + return true; + } + OfflinePlayer p = getOfflinePlayer(uuid); + if (!p.hasPlayedBefore()) { + sender.sendMessage(Phrase.USERNAME_NOT_SEEN.toString()); + return true; + } + if (!plugin.getDB().wasSeenBefore(uuid)) { + sender.sendMessage(Phrase.USERNAME_NOT_KNOWN.toString()); + return true; + } + if (!Arrays.asList(args).contains("-a")) { + sender.sendMessage(Phrase.COMMAND_ADD_CONFIRMATION_ARGUMENT.toString()); + return true; + } + + ChatColor oColor = Phrase.COLOR_MAIN.color(); + ChatColor hColor = Phrase.COLOR_TER.color(); + + (new BukkitRunnable() { + @Override + public void run() { + plugin.getDB().removeAccount(uuid.toString()); + sender.sendMessage(hColor+""+Phrase.ARROWS_RIGHT+" "+oColor+"Data of "+hColor+playerName+oColor+" was removed from Database "+hColor+plugin.getDB().getConfigName()+oColor+"."); + this.cancel(); + } + }).runTaskAsynchronously(plugin); + return true; + } +} diff --git a/Plan/src/main/java/com/djrapitops/plan/command/commands/manage/ManageStatusCommand.java b/Plan/src/main/java/com/djrapitops/plan/command/commands/manage/ManageStatusCommand.java new file mode 100644 index 000000000..f58a727b9 --- /dev/null +++ b/Plan/src/main/java/com/djrapitops/plan/command/commands/manage/ManageStatusCommand.java @@ -0,0 +1,61 @@ +package main.java.com.djrapitops.plan.command.commands.manage; + +import com.djrapitops.plan.Phrase; +import com.djrapitops.plan.Plan; +import com.djrapitops.plan.command.CommandType; +import com.djrapitops.plan.command.SubCommand; + + +import org.bukkit.ChatColor; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; + +/** + * + * @author Rsl1122 + */ +public class ManageStatusCommand extends SubCommand { + + private Plan plugin; + + /** + * Class Constructor. + * + * @param plugin Current instance of Plan + */ + public ManageStatusCommand(Plan plugin) { + super("status", "plan.manage", "Check the status of the Active Database.", CommandType.CONSOLE, ""); + + this.plugin = plugin; + } + + /** + * Subcommand inspect. + * + * Adds player's data from DataCache/DB to the InspectCache for amount of + * time specified in the config, and clears the data from Cache with a timer + * task. + * + * @param sender + * @param cmd + * @param commandLabel + * @param args Player's name or nothing - if empty sender's name is used. + * @return true in all cases. + */ + @Override + public boolean onCommand(CommandSender sender, Command cmd, String commandLabel, String[] args) { + + ChatColor oColor = Phrase.COLOR_MAIN.color(); + ChatColor tColor = Phrase.COLOR_SEC.color(); + ChatColor hColor = Phrase.COLOR_TER.color(); + + // Header + sender.sendMessage(hColor + Phrase.ARROWS_RIGHT.toString() + oColor + " Player Analytics - Database status"); + + sender.sendMessage(tColor + " " + Phrase.BALL.toString() + oColor +" Active Database: "+ tColor + plugin.getDB().getConfigName()); + + // Footer + sender.sendMessage(hColor + Phrase.ARROWS_RIGHT.toString()); + return true; + } +} diff --git a/Plan/src/main/java/com/djrapitops/plan/database/Database.java b/Plan/src/main/java/com/djrapitops/plan/database/Database.java index 4033ae7f4..cdfae54e3 100644 --- a/Plan/src/main/java/com/djrapitops/plan/database/Database.java +++ b/Plan/src/main/java/com/djrapitops/plan/database/Database.java @@ -139,4 +139,9 @@ public abstract class Database { * @return HashMap with save date (long in ms) and ServerData. */ public abstract HashMap getServerDataHashMap(); + + public abstract void removeAccount(String uuid); + public abstract void removeAllData(); + public abstract void saveServerDataHashMap(HashMap serverData); + public abstract void saveCommandUse(HashMap data); } diff --git a/Plan/src/main/java/com/djrapitops/plan/database/databases/SQLDB.java b/Plan/src/main/java/com/djrapitops/plan/database/databases/SQLDB.java index 1879ab6e2..ef899dd57 100644 --- a/Plan/src/main/java/com/djrapitops/plan/database/databases/SQLDB.java +++ b/Plan/src/main/java/com/djrapitops/plan/database/databases/SQLDB.java @@ -357,6 +357,37 @@ public abstract class SQLDB extends Database { return new ServerData(commandUse, newPlayers); } + @Override + public void saveServerDataHashMap(HashMap serverData) { + try { + connection.setAutoCommit(false); + PreparedStatement statement = connection.prepareStatement("INSERT INTO " + serverdataName + " (" + + serverdataColumnDate + ", " + + serverdataColumnNewPlayers + ", " + + serverdataColumnPlayersOnline + + ") VALUES (?, ?, ?)"); + + boolean commitRequired = false; + for (long date : serverData.keySet()) { + statement.setLong(1, date); + ServerData sData = serverData.get(date); + statement.setInt(2, sData.getNewPlayers()); + statement.setInt(3, sData.getPlayersOnline()); + statement.addBatch(); + commitRequired = true; + } + statement.executeBatch(); + if (commitRequired) { + connection.commit(); + } + statement.close(); + connection.setAutoCommit(true); + + } catch (SQLException e) { + e.printStackTrace(); + } + } + @Override public void saveServerData(ServerData data) { try { @@ -378,7 +409,7 @@ public abstract class SQLDB extends Database { } } - private void saveCommandUse(HashMap data) { + public void saveCommandUse(HashMap data) { try { PreparedStatement statement = connection.prepareStatement( "DELETE FROM " + commanduseName); @@ -427,6 +458,7 @@ public abstract class SQLDB extends Database { return commandUse; } + @Override public void removeAccount(String uuid) { checkConnection(); @@ -437,11 +469,6 @@ public abstract class SQLDB extends Database { } PreparedStatement statement; try { - statement = connection.prepareStatement("DELETE FROM " + userName + " WHERE UPPER(" + userColumnUUID + ") LIKE UPPER(?)"); - statement.setString(1, uuid); - statement.execute(); - statement.close(); - statement = connection.prepareStatement("DELETE FROM " + locationName + " WHERE UPPER(" + locationColumnUserID + ") LIKE UPPER(?)"); statement.setString(1, "" + userId); statement.execute(); @@ -459,6 +486,10 @@ public abstract class SQLDB extends Database { statement.setString(1, "" + userId); statement.execute(); statement.close(); + statement = connection.prepareStatement("DELETE FROM " + userName + " WHERE UPPER(" + userColumnUUID + ") LIKE UPPER(?)"); + statement.setString(1, uuid); + statement.execute(); + statement.close(); } catch (SQLException e) { e.printStackTrace(); } @@ -741,7 +772,7 @@ public abstract class SQLDB extends Database { connection.commit(); } saveStatement.close(); - + } catch (SQLException e) { e.printStackTrace(); } @@ -852,17 +883,18 @@ public abstract class SQLDB extends Database { plugin.log("Database Cleaning has not yet been implemented."); } + @Override public void removeAllData() { checkConnection(); - try { - connection.prepareStatement("DELETE FROM " + userName).executeUpdate(); + try { connection.prepareStatement("DELETE FROM " + locationName).executeUpdate(); connection.prepareStatement("DELETE FROM " + nicknamesName).executeUpdate(); connection.prepareStatement("DELETE FROM " + ipsName).executeUpdate(); - connection.prepareStatement("DELETE FROM " + gamemodetimesName).executeUpdate(); - connection.prepareStatement("DELETE FROM " + serverdataName).executeUpdate(); + connection.prepareStatement("DELETE FROM " + gamemodetimesName).executeUpdate(); connection.prepareStatement("DELETE FROM " + commanduseName).executeUpdate(); + connection.prepareStatement("DELETE FROM " + serverdataName).executeUpdate(); + connection.prepareStatement("DELETE FROM " + userName).executeUpdate(); } catch (SQLException e) { e.printStackTrace(); } diff --git a/Plan/src/main/java/com/djrapitops/plan/utilities/MiscUtils.java b/Plan/src/main/java/com/djrapitops/plan/utilities/MiscUtils.java index 49ca48f2d..685ee3c2b 100644 --- a/Plan/src/main/java/com/djrapitops/plan/utilities/MiscUtils.java +++ b/Plan/src/main/java/com/djrapitops/plan/utilities/MiscUtils.java @@ -2,14 +2,20 @@ package com.djrapitops.plan.utilities; import com.djrapitops.plan.Phrase; import com.djrapitops.plan.Plan; +import com.djrapitops.plan.data.DemographicsData; +import com.djrapitops.plan.data.ServerData; +import com.djrapitops.plan.data.UserData; import java.net.URL; import java.util.ArrayList; import java.util.Arrays; +import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Scanner; import java.util.Set; +import java.util.UUID; import org.bukkit.Bukkit; +import org.bukkit.GameMode; import org.bukkit.OfflinePlayer; import org.bukkit.command.CommandSender; import org.bukkit.entity.Player; @@ -89,6 +95,7 @@ public class MiscUtils { /** * Get matching playernames from the offlineplayers + * * @param search Part of a name to search for. * @return Set of OfflinePlayers that match. */ @@ -104,4 +111,107 @@ public class MiscUtils { return matches; } + public static List combineUserDatas(HashMap allFromUserData, HashMap allToUserData, Set uuids) { + List combinedData = new ArrayList<>(); + uuids.forEach((uuid) -> { + UserData fData = allFromUserData.get(uuid); + UserData tData = allToUserData.get(uuid); + if (fData == null) { + combinedData.add(tData); + } else if (tData == null) { + combinedData.add(fData); + } else { + combinedData.add(combineUserData(fData, tData)); + } + }); + return combinedData; + } + + private static UserData combineUserData(UserData fData, UserData tData) { + if (fData.getLastGmSwapTime() < tData.getLastGmSwapTime()) { + fData.setLastGmSwapTime(tData.getLastGmSwapTime()); + fData.setLastGamemode(tData.getLastGamemode()); + } + HashMap gmTimes = fData.getGmTimes(); + HashMap tGmTimes = tData.getGmTimes(); + gmTimes.keySet().stream().forEach((gm) -> { + long fTime = gmTimes.get(gm); + if (tGmTimes.get(gm) != null) { + long tTime = tGmTimes.get(gm); + gmTimes.put(gm, fTime + tTime); + } + }); + if (fData.getLastPlayed() < tData.getLastPlayed()) { + fData.setLastPlayed(tData.getLastPlayed()); + } + fData.setPlayTime(fData.getPlayTime() + tData.getPlayTime()); + fData.setTimesKicked(fData.getTimesKicked() + tData.getTimesKicked()); + fData.setLoginTimes(fData.getLoginTimes() + tData.getLoginTimes()); + fData.addLocations(tData.getLocations()); + fData.addNicknames(tData.getNicknames()); + fData.addIpAddresses(tData.getIps()); + DemographicsData tDemData = tData.getDemData(); + DemographicsData fDemData = fData.getDemData(); + if (tDemData.getAge() > fDemData.getAge()) { + fDemData.setAge(tDemData.getAge()); + } + if (fDemData.getGeoLocation().equals("Not Known")) { + fDemData.setGeoLocation(tDemData.getGeoLocation()); + } + fData.setDemData(fDemData); + return fData; + } + + public static HashMap combineServerDatas(HashMap fData, HashMap tData) { + HashMap combinedData = new HashMap<>(); + Set allDates = new HashSet<>(); + allDates.addAll(fData.keySet()); + allDates.addAll(tData.keySet()); + allDates.parallelStream().forEach((date) -> { + ServerData fServerData = fData.get(date); + ServerData tServerData = tData.get(date); + if (fServerData == null) { + combinedData.put(date, tServerData); + } else if (tServerData == null) { + combinedData.put(date, fServerData); + } else { + if (fServerData.getPlayersOnline() > tServerData.getPlayersOnline()) { + combinedData.put(date, fServerData); + } else { + combinedData.put(date, tServerData); + } + } + }); + return combinedData; + } + + public static HashMap combineCommandUses(HashMap fData, HashMap tData) { + HashMap combinedData = new HashMap<>(); + Set allCommands = new HashSet<>(); + if (fData != null) { + allCommands.addAll(fData.keySet()); + } + if (tData != null) { + allCommands.addAll(tData.keySet()); + } + for (String command : allCommands) { + boolean fDataHasCommand = false; + if (fData != null) { + fDataHasCommand = fData.keySet().contains(command); + } + boolean tDataHasCommand = false; + if (tData != null) { + tDataHasCommand = tData.keySet().contains(command); + } + int value = 0; + if (fDataHasCommand) { + value += fData.get(command); + } + if (tDataHasCommand) { + value += tData.get(command); + } + combinedData.put(command, value); + } + return combinedData; + } }