diff --git a/src/main/java/fr/xephi/authme/AuthMe.java b/src/main/java/fr/xephi/authme/AuthMe.java index 1ae1a6e08..11001ae70 100644 --- a/src/main/java/fr/xephi/authme/AuthMe.java +++ b/src/main/java/fr/xephi/authme/AuthMe.java @@ -44,6 +44,11 @@ import fr.xephi.authme.settings.SettingsMigrationService; import fr.xephi.authme.settings.SpawnLoader; import fr.xephi.authme.settings.properties.DatabaseSettings; import fr.xephi.authme.settings.properties.EmailSettings; + +import static fr.xephi.authme.settings.properties.EmailSettings.MAIL_ACCOUNT; +import static fr.xephi.authme.settings.properties.EmailSettings.MAIL_PASSWORD; +import static fr.xephi.authme.settings.properties.EmailSettings.RECALL_PLAYERS; + import fr.xephi.authme.settings.properties.HooksSettings; import fr.xephi.authme.settings.properties.PluginSettings; import fr.xephi.authme.settings.properties.PurgeSettings; @@ -51,6 +56,7 @@ import fr.xephi.authme.settings.properties.RestrictionSettings; import fr.xephi.authme.settings.properties.SecuritySettings; import fr.xephi.authme.settings.properties.SettingsFieldRetriever; import fr.xephi.authme.settings.propertymap.PropertyMap; +import fr.xephi.authme.task.PurgeTask; import fr.xephi.authme.util.BukkitService; import fr.xephi.authme.util.CollectionUtils; import fr.xephi.authme.util.FileUtils; @@ -58,6 +64,18 @@ import fr.xephi.authme.util.GeoLiteAPI; import fr.xephi.authme.util.MigrationService; import fr.xephi.authme.util.StringUtils; import fr.xephi.authme.util.Utils; + +import java.io.File; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.Calendar; +import java.util.Collection; +import java.util.Date; +import java.util.List; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import java.util.logging.Logger; + import org.apache.logging.log4j.LogManager; import org.bukkit.Bukkit; import org.bukkit.Location; @@ -70,20 +88,6 @@ import org.bukkit.plugin.java.JavaPlugin; import org.bukkit.scheduler.BukkitScheduler; import org.bukkit.scheduler.BukkitTask; -import java.io.File; -import java.sql.SQLException; -import java.util.ArrayList; -import java.util.Calendar; -import java.util.Collection; -import java.util.Date; -import java.util.List; -import java.util.concurrent.ConcurrentHashMap; -import java.util.logging.Logger; - -import static fr.xephi.authme.settings.properties.EmailSettings.MAIL_ACCOUNT; -import static fr.xephi.authme.settings.properties.EmailSettings.MAIL_PASSWORD; -import static fr.xephi.authme.settings.properties.EmailSettings.RECALL_PLAYERS; - /** * The AuthMe main class. */ @@ -671,33 +675,22 @@ public class AuthMe extends JavaPlugin { if (!newSettings.getProperty(PurgeSettings.USE_AUTO_PURGE) || autoPurging) { return; } + autoPurging = true; - getServer().getScheduler().runTaskAsynchronously(this, new Runnable() { - @Override - public void run() { - ConsoleLogger.info("AutoPurging the Database..."); - Calendar calendar = Calendar.getInstance(); - calendar.add(Calendar.DATE, -newSettings.getProperty(PurgeSettings.DAYS_BEFORE_REMOVE_PLAYER)); - long until = calendar.getTimeInMillis(); - List cleared = database.autoPurgeDatabase(until); - if (CollectionUtils.isEmpty(cleared)) { - return; - } - ConsoleLogger.info("AutoPurging the Database: " + cleared.size() + " accounts removed!"); - if (newSettings.getProperty(PurgeSettings.REMOVE_ESSENTIALS_FILES) && pluginHooks.isEssentialsAvailable()) - dataManager.purgeEssentials(cleared); - if (newSettings.getProperty(PurgeSettings.REMOVE_PLAYER_DAT)) - dataManager.purgeDat(cleared); - if (newSettings.getProperty(PurgeSettings.REMOVE_LIMITED_CREATIVE_INVENTORIES)) - dataManager.purgeLimitedCreative(cleared); - if (newSettings.getProperty(PurgeSettings.REMOVE_ANTI_XRAY_FILE)) - dataManager.purgeAntiXray(cleared); - if (newSettings.getProperty(PurgeSettings.REMOVE_PERMISSIONS)) - dataManager.purgePermissions(cleared); - ConsoleLogger.info("AutoPurge Finished!"); - autoPurging = false; - } - }); + + ConsoleLogger.info("AutoPurging the Database..."); + Calendar calendar = Calendar.getInstance(); + calendar.add(Calendar.DATE, -newSettings.getProperty(PurgeSettings.DAYS_BEFORE_REMOVE_PLAYER)); + long until = calendar.getTimeInMillis(); + Set cleared = database.autoPurgeDatabase(until); + if (CollectionUtils.isEmpty(cleared)) { + return; + } + + ConsoleLogger.info("AutoPurging the Database: " + cleared.size() + " accounts removed!"); + ConsoleLogger.info("Purging user accounts..."); + new PurgeTask(plugin, Bukkit.getConsoleSender(), cleared, true, Bukkit.getOfflinePlayers()) + .runTaskTimer(plugin, 0, 1); } // Return the spawn location of a player @@ -808,4 +801,7 @@ public class AuthMe extends JavaPlugin { return pluginHooks; } + public void notifyAutoPurgeEnd() { + this.autoPurging = false; + } } diff --git a/src/main/java/fr/xephi/authme/DataManager.java b/src/main/java/fr/xephi/authme/DataManager.java index f5751d429..feaa69d49 100644 --- a/src/main/java/fr/xephi/authme/DataManager.java +++ b/src/main/java/fr/xephi/authme/DataManager.java @@ -6,16 +6,14 @@ import fr.xephi.authme.settings.NewSetting; import fr.xephi.authme.settings.properties.PurgeSettings; import fr.xephi.authme.util.BukkitService; import fr.xephi.authme.util.Utils; -import org.bukkit.Bukkit; import org.bukkit.OfflinePlayer; import org.bukkit.Server; import javax.inject.Inject; import java.io.File; -import java.util.ArrayList; -import java.util.List; import static fr.xephi.authme.util.StringUtils.makePath; +import java.util.Set; /** */ @@ -34,25 +32,14 @@ public class DataManager { DataManager() { } - private List getOfflinePlayers(List names) { - List result = new ArrayList<>(); - for (OfflinePlayer op : Bukkit.getOfflinePlayers()) { - for (String name : names) { - if (name.equalsIgnoreCase(op.getName())) { - result.add(op); - } - } - } - return result; - } - - public void purgeAntiXray(List cleared) { + public void purgeAntiXray(Set cleared) { int i = 0; File dataFolder = new File("." + File.separator + "plugins" + File.separator + "AntiXRayData" + File.separator + "PlayerData"); if (!dataFolder.exists() || !dataFolder.isDirectory()) { return; } + for (String file : dataFolder.list()) { if (cleared.contains(file.toLowerCase())) { File playerFile = new File(dataFolder, file); @@ -61,10 +48,11 @@ public class DataManager { } } } + ConsoleLogger.info("AutoPurge: Removed " + i + " AntiXRayData Files"); } - public synchronized void purgeLimitedCreative(List cleared) { + public synchronized void purgeLimitedCreative(Set cleared) { int i = 0; File dataFolder = new File("." + File.separator + "plugins" + File.separator + "LimitedCreative" + File.separator + "inventories"); @@ -101,17 +89,18 @@ public class DataManager { ConsoleLogger.info("AutoPurge: Removed " + i + " LimitedCreative Survival, Creative and Adventure files"); } - public synchronized void purgeDat(List cleared) { + public synchronized void purgeDat(Set cleared) { int i = 0; - File dataFolder = new File(server.getWorldContainer(), - makePath(settings.getProperty(PurgeSettings.DEFAULT_WORLD), "players")); - List offlinePlayers = getOfflinePlayers(cleared); - for (OfflinePlayer player : offlinePlayers) { - File playerFile = new File(dataFolder, Utils.getUUIDorName(player) + ".dat"); + File dataFolder = new File(server.getWorldContainer() + , makePath(settings.getProperty(PurgeSettings.DEFAULT_WORLD), "players")); + + for (OfflinePlayer offlinePlayer : cleared) { + File playerFile = new File(dataFolder, Utils.getUUIDorName(offlinePlayer) + ".dat"); if (playerFile.delete()) { i++; } } + ConsoleLogger.info("AutoPurge: Removed " + i + " .dat Files"); } @@ -120,7 +109,7 @@ public class DataManager { * * @param cleared List of String */ - public void purgeEssentials(List cleared) { + public void purgeEssentials(Set cleared) { int i = 0; File essentialsDataFolder = pluginHooks.getEssentialsDataFolder(); if (essentialsDataFolder == null) { @@ -132,9 +121,9 @@ public class DataManager { if (!userDataFolder.exists() || !userDataFolder.isDirectory()) { return; } - List offlinePlayers = getOfflinePlayers(cleared); - for (OfflinePlayer player : offlinePlayers) { - File playerFile = new File(userDataFolder, Utils.getUUIDorName(player) + ".yml"); + + for (OfflinePlayer offlinePlayer : cleared) { + File playerFile = new File(userDataFolder, Utils.getUUIDorName(offlinePlayer) + ".yml"); if (playerFile.exists() && playerFile.delete()) { i++; } @@ -145,10 +134,12 @@ public class DataManager { // TODO: What is this method for? Is it correct? // TODO: Make it work with OfflinePlayers group data. - public synchronized void purgePermissions(List cleared) { - for (String name : cleared) { + public synchronized void purgePermissions(Set cleared) { + for (OfflinePlayer offlinePlayer : cleared) { + String name = offlinePlayer.getName(); permissionsManager.removeAllGroups(bukkitService.getPlayerExact(name)); } + ConsoleLogger.info("AutoPurge: Removed permissions from " + cleared.size() + " player(s)."); } } diff --git a/src/main/java/fr/xephi/authme/command/executable/authme/PurgeBannedPlayersCommand.java b/src/main/java/fr/xephi/authme/command/executable/authme/PurgeBannedPlayersCommand.java index d9508eea0..92a4b50c0 100644 --- a/src/main/java/fr/xephi/authme/command/executable/authme/PurgeBannedPlayersCommand.java +++ b/src/main/java/fr/xephi/authme/command/executable/authme/PurgeBannedPlayersCommand.java @@ -4,15 +4,18 @@ import fr.xephi.authme.AuthMe; import fr.xephi.authme.command.CommandService; import fr.xephi.authme.command.ExecutableCommand; import fr.xephi.authme.datasource.DataSource; -import fr.xephi.authme.hooks.PluginHooks; -import fr.xephi.authme.settings.properties.PurgeSettings; +import fr.xephi.authme.task.PurgeTask; import fr.xephi.authme.util.BukkitService; -import org.bukkit.OfflinePlayer; -import org.bukkit.command.CommandSender; + +import java.util.HashSet; +import java.util.List; +import java.util.Set; import javax.inject.Inject; -import java.util.ArrayList; -import java.util.List; +import org.bukkit.ChatColor; + +import org.bukkit.OfflinePlayer; +import org.bukkit.command.CommandSender; /** * Command for purging data of banned players. Depending on the settings @@ -23,9 +26,6 @@ public class PurgeBannedPlayersCommand implements ExecutableCommand { @Inject private DataSource dataSource; - @Inject - private PluginHooks pluginHooks; - @Inject private AuthMe plugin; @@ -35,24 +35,18 @@ public class PurgeBannedPlayersCommand implements ExecutableCommand { @Override public void executeCommand(CommandSender sender, List arguments, CommandService commandService) { // Get the list of banned players - List bannedPlayers = new ArrayList<>(); - for (OfflinePlayer offlinePlayer : bukkitService.getBannedPlayers()) { - bannedPlayers.add(offlinePlayer.getName().toLowerCase()); + Set namedBanned = new HashSet<>(); + Set bannedPlayers = bukkitService.getBannedPlayers(); + for (OfflinePlayer offlinePlayer : bannedPlayers) { + namedBanned.add(offlinePlayer.getName().toLowerCase()); } + //todo: note this should may run async because it may executes a SQL-Query // Purge the banned players - dataSource.purgeBanned(bannedPlayers); - if (commandService.getProperty(PurgeSettings.REMOVE_ESSENTIALS_FILES) - && pluginHooks.isEssentialsAvailable()) - plugin.dataManager.purgeEssentials(bannedPlayers); - if (commandService.getProperty(PurgeSettings.REMOVE_PLAYER_DAT)) - plugin.dataManager.purgeDat(bannedPlayers); - if (commandService.getProperty(PurgeSettings.REMOVE_LIMITED_CREATIVE_INVENTORIES)) - plugin.dataManager.purgeLimitedCreative(bannedPlayers); - if (commandService.getProperty(PurgeSettings.REMOVE_ANTI_XRAY_FILE)) - plugin.dataManager.purgeAntiXray(bannedPlayers); + dataSource.purgeBanned(namedBanned); // Show a status message - sender.sendMessage("[AuthMe] Database has been purged correctly"); + sender.sendMessage(ChatColor.GOLD + "Purging user accounts..."); + new PurgeTask(plugin, sender, namedBanned, bannedPlayers).runTaskTimer(plugin, 0, 1); } } diff --git a/src/main/java/fr/xephi/authme/command/executable/authme/PurgeCommand.java b/src/main/java/fr/xephi/authme/command/executable/authme/PurgeCommand.java index afc04c5d5..a0811cfed 100644 --- a/src/main/java/fr/xephi/authme/command/executable/authme/PurgeCommand.java +++ b/src/main/java/fr/xephi/authme/command/executable/authme/PurgeCommand.java @@ -5,13 +5,15 @@ import fr.xephi.authme.command.CommandService; import fr.xephi.authme.command.ExecutableCommand; import fr.xephi.authme.datasource.DataSource; import fr.xephi.authme.hooks.PluginHooks; -import fr.xephi.authme.settings.properties.PurgeSettings; +import fr.xephi.authme.task.PurgeTask; +import fr.xephi.authme.util.BukkitService; import org.bukkit.ChatColor; import org.bukkit.command.CommandSender; import javax.inject.Inject; import java.util.Calendar; import java.util.List; +import java.util.Set; /** * Command for purging the data of players which have not been since for a given number @@ -27,6 +29,9 @@ public class PurgeCommand implements ExecutableCommand { @Inject private PluginHooks pluginHooks; + @Inject + private BukkitService bukkitService; + @Inject private AuthMe plugin; @@ -56,24 +61,13 @@ public class PurgeCommand implements ExecutableCommand { calendar.add(Calendar.DATE, -days); long until = calendar.getTimeInMillis(); + //todo: note this should may run async because it may executes a SQL-Query // Purge the data, get the purged values - List purged = dataSource.autoPurgeDatabase(until); + Set purged = dataSource.autoPurgeDatabase(until); // Show a status message sender.sendMessage(ChatColor.GOLD + "Deleted " + purged.size() + " user accounts"); - - // Purge other data - if (commandService.getProperty(PurgeSettings.REMOVE_ESSENTIALS_FILES) - && pluginHooks.isEssentialsAvailable()) - plugin.dataManager.purgeEssentials(purged); - if (commandService.getProperty(PurgeSettings.REMOVE_PLAYER_DAT)) - plugin.dataManager.purgeDat(purged); - if (commandService.getProperty(PurgeSettings.REMOVE_LIMITED_CREATIVE_INVENTORIES)) - plugin.dataManager.purgeLimitedCreative(purged); - if (commandService.getProperty(PurgeSettings.REMOVE_ANTI_XRAY_FILE)) - plugin.dataManager.purgeAntiXray(purged); - - // Show a status message - sender.sendMessage(ChatColor.GREEN + "[AuthMe] Database has been purged correctly"); + sender.sendMessage(ChatColor.GOLD + "Purging user accounts..."); + new PurgeTask(plugin, sender, purged).runTaskTimer(plugin, 0, 1); } } diff --git a/src/main/java/fr/xephi/authme/datasource/CacheDataSource.java b/src/main/java/fr/xephi/authme/datasource/CacheDataSource.java index 12729fc10..9f2843d1c 100644 --- a/src/main/java/fr/xephi/authme/datasource/CacheDataSource.java +++ b/src/main/java/fr/xephi/authme/datasource/CacheDataSource.java @@ -1,11 +1,5 @@ package fr.xephi.authme.datasource; -import java.util.ArrayList; -import java.util.List; -import java.util.concurrent.Callable; -import java.util.concurrent.Executors; -import java.util.concurrent.TimeUnit; - import com.google.common.base.Optional; import com.google.common.cache.CacheBuilder; import com.google.common.cache.CacheLoader; @@ -20,8 +14,13 @@ import fr.xephi.authme.cache.auth.PlayerAuth; import fr.xephi.authme.cache.auth.PlayerCache; import fr.xephi.authme.security.crypts.HashedPassword; -/** - */ +import java.util.ArrayList; +import java.util.List; +import java.util.Set; +import java.util.concurrent.Callable; +import java.util.concurrent.Executors; +import java.util.concurrent.TimeUnit; + public class CacheDataSource implements DataSource { private final DataSource source; @@ -140,11 +139,12 @@ public class CacheDataSource implements DataSource { } @Override - public List autoPurgeDatabase(long until) { - List cleared = source.autoPurgeDatabase(until); + public Set autoPurgeDatabase(long until) { + Set cleared = source.autoPurgeDatabase(until); for (String name : cleared) { cachedAuths.invalidate(name); } + return cleared; } @@ -190,7 +190,7 @@ public class CacheDataSource implements DataSource { } @Override - public synchronized void purgeBanned(final List banned) { + public synchronized void purgeBanned(final Set banned) { source.purgeBanned(banned); cachedAuths.invalidateAll(banned); } diff --git a/src/main/java/fr/xephi/authme/datasource/DataSource.java b/src/main/java/fr/xephi/authme/datasource/DataSource.java index d80affc4f..dd80bc769 100644 --- a/src/main/java/fr/xephi/authme/datasource/DataSource.java +++ b/src/main/java/fr/xephi/authme/datasource/DataSource.java @@ -5,6 +5,7 @@ import fr.xephi.authme.initialization.Reloadable; import fr.xephi.authme.security.crypts.HashedPassword; import java.util.List; +import java.util.Set; /** * Interface for manipulating {@link PlayerAuth} objects from a data source. @@ -75,7 +76,7 @@ public interface DataSource extends Reloadable { * @param until The minimum last login * @return The account names that have been removed */ - List autoPurgeDatabase(long until); + Set autoPurgeDatabase(long until); /** * Remove a user record from the database. @@ -127,7 +128,7 @@ public interface DataSource extends Reloadable { * * @param banned the list of players to delete */ - void purgeBanned(List banned); + void purgeBanned(Set banned); /** * Return the data source type. diff --git a/src/main/java/fr/xephi/authme/datasource/FlatFile.java b/src/main/java/fr/xephi/authme/datasource/FlatFile.java index b36fa2794..fa9d291ec 100644 --- a/src/main/java/fr/xephi/authme/datasource/FlatFile.java +++ b/src/main/java/fr/xephi/authme/datasource/FlatFile.java @@ -17,7 +17,9 @@ import java.io.FileReader; import java.io.FileWriter; import java.io.IOException; import java.util.ArrayList; +import java.util.HashSet; import java.util.List; +import java.util.Set; /** * Deprecated flat file datasource. The only method guaranteed to work is {@link FlatFile#getAllAuths()} @@ -227,11 +229,11 @@ public class FlatFile implements DataSource { } @Override - public List autoPurgeDatabase(long until) { + public Set autoPurgeDatabase(long until) { BufferedReader br = null; BufferedWriter bw = null; ArrayList lines = new ArrayList<>(); - List cleared = new ArrayList<>(); + Set cleared = new HashSet<>(); try { br = new BufferedReader(new FileReader(source)); String line; @@ -256,6 +258,7 @@ public class FlatFile implements DataSource { silentClose(br); silentClose(bw); } + return cleared; } @@ -414,7 +417,7 @@ public class FlatFile implements DataSource { } @Override - public void purgeBanned(List banned) { + public void purgeBanned(Set banned) { BufferedReader br = null; BufferedWriter bw = null; ArrayList lines = new ArrayList<>(); diff --git a/src/main/java/fr/xephi/authme/datasource/MySQL.java b/src/main/java/fr/xephi/authme/datasource/MySQL.java index 9e274e5e6..694c2fc8d 100644 --- a/src/main/java/fr/xephi/authme/datasource/MySQL.java +++ b/src/main/java/fr/xephi/authme/datasource/MySQL.java @@ -1,16 +1,5 @@ package fr.xephi.authme.datasource; -import java.sql.Blob; -import java.sql.Connection; -import java.sql.DatabaseMetaData; -import java.sql.PreparedStatement; -import java.sql.ResultSet; -import java.sql.SQLException; -import java.sql.Statement; -import java.sql.Types; -import java.util.ArrayList; -import java.util.List; - import com.google.common.annotations.VisibleForTesting; import com.zaxxer.hikari.HikariDataSource; import com.zaxxer.hikari.pool.HikariPool.PoolInitializationException; @@ -27,8 +16,19 @@ import fr.xephi.authme.settings.properties.HooksSettings; import fr.xephi.authme.settings.properties.SecuritySettings; import fr.xephi.authme.util.StringUtils; -/** - */ +import java.sql.Blob; +import java.sql.Connection; +import java.sql.DatabaseMetaData; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; +import java.sql.Types; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + public class MySQL implements DataSource { private final String host; @@ -611,8 +611,8 @@ public class MySQL implements DataSource { } @Override - public synchronized List autoPurgeDatabase(long until) { - List list = new ArrayList<>(); + public synchronized Set autoPurgeDatabase(long until) { + Set list = new HashSet<>(); String select = "SELECT " + col.NAME + " FROM " + tableName + " WHERE " + col.LAST_LOGIN + " banned) { + public synchronized void purgeBanned(Set banned) { String sql = "DELETE FROM " + tableName + " WHERE " + col.NAME + "=?;"; try (Connection con = getConnection(); PreparedStatement pst = con.prepareStatement(sql)) { for (String name : banned) { diff --git a/src/main/java/fr/xephi/authme/datasource/SQLite.java b/src/main/java/fr/xephi/authme/datasource/SQLite.java index fe6708134..fa204a6f3 100644 --- a/src/main/java/fr/xephi/authme/datasource/SQLite.java +++ b/src/main/java/fr/xephi/authme/datasource/SQLite.java @@ -7,7 +7,9 @@ import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; import java.util.ArrayList; +import java.util.HashSet; import java.util.List; +import java.util.Set; import com.google.common.annotations.VisibleForTesting; @@ -291,8 +293,8 @@ public class SQLite implements DataSource { } @Override - public List autoPurgeDatabase(long until) { - List list = new ArrayList<>(); + public Set autoPurgeDatabase(long until) { + Set list = new HashSet<>(); String select = "SELECT " + col.NAME + " FROM " + tableName + " WHERE " + col.LAST_LOGIN + " banned) { + public void purgeBanned(Set banned) { String sql = "DELETE FROM " + tableName + " WHERE " + col.NAME + "=?;"; try (PreparedStatement pst = con.prepareStatement(sql)) { for (String name : banned) { diff --git a/src/main/java/fr/xephi/authme/task/PurgeTask.java b/src/main/java/fr/xephi/authme/task/PurgeTask.java new file mode 100644 index 000000000..97ffcc88f --- /dev/null +++ b/src/main/java/fr/xephi/authme/task/PurgeTask.java @@ -0,0 +1,158 @@ +package fr.xephi.authme.task; + +import fr.xephi.authme.AuthMe; +import fr.xephi.authme.ConsoleLogger; +import fr.xephi.authme.settings.NewSetting; +import fr.xephi.authme.settings.properties.PurgeSettings; + +import java.util.HashSet; +import java.util.Set; +import java.util.UUID; + +import org.bukkit.Bukkit; +import org.bukkit.ChatColor; +import org.bukkit.OfflinePlayer; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; +import org.bukkit.scheduler.BukkitRunnable; + +public class PurgeTask extends BukkitRunnable { + + //how many players we should check for each tick + private static final int INTERVALL_CHECK = 5; + + private final AuthMe plugin; + private final NewSetting newSetting; + + private final UUID sender; + private final Set toPurge; + + private final OfflinePlayer[] offlinePlayers; + + private final boolean autoPurging; + private final int totalPurgeCount; + + private int currentPage = 0; + + public PurgeTask(AuthMe plugin, CommandSender sender, Set purged) { + this(plugin, sender, purged, false, Bukkit.getOfflinePlayers()); + } + + public PurgeTask(AuthMe plugin, CommandSender sender, Set purged, Set offlinePlayers) { + this(plugin, sender, purged, false + , offlinePlayers.toArray(new OfflinePlayer[offlinePlayers.size()])); + } + + public PurgeTask(AuthMe plugin, CommandSender sender, Set purged + , boolean autoPurge, OfflinePlayer[] offlinePlayers) { + this.plugin = plugin; + this.newSetting = plugin.getSettings(); + + if (sender instanceof Player) { + this.sender = ((Player) sender).getUniqueId(); + } else { + this.sender = null; + } + + this.toPurge = purged; + this.totalPurgeCount = purged.size(); + this.autoPurging = autoPurge; + this.offlinePlayers = offlinePlayers; + + //this is commented out because I assume all players in the database already have an lowercase name +// toPurge = new HashSet<>(purged.size()); + //make a new list with lowercase names to make the username test based on a hash +// for (String username : purged) { +// toPurge.add(username.toLowerCase()); +// } + } + + @Override + public void run() { + if (toPurge.isEmpty()) { + //everything was removed + finish(); + return; + } + + Set playerPortion = new HashSet(INTERVALL_CHECK); + Set namePortion = new HashSet(INTERVALL_CHECK); + for (int i = 0; i < INTERVALL_CHECK; i++) { + int nextPosition = (currentPage * INTERVALL_CHECK) + i; + if (offlinePlayers.length >= nextPosition) { + //no more offline players on this page + break; + } + + OfflinePlayer offlinePlayer = offlinePlayers[nextPosition]; + String offlineName = offlinePlayer.getName(); + //remove to speed up later lookups + if (toPurge.remove(offlineName.toLowerCase())) { + playerPortion.add(offlinePlayer); + namePortion.add(offlinePlayer.getName()); + } + } + + if (!toPurge.isEmpty() && playerPortion.isEmpty()) { + ConsoleLogger.info("Finished lookup up offlinePlayers. Begin looking purging player names only"); + + //we went through all offlineplayers but there are still names remaining + namePortion.addAll(toPurge); + toPurge.clear(); + } + + currentPage++; + purgeData(playerPortion, namePortion); + if (currentPage % 20 == 0) { + int completed = totalPurgeCount - toPurge.size(); + sendMessage("[AuthMe] Purge progress " + completed + '/' + totalPurgeCount); + } + } + + private void purgeData(Set playerPortion, Set namePortion) { + // Purge other data + if (newSetting.getProperty(PurgeSettings.REMOVE_ESSENTIALS_FILES) + && plugin.getPluginHooks().isEssentialsAvailable()) { + plugin.dataManager.purgeEssentials(playerPortion); + } + + if (newSetting.getProperty(PurgeSettings.REMOVE_PLAYER_DAT)) { + plugin.dataManager.purgeDat(playerPortion); + } + + if (newSetting.getProperty(PurgeSettings.REMOVE_LIMITED_CREATIVE_INVENTORIES)) { + plugin.dataManager.purgeLimitedCreative(namePortion); + } + + if (newSetting.getProperty(PurgeSettings.REMOVE_ANTI_XRAY_FILE)) { + plugin.dataManager.purgeAntiXray(namePortion); + } + + if (newSetting.getProperty(PurgeSettings.REMOVE_PERMISSIONS)) { + plugin.dataManager.purgePermissions(playerPortion); + } + } + + private void finish() { + cancel(); + + // Show a status message + sendMessage(ChatColor.GREEN + "[AuthMe] Database has been purged correctly"); + + ConsoleLogger.info("AutoPurge Finished!"); + if (autoPurging) { + plugin.notifyAutoPurgeEnd(); + } + } + + private void sendMessage(String message) { + if (sender == null) { + Bukkit.getConsoleSender().sendMessage(message); + } else { + Player player = Bukkit.getPlayer(sender); + if (player != null) { + player.sendMessage(message); + } + } + } +} diff --git a/src/test/java/fr/xephi/authme/datasource/AbstractDataSourceIntegrationTest.java b/src/test/java/fr/xephi/authme/datasource/AbstractDataSourceIntegrationTest.java index 30f3484ed..bf8d54d5e 100644 --- a/src/test/java/fr/xephi/authme/datasource/AbstractDataSourceIntegrationTest.java +++ b/src/test/java/fr/xephi/authme/datasource/AbstractDataSourceIntegrationTest.java @@ -10,6 +10,8 @@ import java.util.List; import static fr.xephi.authme.AuthMeMatchers.equalToHash; import static fr.xephi.authme.AuthMeMatchers.hasAuthBasicData; import static fr.xephi.authme.AuthMeMatchers.hasAuthLocation; +import java.util.HashSet; +import java.util.Set; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.hasItem; import static org.hamcrest.Matchers.hasSize; @@ -221,7 +223,7 @@ public abstract class AbstractDataSourceIntegrationTest { public void shouldDeletePlayers() { // given DataSource dataSource = getDataSource(); - List playersToDelete = Arrays.asList("bobby", "doesNotExist"); + Set playersToDelete = new HashSet<>(Arrays.asList("bobby", "doesNotExist")); assumeThat(dataSource.getAccountsRegistered(), equalTo(2)); // when diff --git a/src/test/java/fr/xephi/authme/datasource/AbstractResourceClosingTest.java b/src/test/java/fr/xephi/authme/datasource/AbstractResourceClosingTest.java index 75908e3e2..b17f51350 100644 --- a/src/test/java/fr/xephi/authme/datasource/AbstractResourceClosingTest.java +++ b/src/test/java/fr/xephi/authme/datasource/AbstractResourceClosingTest.java @@ -32,6 +32,7 @@ import java.sql.SQLException; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; +import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; @@ -192,8 +193,8 @@ public abstract class AbstractResourceClosingTest { for (Class paramType : method.getParameterTypes()) { // Checking List.class == paramType instead of Class#isAssignableFrom means we really only accept List, // but that is a sensible assumption and makes our life much easier later on when juggling with Type - Object param = (List.class == paramType) - ? getTypedList(method.getGenericParameterTypes()[index]) + Object param = Collection.class.isAssignableFrom(paramType) + ? getTypedCollection(method.getGenericParameterTypes()[index]) : PARAM_VALUES.get(paramType); Preconditions.checkNotNull(param, "No param type for " + paramType); params.add(param); @@ -208,15 +209,21 @@ public abstract class AbstractResourceClosingTest { * @param type The list type to process and build a test list for * @return Test list with sample elements of the correct type */ - private static List getTypedList(Type type) { + private static Collection getTypedCollection(Type type) { if (type instanceof ParameterizedType) { ParameterizedType parameterizedType = (ParameterizedType) type; - Preconditions.checkArgument(List.class == parameterizedType.getRawType(), type + " should be a List"); + Preconditions.checkArgument(Collection.class.isAssignableFrom((Class) parameterizedType.getRawType()), + type + " should extend from Collection"); Type genericType = parameterizedType.getActualTypeArguments()[0]; Object element = PARAM_VALUES.get(genericType); Preconditions.checkNotNull(element, "No sample element for list of generic type " + genericType); - return Arrays.asList(element, element, element); + if (List.class == parameterizedType.getRawType()) { + return Arrays.asList(element, element, element); + } else if (Set.class == parameterizedType.getRawType()) { + return new HashSet<>(Arrays.asList(element, element, element)); + } + throw new IllegalStateException("Unknown collection type " + parameterizedType.getRawType()); } throw new IllegalStateException("Cannot build list for unexpected Type: " + type); }