diff --git a/Plan/src/main/java/com/djrapitops/plan/systems/info/ImporterManager.java b/Plan/src/main/java/com/djrapitops/plan/systems/info/ImporterManager.java new file mode 100644 index 000000000..0d93b5ba2 --- /dev/null +++ b/Plan/src/main/java/com/djrapitops/plan/systems/info/ImporterManager.java @@ -0,0 +1,50 @@ +/* + * Licence is provided in the jar as license.yml also here: + * https://github.com/Rsl1122/Plan-PlayerAnalytics/blob/master/Plan/src/main/resources/license.yml + */ +package main.java.com.djrapitops.plan.systems.info; + +import main.java.com.djrapitops.plan.systems.processing.importing.importers.Importer; + +import java.util.ArrayList; +import java.util.List; + +/** + * @author Fuzzlemann + */ +public class ImporterManager { + + /** + * Constructor used to hide the public constructor + */ + private ImporterManager() { + throw new IllegalStateException("Utility class"); + } + + private static final List registry = new ArrayList<>(); + + public static void registerImporter(Importer importer) { + String firstName = importer.getNames().get(0); + + if (getImporter(firstName) != null) { + removeImporter(firstName); + } + + registry.add(importer); + } + + public static List getImporters() { + return registry; + } + + public static Importer getImporter(String name) { + return registry.stream() + .filter(importer -> importer.getNames().contains(name)) + .findAny() + .orElse(null); + } + + public static void removeImporter(String name) { + registry.removeIf(importer -> importer.getNames().contains(name)); + } +} diff --git a/Plan/src/main/java/com/djrapitops/plan/systems/processing/importing/Importer.java b/Plan/src/main/java/com/djrapitops/plan/systems/processing/importing/Importer.java deleted file mode 100644 index 8c8e624f8..000000000 --- a/Plan/src/main/java/com/djrapitops/plan/systems/processing/importing/Importer.java +++ /dev/null @@ -1,11 +0,0 @@ -package main.java.com.djrapitops.plan.systems.processing.importing; - -/** - * Abstract class used for importing data from other plugins. - * - * @author Rsl1122 - * @since 4.0.0 - */ -public abstract class Importer { - // TODO write new Importer -} \ No newline at end of file diff --git a/Plan/src/main/java/com/djrapitops/plan/systems/processing/importing/OfflinePlayerImporter.java b/Plan/src/main/java/com/djrapitops/plan/systems/processing/importing/OfflinePlayerImporter.java deleted file mode 100644 index bfb6752de..000000000 --- a/Plan/src/main/java/com/djrapitops/plan/systems/processing/importing/OfflinePlayerImporter.java +++ /dev/null @@ -1,15 +0,0 @@ -package main.java.com.djrapitops.plan.systems.processing.importing; - -/** - * Imports all players who have not joined since Plan was installed. - * - * @author Rsl1122 - * @since 3.5.0 - */ -public class OfflinePlayerImporter extends Importer { - - public OfflinePlayerImporter() { - // TODO Rewrite - } - -} diff --git a/Plan/src/main/java/com/djrapitops/plan/systems/processing/importing/ServerImportData.java b/Plan/src/main/java/com/djrapitops/plan/systems/processing/importing/ServerImportData.java new file mode 100644 index 000000000..1fd330eb9 --- /dev/null +++ b/Plan/src/main/java/com/djrapitops/plan/systems/processing/importing/ServerImportData.java @@ -0,0 +1,82 @@ +/* + * Licence is provided in the jar as license.yml also here: + * https://github.com/Rsl1122/Plan-PlayerAnalytics/blob/master/Plan/src/main/resources/license.yml + */ +package main.java.com.djrapitops.plan.systems.processing.importing; + +import main.java.com.djrapitops.plan.data.TPS; + +import java.util.*; + +/** + * @author Fuzzlemann + */ +public class ServerImportData { + + private Map commandUsages; + private List tpsData; + + private ServerImportData(Map commandUsages, List tpsData) { + this.commandUsages = commandUsages; + this.tpsData = tpsData; + } + + public static ServerImportDataBuilder builder() { + return new ServerImportDataBuilder(); + } + + public Map getCommandUsages() { + return commandUsages; + } + + public void setCommandUsages(Map commandUsages) { + this.commandUsages = commandUsages; + } + + public List getTpsData() { + return tpsData; + } + + public void setTpsData(List tpsData) { + this.tpsData = tpsData; + } + + public static final class ServerImportDataBuilder { + private final Map commandUsages = new HashMap<>(); + private final List tpsData = new ArrayList<>(); + + private ServerImportDataBuilder() { + throw new IllegalStateException("Builder class"); + } + + public ServerImportDataBuilder commandUsage(String command, Integer usages) { + this.commandUsages.put(command, usages); + return this; + } + + public ServerImportDataBuilder commandUsages(Map commandUsages) { + this.commandUsages.putAll(commandUsages); + return this; + } + + public ServerImportDataBuilder tpsData(long date, double ticksPerSecond, int players, double cpuUsage, long usedMemory, int entityCount, int chunksLoaded) { + TPS tps = new TPS(date, ticksPerSecond, players, cpuUsage, usedMemory, entityCount, chunksLoaded); + this.tpsData.add(tps); + return this; + } + + public ServerImportDataBuilder tpsData(TPS... tpsData) { + this.tpsData.addAll(Arrays.asList(tpsData)); + return this; + } + + public ServerImportDataBuilder tpsData(Collection tpsData) { + this.tpsData.addAll(tpsData); + return this; + } + + public ServerImportData build() { + return new ServerImportData(commandUsages, tpsData); + } + } +} diff --git a/Plan/src/main/java/com/djrapitops/plan/systems/processing/importing/UserImportData.java b/Plan/src/main/java/com/djrapitops/plan/systems/processing/importing/UserImportData.java new file mode 100644 index 000000000..9e20cc127 --- /dev/null +++ b/Plan/src/main/java/com/djrapitops/plan/systems/processing/importing/UserImportData.java @@ -0,0 +1,276 @@ +/* + * Licence is provided in the jar as license.yml also here: + * https://github.com/Rsl1122/Plan-PlayerAnalytics/blob/master/Plan/src/main/resources/license.yml + */ +package main.java.com.djrapitops.plan.systems.processing.importing; + +import main.java.com.djrapitops.plan.data.PlayerKill; +import main.java.com.djrapitops.plan.data.time.GMTimes; + +import java.util.*; + +/** + * @author Fuzzlemann + * @since 4.0.0 + */ +public class UserImportData { + + private String name; + private String uuid; + private List nicknames; + + private long registered; + private boolean op; + + private boolean banned; + private int timesKicked; + + private List ips; + private Map worldTimes; + + private List kills; + private int mobKills; + private int deaths; + + private UserImportData(String name, String uuid, List nicknames, long registered, boolean op, boolean banned, int timesKicked, List ips, Map worldTimes, List kills, int mobKills, int deaths) { + this.name = name; + this.uuid = uuid; + this.nicknames = nicknames; + this.registered = registered; + this.op = op; + this.banned = banned; + this.timesKicked = timesKicked; + this.ips = ips; + this.worldTimes = worldTimes; + this.kills = kills; + this.mobKills = mobKills; + this.deaths = deaths; + } + + public static UserImportDataBuilder builder() { + return new UserImportDataBuilder(); + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getUuid() { + return uuid; + } + + public void setUuid(String uuid) { + this.uuid = uuid; + } + + public List getNicknames() { + return nicknames; + } + + public void setNicknames(List nicknames) { + this.nicknames = nicknames; + } + + public long getRegistered() { + return registered; + } + + public void setRegistered(long registered) { + this.registered = registered; + } + + public boolean isOp() { + return op; + } + + public void setOp(boolean op) { + this.op = op; + } + + public boolean isBanned() { + return banned; + } + + public void setBanned(boolean banned) { + this.banned = banned; + } + + public int getTimesKicked() { + return timesKicked; + } + + public void setTimesKicked(int timesKicked) { + this.timesKicked = timesKicked; + } + + public List getIps() { + return ips; + } + + public void setIps(List ips) { + this.ips = ips; + } + + public Map getWorldTimes() { + return worldTimes; + } + + public void setWorldTimes(Map worldTimes) { + this.worldTimes = worldTimes; + } + + public List getKills() { + return kills; + } + + public void setKills(List kills) { + this.kills = kills; + } + + public int getMobKills() { + return mobKills; + } + + public void setMobKills(int mobKills) { + this.mobKills = mobKills; + } + + public int getDeaths() { + return deaths; + } + + public void setDeaths(int deaths) { + this.deaths = deaths; + } + + public static final class UserImportDataBuilder { + private String name; + private String uuid; + + private long registered; + private boolean op; + private final List nicknames = new ArrayList<>(); + + private boolean banned; + private int timesKicked; + + private final List ips = new ArrayList<>(); + private final Map worldTimes = new HashMap<>(); + + private final List kills = new ArrayList<>(); + private int mobKills; + private int deaths; + + private UserImportDataBuilder() { + throw new IllegalStateException("Builder class"); + } + + public UserImportDataBuilder name(String name) { + this.name = name; + return this; + } + + public UserImportDataBuilder uuid(UUID uuid) { + return uuid(uuid.toString()); + } + + public UserImportDataBuilder uuid(String uuid) { + this.uuid = uuid; + return this; + } + + public UserImportDataBuilder registered(long registered) { + this.registered = registered; + return this; + } + + public UserImportDataBuilder op() { + return op(true); + } + + public UserImportDataBuilder op(boolean op) { + this.op = op; + return this; + } + + public UserImportDataBuilder nicknames(String... nicknames) { + this.nicknames.addAll(Arrays.asList(nicknames)); + return this; + } + + public UserImportDataBuilder nickNames(Collection nicknames) { + this.nicknames.addAll(nicknames); + return this; + } + + public UserImportDataBuilder banned() { + return banned(true); + } + + public UserImportDataBuilder banned(boolean banned) { + this.banned = banned; + return this; + } + + public UserImportDataBuilder timesKicked(int timesKicked) { + this.timesKicked += timesKicked; + return this; + } + + public UserImportDataBuilder ips(String... ips) { + this.ips.addAll(Arrays.asList(ips)); + return this; + } + + public UserImportDataBuilder ips(Collection ips) { + this.ips.addAll(ips); + return this; + } + + public UserImportDataBuilder worldTimes(String worldName, long... times) { + GMTimes gmTimes = new GMTimes(); + gmTimes.setAllGMTimes(times); + + this.worldTimes.put(worldName, gmTimes); + return this; + } + + public UserImportDataBuilder worldTimes(String worldName, GMTimes gmTimes) { + this.worldTimes.put(worldName, gmTimes); + return this; + } + + public UserImportDataBuilder worldTimes(Map worldTimes) { + this.worldTimes.putAll(worldTimes); + return this; + } + + public UserImportDataBuilder kills(PlayerKill... kills) { + this.kills.addAll(Arrays.asList(kills)); + return this; + } + + public UserImportDataBuilder kills(Collection kills) { + this.kills.addAll(kills); + return this; + } + + public UserImportDataBuilder mobKills(int mobKills) { + this.mobKills += mobKills; + return this; + } + + public UserImportDataBuilder deaths(int deaths) { + this.deaths += deaths; + return this; + } + + public UserImportData build() { + return new UserImportData(name, uuid, nicknames, registered, op, banned, timesKicked, ips, worldTimes, kills, mobKills, deaths); + } + } +} diff --git a/Plan/src/main/java/com/djrapitops/plan/systems/processing/importing/UserImportRefiner.java b/Plan/src/main/java/com/djrapitops/plan/systems/processing/importing/UserImportRefiner.java new file mode 100644 index 000000000..2c013c784 --- /dev/null +++ b/Plan/src/main/java/com/djrapitops/plan/systems/processing/importing/UserImportRefiner.java @@ -0,0 +1,253 @@ +/* + * Licence is provided in the jar as license.yml also here: + * https://github.com/Rsl1122/Plan-PlayerAnalytics/blob/master/Plan/src/main/resources/license.yml + */ +package main.java.com.djrapitops.plan.systems.processing.importing; + +import com.djrapitops.plugin.utilities.player.UUIDFetcher; +import main.java.com.djrapitops.plan.Log; +import main.java.com.djrapitops.plan.Plan; +import main.java.com.djrapitops.plan.utilities.Benchmark; +import org.bukkit.OfflinePlayer; +import org.bukkit.World; + +import java.util.*; +import java.util.stream.Collectors; + +/** + * A class for refining the {@link UserImportData}s + * so no {@code null} is left in any field. + * It also removes invalid data. + * + * @author Fuzzlemann + * @since 4.0.0 + */ +public class UserImportRefiner { + + private final Plan plugin; + private final boolean onlineMode; + + private final Vector importers = new Vector<>(); + + private final Map worlds = new Hashtable<>(); + + private final Map uuidsMissing = new Hashtable<>(); + private final Map namesMissing = new Hashtable<>(); + + private final Map foundUUIDs = new Hashtable<>(); + private final Map foundNames = new Hashtable<>(); + + public UserImportRefiner(Plan plugin, List importers) { + this.plugin = plugin; + this.importers.addAll(importers); + + onlineMode = plugin.getServer().getOnlineMode(); + } + + public List refineData() { + String benchmarkName = "Refining UserImportData"; + + Benchmark.start(benchmarkName); + processMissingIdentifiers(); + processOldWorlds(); + Benchmark.stop(benchmarkName); + + return importers; + } + + private void processOldWorlds() { + String benchmarkName = "Processing old worlds"; + + Benchmark.start(benchmarkName); + + importers.parallelStream() + .flatMap(importer -> importer.getWorldTimes().keySet().stream()) + .forEach(this::checkOldWorld); + + if (!worlds.containsValue(true)) { + return; + } + + worlds.values().removeIf(old -> false); + + importers.parallelStream() + .forEach(importer -> importer.getWorldTimes().keySet().removeAll(worlds.keySet())); + + Benchmark.stop(benchmarkName); + } + + private void checkOldWorld(String worldName) { + if (worlds.containsKey(worldName)) { + return; + } + + World world = plugin.getServer().getWorld(worldName); + boolean old = world == null; + + worlds.put(worldName, old); + } + + private void processMissingIdentifiers() { + String benchmarkName = "Processing missing identifiers"; + + Benchmark.start(benchmarkName); + + Vector invalidData = new Vector<>(); + + importers.parallelStream().forEach(importer -> { + String name = importer.getName(); + String uuid = importer.getUuid(); + + boolean nameNull = name == null; + boolean uuidNull = uuid == null; + + if (nameNull && uuidNull) { + invalidData.add(importer); + } + + if (nameNull) { + namesMissing.put(importer, uuid); + } else if (uuidNull) { + uuidsMissing.put(importer, name); + } + }); + + importers.removeAll(invalidData); + + processMissingUUIDs(); + processMissingNames(); + + Benchmark.stop(benchmarkName); + } + + private void processMissingUUIDs() { + String benchmarkName = "Processing missing UUIDs"; + + Benchmark.start(benchmarkName); + + if (onlineMode) { + addMissingUUIDsOverFetcher(); + addMissingUUIDsOverOfflinePlayer(); + } else { + addMissingUUIDsOverOfflinePlayer(); + addMissingUUIDsOverFetcher(); + } + + foundUUIDs.entrySet().parallelStream().forEach(entry -> entry.getKey().setUuid(entry.getValue())); + + importers.removeAll(uuidsMissing.keySet()); + + Benchmark.stop(benchmarkName); + } + + private void addMissingUUIDsOverFetcher() { + UUIDFetcher uuidFetcher = new UUIDFetcher(new ArrayList<>(uuidsMissing.values())); + + Map result; + + try { + result = uuidFetcher.call().entrySet().parallelStream() + .collect(Collectors.toMap(entry -> entry.getValue().toString(), Map.Entry::getKey)); + } catch (Exception e) { + Log.toLog(this.getClass().getName(), e); + return; + } + + addFoundUUIDs(result); + } + + private void addMissingUUIDsOverOfflinePlayer() { + Map result = new Hashtable<>(); + + for (String name : uuidsMissing.values()) { + String uuid = getUuidByOfflinePlayer(name); + + if (uuid == null) { + continue; + } + + result.put(name, uuid); + } + + addFoundUUIDs(result); + } + + private void addFoundUUIDs(Map foundUUIDs) { + Vector found = new Vector<>(); + + uuidsMissing.entrySet().parallelStream().forEach((entry) -> { + UserImportData importer = entry.getKey(); + String name = entry.getValue(); + + String uuid = foundUUIDs.get(name); + + this.foundUUIDs.put(importer, uuid); + found.add(importer); + }); + + uuidsMissing.keySet().removeAll(found); + } + + @SuppressWarnings("deprecation") + private String getUuidByOfflinePlayer(String name) { + OfflinePlayer player = plugin.getServer().getOfflinePlayer(name); + + if (!player.hasPlayedBefore()) { + return null; + } + + return player.getUniqueId().toString(); + } + + private void processMissingNames() { + String benchmarkNames = "Processing missing names"; + + Benchmark.start(benchmarkNames); + + addMissingNames(); + + foundNames.entrySet().parallelStream().forEach(entry -> entry.getKey().setName(entry.getValue())); + + importers.removeAll(namesMissing.keySet()); + + Benchmark.stop(benchmarkNames); + } + + private void addMissingNames() { + Map result = new Hashtable<>(); + + namesMissing.values().parallelStream().forEach(uuid -> { + String name = getNameByOfflinePlayer(uuid); + + result.put(uuid, name); + }); + + addFoundNames(result); + } + + private void addFoundNames(Map foundNames) { + Vector found = new Vector<>(); + + namesMissing.entrySet().parallelStream().forEach(entry -> { + UserImportData importer = entry.getKey(); + String uuid = entry.getValue(); + + String name = foundNames.get(uuid); + + this.foundNames.put(importer, name); + found.add(importer); + }); + + namesMissing.keySet().removeAll(found); + } + + private String getNameByOfflinePlayer(String uuid) { + OfflinePlayer player = plugin.getServer().getOfflinePlayer(UUID.fromString(uuid)); + + if (!player.hasPlayedBefore()) { + return null; + } + + return player.getName(); + } +} diff --git a/Plan/src/main/java/com/djrapitops/plan/systems/processing/importing/importers/Importer.java b/Plan/src/main/java/com/djrapitops/plan/systems/processing/importing/importers/Importer.java new file mode 100644 index 000000000..5262831fc --- /dev/null +++ b/Plan/src/main/java/com/djrapitops/plan/systems/processing/importing/importers/Importer.java @@ -0,0 +1,91 @@ +/* + * Licence is provided in the jar as license.yml also here: + * https://github.com/Rsl1122/Plan-PlayerAnalytics/blob/master/Plan/src/main/resources/license.yml + */ +package main.java.com.djrapitops.plan.systems.processing.importing.importers; + +import com.djrapitops.plugin.utilities.Verify; +import main.java.com.djrapitops.plan.Log; +import main.java.com.djrapitops.plan.Plan; +import main.java.com.djrapitops.plan.systems.processing.importing.ServerImportData; +import main.java.com.djrapitops.plan.systems.processing.importing.UserImportData; +import main.java.com.djrapitops.plan.systems.processing.importing.UserImportRefiner; +import main.java.com.djrapitops.plan.utilities.Benchmark; + +import java.util.List; + +/** + * @author Fuzzlemann + * @since 4.0.0 + */ +public abstract class Importer { + + public abstract List getNames(); + + public abstract ServerImportData getServerImportData(); + + public abstract List getUserImportData(); + + public void processImport() { + String benchmarkName = "Import processing"; + String serverBenchmarkName = "Server Data processing"; + String userDataBenchmarkName = "User Data processing"; + + Benchmark.start(benchmarkName); + + Benchmark.start(serverBenchmarkName); + processServerData(); + Benchmark.stop(serverBenchmarkName); + + Benchmark.start(userDataBenchmarkName); + processUserData(); + Benchmark.stop(userDataBenchmarkName); + + Benchmark.stop(benchmarkName); + } + + private void processServerData() { + String benchmarkName = "Processing Server Data"; + String getDataBenchmarkName = "Getting Server Data"; + + Benchmark.start(benchmarkName); + Benchmark.start(getDataBenchmarkName); + + ServerImportData serverImportData = getServerImportData(); + + Benchmark.stop(getDataBenchmarkName); + + if (serverImportData == null) { + Log.debug("Server Import Data null, skipping"); + return; + } + + //TODO + + Benchmark.start(benchmarkName); + } + + private void processUserData() { + String benchmarkName = "Processing User Data"; + String getDataBenchmarkName = "Getting User Data"; + + Benchmark.start(benchmarkName); + Benchmark.start(getDataBenchmarkName); + + List userImportData = getUserImportData(); + + Benchmark.stop(getDataBenchmarkName); + + if (Verify.isEmpty(userImportData)) { + Log.debug("User Import Data null or empty, skipping"); + return; + } + + UserImportRefiner userImportRefiner = new UserImportRefiner(Plan.getInstance(), userImportData); + userImportData = userImportRefiner.refineData(); + + //TODO + + Benchmark.stop(benchmarkName); + } +} diff --git a/Plan/src/main/java/com/djrapitops/plan/systems/processing/importing/importers/OfflinePlayerImporter.java b/Plan/src/main/java/com/djrapitops/plan/systems/processing/importing/importers/OfflinePlayerImporter.java new file mode 100644 index 000000000..07e07b02c --- /dev/null +++ b/Plan/src/main/java/com/djrapitops/plan/systems/processing/importing/importers/OfflinePlayerImporter.java @@ -0,0 +1,47 @@ +/* + * Licence is provided in the jar as license.yml also here: + * https://github.com/Rsl1122/Plan-PlayerAnalytics/blob/master/Plan/src/main/resources/license.yml + */ +package main.java.com.djrapitops.plan.systems.processing.importing.importers; + +import main.java.com.djrapitops.plan.systems.processing.importing.ServerImportData; +import main.java.com.djrapitops.plan.systems.processing.importing.UserImportData; +import org.bukkit.Bukkit; + +import java.util.Arrays; +import java.util.List; +import java.util.Vector; + +/** + * @author Fuzzlemann + * @since 4.0.0 + */ +public class OfflinePlayerImporter extends Importer { + + @Override + public List getNames() { + return Arrays.asList("offline", "offlineplayer"); + } + + @Override + public ServerImportData getServerImportData() { + return null; + } + + @Override + public List getUserImportData() { + Vector dataList = new Vector<>(); + + Arrays.stream(Bukkit.getOfflinePlayers()).parallel().forEach(player -> { + UserImportData.UserImportDataBuilder builder = UserImportData.builder(); + builder.name(player.getName()) + .uuid(player.getUniqueId()) + .op(player.isOp()) + .registered(player.getFirstPlayed()) + .banned(player.isBanned()); + dataList.add(builder.build()); + }); + + return dataList; + } +} diff --git a/Plan/src/main/java/com/djrapitops/plan/systems/tasks/TPSCountTimer.java b/Plan/src/main/java/com/djrapitops/plan/systems/tasks/TPSCountTimer.java index 1ce0a5546..aaefd6212 100644 --- a/Plan/src/main/java/com/djrapitops/plan/systems/tasks/TPSCountTimer.java +++ b/Plan/src/main/java/com/djrapitops/plan/systems/tasks/TPSCountTimer.java @@ -94,8 +94,8 @@ public class TPSCountTimer extends AbsRunnable { entityCount = getEntityCount(); // 40ms removed because the run appears to take 40-50ms, screwing the tps. - long fourtyMsAsNs = TimeAmount.MILLISECOND.ns() * 40L; - return getTPS(diff - fourtyMsAsNs, now, averageCPUUsage, usedMemory, entityCount, loadedChunks, playersOnline); + long fortyMsAsNs = TimeAmount.MILLISECOND.ns() * 40L; + return getTPS(diff - fortyMsAsNs, now, averageCPUUsage, usedMemory, entityCount, loadedChunks, playersOnline); } }