diff --git a/.github/ISSUE_TEMPLATE b/.github/ISSUE_TEMPLATE deleted file mode 100644 index 9545ede0b..000000000 --- a/.github/ISSUE_TEMPLATE +++ /dev/null @@ -1,11 +0,0 @@ -Instructions for issue reporting: -- Write description of the issue -- Copy possible errors here, If you're using 4.0.7+ head to `
/debug` & copy the information to the ticket. - -For suggestions use any format you want. :) - ----- - -**Description of the issue** - - diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 000000000..299526883 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,28 @@ +--- +name: Bug report +about: Create a report to help improving the plugin + +--- + +### Describe the bug + + + + +### Server information + + + + +### Exceptions & Other Logs + + + + + +### Additional information + + +**Frequency of occurrence**: <> + +**Other:** diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md new file mode 100644 index 000000000..e1788905f --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -0,0 +1,16 @@ +--- +name: Suggestion +about: Feature request or an addition +--- + +### Is your feature request related to a problem? Please describe. + + +### Describe the solution you'd like + + +### Describe alternatives you've considered + + +### Additional context + diff --git a/.travis.yml b/.travis.yml index fa2df3f4e..184c62fae 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,14 +2,11 @@ language: java sudo: false install: true before_install: - - pwd - cd PlanPluginBridge - - ls - mvn install:install-file -Dfile=./PlanPluginBridge-4.4.0.jar -DpomFile=./pom.xml - cd ../Plan - - ls install: - - mvn install -DskipTests + - mvn clean addons: sonarcloud: organization: "player-analytics-plan" @@ -18,9 +15,8 @@ addons: jdk: - oraclejdk8 script: - - pwd - 'if [ "$TRAVIS_PULL_REQUEST" != "false" ]; then mvn test; fi' - - 'if [ "$TRAVIS_PULL_REQUEST" = "false" ]; then mvn clean org.jacoco:jacoco-maven-plugin:prepare-agent package sonar:sonar; fi' + - 'if [ "$TRAVIS_PULL_REQUEST" = "false" ]; then mvn org.jacoco:jacoco-maven-plugin:prepare-agent package sonar:sonar; fi' cache: directories: - '$HOME/.m2/repository' diff --git a/Plan/dependency-reduced-pom.xml b/Plan/dependency-reduced-pom.xml index 53ca4a42f..002ae44e4 100644 --- a/Plan/dependency-reduced-pom.xml +++ b/Plan/dependency-reduced-pom.xml @@ -90,14 +90,11 @@ org.slf4j plan.org.slf4j - - org.slf4j.Logger - - - org.bstats - com.djrapitops.plan.utilities.metrics - + + org.bstats + com.djrapitops.plan.utilities.metrics + @@ -156,14 +153,14 @@ sponge-repo https://repo.spongepowered.org/maven - - md_5-snapshots - http://repo.md-5.net/content/repositories/snapshots/ - - - bstats-repo - http://repo.bstats.org/content/repositories/releases/ - + + md_5-snapshots + http://repo.md-5.net/content/repositories/snapshots/ + + + bstats-repo + http://repo.bstats.org/content/repositories/releases/ + @@ -316,10 +313,6 @@ guice com.google.inject - - caffeine - com.github.ben-manes.caffeine - guava com.github.ben-manes.caffeine @@ -366,12 +359,12 @@ - - com.imaginarycode.minecraft - RedisBungee - 0.3.8-SNAPSHOT - provided - + + com.imaginarycode.minecraft + RedisBungee + 0.3.8-SNAPSHOT + provided + org.mockito mockito-core @@ -395,7 +388,7 @@ org.xerial sqlite-jdbc - 3.23.1 + 3.23.1 test diff --git a/Plan/pom.xml b/Plan/pom.xml index 14ca31944..e7b7821a8 100644 --- a/Plan/pom.xml +++ b/Plan/pom.xml @@ -24,6 +24,10 @@ sponge-repo https://repo.spongepowered.org/maven + + velocity-repo + https://repo.velocitypowered.com/snapshots/ + md_5-snapshots @@ -79,6 +83,14 @@ jar provided + + + com.velocitypowered + velocity-api + 1.0-SNAPSHOT + jar + provided + org.spongepowered @@ -112,7 +124,7 @@ com.zaxxer HikariCP - 3.1.0 + 3.2.0 @@ -122,6 +134,13 @@ 2.9.0 + + + com.github.ben-manes.caffeine + caffeine + 2.6.2 + + org.bstats @@ -139,6 +158,13 @@ 1.2 + + + com.googlecode.htmlcompressor + htmlcompressor + 1.5.2 + + org.mockito @@ -236,6 +262,7 @@ org.mockito:* org.easymock:* junit:* + @@ -261,9 +288,6 @@ org.slf4j plan.org.slf4j - - org.slf4j.Logger - org.bstats diff --git a/Plan/src/main/java/com/djrapitops/plan/PlanSponge.java b/Plan/src/main/java/com/djrapitops/plan/PlanSponge.java index bcf2623d9..e1110462c 100644 --- a/Plan/src/main/java/com/djrapitops/plan/PlanSponge.java +++ b/Plan/src/main/java/com/djrapitops/plan/PlanSponge.java @@ -29,6 +29,7 @@ import org.spongepowered.api.config.ConfigDir; import org.spongepowered.api.event.Listener; import org.spongepowered.api.event.game.state.GameStartedServerEvent; import org.spongepowered.api.event.game.state.GameStoppingServerEvent; +import org.spongepowered.api.plugin.Dependency; import org.spongepowered.api.plugin.Plugin; import javax.inject.Named; @@ -80,7 +81,17 @@ class SpongePlanModule { } } -@Plugin(id = "plan", name = "Plan", version = "4.4.3", description = "Player Analytics Plugin by Rsl1122", authors = {"Rsl1122"}) +@Plugin( + id = "plan", + name = "Plan", + version = "4.4.7", + description = "Player Analytics Plugin by Rsl1122", + authors = {"Rsl1122"}, + dependencies = { + @Dependency(id = "nucleus", optional = true), + @Dependency(id = "luckperms", optional = true) + } +) public class PlanSponge extends SpongePlugin implements PlanPlugin { @Inject diff --git a/Plan/src/main/java/com/djrapitops/plan/PlanVelocity.java b/Plan/src/main/java/com/djrapitops/plan/PlanVelocity.java new file mode 100644 index 000000000..7a9804902 --- /dev/null +++ b/Plan/src/main/java/com/djrapitops/plan/PlanVelocity.java @@ -0,0 +1,136 @@ +/* + * License is provided in the jar as LICENSE also here: + * https://github.com/Rsl1122/Plan-PlayerAnalytics/blob/master/Plan/src/main/resources/LICENSE + */ +package com.djrapitops.plan; + +import com.djrapitops.plan.api.exceptions.EnableException; +import com.djrapitops.plan.command.PlanVelocityCommand; +import com.djrapitops.plan.system.VelocitySystem; +import com.djrapitops.plan.system.locale.Locale; +import com.djrapitops.plan.system.locale.lang.PluginLang; +import com.djrapitops.plan.system.settings.theme.PlanColorScheme; +import com.djrapitops.plugin.StaticHolder; +import com.djrapitops.plugin.VelocityPlugin; +import com.djrapitops.plugin.api.Benchmark; +import com.djrapitops.plugin.api.utility.log.DebugLog; +import com.djrapitops.plugin.api.utility.log.Log; +import com.djrapitops.plugin.settings.ColorScheme; +import com.google.inject.Inject; +import com.velocitypowered.api.plugin.Plugin; +import com.velocitypowered.api.plugin.annotation.DataDirectory; +import com.velocitypowered.api.proxy.ProxyServer; +import org.slf4j.Logger; + +import java.io.File; +import java.io.InputStream; +import java.nio.file.Path; + +/** + * Velocity Main class. + * + * Based on the PlanBungee class + * + * @author MicleBrick + */ +@Plugin(id = "plan", name = "Plan", version = "4.4.6", description = "Player Analytics Plugin by Rsl1122", authors = {"Rsl1122"}) +public class PlanVelocity extends VelocityPlugin implements PlanPlugin { + + private VelocitySystem system; + private Locale locale; + + public static PlanVelocity getInstance() { + return (PlanVelocity) StaticHolder.getInstance(PlanVelocity.class); + } + + @Inject + @DataDirectory + private Path dataFolderPath; + + @Override + public File getDataFolder() { + return dataFolderPath.toFile(); + } + + @Override + public void onEnable() { + super.onEnable(); + try { + system = new VelocitySystem(this); + locale = system.getLocaleSystem().getLocale(); + system.enable(); + + Log.info(locale.getString(PluginLang.ENABLED)); + } catch (AbstractMethodError e) { + Log.error("Plugin ran into AbstractMethodError - Server restart is required. Likely cause is updating the jar without a restart."); + } catch (EnableException e) { + Log.error("----------------------------------------"); + Log.error("Error: " + e.getMessage()); + Log.error("----------------------------------------"); + Log.error("Plugin Failed to Initialize Correctly. If this issue is caused by config settings you can use /planvelocity reload"); + onDisable(); + } catch (Exception e) { + getLogger().error(this.getClass().getSimpleName() + "-v" + getVersion(), e); + Log.error("Plugin Failed to Initialize Correctly. If this issue is caused by config settings you can use /planvelocity reload"); + Log.error("This error should be reported at https://github.com/Rsl1122/Plan-PlayerAnalytics/issues"); + onDisable(); + } + registerCommand("planvelocity", new PlanVelocityCommand(this)); + } + + @Override + public void onDisable() { + system.disable(); + + Log.info(locale.getString(PluginLang.DISABLED)); + Benchmark.pluginDisabled(PlanVelocity.class); + DebugLog.pluginDisabled(PlanVelocity.class); + } + + @Override + public String getVersion() { + return getClass().getAnnotation(Plugin.class).version(); + } + + + @Override + public void onReload() { + // Nothing to be done, systems are disabled + } + + @Override + public InputStream getResource(String resource) { + return getClass().getResourceAsStream(resource); + } + + @Override + public ColorScheme getColorScheme() { + return PlanColorScheme.create(); + } + + @Override + public VelocitySystem getSystem() { + return system; + } + + @Override + public boolean isReloading() { + return reloading; + } + + @Inject + private ProxyServer proxy; + + @Override + public ProxyServer getProxy() { + return proxy; + } + + @Inject + private Logger logger; + + @Override + protected Logger getLogger() { + return logger; + } +} diff --git a/Plan/src/main/java/com/djrapitops/plan/api/BungeeAPI.java b/Plan/src/main/java/com/djrapitops/plan/api/ProxyAPI.java similarity index 100% rename from Plan/src/main/java/com/djrapitops/plan/api/BungeeAPI.java rename to Plan/src/main/java/com/djrapitops/plan/api/ProxyAPI.java diff --git a/Plan/src/main/java/com/djrapitops/plan/command/PlanBungeeCommand.java b/Plan/src/main/java/com/djrapitops/plan/command/PlanBungeeCommand.java index 301eb2001..a1c2dc7b9 100644 --- a/Plan/src/main/java/com/djrapitops/plan/command/PlanBungeeCommand.java +++ b/Plan/src/main/java/com/djrapitops/plan/command/PlanBungeeCommand.java @@ -3,6 +3,7 @@ package com.djrapitops.plan.command; import com.djrapitops.plan.command.commands.*; import com.djrapitops.plan.command.commands.manage.ManageConDebugCommand; import com.djrapitops.plan.command.commands.manage.ManageRawDataCommand; +import com.djrapitops.plan.command.commands.manage.ManageUninstalledCommand; import com.djrapitops.plan.system.locale.Locale; import com.djrapitops.plan.system.locale.lang.DeepHelpLang; import com.djrapitops.plan.system.settings.Permissions; @@ -36,6 +37,7 @@ public class PlanBungeeCommand extends TreeCmdNode { private final BungeeSetupToggleCommand setupToggleCommand; private final ReloadCommand reloadCommand; private final DisableCommand disableCommand; + private final ManageUninstalledCommand uninstalledCommand; private boolean commandsRegistered; @@ -54,10 +56,12 @@ public class PlanBungeeCommand extends TreeCmdNode { ManageConDebugCommand conDebugCommand, ManageRawDataCommand rawDataCommand, BungeeSetupToggleCommand setupToggleCommand, + ManageUninstalledCommand uninstalledCommand, ReloadCommand reloadCommand, DisableCommand disableCommand ) { super("planbungee", Permissions.MANAGE.getPermission(), CommandType.CONSOLE, null); + this.uninstalledCommand = uninstalledCommand; commandsRegistered = false; @@ -94,6 +98,7 @@ public class PlanBungeeCommand extends TreeCmdNode { conDebugCommand, rawDataCommand, setupToggleCommand, + uninstalledCommand, reloadCommand, disableCommand }; diff --git a/Plan/src/main/java/com/djrapitops/plan/command/PlanVelocityCommand.java b/Plan/src/main/java/com/djrapitops/plan/command/PlanVelocityCommand.java new file mode 100644 index 000000000..a1c2dc7b9 --- /dev/null +++ b/Plan/src/main/java/com/djrapitops/plan/command/PlanVelocityCommand.java @@ -0,0 +1,108 @@ +package com.djrapitops.plan.command; + +import com.djrapitops.plan.command.commands.*; +import com.djrapitops.plan.command.commands.manage.ManageConDebugCommand; +import com.djrapitops.plan.command.commands.manage.ManageRawDataCommand; +import com.djrapitops.plan.command.commands.manage.ManageUninstalledCommand; +import com.djrapitops.plan.system.locale.Locale; +import com.djrapitops.plan.system.locale.lang.DeepHelpLang; +import com.djrapitops.plan.system.settings.Permissions; +import com.djrapitops.plugin.command.ColorScheme; +import com.djrapitops.plugin.command.CommandNode; +import com.djrapitops.plugin.command.CommandType; +import com.djrapitops.plugin.command.TreeCmdNode; +import dagger.Lazy; + +import javax.inject.Inject; +import javax.inject.Singleton; + +/** + * TreeCommand for the /plan command, and all subcommands. + *

+ * Uses the Abstract Plugin Framework for easier command management. + * + * @author Rsl1122 + * @since 1.0.0 + */ +@Singleton +public class PlanBungeeCommand extends TreeCmdNode { + + private final NetworkCommand networkCommand; + private final ListServersCommand listServersCommand; + private final ListPlayersCommand listPlayersCommand; + private final RegisterCommand registerCommand; + private final Lazy webUserCommand; + private final ManageConDebugCommand conDebugCommand; + private final ManageRawDataCommand rawDataCommand; + private final BungeeSetupToggleCommand setupToggleCommand; + private final ReloadCommand reloadCommand; + private final DisableCommand disableCommand; + private final ManageUninstalledCommand uninstalledCommand; + + private boolean commandsRegistered; + + @Inject + public PlanBungeeCommand( + ColorScheme colorScheme, + Locale locale, + // Group 1 + NetworkCommand networkCommand, + ListServersCommand listServersCommand, + ListPlayersCommand listPlayersCommand, + // Group 2 + RegisterCommand registerCommand, + Lazy webUserCommand, + // Group 3 + ManageConDebugCommand conDebugCommand, + ManageRawDataCommand rawDataCommand, + BungeeSetupToggleCommand setupToggleCommand, + ManageUninstalledCommand uninstalledCommand, + ReloadCommand reloadCommand, + DisableCommand disableCommand + ) { + super("planbungee", Permissions.MANAGE.getPermission(), CommandType.CONSOLE, null); + this.uninstalledCommand = uninstalledCommand; + + commandsRegistered = false; + + this.networkCommand = networkCommand; + this.listServersCommand = listServersCommand; + this.listPlayersCommand = listPlayersCommand; + this.registerCommand = registerCommand; + this.webUserCommand = webUserCommand; + this.conDebugCommand = conDebugCommand; + this.rawDataCommand = rawDataCommand; + this.setupToggleCommand = setupToggleCommand; + this.reloadCommand = reloadCommand; + this.disableCommand = disableCommand; + + setColorScheme(colorScheme); + setInDepthHelp(locale.getArray(DeepHelpLang.PLAN)); + } + + public void registerCommands() { + if (commandsRegistered) { + return; + } + + CommandNode[] analyticsGroup = { + networkCommand, + listServersCommand, + listPlayersCommand + }; + CommandNode[] webGroup = { + registerCommand, + webUserCommand.get() + }; + CommandNode[] manageGroup = { + conDebugCommand, + rawDataCommand, + setupToggleCommand, + uninstalledCommand, + reloadCommand, + disableCommand + }; + setNodeGroups(analyticsGroup, webGroup, manageGroup); + commandsRegistered = true; + } +} diff --git a/Plan/src/main/java/com/djrapitops/plan/command/commands/manage/ManageUninstalledCommand.java b/Plan/src/main/java/com/djrapitops/plan/command/commands/manage/ManageUninstalledCommand.java index 09f2a9ee2..8922bf89c 100644 --- a/Plan/src/main/java/com/djrapitops/plan/command/commands/manage/ManageUninstalledCommand.java +++ b/Plan/src/main/java/com/djrapitops/plan/command/commands/manage/ManageUninstalledCommand.java @@ -2,7 +2,6 @@ package com.djrapitops.plan.command.commands.manage; import com.djrapitops.plan.api.exceptions.database.DBOpException; import com.djrapitops.plan.system.database.databases.Database; -import com.djrapitops.plan.system.info.connection.ConnectionSystem; import com.djrapitops.plan.system.info.server.Server; import com.djrapitops.plan.system.info.server.ServerInfo; import com.djrapitops.plan.system.locale.Locale; @@ -90,7 +89,7 @@ public class ManageUninstalledCommand extends CommandNode { } private Optional getServer(String[] args) { - if (args.length >= 1 && connectionSystem.isServerAvailable()) { + if (args.length >= 1) { Map bukkitServers = database.fetch().getBukkitServers(); String serverIdentifier = getGivenIdentifier(args); for (Map.Entry entry : bukkitServers.entrySet()) { diff --git a/Plan/src/main/java/com/djrapitops/plan/data/container/Session.java b/Plan/src/main/java/com/djrapitops/plan/data/container/Session.java index ed0366c30..1c7f8ef95 100644 --- a/Plan/src/main/java/com/djrapitops/plan/data/container/Session.java +++ b/Plan/src/main/java/com/djrapitops/plan/data/container/Session.java @@ -152,11 +152,11 @@ public class Session extends DataContainer implements DateHolder { } public void setWorldTimes(WorldTimes worldTimes) { - putRawData(SessionKeys.WORLD_TIMES, worldTimes); + this.worldTimes = worldTimes; } public void setPlayerKills(List playerKills) { - putRawData(SessionKeys.PLAYER_KILLS, playerKills); + this.playerKills = playerKills; } public boolean isFetchedFromDB() { @@ -180,14 +180,8 @@ public class Session extends DataContainer implements DateHolder { getValue(SessionKeys.END).orElse(-1L).equals(session.getValue(SessionKeys.END).orElse(-1L)) && mobKills == session.mobKills && deaths == session.deaths && - Objects.equals( - getValue(SessionKeys.WORLD_TIMES).orElse(null), - session.getValue(SessionKeys.WORLD_TIMES).orElse(null) - ) && - Objects.equals( - getValue(SessionKeys.PLAYER_KILLS).orElse(new ArrayList<>()), - session.getValue(SessionKeys.PLAYER_KILLS).orElse(new ArrayList<>()) - ); + Objects.equals(playerKills, session.playerKills) && + Objects.equals(worldTimes, session.worldTimes); } @Override @@ -203,7 +197,7 @@ public class Session extends DataContainer implements DateHolder { return worldTimes; } - private List getPlayerKills() { + public List getPlayerKills() { return playerKills; } diff --git a/Plan/src/main/java/com/djrapitops/plan/data/element/InspectContainer.java b/Plan/src/main/java/com/djrapitops/plan/data/element/InspectContainer.java index 9b2f6fa60..f3a5b7b14 100644 --- a/Plan/src/main/java/com/djrapitops/plan/data/element/InspectContainer.java +++ b/Plan/src/main/java/com/djrapitops/plan/data/element/InspectContainer.java @@ -5,6 +5,8 @@ package com.djrapitops.plan.data.element; import java.io.Serializable; +import java.util.ArrayList; +import java.util.List; import java.util.Map; import java.util.TreeMap; @@ -25,18 +27,18 @@ import java.util.TreeMap; */ public class InspectContainer { - protected TreeMap values; + protected List values; protected TreeMap html; protected TreeMap tables; public InspectContainer() { - values = new TreeMap<>(); + values = new ArrayList<>(); html = new TreeMap<>(); tables = new TreeMap<>(); } public final void addValue(String label, Serializable value) { - values.put(label, value.toString()); + values.add(label + ": " + value.toString()); } public final void addHtml(String key, String html) { @@ -52,8 +54,8 @@ public class InspectContainer { if (!values.isEmpty()) { parsed.append("

"); - for (Map.Entry entry : values.entrySet()) { - parsed.append("

").append(entry.getKey()).append(": ").append(entry.getValue()).append("

"); + for (String value : values) { + parsed.append("

").append(value).append("

"); } parsed.append("
"); } diff --git a/Plan/src/main/java/com/djrapitops/plan/data/store/containers/AnalysisContainer.java b/Plan/src/main/java/com/djrapitops/plan/data/store/containers/AnalysisContainer.java index d119bc9da..d12d4f945 100644 --- a/Plan/src/main/java/com/djrapitops/plan/data/store/containers/AnalysisContainer.java +++ b/Plan/src/main/java/com/djrapitops/plan/data/store/containers/AnalysisContainer.java @@ -90,9 +90,9 @@ public class AnalysisContainer extends DataContainer { } private void addAnalysisSuppliers() { - putSupplier(AnalysisKeys.SESSIONS_MUTATOR, () -> SessionsMutator.forContainer(serverContainer)); - putSupplier(AnalysisKeys.TPS_MUTATOR, () -> TPSMutator.forContainer(serverContainer)); - putSupplier(AnalysisKeys.PLAYERS_MUTATOR, () -> PlayersMutator.forContainer(serverContainer)); + putCachingSupplier(AnalysisKeys.SESSIONS_MUTATOR, () -> SessionsMutator.forContainer(serverContainer)); + putCachingSupplier(AnalysisKeys.TPS_MUTATOR, () -> TPSMutator.forContainer(serverContainer)); + putCachingSupplier(AnalysisKeys.PLAYERS_MUTATOR, () -> PlayersMutator.forContainer(serverContainer)); addConstants(); addPlayerSuppliers(); @@ -123,7 +123,7 @@ public class AnalysisContainer extends DataContainer { } private void addServerProperties() { - putSupplier(AnalysisKeys.SERVER_NAME, () -> + putCachingSupplier(AnalysisKeys.SERVER_NAME, () -> getUnsafe(serverNames).getOrDefault(serverContainer.getUnsafe(ServerKeys.SERVER_UUID), "Plan") ); @@ -147,7 +147,7 @@ public class AnalysisContainer extends DataContainer { } private void addPlayerSuppliers() { - putSupplier(AnalysisKeys.PLAYER_NAMES, () -> serverContainer.getValue(ServerKeys.PLAYERS).orElse(new ArrayList<>()) + putCachingSupplier(AnalysisKeys.PLAYER_NAMES, () -> serverContainer.getValue(ServerKeys.PLAYERS).orElse(new ArrayList<>()) .stream().collect(Collectors.toMap( p -> p.getUnsafe(PlayerKeys.UUID), p -> p.getValue(PlayerKeys.NAME).orElse("?")) ) @@ -190,22 +190,22 @@ public class AnalysisContainer extends DataContainer { Key uniqueDay = new Key<>(PlayersMutator.class, "UNIQUE_DAY"); Key uniqueWeek = new Key<>(PlayersMutator.class, "UNIQUE_WEEK"); Key uniqueMonth = new Key<>(PlayersMutator.class, "UNIQUE_MONTH"); - putSupplier(newDay, () -> getUnsafe(AnalysisKeys.PLAYERS_MUTATOR) + putCachingSupplier(newDay, () -> getUnsafe(AnalysisKeys.PLAYERS_MUTATOR) .filterRegisteredBetween(getUnsafe(AnalysisKeys.ANALYSIS_TIME_DAY_AGO), getUnsafe(AnalysisKeys.ANALYSIS_TIME)) ); - putSupplier(newWeek, () -> getUnsafe(AnalysisKeys.PLAYERS_MUTATOR) + putCachingSupplier(newWeek, () -> getUnsafe(AnalysisKeys.PLAYERS_MUTATOR) .filterRegisteredBetween(getUnsafe(AnalysisKeys.ANALYSIS_TIME_WEEK_AGO), getUnsafe(AnalysisKeys.ANALYSIS_TIME)) ); - putSupplier(newMonth, () -> getUnsafe(AnalysisKeys.PLAYERS_MUTATOR) + putCachingSupplier(newMonth, () -> getUnsafe(AnalysisKeys.PLAYERS_MUTATOR) .filterRegisteredBetween(getUnsafe(AnalysisKeys.ANALYSIS_TIME_MONTH_AGO), getUnsafe(AnalysisKeys.ANALYSIS_TIME)) ); - putSupplier(uniqueDay, () -> getUnsafe(AnalysisKeys.PLAYERS_MUTATOR) + putCachingSupplier(uniqueDay, () -> getUnsafe(AnalysisKeys.PLAYERS_MUTATOR) .filterPlayedBetween(getUnsafe(AnalysisKeys.ANALYSIS_TIME_DAY_AGO), getUnsafe(AnalysisKeys.ANALYSIS_TIME)) ); - putSupplier(uniqueWeek, () -> getUnsafe(AnalysisKeys.PLAYERS_MUTATOR) + putCachingSupplier(uniqueWeek, () -> getUnsafe(AnalysisKeys.PLAYERS_MUTATOR) .filterPlayedBetween(getUnsafe(AnalysisKeys.ANALYSIS_TIME_WEEK_AGO), getUnsafe(AnalysisKeys.ANALYSIS_TIME)) ); - putSupplier(uniqueMonth, () -> getUnsafe(AnalysisKeys.PLAYERS_MUTATOR) + putCachingSupplier(uniqueMonth, () -> getUnsafe(AnalysisKeys.PLAYERS_MUTATOR) .filterPlayedBetween(getUnsafe(AnalysisKeys.ANALYSIS_TIME_MONTH_AGO), getUnsafe(AnalysisKeys.ANALYSIS_TIME)) ); @@ -231,7 +231,7 @@ public class AnalysisContainer extends DataContainer { Key retentionDay = new Key<>(Integer.class, "RETENTION_DAY"); // compareAndFindThoseLikelyToBeRetained can throw exception. - putSupplier(retentionDay, () -> getUnsafe(AnalysisKeys.PLAYERS_MUTATOR).compareAndFindThoseLikelyToBeRetained( + putCachingSupplier(retentionDay, () -> getUnsafe(AnalysisKeys.PLAYERS_MUTATOR).compareAndFindThoseLikelyToBeRetained( getUnsafe(newDay).all(), getUnsafe(AnalysisKeys.ANALYSIS_TIME_MONTH_AGO), getUnsafe(AnalysisKeys.PLAYERS_ONLINE_RESOLVER), config.getNumber(Settings.ACTIVE_PLAY_THRESHOLD), @@ -245,13 +245,13 @@ public class AnalysisContainer extends DataContainer { return 0; } }); - putSupplier(AnalysisKeys.PLAYERS_RETAINED_WEEK, () -> + putCachingSupplier(AnalysisKeys.PLAYERS_RETAINED_WEEK, () -> getUnsafe(newWeek).filterRetained( getUnsafe(AnalysisKeys.ANALYSIS_TIME_WEEK_AGO), getUnsafe(AnalysisKeys.ANALYSIS_TIME) ).count() ); - putSupplier(AnalysisKeys.PLAYERS_RETAINED_MONTH, () -> + putCachingSupplier(AnalysisKeys.PLAYERS_RETAINED_MONTH, () -> getUnsafe(newMonth).filterRetained( getUnsafe(AnalysisKeys.ANALYSIS_TIME_MONTH_AGO), getUnsafe(AnalysisKeys.ANALYSIS_TIME) @@ -283,8 +283,8 @@ public class AnalysisContainer extends DataContainer { private void addSessionSuppliers() { Key sessionAccordion = new Key<>(SessionAccordion.class, "SESSION_ACCORDION"); - putSupplier(serverNames, () -> database.fetch().getServerNames()); - putSupplier(sessionAccordion, () -> accordions.serverSessionAccordion( + putCachingSupplier(serverNames, () -> database.fetch().getServerNames()); + putCachingSupplier(sessionAccordion, () -> accordions.serverSessionAccordion( getUnsafe(AnalysisKeys.SESSIONS_MUTATOR).all(), getSupplier(serverNames), () -> getUnsafe(AnalysisKeys.PLAYER_NAMES) @@ -325,13 +325,13 @@ public class AnalysisContainer extends DataContainer { Key sessionsDay = new Key<>(SessionsMutator.class, "SESSIONS_DAY"); Key sessionsWeek = new Key<>(SessionsMutator.class, "SESSIONS_WEEK"); Key sessionsMonth = new Key<>(SessionsMutator.class, "SESSIONS_MONTH"); - putSupplier(sessionsDay, () -> getUnsafe(AnalysisKeys.SESSIONS_MUTATOR) + putCachingSupplier(sessionsDay, () -> getUnsafe(AnalysisKeys.SESSIONS_MUTATOR) .filterSessionsBetween(getUnsafe(AnalysisKeys.ANALYSIS_TIME_DAY_AGO), getUnsafe(AnalysisKeys.ANALYSIS_TIME)) ); - putSupplier(sessionsWeek, () -> getUnsafe(AnalysisKeys.SESSIONS_MUTATOR) + putCachingSupplier(sessionsWeek, () -> getUnsafe(AnalysisKeys.SESSIONS_MUTATOR) .filterSessionsBetween(getUnsafe(AnalysisKeys.ANALYSIS_TIME_WEEK_AGO), getUnsafe(AnalysisKeys.ANALYSIS_TIME)) ); - putSupplier(sessionsMonth, () -> getUnsafe(AnalysisKeys.SESSIONS_MUTATOR) + putCachingSupplier(sessionsMonth, () -> getUnsafe(AnalysisKeys.SESSIONS_MUTATOR) .filterSessionsBetween(getUnsafe(AnalysisKeys.ANALYSIS_TIME_MONTH_AGO), getUnsafe(AnalysisKeys.ANALYSIS_TIME)) ); @@ -344,7 +344,7 @@ public class AnalysisContainer extends DataContainer { private void addGraphSuppliers() { Key worldPie = new Key<>(WorldPie.class, "WORLD_PIE"); - putSupplier(worldPie, () -> graphs.pie().worldPie( + putCachingSupplier(worldPie, () -> graphs.pie().worldPie( serverContainer.getValue(ServerKeys.WORLD_TIMES).orElse(new WorldTimes(new HashMap<>())) )); putSupplier(AnalysisKeys.WORLD_PIE_SERIES, () -> getUnsafe(worldPie).toHighChartsSeries()); @@ -361,12 +361,12 @@ public class AnalysisContainer extends DataContainer { graphs.special().worldMap(getUnsafe(AnalysisKeys.PLAYERS_MUTATOR)).toHighChartsSeries() ); Key geolocationBarChart = new Key<>(BarGraph.class, "GEOLOCATION_BAR_GRAPH"); - putSupplier(geolocationBarChart, () -> graphs.bar().geolocationBarGraph(getUnsafe(AnalysisKeys.PLAYERS_MUTATOR))); + putCachingSupplier(geolocationBarChart, () -> graphs.bar().geolocationBarGraph(getUnsafe(AnalysisKeys.PLAYERS_MUTATOR))); putSupplier(AnalysisKeys.COUNTRY_CATEGORIES, () -> getUnsafe(geolocationBarChart).toHighChartsCategories()); putSupplier(AnalysisKeys.COUNTRY_SERIES, () -> getUnsafe(geolocationBarChart).toHighChartsSeries()); Key pingGraph = new Key<>(PingGraph.class, "PING_GRAPH"); - putSupplier(pingGraph, () -> + putCachingSupplier(pingGraph, () -> graphs.line().pingGraph(PingMutator.forContainer(serverContainer).mutateToByMinutePings().all()) ); putSupplier(AnalysisKeys.AVG_PING_SERIES, () -> getUnsafe(pingGraph).toAvgSeries()); @@ -379,9 +379,9 @@ public class AnalysisContainer extends DataContainer { getUnsafe(AnalysisKeys.NEW_PLAYERS_PER_DAY) ).toCalendarSeries()); - putSupplier(AnalysisKeys.ACTIVITY_DATA, () -> getUnsafe(AnalysisKeys.PLAYERS_MUTATOR).toActivityDataMap(getUnsafe(AnalysisKeys.ANALYSIS_TIME), config.getNumber(Settings.ACTIVE_PLAY_THRESHOLD), config.getNumber(Settings.ACTIVE_LOGIN_THRESHOLD))); + putCachingSupplier(AnalysisKeys.ACTIVITY_DATA, () -> getUnsafe(AnalysisKeys.PLAYERS_MUTATOR).toActivityDataMap(getUnsafe(AnalysisKeys.ANALYSIS_TIME), config.getNumber(Settings.ACTIVE_PLAY_THRESHOLD), config.getNumber(Settings.ACTIVE_LOGIN_THRESHOLD))); Key activityStackGraph = new Key<>(StackGraph.class, "ACTIVITY_STACK_GRAPH"); - putSupplier(activityStackGraph, () -> graphs.stack().activityStackGraph(getUnsafe(AnalysisKeys.ACTIVITY_DATA))); + putCachingSupplier(activityStackGraph, () -> graphs.stack().activityStackGraph(getUnsafe(AnalysisKeys.ACTIVITY_DATA))); putSupplier(AnalysisKeys.ACTIVITY_STACK_CATEGORIES, () -> getUnsafe(activityStackGraph).toHighChartsLabels()); putSupplier(AnalysisKeys.ACTIVITY_STACK_SERIES, () -> getUnsafe(activityStackGraph).toHighChartsSeries()); putSupplier(AnalysisKeys.ACTIVITY_PIE_SERIES, () -> graphs.pie().activityPie( @@ -402,17 +402,17 @@ public class AnalysisContainer extends DataContainer { Key tpsWeek = new Key<>(TPSMutator.class, "TPS_WEEK"); Key tpsDay = new Key<>(TPSMutator.class, "TPS_DAY"); - putSupplier(tpsMonth, () -> getUnsafe(AnalysisKeys.TPS_MUTATOR) + putCachingSupplier(tpsMonth, () -> getUnsafe(AnalysisKeys.TPS_MUTATOR) .filterDataBetween(getUnsafe(AnalysisKeys.ANALYSIS_TIME_MONTH_AGO), getUnsafe(AnalysisKeys.ANALYSIS_TIME)) ); - putSupplier(tpsWeek, () -> getUnsafe(AnalysisKeys.TPS_MUTATOR) + putCachingSupplier(tpsWeek, () -> getUnsafe(AnalysisKeys.TPS_MUTATOR) .filterDataBetween(getUnsafe(AnalysisKeys.ANALYSIS_TIME_WEEK_AGO), getUnsafe(AnalysisKeys.ANALYSIS_TIME)) ); - putSupplier(tpsDay, () -> getUnsafe(AnalysisKeys.TPS_MUTATOR) + putCachingSupplier(tpsDay, () -> getUnsafe(AnalysisKeys.TPS_MUTATOR) .filterDataBetween(getUnsafe(AnalysisKeys.ANALYSIS_TIME_DAY_AGO), getUnsafe(AnalysisKeys.ANALYSIS_TIME)) ); - putSupplier(AnalysisKeys.PLAYERS_ONLINE_RESOLVER, () -> new PlayersOnlineResolver(getUnsafe(AnalysisKeys.TPS_MUTATOR))); + putCachingSupplier(AnalysisKeys.PLAYERS_ONLINE_RESOLVER, () -> new PlayersOnlineResolver(getUnsafe(AnalysisKeys.TPS_MUTATOR))); int threshold = config.getNumber(Settings.THEME_GRAPH_TPS_THRESHOLD_MED); @@ -444,7 +444,7 @@ public class AnalysisContainer extends DataContainer { private void addServerHealth() { Key healthInformation = new Key<>(HealthInformation.class, "HEALTH_INFORMATION"); - putSupplier(healthInformation, () -> new HealthInformation( + putCachingSupplier(healthInformation, () -> new HealthInformation( this, config.getNumber(Settings.THEME_GRAPH_TPS_THRESHOLD_MED), config.getNumber(Settings.ACTIVE_PLAY_THRESHOLD), @@ -458,7 +458,7 @@ public class AnalysisContainer extends DataContainer { private void addPluginSuppliers() { // TODO Refactor into a system that supports running the analysis on Bungee Key navAndTabs = new Key<>(new Type() {}, "NAV_AND_TABS"); - putSupplier(navAndTabs, () -> pluginsTabContentCreator.createContent(this)); + putCachingSupplier(navAndTabs, () -> pluginsTabContentCreator.createContent(this)); putSupplier(AnalysisKeys.PLUGINS_TAB_NAV, () -> getUnsafe(navAndTabs)[0]); putSupplier(AnalysisKeys.PLUGINS_TAB, () -> getUnsafe(navAndTabs)[1]); } diff --git a/Plan/src/main/java/com/djrapitops/plan/data/store/containers/DataContainer.java b/Plan/src/main/java/com/djrapitops/plan/data/store/containers/DataContainer.java index 16f7aeda7..e6c26f85c 100644 --- a/Plan/src/main/java/com/djrapitops/plan/data/store/containers/DataContainer.java +++ b/Plan/src/main/java/com/djrapitops/plan/data/store/containers/DataContainer.java @@ -44,6 +44,13 @@ public class DataContainer { } public void putSupplier(Key key, Supplier supplier) { + if (supplier == null) { + return; + } + map.put(key, supplier); + } + + public void putCachingSupplier(Key key, Supplier supplier) { if (supplier == null) { return; } diff --git a/Plan/src/main/java/com/djrapitops/plan/data/store/containers/NetworkContainer.java b/Plan/src/main/java/com/djrapitops/plan/data/store/containers/NetworkContainer.java index 7618b2709..b3a0ea600 100644 --- a/Plan/src/main/java/com/djrapitops/plan/data/store/containers/NetworkContainer.java +++ b/Plan/src/main/java/com/djrapitops/plan/data/store/containers/NetworkContainer.java @@ -69,7 +69,7 @@ public class NetworkContainer extends DataContainer { this.formatters = formatters; this.graphs = graphs; - putSupplier(NetworkKeys.PLAYERS_MUTATOR, () -> PlayersMutator.forContainer(bungeeContainer)); + putCachingSupplier(NetworkKeys.PLAYERS_MUTATOR, () -> PlayersMutator.forContainer(bungeeContainer)); addConstants(); addServerBoxes(); @@ -99,14 +99,14 @@ public class NetworkContainer extends DataContainer { private void addNetworkHealth() { Key healthInformation = new Key<>(NetworkHealthInformation.class, "HEALTH_INFORMATION"); - putSupplier(healthInformation, () -> new NetworkHealthInformation( + putCachingSupplier(healthInformation, () -> new NetworkHealthInformation( this, config.getNumber(Settings.ACTIVE_PLAY_THRESHOLD), config.getNumber(Settings.ACTIVE_LOGIN_THRESHOLD), formatters.timeAmount(), formatters.decimals(), formatters.percentage() )); - putSupplier(NetworkKeys.HEALTH_INDEX, () -> getUnsafe(healthInformation).getServerHealth()); - putSupplier(NetworkKeys.HEALTH_NOTES, () -> getUnsafe(healthInformation).toHtml()); + putCachingSupplier(NetworkKeys.HEALTH_INDEX, () -> getUnsafe(healthInformation).getServerHealth()); + putCachingSupplier(NetworkKeys.HEALTH_NOTES, () -> getUnsafe(healthInformation).toHtml()); } private void addConstants() { @@ -120,7 +120,7 @@ public class NetworkContainer extends DataContainer { putRawData(NetworkKeys.VERSION, version); putSupplier(NetworkKeys.TIME_ZONE, config::getTimeZoneOffsetHours); - putSupplier(NetworkKeys.NETWORK_NAME, () -> + putCachingSupplier(NetworkKeys.NETWORK_NAME, () -> Check.isBungeeAvailable() ? config.getString(Settings.BUNGEE_NETWORK_NAME) : bungeeContainer.getValue(ServerKeys.NAME).orElse("Plan") @@ -176,22 +176,22 @@ public class NetworkContainer extends DataContainer { Key uniqueDay = new Key<>(PlayersMutator.class, "UNIQUE_DAY"); Key uniqueWeek = new Key<>(PlayersMutator.class, "UNIQUE_WEEK"); Key uniqueMonth = new Key<>(PlayersMutator.class, "UNIQUE_MONTH"); - putSupplier(newDay, () -> getUnsafe(NetworkKeys.PLAYERS_MUTATOR) + putCachingSupplier(newDay, () -> getUnsafe(NetworkKeys.PLAYERS_MUTATOR) .filterRegisteredBetween(getUnsafe(NetworkKeys.REFRESH_TIME_DAY_AGO), getUnsafe(NetworkKeys.REFRESH_TIME)) ); - putSupplier(newWeek, () -> getUnsafe(NetworkKeys.PLAYERS_MUTATOR) + putCachingSupplier(newWeek, () -> getUnsafe(NetworkKeys.PLAYERS_MUTATOR) .filterRegisteredBetween(getUnsafe(NetworkKeys.REFRESH_TIME_WEEK_AGO), getUnsafe(NetworkKeys.REFRESH_TIME)) ); - putSupplier(newMonth, () -> getUnsafe(NetworkKeys.PLAYERS_MUTATOR) + putCachingSupplier(newMonth, () -> getUnsafe(NetworkKeys.PLAYERS_MUTATOR) .filterRegisteredBetween(getUnsafe(NetworkKeys.REFRESH_TIME_MONTH_AGO), getUnsafe(NetworkKeys.REFRESH_TIME)) ); - putSupplier(uniqueDay, () -> getUnsafe(NetworkKeys.PLAYERS_MUTATOR) + putCachingSupplier(uniqueDay, () -> getUnsafe(NetworkKeys.PLAYERS_MUTATOR) .filterPlayedBetween(getUnsafe(NetworkKeys.REFRESH_TIME_DAY_AGO), getUnsafe(NetworkKeys.REFRESH_TIME)) ); - putSupplier(uniqueWeek, () -> getUnsafe(NetworkKeys.PLAYERS_MUTATOR) + putCachingSupplier(uniqueWeek, () -> getUnsafe(NetworkKeys.PLAYERS_MUTATOR) .filterPlayedBetween(getUnsafe(NetworkKeys.REFRESH_TIME_WEEK_AGO), getUnsafe(NetworkKeys.REFRESH_TIME)) ); - putSupplier(uniqueMonth, () -> getUnsafe(NetworkKeys.PLAYERS_MUTATOR) + putCachingSupplier(uniqueMonth, () -> getUnsafe(NetworkKeys.PLAYERS_MUTATOR) .filterPlayedBetween(getUnsafe(NetworkKeys.REFRESH_TIME_MONTH_AGO), getUnsafe(NetworkKeys.REFRESH_TIME)) ); @@ -203,6 +203,10 @@ public class NetworkContainer extends DataContainer { putSupplier(NetworkKeys.PLAYERS_MONTH, () -> getUnsafe(uniqueMonth).count()); } + public ServerContainer getBungeeContainer() { + return bungeeContainer; + } + @Singleton public static class Factory { diff --git a/Plan/src/main/java/com/djrapitops/plan/data/store/mutators/SessionsMutator.java b/Plan/src/main/java/com/djrapitops/plan/data/store/mutators/SessionsMutator.java index 10be83942..203d1c91a 100644 --- a/Plan/src/main/java/com/djrapitops/plan/data/store/mutators/SessionsMutator.java +++ b/Plan/src/main/java/com/djrapitops/plan/data/store/mutators/SessionsMutator.java @@ -7,6 +7,7 @@ import com.djrapitops.plan.data.store.containers.DataContainer; import com.djrapitops.plan.data.store.keys.CommonKeys; import com.djrapitops.plan.data.store.keys.SessionKeys; import com.djrapitops.plan.data.time.WorldTimes; +import com.djrapitops.plan.utilities.analysis.Median; import java.util.*; import java.util.function.Predicate; @@ -142,13 +143,8 @@ public class SessionsMutator { } public long toMedianSessionLength() { - List sessionLengths = sessions.stream().map(Session::getLength) - .sorted() - .collect(Collectors.toList()); - if (sessionLengths.isEmpty()) { - return 0; - } - return sessionLengths.get(sessionLengths.size() / 2); + List sessionLengths = sessions.stream().map(Session::getLength).collect(Collectors.toList()); + return (long) Median.forLong(sessionLengths).calculate(); } public int toAverageUniqueJoinsPerDay() { @@ -200,5 +196,4 @@ public class SessionsMutator { return toPlayerDeathList().size(); } - } \ No newline at end of file diff --git a/Plan/src/main/java/com/djrapitops/plan/modules/proxy/ProxySuperClassBindingModule.java b/Plan/src/main/java/com/djrapitops/plan/modules/proxy/ProxySuperClassBindingModule.java new file mode 100644 index 000000000..057dd749f --- /dev/null +++ b/Plan/src/main/java/com/djrapitops/plan/modules/proxy/ProxySuperClassBindingModule.java @@ -0,0 +1,9 @@ +package com.djrapitops.plan.modules.bungee.proxy; + +/** + * //TODO Class Javadoc Comment + * + * @author Rsl1122 + */ +public class ProxySuperClassBindingModule { +} \ No newline at end of file diff --git a/Plan/src/main/java/com/djrapitops/plan/modules/bungee/BungeeInfoModule.java b/Plan/src/main/java/com/djrapitops/plan/modules/proxy/bungee/BungeeServerPropertiesModule.java similarity index 100% rename from Plan/src/main/java/com/djrapitops/plan/modules/bungee/BungeeInfoModule.java rename to Plan/src/main/java/com/djrapitops/plan/modules/proxy/bungee/BungeeServerPropertiesModule.java diff --git a/Plan/src/main/java/com/djrapitops/plan/modules/bungee/BungeeSuperClassBindingModule.java b/Plan/src/main/java/com/djrapitops/plan/modules/proxy/bungee/BungeeSuperClassBindingModule.java similarity index 100% rename from Plan/src/main/java/com/djrapitops/plan/modules/bungee/BungeeSuperClassBindingModule.java rename to Plan/src/main/java/com/djrapitops/plan/modules/proxy/bungee/BungeeSuperClassBindingModule.java diff --git a/Plan/src/main/java/com/djrapitops/plan/modules/proxy/velocity/VelocityServerPropertiesModule.java b/Plan/src/main/java/com/djrapitops/plan/modules/proxy/velocity/VelocityServerPropertiesModule.java new file mode 100644 index 000000000..342d99862 --- /dev/null +++ b/Plan/src/main/java/com/djrapitops/plan/modules/proxy/velocity/VelocityServerPropertiesModule.java @@ -0,0 +1,9 @@ +package com.djrapitops.plan.modules.proxy.velocity; + +/** + * //TODO Class Javadoc Comment + * + * @author Rsl1122 + */ +public class VelocityServerProperties { +} \ No newline at end of file diff --git a/Plan/src/main/java/com/djrapitops/plan/modules/proxy/velocity/VelocitySuperClassBindingModule.java b/Plan/src/main/java/com/djrapitops/plan/modules/proxy/velocity/VelocitySuperClassBindingModule.java new file mode 100644 index 000000000..b952e4829 --- /dev/null +++ b/Plan/src/main/java/com/djrapitops/plan/modules/proxy/velocity/VelocitySuperClassBindingModule.java @@ -0,0 +1,47 @@ +package com.djrapitops.plan.modules.proxy.bungee; + +import com.djrapitops.plan.system.info.server.BungeeServerInfo; +import com.djrapitops.plan.system.info.server.ServerInfo; +import com.djrapitops.plan.system.listeners.BungeeListenerSystem; +import com.djrapitops.plan.system.listeners.ListenerSystem; +import com.djrapitops.plan.system.settings.config.BungeeConfigSystem; +import com.djrapitops.plan.system.settings.config.ConfigSystem; +import com.djrapitops.plan.system.tasks.BungeeTaskSystem; +import com.djrapitops.plan.system.tasks.TaskSystem; +import dagger.Module; +import dagger.Provides; + +import javax.inject.Singleton; + +/** + * Module for binding Bungee specific classes to the interface implementations. + * + * @author Rsl1122 + */ +@Module +public class BungeeSuperClassBindingModule { + + @Provides + @Singleton + ServerInfo provideBungeeServerInfo(BungeeServerInfo bungeeServerInfo) { + return bungeeServerInfo; + } + + @Provides + @Singleton + ConfigSystem provideBungeeConfigSystem(BungeeConfigSystem bungeeConfigSystem) { + return bungeeConfigSystem; + } + + @Provides + @Singleton + TaskSystem provideBungeeTaskSystem(BungeeTaskSystem bungeeTaskSystem) { + return bungeeTaskSystem; + } + + @Provides + @Singleton + ListenerSystem provideBungeeListenerSystem(BungeeListenerSystem bungeeListenerSystem) { + return bungeeListenerSystem; + } +} \ No newline at end of file diff --git a/Plan/src/main/java/com/djrapitops/plan/modules/server/bukkit/BukkitInfoModule.java b/Plan/src/main/java/com/djrapitops/plan/modules/server/bukkit/BukkitServerPropertiesModule.java similarity index 100% rename from Plan/src/main/java/com/djrapitops/plan/modules/server/bukkit/BukkitInfoModule.java rename to Plan/src/main/java/com/djrapitops/plan/modules/server/bukkit/BukkitServerPropertiesModule.java diff --git a/Plan/src/main/java/com/djrapitops/plan/modules/server/sponge/SpongeInfoModule.java b/Plan/src/main/java/com/djrapitops/plan/modules/server/sponge/SpongeServerPropertiesModule.java similarity index 100% rename from Plan/src/main/java/com/djrapitops/plan/modules/server/sponge/SpongeInfoModule.java rename to Plan/src/main/java/com/djrapitops/plan/modules/server/sponge/SpongeServerPropertiesModule.java diff --git a/Plan/src/main/java/com/djrapitops/plan/system/BungeeSystem.java b/Plan/src/main/java/com/djrapitops/plan/system/BungeeSystem.java new file mode 100644 index 000000000..e69de29bb diff --git a/Plan/src/main/java/com/djrapitops/plan/system/afk/AFKTracker.java b/Plan/src/main/java/com/djrapitops/plan/system/afk/AFKTracker.java index 371788b14..985cdb530 100644 --- a/Plan/src/main/java/com/djrapitops/plan/system/afk/AFKTracker.java +++ b/Plan/src/main/java/com/djrapitops/plan/system/afk/AFKTracker.java @@ -66,4 +66,14 @@ public class AFKTracker { lastMovement.remove(uuid); usedAFKCommand.remove(uuid); } + + public boolean isAfk(UUID uuid) { + long time = System.currentTimeMillis(); + + Long lastMoved = lastMovement.get(uuid); + if (lastMoved == null || lastMoved == -1) { + return false; + } + return time - lastMoved > afkThresholdMs; + } } \ No newline at end of file diff --git a/Plan/src/main/java/com/djrapitops/plan/system/cache/BungeeCacheSystem.java b/Plan/src/main/java/com/djrapitops/plan/system/cache/BungeeCacheSystem.java deleted file mode 100644 index 49d1503a6..000000000 --- a/Plan/src/main/java/com/djrapitops/plan/system/cache/BungeeCacheSystem.java +++ /dev/null @@ -1,19 +0,0 @@ -package com.djrapitops.plan.system.cache; - -import javax.inject.Inject; - -/** - * CacheSystem for Bungee. - *

- * Used for overriding {@link DataCache} with {@link BungeeDataCache} - * - * @author Rsl1122 - */ -public class BungeeCacheSystem extends CacheSystem { - - @Inject - public BungeeCacheSystem(BungeeDataCache dataCache, GeolocationCache geolocationCache) { - super(dataCache, geolocationCache); - } - -} diff --git a/Plan/src/main/java/com/djrapitops/plan/system/cache/GeolocationCache.java b/Plan/src/main/java/com/djrapitops/plan/system/cache/GeolocationCache.java index d4f7481bd..dc7191506 100644 --- a/Plan/src/main/java/com/djrapitops/plan/system/cache/GeolocationCache.java +++ b/Plan/src/main/java/com/djrapitops/plan/system/cache/GeolocationCache.java @@ -19,6 +19,7 @@ import javax.inject.Singleton; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; +import java.io.InputStream; import java.net.InetAddress; import java.net.URL; import java.net.UnknownHostException; @@ -158,8 +159,12 @@ public class GeolocationCache implements SubSystem { return; } URL downloadSite = new URL("http://geolite.maxmind.com/download/geoip/database/GeoLite2-Country.mmdb.gz"); - try (ReadableByteChannel rbc = Channels.newChannel(new GZIPInputStream(downloadSite.openStream())); - FileOutputStream fos = new FileOutputStream(geolocationDB.getAbsoluteFile())) { + try ( + InputStream in = downloadSite.openStream(); + GZIPInputStream gzipIn = new GZIPInputStream(in); + ReadableByteChannel rbc = Channels.newChannel(gzipIn); + FileOutputStream fos = new FileOutputStream(geolocationDB.getAbsoluteFile()) + ) { fos.getChannel().transferFrom(rbc, 0, Long.MAX_VALUE); } } diff --git a/Plan/src/main/java/com/djrapitops/plan/system/cache/BungeeDataCache.java b/Plan/src/main/java/com/djrapitops/plan/system/cache/ProxyDataCache.java similarity index 69% rename from Plan/src/main/java/com/djrapitops/plan/system/cache/BungeeDataCache.java rename to Plan/src/main/java/com/djrapitops/plan/system/cache/ProxyDataCache.java index d4ffe1e1b..4c1a844a8 100644 --- a/Plan/src/main/java/com/djrapitops/plan/system/cache/BungeeDataCache.java +++ b/Plan/src/main/java/com/djrapitops/plan/system/cache/ProxyDataCache.java @@ -8,23 +8,23 @@ import javax.inject.Singleton; import java.util.UUID; /** - * Bungee specific DataCache. + * Proxy server specific DataCache. *

* Used for overriding {@link SessionCache#endSession(UUID, long)}. * * @author Rsl1122 */ @Singleton -public class BungeeDataCache extends DataCache { +public class ProxyDataCache extends DataCache { @Inject - public BungeeDataCache(Database database, ErrorHandler errorHandler) { + public ProxyDataCache(Database database, ErrorHandler errorHandler) { super(database, errorHandler); } @Override public void endSession(UUID uuid, long time) { removeSessionFromCache(uuid); - /* Bungee should not save sessions so session is not removed.. */ + /* Proxy should not save sessions so session is not removed.. */ } } diff --git a/Plan/src/main/java/com/djrapitops/plan/system/database/DBSystem.java b/Plan/src/main/java/com/djrapitops/plan/system/database/DBSystem.java index bc0ab6118..f8f5013f3 100644 --- a/Plan/src/main/java/com/djrapitops/plan/system/database/DBSystem.java +++ b/Plan/src/main/java/com/djrapitops/plan/system/database/DBSystem.java @@ -83,7 +83,7 @@ public abstract class DBSystem implements SubSystem { public void enable() throws EnableException { try { db.init(); - db.scheduleClean(1L); + db.scheduleClean(20L); logger.info(locale.getString(PluginLang.ENABLED_DATABASE, db.getName())); } catch (DBInitException e) { Throwable cause = e.getCause(); diff --git a/Plan/src/main/java/com/djrapitops/plan/system/database/BungeeDBSystem.java b/Plan/src/main/java/com/djrapitops/plan/system/database/ProxyDBSystem.java similarity index 89% rename from Plan/src/main/java/com/djrapitops/plan/system/database/BungeeDBSystem.java rename to Plan/src/main/java/com/djrapitops/plan/system/database/ProxyDBSystem.java index e53c807e6..f85a11a3c 100644 --- a/Plan/src/main/java/com/djrapitops/plan/system/database/BungeeDBSystem.java +++ b/Plan/src/main/java/com/djrapitops/plan/system/database/ProxyDBSystem.java @@ -19,10 +19,10 @@ import javax.inject.Singleton; * @author Rsl1122 */ @Singleton -public class BungeeDBSystem extends DBSystem { +public class ProxyDBSystem extends DBSystem { @Inject - public BungeeDBSystem(Locale locale, MySQLDB mySQLDB, + public ProxyDBSystem(Locale locale, MySQLDB mySQLDB, PluginLogger logger, Timings timings, ErrorHandler errorHandler) { super(locale, logger, timings, errorHandler); databases.add(mySQLDB); diff --git a/Plan/src/main/java/com/djrapitops/plan/system/database/databases/sql/PatchTask.java b/Plan/src/main/java/com/djrapitops/plan/system/database/databases/sql/PatchTask.java new file mode 100644 index 000000000..3a5f79711 --- /dev/null +++ b/Plan/src/main/java/com/djrapitops/plan/system/database/databases/sql/PatchTask.java @@ -0,0 +1,56 @@ +package com.djrapitops.plan.system.database.databases.sql; + +import com.djrapitops.plan.PlanPlugin; +import com.djrapitops.plan.system.database.databases.sql.patches.Patch; +import com.djrapitops.plan.system.locale.Locale; +import com.djrapitops.plan.system.locale.lang.PluginLang; +import com.djrapitops.plugin.api.utility.log.Log; +import com.djrapitops.plugin.task.AbsRunnable; + +import java.util.function.Supplier; + +/** + * Task that is in charge on patching the database when the database enables. + * + * @author Rsl1122 + */ +public class PatchTask extends AbsRunnable { + + private final Patch[] patches; + private final Supplier locale; + + public PatchTask(Patch[] patches, Supplier locale) { + this.patches = patches; + this.locale = locale; + } + + @Override + public void run() { + try { + boolean didApply = applyPatches(); + Log.info(locale.get().getString( + didApply ? PluginLang.DB_APPLIED_PATCHES : PluginLang.DB_APPLIED_PATCHES_ALREADY + )); + } catch (Exception e) { + Log.error("----------------------------------------------------"); + Log.error(locale.get().getString(PluginLang.ENABLE_FAIL_DB_PATCH)); + Log.error("----------------------------------------------------"); + Log.toLog(this.getClass(), e); + PlanPlugin.getInstance().onDisable(); + } + } + + private boolean applyPatches() { + boolean didApply = false; + for (Patch patch : patches) { + if (!patch.hasBeenApplied()) { + String patchName = patch.getClass().getSimpleName(); + Log.info(locale.get().getString(PluginLang.DB_APPLY_PATCH, patchName)); + patch.apply(); + didApply = true; + } + } + return didApply; + } + +} \ No newline at end of file diff --git a/Plan/src/main/java/com/djrapitops/plan/system/database/databases/sql/SQLDB.java b/Plan/src/main/java/com/djrapitops/plan/system/database/databases/sql/SQLDB.java index 2ac8bf84c..691fcb51f 100644 --- a/Plan/src/main/java/com/djrapitops/plan/system/database/databases/sql/SQLDB.java +++ b/Plan/src/main/java/com/djrapitops/plan/system/database/databases/sql/SQLDB.java @@ -183,30 +183,12 @@ public abstract class SQLDB extends Database { new VersionTableRemovalPatch(this) }; - runnableFactory.create("Database Patch", new AbsRunnable() { - @Override - public void run() { - try { - boolean applied = false; - for (Patch patch : patches) { - if (!patch.hasBeenApplied()) { - String patchName = patch.getClass().getSimpleName(); - logger.info(locale.getString(PluginLang.DB_APPLY_PATCH, patchName)); - patch.apply(); - applied = true; - } - } - logger.info(locale.getString( - applied ? PluginLang.DB_APPLIED_PATCHES : PluginLang.DB_APPLIED_PATCHES_ALREADY - )); - } catch (Exception e) { - logger.error("----------------------------------------------------"); - logger.error(locale.getString(PluginLang.ENABLE_FAIL_DB_PATCH)); - logger.error("----------------------------------------------------"); - errorHandler.log(L.CRITICAL, this.getClass(), e); - } - } - }).runTaskLaterAsynchronously(TimeAmount.toTicks(5L, TimeUnit.SECONDS)); + try { + runnableFactory.createNew("Database Patch", new PatchTask(patches, locale)) + .runTaskLaterAsynchronously(TimeAmount.SECOND.ticks() * 5L); + } catch (Exception ignore) { + // Task failed to register because plugin is being disabled + } } catch (DBOpException e) { throw new DBInitException("Failed to set-up Database", e); } diff --git a/Plan/src/main/java/com/djrapitops/plan/system/database/databases/sql/operation/SQLFetchOps.java b/Plan/src/main/java/com/djrapitops/plan/system/database/databases/sql/operation/SQLFetchOps.java index 6d0602e85..a95f6532c 100644 --- a/Plan/src/main/java/com/djrapitops/plan/system/database/databases/sql/operation/SQLFetchOps.java +++ b/Plan/src/main/java/com/djrapitops/plan/system/database/databases/sql/operation/SQLFetchOps.java @@ -9,9 +9,11 @@ import com.djrapitops.plan.data.store.mutators.PlayersMutator; import com.djrapitops.plan.data.store.mutators.SessionsMutator; import com.djrapitops.plan.data.store.objects.DateObj; import com.djrapitops.plan.data.time.WorldTimes; +import com.djrapitops.plan.system.cache.SessionCache; import com.djrapitops.plan.system.database.databases.operation.FetchOperations; import com.djrapitops.plan.system.database.databases.sql.SQLDB; import com.djrapitops.plan.system.info.server.Server; +import com.djrapitops.plan.system.info.server.ServerInfo; import java.util.*; import java.util.concurrent.TimeUnit; @@ -25,7 +27,7 @@ public class SQLFetchOps extends SQLOps implements FetchOperations { @Override public NetworkContainer getNetworkContainer() { NetworkContainer networkContainer = db.getNetworkContainerFactory().forBungeeContainer(getBungeeServerContainer()); - networkContainer.putSupplier(NetworkKeys.BUKKIT_SERVERS, () -> getBukkitServers().values()); + networkContainer.putCachingSupplier(NetworkKeys.BUKKIT_SERVERS, () -> getBukkitServers().values()); return networkContainer; } @@ -36,8 +38,8 @@ public class SQLFetchOps extends SQLOps implements FetchOperations { } ServerContainer container = getServerContainer(bungeeInfo.get().getUuid()); - container.putSupplier(ServerKeys.PLAYERS, this::getAllPlayerContainers); - container.putSupplier(ServerKeys.TPS, tpsTable::getNetworkOnlineData); + container.putCachingSupplier(ServerKeys.PLAYERS, this::getAllPlayerContainers); + container.putCachingSupplier(ServerKeys.TPS, tpsTable::getNetworkOnlineData); container.putSupplier(ServerKeys.WORLD_TIMES, null); // Additional Session information not supported container.putSupplier(ServerKeys.PLAYER_KILLS, null); container.putSupplier(ServerKeys.PLAYER_KILL_COUNT, null); @@ -56,12 +58,12 @@ public class SQLFetchOps extends SQLOps implements FetchOperations { container.putRawData(ServerKeys.SERVER_UUID, serverUUID); container.putRawData(ServerKeys.NAME, serverInfo.get().getName()); - container.putSupplier(ServerKeys.PLAYERS, () -> getPlayerContainers(serverUUID)); + container.putCachingSupplier(ServerKeys.PLAYERS, () -> getPlayerContainers(serverUUID)); container.putSupplier(ServerKeys.PLAYER_COUNT, () -> container.getUnsafe(ServerKeys.PLAYERS).size()); - container.putSupplier(ServerKeys.TPS, () -> tpsTable.getTPSData(serverUUID)); - container.putSupplier(ServerKeys.PING, () -> PlayersMutator.forContainer(container).pings()); - container.putSupplier(ServerKeys.ALL_TIME_PEAK_PLAYERS, () -> { + container.putCachingSupplier(ServerKeys.TPS, () -> tpsTable.getTPSData(serverUUID)); + container.putCachingSupplier(ServerKeys.PING, () -> PlayersMutator.forContainer(container).pings()); + container.putCachingSupplier(ServerKeys.ALL_TIME_PEAK_PLAYERS, () -> { Optional allTimePeak = tpsTable.getAllTimePeak(serverUUID); if (allTimePeak.isPresent()) { TPS peak = allTimePeak.get(); @@ -69,7 +71,7 @@ public class SQLFetchOps extends SQLOps implements FetchOperations { } return null; }); - container.putSupplier(ServerKeys.RECENT_PEAK_PLAYERS, () -> { + container.putCachingSupplier(ServerKeys.RECENT_PEAK_PLAYERS, () -> { long twoDaysAgo = System.currentTimeMillis() - (TimeUnit.DAYS.toMillis(2L)); Optional lastPeak = tpsTable.getPeakPlayerCount(serverUUID, twoDaysAgo); if (lastPeak.isPresent()) { @@ -79,16 +81,22 @@ public class SQLFetchOps extends SQLOps implements FetchOperations { return null; }); - container.putSupplier(ServerKeys.COMMAND_USAGE, () -> commandUseTable.getCommandUse(serverUUID)); - container.putSupplier(ServerKeys.WORLD_TIMES, () -> worldTimesTable.getWorldTimesOfServer(serverUUID)); + container.putCachingSupplier(ServerKeys.COMMAND_USAGE, () -> commandUseTable.getCommandUse(serverUUID)); + container.putCachingSupplier(ServerKeys.WORLD_TIMES, () -> worldTimesTable.getWorldTimesOfServer(serverUUID)); // Calculating getters - container.putSupplier(ServerKeys.OPERATORS, () -> PlayersMutator.forContainer(container).operators()); - container.putSupplier(ServerKeys.SESSIONS, () -> PlayersMutator.forContainer(container).getSessions()); - container.putSupplier(ServerKeys.PLAYER_KILLS, () -> SessionsMutator.forContainer(container).toPlayerKillList()); - container.putSupplier(ServerKeys.PLAYER_KILL_COUNT, () -> container.getUnsafe(ServerKeys.PLAYER_KILLS).size()); - container.putSupplier(ServerKeys.MOB_KILL_COUNT, () -> SessionsMutator.forContainer(container).toMobKillCount()); - container.putSupplier(ServerKeys.DEATH_COUNT, () -> SessionsMutator.forContainer(container).toDeathCount()); + container.putCachingSupplier(ServerKeys.OPERATORS, () -> PlayersMutator.forContainer(container).operators()); + container.putCachingSupplier(ServerKeys.SESSIONS, () -> { + List sessions = PlayersMutator.forContainer(container).getSessions(); + if (serverUUID.equals(serverInfo.get().getUuid())) { + sessions.addAll(SessionCache.getActiveSessions().values()); + } + return sessions; + }); + container.putCachingSupplier(ServerKeys.PLAYER_KILLS, () -> SessionsMutator.forContainer(container).toPlayerKillList()); + container.putCachingSupplier(ServerKeys.PLAYER_KILL_COUNT, () -> container.getUnsafe(ServerKeys.PLAYER_KILLS).size()); + container.putCachingSupplier(ServerKeys.MOB_KILL_COUNT, () -> SessionsMutator.forContainer(container).toMobKillCount()); + container.putCachingSupplier(ServerKeys.DEATH_COUNT, () -> SessionsMutator.forContainer(container).toDeathCount()); return container; } @@ -121,13 +129,13 @@ public class SQLFetchOps extends SQLOps implements FetchOperations { container.putRawData(PlayerKeys.KICK_COUNT, timesKicked.get(uuid)); container.putRawData(PlayerKeys.GEO_INFO, geoInfo.get(uuid)); container.putRawData(PlayerKeys.PING, allPings.get(uuid)); - container.putSupplier(PlayerKeys.NICKNAMES, () -> nicknamesTable.getNicknameInformation(uuid)); + container.putCachingSupplier(PlayerKeys.NICKNAMES, () -> nicknamesTable.getNicknameInformation(uuid)); container.putRawData(PlayerKeys.PER_SERVER, perServerInfo.get(uuid)); container.putRawData(PlayerKeys.BANNED, userInfo.isBanned()); container.putRawData(PlayerKeys.OPERATOR, userInfo.isOperator()); - container.putSupplier(PlayerKeys.SESSIONS, () -> { + container.putCachingSupplier(PlayerKeys.SESSIONS, () -> { List playerSessions = sessions.getOrDefault(uuid, new ArrayList<>()); container.getValue(PlayerKeys.ACTIVE_SESSION).ifPresent(playerSessions::add); return playerSessions; @@ -135,7 +143,7 @@ public class SQLFetchOps extends SQLOps implements FetchOperations { ); // Calculating getters - container.putSupplier(PlayerKeys.WORLD_TIMES, () -> { + container.putCachingSupplier(PlayerKeys.WORLD_TIMES, () -> { WorldTimes worldTimes = new PerServerMutator(container.getUnsafe(PlayerKeys.PER_SERVER)).flatMapWorldTimes(); container.getValue(PlayerKeys.ACTIVE_SESSION) .ifPresent(session -> worldTimes.add( @@ -179,10 +187,10 @@ public class SQLFetchOps extends SQLOps implements FetchOperations { container.putRawData(PlayerKeys.KICK_COUNT, timesKicked.get(uuid)); container.putRawData(PlayerKeys.GEO_INFO, geoInfo.get(uuid)); container.putRawData(PlayerKeys.PING, allPings.get(uuid)); - container.putSupplier(PlayerKeys.NICKNAMES, () -> nicknamesTable.getNicknameInformation(uuid)); + container.putCachingSupplier(PlayerKeys.NICKNAMES, () -> nicknamesTable.getNicknameInformation(uuid)); container.putRawData(PlayerKeys.PER_SERVER, perServerInfo.get(uuid)); - container.putSupplier(PlayerKeys.SESSIONS, () -> { + container.putCachingSupplier(PlayerKeys.SESSIONS, () -> { List playerSessions = PerServerMutator.forContainer(container).flatMapSessions(); container.getValue(PlayerKeys.ACTIVE_SESSION).ifPresent(playerSessions::add); return playerSessions; @@ -277,21 +285,21 @@ public class SQLFetchOps extends SQLOps implements FetchOperations { container.putRawData(PlayerKeys.UUID, uuid); container.putAll(usersTable.getUserInformation(uuid)); - container.putSupplier(PlayerKeys.GEO_INFO, () -> geoInfoTable.getGeoInfo(uuid)); - container.putSupplier(PlayerKeys.PING, () -> pingTable.getPing(uuid)); - container.putSupplier(PlayerKeys.NICKNAMES, () -> nicknamesTable.getNicknameInformation(uuid)); - container.putSupplier(PlayerKeys.PER_SERVER, () -> getPerServerData(uuid)); + container.putCachingSupplier(PlayerKeys.GEO_INFO, () -> geoInfoTable.getGeoInfo(uuid)); + container.putCachingSupplier(PlayerKeys.PING, () -> pingTable.getPing(uuid)); + container.putCachingSupplier(PlayerKeys.NICKNAMES, () -> nicknamesTable.getNicknameInformation(uuid)); + container.putCachingSupplier(PlayerKeys.PER_SERVER, () -> getPerServerData(uuid)); container.putSupplier(PlayerKeys.BANNED, () -> new PerServerMutator(container.getUnsafe(PlayerKeys.PER_SERVER)).isBanned()); container.putSupplier(PlayerKeys.OPERATOR, () -> new PerServerMutator(container.getUnsafe(PlayerKeys.PER_SERVER)).isOperator()); - container.putSupplier(PlayerKeys.SESSIONS, () -> { + container.putCachingSupplier(PlayerKeys.SESSIONS, () -> { List sessions = new PerServerMutator(container.getUnsafe(PlayerKeys.PER_SERVER)).flatMapSessions(); container.getValue(PlayerKeys.ACTIVE_SESSION).ifPresent(sessions::add); return sessions; } ); - container.putSupplier(PlayerKeys.WORLD_TIMES, () -> + container.putCachingSupplier(PlayerKeys.WORLD_TIMES, () -> { WorldTimes worldTimes = new PerServerMutator(container.getUnsafe(PlayerKeys.PER_SERVER)).flatMapWorldTimes(); container.getValue(PlayerKeys.ACTIVE_SESSION).ifPresent(session -> worldTimes.add( diff --git a/Plan/src/main/java/com/djrapitops/plan/system/database/databases/sql/patches/GeoInfoLastUsedPatch.java b/Plan/src/main/java/com/djrapitops/plan/system/database/databases/sql/patches/GeoInfoLastUsedPatch.java index 80b0d2ef5..d4ab90098 100644 --- a/Plan/src/main/java/com/djrapitops/plan/system/database/databases/sql/patches/GeoInfoLastUsedPatch.java +++ b/Plan/src/main/java/com/djrapitops/plan/system/database/databases/sql/patches/GeoInfoLastUsedPatch.java @@ -16,7 +16,7 @@ public class GeoInfoLastUsedPatch extends Patch { @Override public void apply() { - addColumns(GeoInfoTable.TABLE_NAME, + addColumn(GeoInfoTable.TABLE_NAME, GeoInfoTable.Col.LAST_USED + " bigint NOT NULL DEFAULT 0" ); } diff --git a/Plan/src/main/java/com/djrapitops/plan/system/database/databases/sql/patches/IPHashPatch.java b/Plan/src/main/java/com/djrapitops/plan/system/database/databases/sql/patches/IPHashPatch.java index a83134639..8f4b675ea 100644 --- a/Plan/src/main/java/com/djrapitops/plan/system/database/databases/sql/patches/IPHashPatch.java +++ b/Plan/src/main/java/com/djrapitops/plan/system/database/databases/sql/patches/IPHashPatch.java @@ -16,6 +16,6 @@ public class IPHashPatch extends Patch { @Override public void apply() { - addColumns(GeoInfoTable.Col.IP_HASH.get() + " varchar(200) DEFAULT ''"); + addColumn(GeoInfoTable.TABLE_NAME, GeoInfoTable.Col.IP_HASH.get() + " varchar(200) DEFAULT ''"); } } diff --git a/Plan/src/main/java/com/djrapitops/plan/system/database/databases/sql/patches/KillsServerIDPatch.java b/Plan/src/main/java/com/djrapitops/plan/system/database/databases/sql/patches/KillsServerIDPatch.java index e6832a7fd..8428524da 100644 --- a/Plan/src/main/java/com/djrapitops/plan/system/database/databases/sql/patches/KillsServerIDPatch.java +++ b/Plan/src/main/java/com/djrapitops/plan/system/database/databases/sql/patches/KillsServerIDPatch.java @@ -40,7 +40,7 @@ public class KillsServerIDPatch extends Patch { @Override public void apply() { - addColumns(KillsTable.Col.SERVER_ID + " integer NOT NULL DEFAULT 0"); + addColumn(KillsTable.TABLE_NAME, KillsTable.Col.SERVER_ID + " integer NOT NULL DEFAULT 0"); Map sessionIDServerIDRelation = db.getSessionsTable().getIDServerIDRelation(); diff --git a/Plan/src/main/java/com/djrapitops/plan/system/database/databases/sql/patches/NicknameLastSeenPatch.java b/Plan/src/main/java/com/djrapitops/plan/system/database/databases/sql/patches/NicknameLastSeenPatch.java index a45bc697b..a9155de2f 100644 --- a/Plan/src/main/java/com/djrapitops/plan/system/database/databases/sql/patches/NicknameLastSeenPatch.java +++ b/Plan/src/main/java/com/djrapitops/plan/system/database/databases/sql/patches/NicknameLastSeenPatch.java @@ -25,7 +25,7 @@ public class NicknameLastSeenPatch extends Patch { @Override public void apply() { - addColumns(NicknamesTable.TABLE_NAME, + addColumn(NicknamesTable.TABLE_NAME, NicknamesTable.Col.LAST_USED + " bigint NOT NULL DEFAULT '0'" ); diff --git a/Plan/src/main/java/com/djrapitops/plan/system/database/databases/sql/patches/Patch.java b/Plan/src/main/java/com/djrapitops/plan/system/database/databases/sql/patches/Patch.java index 4205a114f..716f2055d 100644 --- a/Plan/src/main/java/com/djrapitops/plan/system/database/databases/sql/patches/Patch.java +++ b/Plan/src/main/java/com/djrapitops/plan/system/database/databases/sql/patches/Patch.java @@ -4,6 +4,7 @@ import com.djrapitops.plan.system.database.databases.sql.SQLDB; import com.djrapitops.plan.system.database.databases.sql.processing.QueryAllStatement; import com.djrapitops.plan.system.database.databases.sql.processing.QueryStatement; import com.djrapitops.plan.system.database.databases.sql.statements.TableSqlParser; +import com.djrapitops.plan.system.settings.Settings; import java.sql.PreparedStatement; import java.sql.ResultSet; @@ -30,13 +31,16 @@ public abstract class Patch { public boolean hasTable(String tableName) { String sql = usingMySQL ? - "SELECT * FROM information_schema.TABLES WHERE table_name=? LIMIT 1" : + "SELECT * FROM information_schema.TABLES WHERE table_name=? AND TABLE_SCHEMA=? LIMIT 1" : "SELECT tbl_name FROM sqlite_master WHERE tbl_name=?"; return query(new QueryStatement(sql) { @Override public void prepare(PreparedStatement statement) throws SQLException { statement.setString(1, tableName); + if (usingMySQL) { + statement.setString(2, Settings.DB_DATABASE.toString()); + } } @Override @@ -49,11 +53,12 @@ public abstract class Patch { protected boolean hasColumn(String tableName, String columnName) { return usingMySQL ? query(new QueryStatement("SELECT * FROM information_schema.COLUMNS" + - " WHERE TABLE_NAME=? AND COLUMN_NAME=?") { + " WHERE TABLE_NAME=? AND COLUMN_NAME=? AND TABLE_SCHEMA=?") { @Override public void prepare(PreparedStatement statement) throws SQLException { statement.setString(1, tableName); statement.setString(2, columnName); + statement.setString(3, Settings.DB_DATABASE.toString()); } @Override @@ -74,11 +79,8 @@ public abstract class Patch { }); } - protected void addColumns(String tableName, String... columnInfo) { - for (int i = 0; i < columnInfo.length; i++) { - columnInfo[i] = "ALTER TABLE " + tableName + " ADD " + (usingMySQL ? "" : "COLUMN ") + columnInfo[i]; - } - db.executeUnsafe(columnInfo); + protected void addColumn(String tableName, String columnInfo) { + db.executeUnsafe("ALTER TABLE " + tableName + " ADD " + (usingMySQL ? "" : "COLUMN ") + columnInfo); } protected void dropTable(String name) { diff --git a/Plan/src/main/java/com/djrapitops/plan/system/database/databases/sql/patches/SessionAFKTimePatch.java b/Plan/src/main/java/com/djrapitops/plan/system/database/databases/sql/patches/SessionAFKTimePatch.java index 672780d3c..5e8a28032 100644 --- a/Plan/src/main/java/com/djrapitops/plan/system/database/databases/sql/patches/SessionAFKTimePatch.java +++ b/Plan/src/main/java/com/djrapitops/plan/system/database/databases/sql/patches/SessionAFKTimePatch.java @@ -16,7 +16,7 @@ public class SessionAFKTimePatch extends Patch { @Override public void apply() { - addColumns(SessionsTable.TABLE_NAME, + addColumn(SessionsTable.TABLE_NAME, SessionsTable.Col.AFK_TIME + " bigint NOT NULL DEFAULT 0" ); } diff --git a/Plan/src/main/java/com/djrapitops/plan/system/database/databases/sql/patches/TransferPartitionPatch.java b/Plan/src/main/java/com/djrapitops/plan/system/database/databases/sql/patches/TransferPartitionPatch.java index 147d8e47a..eea4affe9 100644 --- a/Plan/src/main/java/com/djrapitops/plan/system/database/databases/sql/patches/TransferPartitionPatch.java +++ b/Plan/src/main/java/com/djrapitops/plan/system/database/databases/sql/patches/TransferPartitionPatch.java @@ -16,6 +16,6 @@ public class TransferPartitionPatch extends Patch { @Override public void apply() { - addColumns(TransferTable.TABLE_NAME, TransferTable.Col.PART + " bigint NOT NULL DEFAULT 0"); + addColumn(TransferTable.TABLE_NAME, TransferTable.Col.PART + " bigint NOT NULL DEFAULT 0"); } } diff --git a/Plan/src/main/java/com/djrapitops/plan/system/database/databases/sql/patches/VersionTableRemovalPatch.java b/Plan/src/main/java/com/djrapitops/plan/system/database/databases/sql/patches/VersionTableRemovalPatch.java index c205cbded..b4252bfa0 100644 --- a/Plan/src/main/java/com/djrapitops/plan/system/database/databases/sql/patches/VersionTableRemovalPatch.java +++ b/Plan/src/main/java/com/djrapitops/plan/system/database/databases/sql/patches/VersionTableRemovalPatch.java @@ -10,7 +10,7 @@ public class VersionTableRemovalPatch extends Patch { @Override public boolean hasBeenApplied() { - return hasTable("plan_version"); + return !hasTable("plan_version"); } @Override diff --git a/Plan/src/main/java/com/djrapitops/plan/system/database/databases/sql/tables/KillsTable.java b/Plan/src/main/java/com/djrapitops/plan/system/database/databases/sql/tables/KillsTable.java index 2c2fb86f0..1380ceab0 100644 --- a/Plan/src/main/java/com/djrapitops/plan/system/database/databases/sql/tables/KillsTable.java +++ b/Plan/src/main/java/com/djrapitops/plan/system/database/databases/sql/tables/KillsTable.java @@ -118,7 +118,7 @@ public class KillsTable extends UserIDTable { String victimName = set.getString("victim_name"); long date = set.getLong(Col.DATE.get()); String weapon = set.getString(Col.WEAPON.get()); - session.getUnsafe(SessionKeys.PLAYER_KILLS).add(new PlayerKill(victim, weapon, date, victimName)); + session.getPlayerKills().add(new PlayerKill(victim, weapon, date, victimName)); } return null; } @@ -295,7 +295,7 @@ public class KillsTable extends UserIDTable { for (Session session : sessions) { int sessionID = session.getUnsafe(SessionKeys.DB_ID); // Every kill - for (PlayerKill kill : session.getUnsafe(SessionKeys.PLAYER_KILLS)) { + for (PlayerKill kill : session.getPlayerKills()) { UUID victim = kill.getVictim(); long date = kill.getDate(); String weapon = kill.getWeapon(); diff --git a/Plan/src/main/java/com/djrapitops/plan/system/database/databases/sql/tables/ServerTable.java b/Plan/src/main/java/com/djrapitops/plan/system/database/databases/sql/tables/ServerTable.java index 6df2a8a83..d9a3aaf18 100644 --- a/Plan/src/main/java/com/djrapitops/plan/system/database/databases/sql/tables/ServerTable.java +++ b/Plan/src/main/java/com/djrapitops/plan/system/database/databases/sql/tables/ServerTable.java @@ -367,7 +367,7 @@ public class ServerTable extends Table { } public void setAsUninstalled(UUID serverUUID) { - String sql = "UPDATE " + tableName + " SET (" + Col.INSTALLED + "=?) WHERE " + Col.SERVER_UUID + "=?"; + String sql = "UPDATE " + tableName + " SET " + Col.INSTALLED + "=? WHERE " + Col.SERVER_UUID + "=?"; execute(new ExecStatement(sql) { @Override diff --git a/Plan/src/main/java/com/djrapitops/plan/system/database/databases/sql/tables/SessionsTable.java b/Plan/src/main/java/com/djrapitops/plan/system/database/databases/sql/tables/SessionsTable.java index 3f57aecc0..1a12e8ecd 100644 --- a/Plan/src/main/java/com/djrapitops/plan/system/database/databases/sql/tables/SessionsTable.java +++ b/Plan/src/main/java/com/djrapitops/plan/system/database/databases/sql/tables/SessionsTable.java @@ -112,7 +112,7 @@ public class SessionsTable extends UserIDTable { } db.getWorldTimesTable().saveWorldTimes(uuid, sessionID, session.getUnsafe(SessionKeys.WORLD_TIMES)); - db.getKillsTable().savePlayerKills(uuid, sessionID, session.getUnsafe(SessionKeys.PLAYER_KILLS)); + db.getKillsTable().savePlayerKills(uuid, sessionID, session.getPlayerKills()); } /** diff --git a/Plan/src/main/java/com/djrapitops/plan/system/database/databases/sql/tables/TPSTable.java b/Plan/src/main/java/com/djrapitops/plan/system/database/databases/sql/tables/TPSTable.java index bc583183f..f2868648d 100644 --- a/Plan/src/main/java/com/djrapitops/plan/system/database/databases/sql/tables/TPSTable.java +++ b/Plan/src/main/java/com/djrapitops/plan/system/database/databases/sql/tables/TPSTable.java @@ -147,9 +147,12 @@ public class TPSTable extends Table { } public Optional getPeakPlayerCount(UUID serverUUID, long afterDate) { + String subStatement = "SELECT MAX(" + Col.PLAYERS_ONLINE + ") FROM " + tableName + + " WHERE " + Col.SERVER_ID + "=" + serverTable.statementSelectServerID + + " AND " + Col.DATE + ">= ?"; String sql = Select.all(tableName) .where(Col.SERVER_ID + "=" + serverTable.statementSelectServerID) - .and(Col.PLAYERS_ONLINE + "= (SELECT MAX(" + Col.PLAYERS_ONLINE + ") FROM " + tableName + ")") + .and(Col.PLAYERS_ONLINE + "= (" + subStatement + ")") .and(Col.DATE + ">= ?") .toString(); @@ -157,13 +160,14 @@ public class TPSTable extends Table { @Override public void prepare(PreparedStatement statement) throws SQLException { statement.setString(1, serverUUID.toString()); - statement.setLong(2, afterDate); + statement.setString(2, serverUUID.toString()); + statement.setLong(3, afterDate); + statement.setLong(4, afterDate); } @Override public Optional processResults(ResultSet set) throws SQLException { if (set.next()) { - TPS tps = TPSBuilder.get() .date(set.getLong(Col.DATE.get())) .tps(set.getDouble(Col.TPS.get())) diff --git a/Plan/src/main/java/com/djrapitops/plan/system/importing/data/UserImportData.java b/Plan/src/main/java/com/djrapitops/plan/system/importing/data/UserImportData.java index 134f7f3e3..23e4d0b06 100644 --- a/Plan/src/main/java/com/djrapitops/plan/system/importing/data/UserImportData.java +++ b/Plan/src/main/java/com/djrapitops/plan/system/importing/data/UserImportData.java @@ -276,4 +276,28 @@ public class UserImportData { return new UserImportData(name, uuid, nicknames, registered, op, banned, timesKicked, ips, worldTimes, kills, mobKills, deaths); } } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof UserImportData)) return false; + UserImportData that = (UserImportData) o; + return registered == that.registered && + op == that.op && + banned == that.banned && + timesKicked == that.timesKicked && + mobKills == that.mobKills && + deaths == that.deaths && + Objects.equals(name, that.name) && + Objects.equals(uuid, that.uuid) && + Objects.equals(nicknames, that.nicknames) && + Objects.equals(ips, that.ips) && + Objects.equals(worldTimes, that.worldTimes) && + Objects.equals(kills, that.kills); + } + + @Override + public int hashCode() { + return Objects.hash(name, uuid, nicknames, registered, op, banned, timesKicked, ips, worldTimes, kills, mobKills, deaths); + } } diff --git a/Plan/src/main/java/com/djrapitops/plan/system/info/BungeeInfoSystem.java b/Plan/src/main/java/com/djrapitops/plan/system/info/ProxyInfoSystem.java similarity index 81% rename from Plan/src/main/java/com/djrapitops/plan/system/info/BungeeInfoSystem.java rename to Plan/src/main/java/com/djrapitops/plan/system/info/ProxyInfoSystem.java index a55b39146..2b973af21 100644 --- a/Plan/src/main/java/com/djrapitops/plan/system/info/BungeeInfoSystem.java +++ b/Plan/src/main/java/com/djrapitops/plan/system/info/ProxyInfoSystem.java @@ -7,10 +7,7 @@ package com.djrapitops.plan.system.info; import com.djrapitops.plan.api.exceptions.connection.NoServersException; import com.djrapitops.plan.api.exceptions.connection.WebException; import com.djrapitops.plan.system.info.connection.ConnectionSystem; -import com.djrapitops.plan.system.info.request.CacheRequest; -import com.djrapitops.plan.system.info.request.GenerateInspectPageRequest; -import com.djrapitops.plan.system.info.request.InfoRequest; -import com.djrapitops.plan.system.info.request.InfoRequestFactory; +import com.djrapitops.plan.system.info.request.*; import com.djrapitops.plan.system.info.server.ServerInfo; import com.djrapitops.plan.system.webserver.WebServer; import com.djrapitops.plugin.logging.console.PluginLogger; @@ -25,10 +22,10 @@ import javax.inject.Singleton; * @author Rsl1122 */ @Singleton -public class BungeeInfoSystem extends InfoSystem { +public class ProxyInfoSystem extends InfoSystem { @Inject - public BungeeInfoSystem( + public ProxyInfoSystem( InfoRequestFactory infoRequestFactory, ConnectionSystem connectionSystem, ServerInfo serverInfo, @@ -41,7 +38,9 @@ public class BungeeInfoSystem extends InfoSystem { @Override public void runLocally(InfoRequest infoRequest) throws WebException { if (infoRequest instanceof CacheRequest - || infoRequest instanceof GenerateInspectPageRequest) { + || infoRequest instanceof GenerateInspectPageRequest + || infoRequest instanceof GenerateInspectPluginsTabRequest + ) { infoRequest.runLocally(); } else { // runLocally is called when ConnectionSystem has no servers. diff --git a/Plan/src/main/java/com/djrapitops/plan/system/info/connection/ConnectionOut.java b/Plan/src/main/java/com/djrapitops/plan/system/info/connection/ConnectionOut.java index 1d8c0f054..e781667e6 100644 --- a/Plan/src/main/java/com/djrapitops/plan/system/info/connection/ConnectionOut.java +++ b/Plan/src/main/java/com/djrapitops/plan/system/info/connection/ConnectionOut.java @@ -144,6 +144,7 @@ public class ConnectionOut { post.setHeader("Content-Type", "application/x-www-form-urlencoded"); post.setHeader("charset", "UTF-8"); + post.setHeader("Connection", "close"); byte[] toSend = parameters.getBytes(); post.setEntity(new ByteArrayEntity(toSend)); diff --git a/Plan/src/main/java/com/djrapitops/plan/system/info/connection/BungeeConnectionSystem.java b/Plan/src/main/java/com/djrapitops/plan/system/info/connection/ProxyConnectionSystem.java similarity index 86% rename from Plan/src/main/java/com/djrapitops/plan/system/info/connection/BungeeConnectionSystem.java rename to Plan/src/main/java/com/djrapitops/plan/system/info/connection/ProxyConnectionSystem.java index f35419bf5..11c7d6daa 100644 --- a/Plan/src/main/java/com/djrapitops/plan/system/info/connection/BungeeConnectionSystem.java +++ b/Plan/src/main/java/com/djrapitops/plan/system/info/connection/ProxyConnectionSystem.java @@ -22,12 +22,12 @@ import java.util.UUID; import java.util.concurrent.TimeUnit; /** - * ConnectionSystem for Bungee. + * ConnectionSystem for proxy servers. * * @author Rsl1122 */ @Singleton -public class BungeeConnectionSystem extends ConnectionSystem { +public class ProxyConnectionSystem extends ConnectionSystem { private final Database database; private final Lazy webServer; @@ -37,7 +37,7 @@ public class BungeeConnectionSystem extends ConnectionSystem { private long latestServerMapRefresh; @Inject - public BungeeConnectionSystem( + public ProxyConnectionSystem( Database database, Lazy webServer, ConnectionLog connectionLog, @@ -52,7 +52,6 @@ public class BungeeConnectionSystem extends ConnectionSystem { this.webServer = webServer; this.errorHandler = errorHandler; this.webExceptionLogger = webExceptionLogger; - latestServerMapRefresh = 0; } @@ -71,7 +70,9 @@ public class BungeeConnectionSystem extends ConnectionSystem { protected Server selectServerForRequest(InfoRequest infoRequest) throws NoServersException { refreshServerMap(); Server server = null; - if (infoRequest instanceof CacheRequest || infoRequest instanceof GenerateInspectPageRequest) { + if (infoRequest instanceof CacheRequest + || infoRequest instanceof GenerateInspectPageRequest + || infoRequest instanceof GenerateInspectPluginsTabRequest) { // Run locally return serverInfo.getServer(); } else if (infoRequest instanceof GenerateAnalysisPageRequest) { @@ -93,6 +94,10 @@ public class BungeeConnectionSystem extends ConnectionSystem { for (Server server : bukkitServers.values()) { webExceptionLogger.logIfOccurs(this.getClass(), () -> sendInfoRequest(infoRequest, server)); } + // Quick hack + if (infoRequest instanceof GenerateInspectPluginsTabRequest) { + webExceptionLogger.logIfOccurs(this.getClass(), () -> InfoSystem.getInstance().sendRequest(infoRequest)); + } } @Override diff --git a/Plan/src/main/java/com/djrapitops/plan/system/info/server/VelocityServerInfo.java b/Plan/src/main/java/com/djrapitops/plan/system/info/server/VelocityServerInfo.java new file mode 100644 index 000000000..d3bb32488 --- /dev/null +++ b/Plan/src/main/java/com/djrapitops/plan/system/info/server/VelocityServerInfo.java @@ -0,0 +1,88 @@ +/* + * License is provided in the jar as LICENSE also here: + * https://github.com/Rsl1122/Plan-PlayerAnalytics/blob/master/Plan/src/main/resources/LICENSE + */ +package com.djrapitops.plan.system.info.server; + +import com.djrapitops.plan.PlanVelocity; +import com.djrapitops.plan.api.exceptions.EnableException; +import com.djrapitops.plan.api.exceptions.database.DBOpException; +import com.djrapitops.plan.system.database.databases.Database; +import com.djrapitops.plan.system.info.server.properties.VelocityServerProperties; +import com.djrapitops.plan.system.info.server.properties.ServerProperties; +import com.djrapitops.plan.system.webserver.WebServerSystem; +import com.djrapitops.plugin.api.utility.log.Log; + +import java.util.Optional; +import java.util.UUID; + +/** + * Manages Server information on the Velocity instance. + * + * Based on BungeeServerInfo + * + * @author MicleBrick + */ +public class VelocityServerInfo extends ServerInfo { + + public VelocityServerInfo(PlanVelocity plugin) { + super(new VelocityServerProperties(plugin.getProxy())); + } + + @Override + public Server loadServerInfo() throws EnableException { + checkIfDefaultIP(); + + try { + Database db = Database.getActive(); + // doesn't seem like this would need to be different for velocity, perhaps rename to getProxyInformation()? + Optional velocityInfo = db.fetch().getBungeeInformation(); + if (velocityInfo.isPresent()) { + server = velocityInfo.get(); + updateServerInfo(db); + } else { + server = registerVelocityInfo(db); + } + } catch (DBOpException e) { + throw new EnableException("Failed to read Server information from Database."); + } + return server; + } + + private void updateServerInfo(Database db) { + String accessAddress = WebServerSystem.getInstance().getWebServer().getAccessAddress(); + if (!accessAddress.equals(server.getWebAddress())) { + server.setWebAddress(accessAddress); + db.save().serverInfoForThisServer(server); + } + } + + private void checkIfDefaultIP() throws EnableException { + String ip = ServerInfo.getServerProperties().getIp(); + if ("0.0.0.0".equals(ip)) { + Log.error("IP setting still 0.0.0.0 - Configure AlternativeIP/IP that connects to the Proxy server."); + Log.info("Player Analytics partially enabled (Use /planvelocity to reload config)"); + throw new EnableException("IP setting still 0.0.0.0 - Configure AlternativeIP/IP that connects to the Proxy server."); + } + } + + private Server registerVelocityInfo(Database db) throws EnableException { + ServerProperties properties = ServerInfo.getServerProperties(); + UUID serverUUID = generateNewUUID(properties); + String accessAddress = WebServerSystem.getInstance().getWebServer().getAccessAddress(); + + Server velocityCord = new Server(-1, serverUUID, "VelocityCord", accessAddress, properties.getMaxPlayers()); + db.save().serverInfoForThisServer(velocityCord); + + Optional velocityInfo = db.fetch().getBungeeInformation(); + if (velocityInfo.isPresent()) { + return velocityInfo.get(); + } + throw new EnableException("VelocityCord registration failed (DB)"); + } + + private UUID generateNewUUID(ServerProperties properties) { + String seed = properties.getName() + properties.getIp() + properties.getPort() + properties.getVersion() + properties.getImplVersion(); + return UUID.nameUUIDFromBytes(seed.getBytes()); + } +} diff --git a/Plan/src/main/java/com/djrapitops/plan/system/info/server/properties/VelocityServerProperties.java b/Plan/src/main/java/com/djrapitops/plan/system/info/server/properties/VelocityServerProperties.java new file mode 100644 index 000000000..01a76c09d --- /dev/null +++ b/Plan/src/main/java/com/djrapitops/plan/system/info/server/properties/VelocityServerProperties.java @@ -0,0 +1,28 @@ +package com.djrapitops.plan.system.info.server.properties; + +import com.djrapitops.plan.system.settings.Settings; +import com.velocitypowered.api.proxy.ProxyServer; + +/** + * ServerProperties for Velocity. + *

+ * Supports RedisBungee for Players online getting. + * + * @author Rsl1122 + */ +public class VelocityServerProperties extends ServerProperties { + + public VelocityServerProperties(ProxyServer server) { + super( + server.getAllServers().toString(), + "Velocity", + server.getBoundAddress().getPort(), + // not sure how to get these + server.getClass().getPackage().getImplementationVersion(), + server.getClass().getPackage().getImplementationVersion(), + Settings.BUNGEE_IP::toString, + -1, // not sure how to get this + RedisCheck.isClassAvailable() ? new RedisPlayersOnlineSupplier() : server::getPlayerCount + ); + } +} \ No newline at end of file diff --git a/Plan/src/main/java/com/djrapitops/plan/system/listeners/VelocityListenerSystem.java b/Plan/src/main/java/com/djrapitops/plan/system/listeners/VelocityListenerSystem.java new file mode 100644 index 000000000..7d61fd098 --- /dev/null +++ b/Plan/src/main/java/com/djrapitops/plan/system/listeners/VelocityListenerSystem.java @@ -0,0 +1,23 @@ +package com.djrapitops.plan.system.listeners; + +import com.djrapitops.plan.PlanVelocity; +import com.djrapitops.plan.system.listeners.velocity.PlayerOnlineListener; + +public class VelocityListenerSystem extends ListenerSystem { + + private final PlanVelocity plugin; + + public VelocityListenerSystem(PlanVelocity plugin) { + this.plugin = plugin; + } + + @Override + protected void registerListeners() { + plugin.registerListener(new PlayerOnlineListener()); + } + + @Override + protected void unregisterListeners() { + plugin.getProxy().getEventManager().unregisterListeners(plugin); + } +} diff --git a/Plan/src/main/java/com/djrapitops/plan/system/listeners/bukkit/AFKListener.java b/Plan/src/main/java/com/djrapitops/plan/system/listeners/bukkit/AFKListener.java index 482ec9b94..dd8989307 100644 --- a/Plan/src/main/java/com/djrapitops/plan/system/listeners/bukkit/AFKListener.java +++ b/Plan/src/main/java/com/djrapitops/plan/system/listeners/bukkit/AFKListener.java @@ -15,6 +15,8 @@ import org.bukkit.event.player.PlayerEvent; import org.bukkit.event.player.PlayerMoveEvent; import javax.inject.Inject; +import java.util.HashMap; +import java.util.Map; import java.util.UUID; /** @@ -30,11 +32,13 @@ public class AFKListener implements Listener { // Static so that /reload does not cause afk tracking to fail. static AFKTracker AFK_TRACKER; + private final Map ignorePermissionInfo; private final ErrorHandler errorHandler; @Inject public AFKListener(PlanConfig config, ErrorHandler errorHandler) { this.errorHandler = errorHandler; + this.ignorePermissionInfo = new HashMap<>(); AFKListener.assignAFKTracker(config); } @@ -51,8 +55,15 @@ public class AFKListener implements Listener { UUID uuid = player.getUniqueId(); long time = System.currentTimeMillis(); - if (player.hasPermission(Permissions.IGNORE_AFK.getPermission())) { + Boolean ignored = ignorePermissionInfo.get(uuid); + if (ignored == null) { + ignored = player.hasPermission(Permissions.IGNORE_AFK.getPermission()); + } + if (ignored) { AFK_TRACKER.hasIgnorePermission(uuid); + ignorePermissionInfo.put(uuid, true); + } else { + ignorePermissionInfo.put(uuid, false); } AFK_TRACKER.performedAction(uuid, time); diff --git a/Plan/src/main/java/com/djrapitops/plan/system/listeners/bukkit/PlayerOnlineListener.java b/Plan/src/main/java/com/djrapitops/plan/system/listeners/bukkit/PlayerOnlineListener.java index 2cd627cf6..dd924bf5d 100644 --- a/Plan/src/main/java/com/djrapitops/plan/system/listeners/bukkit/PlayerOnlineListener.java +++ b/Plan/src/main/java/com/djrapitops/plan/system/listeners/bukkit/PlayerOnlineListener.java @@ -92,6 +92,10 @@ public class PlayerOnlineListener implements Listener { return; } UUID uuid = event.getPlayer().getUniqueId(); + if (AFKListener.AFK_TRACKER.isAfk(uuid)) { + return; + } + processing.submit(processors.player().kickProcessor(uuid)); } catch (Exception e) { errorHandler.log(L.ERROR, this.getClass(), e); diff --git a/Plan/src/main/java/com/djrapitops/plan/system/listeners/bungee/PlayerOnlineListener.java b/Plan/src/main/java/com/djrapitops/plan/system/listeners/bungee/PlayerOnlineListener.java index 17e8b0bb1..4d4d467d5 100644 --- a/Plan/src/main/java/com/djrapitops/plan/system/listeners/bungee/PlayerOnlineListener.java +++ b/Plan/src/main/java/com/djrapitops/plan/system/listeners/bungee/PlayerOnlineListener.java @@ -16,8 +16,8 @@ import com.djrapitops.plan.system.webserver.cache.ResponseCache; import com.djrapitops.plugin.logging.L; import com.djrapitops.plugin.logging.error.ErrorHandler; import net.md_5.bungee.api.connection.ProxiedPlayer; +import net.md_5.bungee.api.event.PlayerDisconnectEvent; import net.md_5.bungee.api.event.PostLoginEvent; -import net.md_5.bungee.api.event.ServerDisconnectEvent; import net.md_5.bungee.api.event.ServerSwitchEvent; import net.md_5.bungee.api.plugin.Listener; import net.md_5.bungee.event.EventHandler; @@ -81,7 +81,7 @@ public class PlayerOnlineListener implements Listener { } @EventHandler - public void onLogout(ServerDisconnectEvent event) { + public void onLogout(PlayerDisconnectEvent event) { try { ProxiedPlayer player = event.getPlayer(); UUID uuid = player.getUniqueId(); diff --git a/Plan/src/main/java/com/djrapitops/plan/system/listeners/sponge/SpongePlayerListener.java b/Plan/src/main/java/com/djrapitops/plan/system/listeners/sponge/SpongePlayerListener.java index 2f482a88f..a4584e2f5 100644 --- a/Plan/src/main/java/com/djrapitops/plan/system/listeners/sponge/SpongePlayerListener.java +++ b/Plan/src/main/java/com/djrapitops/plan/system/listeners/sponge/SpongePlayerListener.java @@ -3,6 +3,7 @@ package com.djrapitops.plan.system.listeners.sponge; import com.djrapitops.plan.data.container.Session; import com.djrapitops.plan.system.cache.SessionCache; import com.djrapitops.plan.system.info.server.ServerInfo; +import com.djrapitops.plan.system.listeners.bukkit.AFKListener; import com.djrapitops.plan.system.processing.Processing; import com.djrapitops.plan.system.processing.processors.Processors; import com.djrapitops.plan.system.settings.Settings; @@ -81,6 +82,10 @@ public class SpongePlayerListener { public void onKick(KickPlayerEvent event) { try { UUID uuid = event.getTargetEntity().getUniqueId(); + // TODO + if (AFKListener.AFK_TRACKER.isAfk(uuid)) { + return; + } processing.submit(processors.player().kickProcessor(uuid)); } catch (Exception e) { errorHandler.log(L.ERROR, this.getClass(), e); diff --git a/Plan/src/main/java/com/djrapitops/plan/system/listeners/velocity/PlayerOnlineListener.java b/Plan/src/main/java/com/djrapitops/plan/system/listeners/velocity/PlayerOnlineListener.java new file mode 100644 index 000000000..e190c8d81 --- /dev/null +++ b/Plan/src/main/java/com/djrapitops/plan/system/listeners/velocity/PlayerOnlineListener.java @@ -0,0 +1,85 @@ +/* + * License is provided in the jar as LICENSE also here: + * https://github.com/Rsl1122/Plan-PlayerAnalytics/blob/master/Plan/src/main/resources/LICENSE + */ +package com.djrapitops.plan.system.listeners.velocity; + +import com.djrapitops.plan.data.container.Session; +import com.djrapitops.plan.system.cache.SessionCache; +import com.djrapitops.plan.system.info.server.ServerInfo; +import com.djrapitops.plan.system.processing.Processing; +import com.djrapitops.plan.system.processing.processors.info.PlayerPageUpdateProcessor; +import com.djrapitops.plan.system.processing.processors.player.BungeePlayerRegisterProcessor; +import com.djrapitops.plan.system.processing.processors.player.IPUpdateProcessor; +import com.djrapitops.plan.system.webserver.cache.PageId; +import com.djrapitops.plan.system.webserver.cache.ResponseCache; +import com.djrapitops.plugin.api.utility.log.Log; +import com.velocitypowered.api.event.Subscribe; +import com.velocitypowered.api.event.connection.DisconnectEvent; +import com.velocitypowered.api.event.connection.PostLoginEvent; +import com.velocitypowered.api.event.player.ServerConnectedEvent; +import com.velocitypowered.api.proxy.Player; + +import java.net.InetAddress; +import java.util.UUID; + +/** + * Player Join listener for Velocity. + * + * Based on the bungee version. + * + * @author MicleBrick + */ +public class PlayerOnlineListener { + + @Subscribe + public void onPostLogin(PostLoginEvent event) { + try { + Player player = event.getPlayer(); + UUID uuid = player.getUniqueId(); + String name = player.getUsername(); + InetAddress address = player.getRemoteAddress().getAddress(); + long now = System.currentTimeMillis(); + + SessionCache.getInstance().cacheSession(uuid, new Session(uuid, now, "", "")); + + // maybe rename to ProxyPlayerRegisterProcessor? + Processing.submit(new BungeePlayerRegisterProcessor(uuid, name, now, + new IPUpdateProcessor(uuid, address, now)) + ); + Processing.submit(new PlayerPageUpdateProcessor(uuid)); + ResponseCache.clearResponse(PageId.SERVER.of(ServerInfo.getServerUUID())); + } catch (Exception e) { + Log.toLog(this.getClass(), e); + } + } + + @Subscribe + public void onLogout(DisconnectEvent event) { + try { + Player player = event.getPlayer(); + UUID uuid = player.getUniqueId(); + + SessionCache.getInstance().endSession(uuid, System.currentTimeMillis()); + Processing.submit(new PlayerPageUpdateProcessor(uuid)); + ResponseCache.clearResponse(PageId.SERVER.of(ServerInfo.getServerUUID())); + } catch (Exception e) { + Log.toLog(this.getClass(), e); + } + } + + @Subscribe + public void onServerSwitch(ServerConnectedEvent event) { + try { + Player player = event.getPlayer(); + UUID uuid = player.getUniqueId(); + + long now = System.currentTimeMillis(); + // Replaces the current session in the cache. + SessionCache.getInstance().cacheSession(uuid, new Session(uuid, now, "", "")); + Processing.submit(new PlayerPageUpdateProcessor(uuid)); + } catch (Exception e) { + Log.toLog(this.getClass(), e); + } + } +} diff --git a/Plan/src/main/java/com/djrapitops/plan/system/locale/Locale.java b/Plan/src/main/java/com/djrapitops/plan/system/locale/Locale.java index 5d7b08720..e0c950d24 100644 --- a/Plan/src/main/java/com/djrapitops/plan/system/locale/Locale.java +++ b/Plan/src/main/java/com/djrapitops/plan/system/locale/Locale.java @@ -1,14 +1,16 @@ package com.djrapitops.plan.system.locale; import com.djrapitops.plan.PlanPlugin; -import com.djrapitops.plan.system.locale.lang.Lang; +import com.djrapitops.plan.system.locale.lang.*; import java.io.File; import java.io.IOException; import java.io.Serializable; +import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.function.Function; import java.util.stream.Collectors; /** @@ -67,10 +69,22 @@ public class Locale extends HashMap { String replaced = from; - // Longest first so that entries that contain each other don't partially replace. - List> entries = entrySet().stream().sorted( - (one, two) -> Integer.compare(two.getKey().getIdentifier().length(), one.getKey().getIdentifier().length()) - ).collect(Collectors.toList()); + Lang[][] langs = new Lang[][]{ + NetworkPageLang.values(), + PlayerPageLang.values(), + ServerPageLang.values(), + CommonHtmlLang.values() + }; + + List> entries = Arrays.stream(langs) + .flatMap(Arrays::stream) + .collect(Collectors.toMap(Function.identity(), this::get)) + .entrySet().stream() + // Longest first so that entries that contain each other don't partially replace. + .sorted((one, two) -> Integer.compare( + two.getKey().getIdentifier().length(), + one.getKey().getIdentifier().length() + )).collect(Collectors.toList()); for (Entry entry : entries) { String defaultValue = entry.getKey().getDefault(); diff --git a/Plan/src/main/java/com/djrapitops/plan/system/processing/Processing.java b/Plan/src/main/java/com/djrapitops/plan/system/processing/Processing.java index 1f59cdded..e7d847b3d 100644 --- a/Plan/src/main/java/com/djrapitops/plan/system/processing/Processing.java +++ b/Plan/src/main/java/com/djrapitops/plan/system/processing/Processing.java @@ -8,6 +8,7 @@ import com.djrapitops.plugin.logging.L; import com.djrapitops.plugin.logging.console.PluginLogger; import com.djrapitops.plugin.logging.error.ErrorHandler; import dagger.Lazy; +import com.google.common.util.concurrent.ThreadFactoryBuilder; import javax.inject.Inject; import javax.inject.Singleton; @@ -33,8 +34,8 @@ public class Processing implements SubSystem { this.locale = locale; this.logger = logger; this.errorHandler = errorHandler; - nonCriticalExecutor = Executors.newFixedThreadPool(6); - criticalExecutor = Executors.newFixedThreadPool(2); + nonCriticalExecutor = Executors.newFixedThreadPool(6, new ThreadFactoryBuilder().setNameFormat("Plan Non critical-pool-%d").build()); + criticalExecutor = Executors.newFixedThreadPool(2, new ThreadFactoryBuilder().setNameFormat("Plan Critical-pool-%d").build()); } public void submit(Runnable runnable) { diff --git a/Plan/src/main/java/com/djrapitops/plan/system/processing/processors/info/PlayerPageUpdateProcessor.java b/Plan/src/main/java/com/djrapitops/plan/system/processing/processors/info/PlayerPageUpdateProcessor.java index 758e90f2c..653013156 100644 --- a/Plan/src/main/java/com/djrapitops/plan/system/processing/processors/info/PlayerPageUpdateProcessor.java +++ b/Plan/src/main/java/com/djrapitops/plan/system/processing/processors/info/PlayerPageUpdateProcessor.java @@ -1,51 +1,23 @@ package com.djrapitops.plan.system.processing.processors.info; -import com.djrapitops.plan.system.info.InfoSystem; -import com.djrapitops.plan.system.info.connection.WebExceptionLogger; -import com.djrapitops.plugin.api.Check; -import com.djrapitops.plugin.api.TimeAmount; -import com.djrapitops.plugin.task.AbsRunnable; -import com.djrapitops.plugin.task.RunnableFactory; +import com.djrapitops.plan.system.webserver.cache.PageId; +import com.djrapitops.plan.system.webserver.cache.ResponseCache; import java.util.UUID; -import java.util.concurrent.TimeUnit; public class PlayerPageUpdateProcessor implements Runnable { private final UUID uuid; - private final InfoSystem infoSystem; - private final WebExceptionLogger webExceptionLogger; - private final RunnableFactory runnableFactory; - + // TODO Factory method fix PlayerPageUpdateProcessor( - UUID uuid, - InfoSystem infoSystem, - WebExceptionLogger webExceptionLogger, - RunnableFactory runnableFactory + UUID uuid ) { this.uuid = uuid; - this.infoSystem = infoSystem; - this.webExceptionLogger = webExceptionLogger; - this.runnableFactory = runnableFactory; } @Override public void run() { - if (!infoSystem.getConnectionSystem().isServerAvailable() || Check.isBungeeAvailable()) { - runnableFactory.create("Generate Inspect page: " + uuid, new AbsRunnable() { - @Override - public void run() { - try { - - webExceptionLogger.logIfOccurs(PlayerPageUpdateProcessor.class, - () -> infoSystem.generateAndCachePlayerPage(uuid) - ); - } finally { - cancel(); - } - } - }).runTaskLaterAsynchronously(TimeAmount.toTicks(20L, TimeUnit.SECONDS)); - } + ResponseCache.clearResponse(PageId.PLAYER.of(uuid)); } } diff --git a/Plan/src/main/java/com/djrapitops/plan/system/processing/processors/player/PingInsertProcessor.java b/Plan/src/main/java/com/djrapitops/plan/system/processing/processors/player/PingInsertProcessor.java index 2c552a73e..077f1afbb 100644 --- a/Plan/src/main/java/com/djrapitops/plan/system/processing/processors/player/PingInsertProcessor.java +++ b/Plan/src/main/java/com/djrapitops/plan/system/processing/processors/player/PingInsertProcessor.java @@ -8,10 +8,12 @@ import com.djrapitops.plan.data.container.Ping; import com.djrapitops.plan.data.store.objects.DateObj; import com.djrapitops.plan.system.database.databases.Database; import com.djrapitops.plan.system.processing.CriticalRunnable; +import com.djrapitops.plan.utilities.analysis.Median; import java.util.List; import java.util.OptionalInt; import java.util.UUID; +import java.util.stream.Collectors; /** * Processes 60s values of a Ping list. @@ -51,20 +53,25 @@ public class PingInsertProcessor implements CriticalRunnable { return; } - int minValue = history.stream() - .mapToInt(DateObj::getValue) - .filter(i -> i > 0 && i < 4000) - .min().orElse(-1); + int minValue = getMinValue(history); - double avgValue = history.stream() - .mapToInt(DateObj::getValue) - .filter(i -> i > 0 && i < 4000) - .average().orElse(-1); + int meanValue = getMeanValue(history); int maxValue = max.getAsInt(); - Ping ping = new Ping(lastDate, serverUUID, minValue, maxValue, avgValue); + Ping ping = new Ping(lastDate, serverUUID, minValue, maxValue, meanValue); database.save().ping(uuid, ping); } + + int getMinValue(List> history) { + return history.stream() + .mapToInt(DateObj::getValue) + .filter(i -> i > 0 && i < 4000) + .min().orElse(-1); + } + + int getMeanValue(List> history) { + return (int) Median.forInt(history.stream().map(DateObj::getValue).collect(Collectors.toList())).calculate(); + } } diff --git a/Plan/src/main/java/com/djrapitops/plan/system/processing/processors/player/BungeeRegisterProcessor.java b/Plan/src/main/java/com/djrapitops/plan/system/processing/processors/player/ProxyRegisterProcessor.java similarity index 100% rename from Plan/src/main/java/com/djrapitops/plan/system/processing/processors/player/BungeeRegisterProcessor.java rename to Plan/src/main/java/com/djrapitops/plan/system/processing/processors/player/ProxyRegisterProcessor.java diff --git a/Plan/src/main/java/com/djrapitops/plan/system/settings/Permissions.java b/Plan/src/main/java/com/djrapitops/plan/system/settings/Permissions.java index 0a5377ccd..c61c54d5b 100644 --- a/Plan/src/main/java/com/djrapitops/plan/system/settings/Permissions.java +++ b/Plan/src/main/java/com/djrapitops/plan/system/settings/Permissions.java @@ -10,8 +10,8 @@ public enum Permissions { HELP("plan.?"), - INSPECT("plan.inspect"), - QUICK_INSPECT("plan.qinspect"), + INSPECT("plan.inspect.base"), + QUICK_INSPECT("plan.qinspect.base"), INSPECT_OTHER("plan.inspect.other"), QUICK_INSPECT_OTHER("plan.qinspect.other"), @@ -36,7 +36,7 @@ public enum Permissions { /** * Returns the permission node in plugin.yml. * - * @return permission node eg. plan.inspect + * @return permission node eg. plan.inspect.base */ public String getPermission() { return permission; @@ -45,7 +45,7 @@ public enum Permissions { /** * Same as {@link #getPermission()}. * - * @return permission node eg. plan.inspect + * @return permission node eg. plan.inspect.base */ public String getPerm() { return getPermission(); diff --git a/Plan/src/main/java/com/djrapitops/plan/system/settings/Settings.java b/Plan/src/main/java/com/djrapitops/plan/system/settings/Settings.java index 7052bcb45..1774e8f61 100644 --- a/Plan/src/main/java/com/djrapitops/plan/system/settings/Settings.java +++ b/Plan/src/main/java/com/djrapitops/plan/system/settings/Settings.java @@ -44,6 +44,8 @@ public enum Settings implements Setting { AFK_THRESHOLD_MINUTES("Data.AFKThresholdMinutes"), KEEP_LOGS_DAYS("Plugin.KeepLogsForXDays"), KEEP_INACTIVE_PLAYERS_DAYS("Data.KeepInactivePlayerDataForDays"), + PING_SERVER_ENABLE_DELAY("Data.Ping.ServerEnableDelaySeconds"), + PING_PLAYER_LOGIN_DELAY("Data.Ping.PlayerLoginDelaySeconds"), // String DEBUG("Plugin.Debug"), diff --git a/Plan/src/main/java/com/djrapitops/plan/system/settings/config/BungeeConfigSystem.java b/Plan/src/main/java/com/djrapitops/plan/system/settings/config/ProxyConfigSystem.java similarity index 100% rename from Plan/src/main/java/com/djrapitops/plan/system/settings/config/BungeeConfigSystem.java rename to Plan/src/main/java/com/djrapitops/plan/system/settings/config/ProxyConfigSystem.java diff --git a/Plan/src/main/java/com/djrapitops/plan/system/settings/network/NetworkSettings.java b/Plan/src/main/java/com/djrapitops/plan/system/settings/network/NetworkSettings.java index 6bc1da0de..a51d21748 100644 --- a/Plan/src/main/java/com/djrapitops/plan/system/settings/network/NetworkSettings.java +++ b/Plan/src/main/java/com/djrapitops/plan/system/settings/network/NetworkSettings.java @@ -201,7 +201,9 @@ public class NetworkSettings { AFK_THRESHOLD_MINUTES, DATA_GEOLOCATIONS, KEEP_LOGS_DAYS, - KEEP_INACTIVE_PLAYERS_DAYS + KEEP_INACTIVE_PLAYERS_DAYS, + PING_SERVER_ENABLE_DELAY, + PING_PLAYER_LOGIN_DELAY }; logger.debug("NetworkSettings: Adding Config Values.."); PlanConfig planConfig = config.get(); diff --git a/Plan/src/main/java/com/djrapitops/plan/system/tasks/BukkitTaskSystem.java b/Plan/src/main/java/com/djrapitops/plan/system/tasks/BukkitTaskSystem.java index c28935a8e..cd0918c36 100644 --- a/Plan/src/main/java/com/djrapitops/plan/system/tasks/BukkitTaskSystem.java +++ b/Plan/src/main/java/com/djrapitops/plan/system/tasks/BukkitTaskSystem.java @@ -5,13 +5,16 @@ package com.djrapitops.plan.system.tasks; import com.djrapitops.plan.Plan; +import com.djrapitops.plan.system.settings.Settings; import com.djrapitops.plan.system.settings.config.PlanConfig; import com.djrapitops.plan.system.tasks.server.*; import com.djrapitops.plugin.api.Check; +import com.djrapitops.plugin.api.TimeAmount; import com.djrapitops.plugin.task.RunnableFactory; import org.bukkit.Bukkit; import javax.inject.Inject; +import java.util.concurrent.TimeUnit; /** * TaskSystem responsible for registering tasks for Bukkit. @@ -53,8 +56,10 @@ public class BukkitTaskSystem extends ServerTaskSystem { super.enable(); try { plugin.registerListener(pingCountTimer); + // TODO config + long startDelay = TimeAmount.toTicks(Settings.PING_SERVER_ENABLE_DELAY.getNumber(), TimeUnit.SECONDS); registerTask("PingCountTimer", pingCountTimer) - .runTaskTimer(20L, PingCountTimer.PING_INTERVAL); + .runTaskTimer(startDelay, 40L); } catch (ExceptionInInitializerError | NoClassDefFoundError ignore) { // Running CraftBukkit } diff --git a/Plan/src/main/java/com/djrapitops/plan/system/tasks/BungeeTaskSystem.java b/Plan/src/main/java/com/djrapitops/plan/system/tasks/BungeeTaskSystem.java index 49c04d545..dc7f21ff4 100644 --- a/Plan/src/main/java/com/djrapitops/plan/system/tasks/BungeeTaskSystem.java +++ b/Plan/src/main/java/com/djrapitops/plan/system/tasks/BungeeTaskSystem.java @@ -4,9 +4,11 @@ */ package com.djrapitops.plan.system.tasks; +import com.djrapitops.plan.system.settings.Settings; import com.djrapitops.plan.system.settings.config.PlanConfig; import com.djrapitops.plan.system.tasks.bungee.BungeeTPSCountTimer; import com.djrapitops.plan.system.tasks.server.NetworkPageRefreshTask; +import com.djrapitops.plan.system.tasks.server.PingCountTimerBungee; import com.djrapitops.plugin.api.TimeAmount; import com.djrapitops.plugin.task.AbsRunnable; import com.djrapitops.plugin.task.RunnableFactory; @@ -55,5 +57,12 @@ public class BungeeTaskSystem extends TaskSystem { config.getNetworkSettings().placeSettingsToDB(); } }).runTaskAsynchronously(); + + // TODO Move this elsewhere + PingCountTimerBungee pingCountTimer = new PingCountTimerBungee(); + plugin.registerListener(pingCountTimer); + long startDelay = TimeAmount.SECOND.ticks() * (long) Settings.PING_SERVER_ENABLE_DELAY.getNumber(); + RunnableFactory.createNew("PingCountTimer", pingCountTimer) + .runTaskTimer(startDelay, PingCountTimerBungee.PING_INTERVAL); } } diff --git a/Plan/src/main/java/com/djrapitops/plan/system/tasks/LogsFolderCleanTask.java b/Plan/src/main/java/com/djrapitops/plan/system/tasks/LogsFolderCleanTask.java index 1566b894d..8a88ed171 100644 --- a/Plan/src/main/java/com/djrapitops/plan/system/tasks/LogsFolderCleanTask.java +++ b/Plan/src/main/java/com/djrapitops/plan/system/tasks/LogsFolderCleanTask.java @@ -48,7 +48,11 @@ public class LogsFolderCleanTask extends AbsRunnable { } catch (NullPointerException ignore) { /* Ignored - not supposed to occur. */ } finally { - cancel(); + try { + cancel(); + } catch (Exception ignore) { + /* Ignored, TaskCenter concurrent modification exception, will be fixed later in apf-3.3.0. */ + } } } diff --git a/Plan/src/main/java/com/djrapitops/plan/system/tasks/SpongeTaskSystem.java b/Plan/src/main/java/com/djrapitops/plan/system/tasks/SpongeTaskSystem.java index a00c35a43..044527855 100644 --- a/Plan/src/main/java/com/djrapitops/plan/system/tasks/SpongeTaskSystem.java +++ b/Plan/src/main/java/com/djrapitops/plan/system/tasks/SpongeTaskSystem.java @@ -5,8 +5,12 @@ import com.djrapitops.plan.system.settings.config.PlanConfig; import com.djrapitops.plan.system.tasks.server.BootAnalysisTask; import com.djrapitops.plan.system.tasks.server.NetworkPageRefreshTask; import com.djrapitops.plan.system.tasks.server.PeriodicAnalysisTask; +import com.djrapitops.plan.system.settings.Settings; +import com.djrapitops.plan.system.tasks.server.PingCountTimerSponge; import com.djrapitops.plan.system.tasks.server.SpongeTPSCountTimer; import com.djrapitops.plugin.task.RunnableFactory; +import com.djrapitops.plugin.api.TimeAmount; +import com.djrapitops.plugin.task.RunnableFactory; import org.spongepowered.api.Sponge; import org.spongepowered.api.scheduler.Task; @@ -38,6 +42,18 @@ public class SpongeTaskSystem extends ServerTaskSystem { this.plugin = plugin; } + @Override + public void enable() { + super.enable(); + + // TODO Move elsewhere + PingCountTimerSponge pingCountTimer = new PingCountTimerSponge(); + plugin.registerListener(pingCountTimer); + long startDelay = TimeAmount.SECOND.ticks() * (long) Settings.PING_SERVER_ENABLE_DELAY.getNumber(); + RunnableFactory.createNew("PingCountTimer", pingCountTimer) + .runTaskTimer(startDelay, PingCountTimerSponge.PING_INTERVAL); + } + @Override public void disable() { super.disable(); diff --git a/Plan/src/main/java/com/djrapitops/plan/system/tasks/TPSCountTimer.java b/Plan/src/main/java/com/djrapitops/plan/system/tasks/TPSCountTimer.java index 642061513..7e18bca46 100644 --- a/Plan/src/main/java/com/djrapitops/plan/system/tasks/TPSCountTimer.java +++ b/Plan/src/main/java/com/djrapitops/plan/system/tasks/TPSCountTimer.java @@ -8,6 +8,8 @@ import com.djrapitops.plugin.logging.console.PluginLogger; import com.djrapitops.plugin.logging.error.ErrorHandler; import com.djrapitops.plugin.task.AbsRunnable; +import java.lang.management.ManagementFactory; +import java.lang.management.OperatingSystemMXBean; import java.util.ArrayList; import java.util.List; @@ -64,4 +66,27 @@ public abstract class TPSCountTimer extends AbsRunnable { public int getLatestPlayersOnline() { return latestPlayersOnline; } + + protected long getUsedMemory() { + Runtime runtime = Runtime.getRuntime(); + long totalMemory = runtime.totalMemory(); + return (totalMemory - runtime.freeMemory()) / 1000000; + } + + protected double getCPUUsage() { + double averageCPUUsage; + + OperatingSystemMXBean osBean = ManagementFactory.getOperatingSystemMXBean(); + if (osBean instanceof com.sun.management.OperatingSystemMXBean) { + com.sun.management.OperatingSystemMXBean nativeOsBean = (com.sun.management.OperatingSystemMXBean) osBean; + averageCPUUsage = nativeOsBean.getSystemCpuLoad(); + } else { + int availableProcessors = osBean.getAvailableProcessors(); + averageCPUUsage = osBean.getSystemLoadAverage() / availableProcessors; + } + if (averageCPUUsage < 0) { // If unavailable, getSystemLoadAverage() returns -1 + averageCPUUsage = -1; + } + return averageCPUUsage * 100.0; + } } diff --git a/Plan/src/main/java/com/djrapitops/plan/system/tasks/VelocityTaskSystem.java b/Plan/src/main/java/com/djrapitops/plan/system/tasks/VelocityTaskSystem.java new file mode 100644 index 000000000..6a1e0da4b --- /dev/null +++ b/Plan/src/main/java/com/djrapitops/plan/system/tasks/VelocityTaskSystem.java @@ -0,0 +1,49 @@ +/* + * License is provided in the jar as LICENSE also here: + * https://github.com/Rsl1122/Plan-PlayerAnalytics/blob/master/Plan/src/main/resources/LICENSE + */ +package com.djrapitops.plan.system.tasks; + +import com.djrapitops.plan.PlanVelocity; +import com.djrapitops.plan.system.settings.Settings; +import com.djrapitops.plan.system.tasks.proxy.EnableConnectionTask; +import com.djrapitops.plan.system.tasks.velocity.VelocityTPSCountTimer; +import com.djrapitops.plan.system.tasks.server.NetworkPageRefreshTask; +import com.djrapitops.plan.system.tasks.server.PingCountTimerVelocity; +import com.djrapitops.plan.utilities.file.export.HtmlExport; +import com.djrapitops.plugin.api.TimeAmount; +import com.djrapitops.plugin.task.RunnableFactory; + +/** + * TaskSystem responsible for registering tasks for Velocity. + * + * @author Rsl1122 + */ +public class VelocityTaskSystem extends TaskSystem { + + private final PlanVelocity plugin; + + public VelocityTaskSystem(PlanVelocity plugin) { + super(new VelocityTPSCountTimer(plugin)); + this.plugin = plugin; + } + + @Override + public void enable() { + registerTasks(); + } + + private void registerTasks() { + registerTask(new EnableConnectionTask()).runTaskAsynchronously(); + registerTask(tpsCountTimer).runTaskTimerAsynchronously(1000, TimeAmount.SECOND.ticks()); + registerTask(new NetworkPageRefreshTask()).runTaskTimerAsynchronously(1500, TimeAmount.MINUTE.ticks()); + if (Settings.ANALYSIS_EXPORT.isTrue()) { + registerTask(new HtmlExport(plugin)).runTaskAsynchronously(); + } + PingCountTimerVelocity pingCountTimer = new PingCountTimerVelocity(); + plugin.registerListener(pingCountTimer); + long startDelay = TimeAmount.SECOND.ticks() * (long) Settings.PING_SERVER_ENABLE_DELAY.getNumber(); + RunnableFactory.createNew("PingCountTimer", pingCountTimer) + .runTaskTimer(startDelay, PingCountTimerVelocity.PING_INTERVAL); + } +} diff --git a/Plan/src/main/java/com/djrapitops/plan/system/tasks/bungee/BungeeTPSCountTimer.java b/Plan/src/main/java/com/djrapitops/plan/system/tasks/bungee/BungeeTPSCountTimer.java index 1e8cea17e..03cd02016 100644 --- a/Plan/src/main/java/com/djrapitops/plan/system/tasks/bungee/BungeeTPSCountTimer.java +++ b/Plan/src/main/java/com/djrapitops/plan/system/tasks/bungee/BungeeTPSCountTimer.java @@ -36,6 +36,10 @@ public class BungeeTPSCountTimer extends TPSCountTimer { .date(now) .skipTPS() .playersOnline(onlineCount) + .usedCPU(getCPUUsage()) + .usedMemory(getUsedMemory()) + .entities(-1) + .chunksLoaded(-1) .toTPS(); history.add(tps); diff --git a/Plan/src/main/java/com/djrapitops/plan/system/tasks/proxy/EnableConnectionTask.java b/Plan/src/main/java/com/djrapitops/plan/system/tasks/proxy/EnableConnectionTask.java new file mode 100644 index 000000000..e69de29bb diff --git a/Plan/src/main/java/com/djrapitops/plan/system/tasks/server/BukkitTPSCountTimer.java b/Plan/src/main/java/com/djrapitops/plan/system/tasks/server/BukkitTPSCountTimer.java index 741465adf..746bfad76 100644 --- a/Plan/src/main/java/com/djrapitops/plan/system/tasks/server/BukkitTPSCountTimer.java +++ b/Plan/src/main/java/com/djrapitops/plan/system/tasks/server/BukkitTPSCountTimer.java @@ -62,10 +62,7 @@ public class BukkitTPSCountTimer extends TPSCountTimer { private TPS calculateTPS(long diff, long now) { double averageCPUUsage = getCPUUsage(); - Runtime runtime = Runtime.getRuntime(); - - long totalMemory = runtime.totalMemory(); - long usedMemory = (totalMemory - runtime.freeMemory()) / 1000000; + long usedMemory = getUsedMemory(); int playersOnline = serverProperties.getOnlinePlayers(); latestPlayersOnline = playersOnline; @@ -77,22 +74,6 @@ public class BukkitTPSCountTimer extends TPSCountTimer { return getTPS(diff, now, averageCPUUsage, usedMemory, entityCount, loadedChunks, playersOnline); } - private double getCPUUsage() { - double averageCPUUsage; - - OperatingSystemMXBean osBean = ManagementFactory.getOperatingSystemMXBean(); - if (osBean instanceof com.sun.management.OperatingSystemMXBean) { - com.sun.management.OperatingSystemMXBean nativeOsBean = (com.sun.management.OperatingSystemMXBean) osBean; - averageCPUUsage = nativeOsBean.getSystemCpuLoad(); - } else { - int availableProcessors = osBean.getAvailableProcessors(); - averageCPUUsage = osBean.getSystemLoadAverage() / availableProcessors; - } - if (averageCPUUsage < 0) { // If unavailable, getSystemLoadAverage() returns -1 - averageCPUUsage = -1; - } - return averageCPUUsage * 100.0; - } /** * Gets the TPS for Spigot / Bukkit diff --git a/Plan/src/main/java/com/djrapitops/plan/system/tasks/server/PingCountTimer.java b/Plan/src/main/java/com/djrapitops/plan/system/tasks/server/PingCountTimerBukkit.java similarity index 89% rename from Plan/src/main/java/com/djrapitops/plan/system/tasks/server/PingCountTimer.java rename to Plan/src/main/java/com/djrapitops/plan/system/tasks/server/PingCountTimerBukkit.java index cade2a566..362a1e959 100644 --- a/Plan/src/main/java/com/djrapitops/plan/system/tasks/server/PingCountTimer.java +++ b/Plan/src/main/java/com/djrapitops/plan/system/tasks/server/PingCountTimerBukkit.java @@ -26,6 +26,7 @@ package com.djrapitops.plan.system.tasks.server; import com.djrapitops.plan.data.store.objects.DateObj; import com.djrapitops.plan.system.processing.Processing; import com.djrapitops.plan.system.processing.processors.Processors; +import com.djrapitops.plan.system.settings.Settings; import com.djrapitops.plan.utilities.java.Reflection; import com.djrapitops.plugin.api.TimeAmount; import com.djrapitops.plugin.task.AbsRunnable; @@ -57,23 +58,23 @@ import java.util.logging.Logger; * @author games647 */ @Singleton -public class PingCountTimer extends AbsRunnable implements Listener { +public class PingCountTimerBukkit extends AbsRunnable implements Listener { //the server is pinging the client every 40 Ticks (2 sec) - so check it then //https://github.com/bergerkiller/CraftSource/blob/master/net.minecraft.server/PlayerConnection.java#L178 public static final int PING_INTERVAL = 2 * 20; - private static final boolean pingMethodAvailable; + private static final boolean PING_METHOD_AVAILABLE; - private static final MethodHandle pingField; - private static final MethodHandle getHandleMethod; + private static final MethodHandle PING_FIELD; + private static final MethodHandle GET_HANDLE_METHOD; static { - pingMethodAvailable = isPingMethodAvailable(); + PING_METHOD_AVAILABLE = isPingMethodAvailable(); MethodHandle localHandle = null; MethodHandle localPing = null; - if (!pingMethodAvailable) { + if (!PING_METHOD_AVAILABLE) { Class craftPlayerClass = Reflection.getCraftBukkitClass("entity.CraftPlayer"); Class entityPlayer = Reflection.getMinecraftClass("EntityPlayer"); @@ -92,8 +93,8 @@ public class PingCountTimer extends AbsRunnable implements Listener { } } - getHandleMethod = localHandle; - pingField = localPing; + GET_HANDLE_METHOD = localHandle; + PING_FIELD = localPing; } private final Map>> playerHistory; @@ -103,7 +104,7 @@ public class PingCountTimer extends AbsRunnable implements Listener { private final RunnableFactory runnableFactory; @Inject - public PingCountTimer( + public PingCountTimerBukkit( Processors processors, Processing processing, RunnableFactory runnableFactory @@ -157,7 +158,7 @@ public class PingCountTimer extends AbsRunnable implements Listener { } private int getPing(Player player) { - if (pingMethodAvailable) { + if (PING_METHOD_AVAILABLE) { return player.spigot().getPing(); } @@ -166,8 +167,8 @@ public class PingCountTimer extends AbsRunnable implements Listener { private int getReflectionPing(Player player) { try { - Object entityPlayer = getHandleMethod.invoke(player); - return (int) pingField.invoke(entityPlayer); + Object entityPlayer = GET_HANDLE_METHOD.invoke(player); + return (int) PING_FIELD.invoke(entityPlayer); } catch (Exception ex) { return -1; } catch (Throwable throwable) { @@ -184,8 +185,8 @@ public class PingCountTimer extends AbsRunnable implements Listener { if (player.isOnline()) { addPlayer(player); } - } - }).runTaskLater(TimeAmount.toTicks(15L, TimeUnit.SECONDS)); + }// TODO Config + }).runTaskLater(TimeAmount.toTicks(Settings.PING_PLAYER_LOGIN_DELAY.getNumber(), TimeUnit.SECONDS)); } @EventHandler diff --git a/Plan/src/main/java/com/djrapitops/plan/system/tasks/server/PingCountTimerBungee.java b/Plan/src/main/java/com/djrapitops/plan/system/tasks/server/PingCountTimerBungee.java new file mode 100644 index 000000000..cb24e2aa5 --- /dev/null +++ b/Plan/src/main/java/com/djrapitops/plan/system/tasks/server/PingCountTimerBungee.java @@ -0,0 +1,111 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2016-2018 + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package com.djrapitops.plan.system.tasks.server; + +import com.djrapitops.plan.data.store.objects.DateObj; +import com.djrapitops.plan.system.processing.Processing; +import com.djrapitops.plan.system.processing.processors.player.PingInsertProcessor; +import com.djrapitops.plan.system.settings.Settings; +import com.djrapitops.plugin.api.TimeAmount; +import com.djrapitops.plugin.task.AbsRunnable; +import com.djrapitops.plugin.task.RunnableFactory; +import net.md_5.bungee.api.ProxyServer; +import net.md_5.bungee.api.connection.ProxiedPlayer; +import net.md_5.bungee.api.event.ServerConnectedEvent; +import net.md_5.bungee.api.event.ServerDisconnectEvent; +import net.md_5.bungee.api.plugin.Listener; +import net.md_5.bungee.event.EventHandler; +import java.util.*; + +/** + * Task that handles player ping calculation on Bungee based servers. + * + * @author BrainStone + */ +public class PingCountTimerBungee extends AbsRunnable implements Listener { + + //the server is pinging the client every 40 Ticks (2 sec) - so check it then + //https://github.com/bergerkiller/CraftSource/blob/master/net.minecraft.server/PlayerConnection.java#L178 + public static final int PING_INTERVAL = 2 * 20; + + private final Map>> playerHistory = new HashMap<>(); + + @Override + public void run() { + List loggedOut = new ArrayList<>(); + long time = System.currentTimeMillis(); + playerHistory.forEach((uuid, history) -> { + ProxiedPlayer player = ProxyServer.getInstance().getPlayer(uuid); + if (player != null) { + int ping = getPing(player); + if (ping < -1 || ping > TimeAmount.SECOND.ms() * 8L) { + // Don't accept bad values + return; + } + history.add(new DateObj<>(time, ping)); + if (history.size() >= 30) { + Processing.submit(new PingInsertProcessor(uuid, new ArrayList<>(history))); + history.clear(); + } + } else { + loggedOut.add(uuid); + } + }); + loggedOut.forEach(playerHistory::remove); + } + + public void addPlayer(ProxiedPlayer player) { + playerHistory.put(player.getUniqueId(), new ArrayList<>()); + } + + public void removePlayer(ProxiedPlayer player) { + playerHistory.remove(player.getUniqueId()); + } + + private int getPing(ProxiedPlayer player) { + return player.getPing(); + } + + @EventHandler + public void onPlayerJoin(ServerConnectedEvent joinEvent) { + ProxiedPlayer player = joinEvent.getPlayer(); + RunnableFactory.createNew("Add Player to Ping list", new AbsRunnable() { + @Override + public void run() { + if (player.isConnected()) { + addPlayer(player); + } + } + }).runTaskLater(TimeAmount.SECOND.ticks() * (long) Settings.PING_PLAYER_LOGIN_DELAY.getNumber()); + } + + @EventHandler + public void onPlayerQuit(ServerDisconnectEvent quitEvent) { + removePlayer(quitEvent.getPlayer()); + } + + public void clear() { + playerHistory.clear(); + } +} diff --git a/Plan/src/main/java/com/djrapitops/plan/system/tasks/server/PingCountTimerSponge.java b/Plan/src/main/java/com/djrapitops/plan/system/tasks/server/PingCountTimerSponge.java new file mode 100644 index 000000000..cfed96452 --- /dev/null +++ b/Plan/src/main/java/com/djrapitops/plan/system/tasks/server/PingCountTimerSponge.java @@ -0,0 +1,110 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2016-2018 + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package com.djrapitops.plan.system.tasks.server; + +import com.djrapitops.plan.data.store.objects.DateObj; +import com.djrapitops.plan.system.processing.Processing; +import com.djrapitops.plan.system.processing.processors.player.PingInsertProcessor; +import com.djrapitops.plan.system.settings.Settings; +import com.djrapitops.plugin.api.TimeAmount; +import com.djrapitops.plugin.task.AbsRunnable; +import com.djrapitops.plugin.task.RunnableFactory; +import org.spongepowered.api.Sponge; +import org.spongepowered.api.entity.living.player.Player; +import org.spongepowered.api.event.Listener; +import org.spongepowered.api.event.network.ClientConnectionEvent; + +import java.util.*; + +/** + * Task that handles player ping calculation on Sponge based servers. + * + * @author BrainStone + */ +public class PingCountTimerSponge extends AbsRunnable { + + //the server is pinging the client every 40 Ticks (2 sec) - so check it then + //https://github.com/bergerkiller/CraftSource/blob/master/net.minecraft.server/PlayerConnection.java#L178 + public static final int PING_INTERVAL = 2 * 20; + + private final Map>> playerHistory = new HashMap<>(); + + @Override + public void run() { + List loggedOut = new ArrayList<>(); + long time = System.currentTimeMillis(); + playerHistory.forEach((uuid, history) -> { + Optional player = Sponge.getServer().getPlayer(uuid); + if (player.isPresent()) { + int ping = getPing(player.get()); + if (ping < -1 || ping > TimeAmount.SECOND.ms() * 8L) { + // Don't accept bad values + return; + } + history.add(new DateObj<>(time, ping)); + if (history.size() >= 30) { + Processing.submit(new PingInsertProcessor(uuid, new ArrayList<>(history))); + history.clear(); + } + } else { + loggedOut.add(uuid); + } + }); + loggedOut.forEach(playerHistory::remove); + } + + public void addPlayer(Player player) { + playerHistory.put(player.getUniqueId(), new ArrayList<>()); + } + + public void removePlayer(Player player) { + playerHistory.remove(player.getUniqueId()); + } + + private int getPing(Player player) { + return player.getConnection().getLatency(); + } + + @Listener + public void onPlayerJoin(ClientConnectionEvent.Join joinEvent) { + Player player = joinEvent.getTargetEntity(); + RunnableFactory.createNew("Add Player to Ping list", new AbsRunnable() { + @Override + public void run() { + if (player.isOnline()) { + addPlayer(player); + } + } + }).runTaskLater(TimeAmount.SECOND.ticks() * (long) Settings.PING_PLAYER_LOGIN_DELAY.getNumber()); + } + + @Listener + public void onPlayerQuit(ClientConnectionEvent.Disconnect quitEvent) { + removePlayer(quitEvent.getTargetEntity()); + } + + public void clear() { + playerHistory.clear(); + } +} diff --git a/Plan/src/main/java/com/djrapitops/plan/system/tasks/server/PingCountTimerVelocity.java b/Plan/src/main/java/com/djrapitops/plan/system/tasks/server/PingCountTimerVelocity.java new file mode 100644 index 000000000..493940387 --- /dev/null +++ b/Plan/src/main/java/com/djrapitops/plan/system/tasks/server/PingCountTimerVelocity.java @@ -0,0 +1,113 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2016-2018 + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package com.djrapitops.plan.system.tasks.server; + +import com.djrapitops.plan.PlanVelocity; +import com.djrapitops.plan.data.store.objects.DateObj; +import com.djrapitops.plan.system.processing.Processing; +import com.djrapitops.plan.system.processing.processors.player.PingInsertProcessor; +import com.djrapitops.plan.system.settings.Settings; +import com.djrapitops.plugin.api.TimeAmount; +import com.djrapitops.plugin.task.AbsRunnable; +import com.djrapitops.plugin.task.RunnableFactory; +import com.velocitypowered.api.event.Subscribe; +import com.velocitypowered.api.event.connection.DisconnectEvent; +import com.velocitypowered.api.event.player.ServerConnectedEvent; +import com.velocitypowered.api.proxy.Player; + +import java.util.*; + +/** + * Task that handles player ping calculation on Velocity based servers. + * + * Based on PingCountTimerBungee + * + * @author MicleBrick + */ +public class PingCountTimerVelocity extends AbsRunnable { + + //the server is pinging the client every 40 Ticks (2 sec) - so check it then + //https://github.com/bergerkiller/CraftSource/blob/master/net.minecraft.server/PlayerConnection.java#L178 + public static final int PING_INTERVAL = 2 * 20; + + private final Map>> playerHistory = new HashMap<>(); + + @Override + public void run() { + List loggedOut = new ArrayList<>(); + long time = System.currentTimeMillis(); + playerHistory.forEach((uuid, history) -> { + Player player = PlanVelocity.getInstance().getProxy().getPlayer(uuid).orElse(null); + if (player != null) { + int ping = getPing(player); + if (ping < -1 || ping > TimeAmount.SECOND.ms() * 8L) { + // Don't accept bad values + return; + } + history.add(new DateObj<>(time, ping)); + if (history.size() >= 30) { + Processing.submit(new PingInsertProcessor(uuid, new ArrayList<>(history))); + history.clear(); + } + } else { + loggedOut.add(uuid); + } + }); + loggedOut.forEach(playerHistory::remove); + } + + public void addPlayer(Player player) { + playerHistory.put(player.getUniqueId(), new ArrayList<>()); + } + + public void removePlayer(Player player) { + playerHistory.remove(player.getUniqueId()); + } + + private int getPing(Player player) { + return (int) player.getPing(); + } + + @Subscribe + public void onPlayerJoin(ServerConnectedEvent joinEvent) { + Player player = joinEvent.getPlayer(); + RunnableFactory.createNew("Add Player to Ping list", new AbsRunnable() { + @Override + public void run() { + if (player.isActive()) { + addPlayer(player); + } + } + }).runTaskLater(TimeAmount.SECOND.ticks() * (long) Settings.PING_PLAYER_LOGIN_DELAY.getNumber()); + } + + @Subscribe + public void onPlayerQuit(DisconnectEvent quitEvent) { + removePlayer(quitEvent.getPlayer()); + } + + public void clear() { + playerHistory.clear(); + } +} diff --git a/Plan/src/main/java/com/djrapitops/plan/system/tasks/server/SpongeTPSCountTimer.java b/Plan/src/main/java/com/djrapitops/plan/system/tasks/server/SpongeTPSCountTimer.java index 0cbac21ea..c91af39ce 100644 --- a/Plan/src/main/java/com/djrapitops/plan/system/tasks/server/SpongeTPSCountTimer.java +++ b/Plan/src/main/java/com/djrapitops/plan/system/tasks/server/SpongeTPSCountTimer.java @@ -56,18 +56,9 @@ public class SpongeTPSCountTimer extends TPSCountTimer { * @return the TPS */ private TPS calculateTPS(long now) { - OperatingSystemMXBean operatingSystemMXBean = ManagementFactory.getOperatingSystemMXBean(); - int availableProcessors = operatingSystemMXBean.getAvailableProcessors(); - double averageCPUUsage = operatingSystemMXBean.getSystemLoadAverage() / availableProcessors * 100.0; + double averageCPUUsage = getCPUUsage(); - if (averageCPUUsage < 0) { // If unavailable, getSystemLoadAverage() returns -1 - averageCPUUsage = -1; - } - - Runtime runtime = Runtime.getRuntime(); - - long totalMemory = runtime.totalMemory(); - long usedMemory = (totalMemory - runtime.freeMemory()) / 1000000; + long usedMemory = getUsedMemory(); double tps = Sponge.getGame().getServer().getTicksPerSecond(); int playersOnline = serverProperties.getOnlinePlayers(); diff --git a/Plan/src/main/java/com/djrapitops/plan/system/tasks/velocity/VelocityTPSCountTimer.java b/Plan/src/main/java/com/djrapitops/plan/system/tasks/velocity/VelocityTPSCountTimer.java new file mode 100644 index 000000000..2121fc14f --- /dev/null +++ b/Plan/src/main/java/com/djrapitops/plan/system/tasks/velocity/VelocityTPSCountTimer.java @@ -0,0 +1,31 @@ +package com.djrapitops.plan.system.tasks.velocity; + +import com.djrapitops.plan.PlanVelocity; +import com.djrapitops.plan.data.container.TPS; +import com.djrapitops.plan.data.container.builders.TPSBuilder; +import com.djrapitops.plan.system.info.server.ServerInfo; +import com.djrapitops.plan.system.tasks.TPSCountTimer; + +public class VelocityTPSCountTimer extends TPSCountTimer { + + public VelocityTPSCountTimer(PlanVelocity plugin) { + super(plugin); + } + + @Override + public void addNewTPSEntry(long nanoTime, long now) { + int onlineCount = ServerInfo.getServerProperties().getOnlinePlayers(); + TPS tps = TPSBuilder.get() + .date(now) + .skipTPS() + .playersOnline(onlineCount) + .usedCPU(getCPUUsage()) + .usedMemory(getUsedMemory()) + .entities(-1) + .chunksLoaded(-1) + .toTPS(); + + history.add(tps); + latestPlayersOnline = onlineCount; + } +} diff --git a/Plan/src/main/java/com/djrapitops/plan/system/webserver/WebServer.java b/Plan/src/main/java/com/djrapitops/plan/system/webserver/WebServer.java index 3161bc2e5..6024428b0 100644 --- a/Plan/src/main/java/com/djrapitops/plan/system/webserver/WebServer.java +++ b/Plan/src/main/java/com/djrapitops/plan/system/webserver/WebServer.java @@ -12,6 +12,7 @@ import com.djrapitops.plugin.api.Check; import com.djrapitops.plugin.logging.L; import com.djrapitops.plugin.logging.console.PluginLogger; import com.djrapitops.plugin.logging.error.ErrorHandler; +import com.google.common.util.concurrent.ThreadFactoryBuilder; import com.sun.net.httpserver.HttpServer; import com.sun.net.httpserver.HttpsConfigurator; import com.sun.net.httpserver.HttpsParameters; @@ -29,9 +30,7 @@ import java.nio.file.Paths; import java.security.*; import java.security.cert.Certificate; import java.security.cert.CertificateException; -import java.util.concurrent.ArrayBlockingQueue; -import java.util.concurrent.ThreadPoolExecutor; -import java.util.concurrent.TimeUnit; +import java.util.concurrent.*; /** * @author Rsl1122 @@ -121,7 +120,11 @@ public class WebServer implements SubSystem { } server.createContext("/", requestHandler); - server.setExecutor(new ThreadPoolExecutor(4, 8, 30, TimeUnit.SECONDS, new ArrayBlockingQueue<>(100))); + ExecutorService executor = new ThreadPoolExecutor( + 4, 8, 30, TimeUnit.SECONDS, new ArrayBlockingQueue<>(100), + new ThreadFactoryBuilder().setNameFormat("Plan WebServer Thread-%d").build() + ); + server.setExecutor(executor); server.start(); enabled = true; @@ -212,12 +215,26 @@ public class WebServer implements SubSystem { @Override public void disable() { if (server != null) { + shutdown(); logger.info(locale.getString(PluginLang.DISABLED_WEB_SERVER)); - server.stop(0); } enabled = false; } + private void shutdown() { + server.stop(0); + Executor executor = server.getExecutor(); + if (executor instanceof ExecutorService) { + ExecutorService service = (ExecutorService) executor; + service.shutdown(); + try { + service.awaitTermination(5, TimeUnit.SECONDS); + } catch (InterruptedException timeoutExceededEx) { + service.shutdownNow(); + } + } + } + public String getProtocol() { return usingHttps ? "https" : "http"; } diff --git a/Plan/src/main/java/com/djrapitops/plan/system/webserver/cache/ResponseCache.java b/Plan/src/main/java/com/djrapitops/plan/system/webserver/cache/ResponseCache.java index 772e04180..0834953af 100644 --- a/Plan/src/main/java/com/djrapitops/plan/system/webserver/cache/ResponseCache.java +++ b/Plan/src/main/java/com/djrapitops/plan/system/webserver/cache/ResponseCache.java @@ -1,10 +1,11 @@ package com.djrapitops.plan.system.webserver.cache; import com.djrapitops.plan.system.webserver.response.Response; +import com.github.benmanes.caffeine.cache.Cache; +import com.github.benmanes.caffeine.cache.Caffeine; -import java.util.HashMap; -import java.util.Map; import java.util.Set; +import java.util.concurrent.TimeUnit; import java.util.function.Supplier; /** @@ -19,7 +20,9 @@ import java.util.function.Supplier; */ public class ResponseCache { - private static final Map cache = new HashMap<>(); + private static final Cache cache = Caffeine.newBuilder() + .expireAfterWrite(5, TimeUnit.MINUTES) + .build(); /** * Constructor used to hide the public constructor @@ -41,17 +44,7 @@ public class ResponseCache { * @return The Response that was cached or created by the the {@link Response} {@link Supplier} */ public static Response loadResponse(String identifier, Supplier loader) { - Response response = loadResponse(identifier); - - if (response != null) { - return response; - } - - response = loader.get(); - - cache.put(identifier, response); - - return response; + return cache.get(identifier, k -> loader.get()); } /** @@ -61,7 +54,7 @@ public class ResponseCache { * @return The Response that was cached or {@code null} if it wasn't */ public static Response loadResponse(String identifier) { - return cache.get(identifier); + return cache.getIfPresent(identifier); } /** @@ -84,21 +77,25 @@ public class ResponseCache { * @return true if the page is cached */ public static boolean isCached(String identifier) { - return cache.containsKey(identifier); + return cache.getIfPresent(identifier) != null; } /** * Clears the cache from all its contents. */ public static void clearCache() { - cache.clear(); + cache.invalidateAll(); } public static Set getCacheKeys() { - return cache.keySet(); + return cache.asMap().keySet(); + } + + public static long getEstimatedSize() { + return cache.estimatedSize(); } public static void clearResponse(String identifier) { - cache.remove(identifier); + cache.invalidate(identifier); } } diff --git a/Plan/src/main/java/com/djrapitops/plan/system/webserver/response/Response.java b/Plan/src/main/java/com/djrapitops/plan/system/webserver/response/Response.java index 8a002a9d6..afc9ae30d 100644 --- a/Plan/src/main/java/com/djrapitops/plan/system/webserver/response/Response.java +++ b/Plan/src/main/java/com/djrapitops/plan/system/webserver/response/Response.java @@ -96,8 +96,10 @@ public abstract class Response { } sentContent = theme.replaceThemeColors(sentContent); - try (GZIPOutputStream out = new GZIPOutputStream(exchange.getResponseBody()); - ByteArrayInputStream bis = new ByteArrayInputStream(sentContent.getBytes(StandardCharsets.UTF_8))) { + try ( + GZIPOutputStream out = new GZIPOutputStream(exchange.getResponseBody()); + ByteArrayInputStream bis = new ByteArrayInputStream(sentContent.getBytes(StandardCharsets.UTF_8)) + ) { byte[] buffer = new byte[2048]; int count; while ((count = bis.read(buffer)) != -1) { diff --git a/Plan/src/main/java/com/djrapitops/plan/system/webserver/response/errors/ErrorResponse.java b/Plan/src/main/java/com/djrapitops/plan/system/webserver/response/errors/ErrorResponse.java index 5cd3f4611..92d13e04e 100644 --- a/Plan/src/main/java/com/djrapitops/plan/system/webserver/response/errors/ErrorResponse.java +++ b/Plan/src/main/java/com/djrapitops/plan/system/webserver/response/errors/ErrorResponse.java @@ -5,7 +5,7 @@ package com.djrapitops.plan.system.webserver.response.errors; import com.djrapitops.plan.system.file.PlanFiles; -import com.djrapitops.plan.system.webserver.response.Response; +import com.djrapitops.plan.system.webserver.response.pages.PageResponse; import org.apache.commons.text.StringSubstitutor; import java.io.IOException; @@ -17,7 +17,7 @@ import java.util.Map; * * @author Rsl1122 */ -public class ErrorResponse extends Response { +public class ErrorResponse extends PageResponse { private String title; private String paragraph; diff --git a/Plan/src/main/java/com/djrapitops/plan/system/webserver/response/pages/AnalysisPageResponse.java b/Plan/src/main/java/com/djrapitops/plan/system/webserver/response/pages/AnalysisPageResponse.java index af97a5201..38b533215 100644 --- a/Plan/src/main/java/com/djrapitops/plan/system/webserver/response/pages/AnalysisPageResponse.java +++ b/Plan/src/main/java/com/djrapitops/plan/system/webserver/response/pages/AnalysisPageResponse.java @@ -6,7 +6,7 @@ import com.djrapitops.plan.system.webserver.response.Response; * @author Rsl1122 * @since 3.5.2 */ -public class AnalysisPageResponse extends Response { +public class AnalysisPageResponse extends PageResponse { public AnalysisPageResponse(String html) { super.setHeader("HTTP/1.1 200 OK"); diff --git a/Plan/src/main/java/com/djrapitops/plan/system/webserver/response/pages/InspectPageResponse.java b/Plan/src/main/java/com/djrapitops/plan/system/webserver/response/pages/InspectPageResponse.java index f11f93823..abd17f53d 100644 --- a/Plan/src/main/java/com/djrapitops/plan/system/webserver/response/pages/InspectPageResponse.java +++ b/Plan/src/main/java/com/djrapitops/plan/system/webserver/response/pages/InspectPageResponse.java @@ -8,13 +8,14 @@ import org.apache.commons.text.StringSubstitutor; import java.util.HashMap; import java.util.Map; +import java.util.Objects; import java.util.UUID; /** * @author Rsl1122 * @since 3.5.2 */ -public class InspectPageResponse extends Response { +public class InspectPageResponse extends PageResponse { private final UUID uuid; @@ -39,4 +40,18 @@ public class InspectPageResponse extends Response { private String[] getCalculating() { return new String[]{"

  • Calculating...
  • ", ""}; } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof InspectPageResponse)) return false; + if (!super.equals(o)) return false; + InspectPageResponse that = (InspectPageResponse) o; + return Objects.equals(uuid, that.uuid); + } + + @Override + public int hashCode() { + return Objects.hash(super.hashCode(), uuid); + } } diff --git a/Plan/src/main/java/com/djrapitops/plan/system/webserver/response/pages/NetworkPageResponse.java b/Plan/src/main/java/com/djrapitops/plan/system/webserver/response/pages/NetworkPageResponse.java index aa244a08c..7bf6d2523 100644 --- a/Plan/src/main/java/com/djrapitops/plan/system/webserver/response/pages/NetworkPageResponse.java +++ b/Plan/src/main/java/com/djrapitops/plan/system/webserver/response/pages/NetworkPageResponse.java @@ -2,6 +2,8 @@ package com.djrapitops.plan.system.webserver.response.pages; import com.djrapitops.plan.api.exceptions.ParseException; import com.djrapitops.plan.system.webserver.response.Response; +import com.djrapitops.plan.data.store.containers.NetworkContainer; +import com.djrapitops.plan.system.database.databases.Database; import com.djrapitops.plan.utilities.html.pages.NetworkPage; /** @@ -9,7 +11,7 @@ import com.djrapitops.plan.utilities.html.pages.NetworkPage; * * @author Rsl1122 */ -public class NetworkPageResponse extends Response { +public class NetworkPageResponse extends PageResponse { public NetworkPageResponse(NetworkPage networkPage) throws ParseException { setHeader("HTTP/1.1 200 OK"); diff --git a/Plan/src/main/java/com/djrapitops/plan/system/webserver/response/pages/PageResponse.java b/Plan/src/main/java/com/djrapitops/plan/system/webserver/response/pages/PageResponse.java new file mode 100644 index 000000000..6b5ceac0e --- /dev/null +++ b/Plan/src/main/java/com/djrapitops/plan/system/webserver/response/pages/PageResponse.java @@ -0,0 +1,27 @@ +package com.djrapitops.plan.system.webserver.response.pages; + +import com.djrapitops.plan.system.webserver.response.Response; +import com.djrapitops.plan.system.webserver.response.ResponseType; +import com.googlecode.htmlcompressor.compressor.HtmlCompressor; + +/** + * Response for all HTML Page responses. + * + * @author Rsl1122 + */ +public class PageResponse extends Response { + + public PageResponse(ResponseType type) { + super(type); + } + + public PageResponse() { + } + + @Override + public void setContent(String content) { + HtmlCompressor compressor = new HtmlCompressor(); + compressor.setRemoveIntertagSpaces(true); + super.setContent(compressor.compress(content)); + } +} \ No newline at end of file diff --git a/Plan/src/main/java/com/djrapitops/plan/system/webserver/response/pages/PlayersPageResponse.java b/Plan/src/main/java/com/djrapitops/plan/system/webserver/response/pages/PlayersPageResponse.java index 5176f2cc4..6322e9a1a 100644 --- a/Plan/src/main/java/com/djrapitops/plan/system/webserver/response/pages/PlayersPageResponse.java +++ b/Plan/src/main/java/com/djrapitops/plan/system/webserver/response/pages/PlayersPageResponse.java @@ -1,14 +1,13 @@ package com.djrapitops.plan.system.webserver.response.pages; import com.djrapitops.plan.api.exceptions.ParseException; -import com.djrapitops.plan.system.webserver.response.Response; import com.djrapitops.plan.utilities.html.pages.PlayersPage; /** * @author Rsl1122 * @since 3.5.2 */ -public class PlayersPageResponse extends Response { +public class PlayersPageResponse extends PageResponse { public PlayersPageResponse(PlayersPage playersPage) throws ParseException { setHeader("HTTP/1.1 200 OK"); diff --git a/Plan/src/main/java/com/djrapitops/plan/system/webserver/response/pages/parts/InspectPagePluginsContent.java b/Plan/src/main/java/com/djrapitops/plan/system/webserver/response/pages/parts/InspectPagePluginsContent.java index 2c3ff5d4a..be4fee519 100644 --- a/Plan/src/main/java/com/djrapitops/plan/system/webserver/response/pages/parts/InspectPagePluginsContent.java +++ b/Plan/src/main/java/com/djrapitops/plan/system/webserver/response/pages/parts/InspectPagePluginsContent.java @@ -8,7 +8,7 @@ import com.djrapitops.plan.data.element.InspectContainer; import com.djrapitops.plan.data.plugin.HookHandler; import com.djrapitops.plan.data.plugin.PluginData; import com.djrapitops.plan.system.info.server.ServerInfo; -import com.djrapitops.plan.system.webserver.response.Response; +import com.djrapitops.plan.system.webserver.response.pages.PageResponse; import com.djrapitops.plan.utilities.comparators.PluginDataNameComparator; import com.djrapitops.plan.utilities.html.Html; import com.djrapitops.plan.utilities.html.HtmlStructure; @@ -23,7 +23,7 @@ import java.util.*; * * @author Rsl1122 */ -public class InspectPagePluginsContent extends Response { +public class InspectPagePluginsContent extends PageResponse { // ServerUUID, {nav, html} private final Map pluginsTab; diff --git a/Plan/src/main/java/com/djrapitops/plan/system/webserver/response/pages/parts/NetworkPageContent.java b/Plan/src/main/java/com/djrapitops/plan/system/webserver/response/pages/parts/NetworkPageContent.java index 3eaa8bfb3..16999e6c3 100644 --- a/Plan/src/main/java/com/djrapitops/plan/system/webserver/response/pages/parts/NetworkPageContent.java +++ b/Plan/src/main/java/com/djrapitops/plan/system/webserver/response/pages/parts/NetworkPageContent.java @@ -4,7 +4,7 @@ */ package com.djrapitops.plan.system.webserver.response.pages.parts; -import com.djrapitops.plan.system.webserver.response.Response; +import com.djrapitops.plan.system.webserver.response.pages.PageResponse; import java.util.*; @@ -15,7 +15,7 @@ import java.util.*; * * @author Rsl1122 */ -public class NetworkPageContent extends Response { +public class NetworkPageContent extends PageResponse { private final Map content; diff --git a/Plan/src/main/java/com/djrapitops/plan/utilities/analysis/Median.java b/Plan/src/main/java/com/djrapitops/plan/utilities/analysis/Median.java new file mode 100644 index 000000000..92abc49cc --- /dev/null +++ b/Plan/src/main/java/com/djrapitops/plan/utilities/analysis/Median.java @@ -0,0 +1,58 @@ +package com.djrapitops.plan.utilities.analysis; + +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.stream.Collectors; + +/** + * Math utility for calculating the median from Integer values. + * + * @author Rsl1122 + */ +public class Median { + + private final List values; + private int size; + + private Median(Collection values, int b) { + this(values.stream().map(i -> (long) i).collect(Collectors.toList())); + } + + private Median(List values) { + this.values = values; + Collections.sort(values); + size = values.size(); + } + + public static Median forInt(Collection integers) { + return new Median(integers, 0); + } + + public static Median forLong(List longs) { + return new Median(longs); + } + + public double calculate() { + if (values.isEmpty()) { + return -1; + } + if (size % 2 == 0) { + return calculateEven(); + } else { + return calculateOdd(); + } + } + + private double calculateEven() { + int half = size / 2; + double x1 = values.get(half); + double x2 = values.get(half - 1); + return (x1 + x2) / 2; + } + + private double calculateOdd() { + int half = size / 2; + return (double) values.get(half); + } +} \ No newline at end of file diff --git a/Plan/src/main/java/com/djrapitops/plan/utilities/formatting/time/TimeAmountFormatter.java b/Plan/src/main/java/com/djrapitops/plan/utilities/formatting/time/TimeAmountFormatter.java index 0b82af52f..872229e08 100644 --- a/Plan/src/main/java/com/djrapitops/plan/utilities/formatting/time/TimeAmountFormatter.java +++ b/Plan/src/main/java/com/djrapitops/plan/utilities/formatting/time/TimeAmountFormatter.java @@ -65,20 +65,13 @@ public class TimeAmountFormatter implements Formatter { return formattedTime; } - private void appendSeconds(StringBuilder builder, long seconds, long minutes, long hours, String fHours, String fMinutes, String fSeconds) { - if (seconds != 0) { - String s = fSeconds.replace(SECONDS_PH, String.valueOf(seconds)); - if (minutes == 0 && s.contains(MINUTES_PH)) { - if (hours == 0 && fMinutes.contains(HOURS_PH)) { - builder.append(fHours.replace(ZERO_PH, "0").replace(HOURS_PH, "0")); - } - builder.append(fMinutes.replace(HOURS_PH, "").replace(ZERO_PH, "0").replace(MINUTES_PH, "0")); - } - s = s.replace(MINUTES_PH, ""); - if (s.contains(ZERO_PH) && String.valueOf(seconds).length() == 1) { + private void appendHours(StringBuilder builder, long hours, String fHours) { + if (hours != 0) { + String h = fHours.replace(HOURS_PH, String.valueOf(hours)); + if (h.contains(ZERO_PH) && String.valueOf(hours).length() == 1) { builder.append('0'); } - builder.append(s); + builder.append(h); } } @@ -97,13 +90,20 @@ public class TimeAmountFormatter implements Formatter { } } - private void appendHours(StringBuilder builder, long hours, String fHours) { - if (hours != 0) { - String h = fHours.replace(HOURS_PH, String.valueOf(hours)); - if (h.contains(ZERO_PH) && String.valueOf(hours).length() == 1) { + private void appendSeconds(StringBuilder builder, long seconds, long minutes, long hours, String fHours, String fMinutes, String fSeconds) { + if (seconds != 0 || fSeconds.contains(ZERO_PH)) { + String s = fSeconds.replace(SECONDS_PH, String.valueOf(seconds)); + if (minutes == 0 && s.contains(MINUTES_PH)) { + if (hours == 0 && fMinutes.contains(HOURS_PH)) { + builder.append(fHours.replace(ZERO_PH, "0").replace(HOURS_PH, "0")); + } + builder.append(fMinutes.replace(HOURS_PH, "").replace(ZERO_PH, "0").replace(MINUTES_PH, "0")); + } + s = s.replace(MINUTES_PH, ""); + if (s.contains(ZERO_PH) && String.valueOf(seconds).length() == 1) { builder.append('0'); } - builder.append(h); + builder.append(s); } } diff --git a/Plan/src/main/java/com/djrapitops/plan/utilities/html/graphs/ProgressBar.java b/Plan/src/main/java/com/djrapitops/plan/utilities/html/graphs/ProgressBar.java new file mode 100644 index 000000000..a29ebc932 --- /dev/null +++ b/Plan/src/main/java/com/djrapitops/plan/utilities/html/graphs/ProgressBar.java @@ -0,0 +1,39 @@ +package com.djrapitops.plan.utilities.html.graphs; + +import com.djrapitops.plan.data.store.mutators.formatting.Formatters; + +/** + * Utility for creating ProgressBars. + * + * @author Rsl1122 + */ +public class ProgressBar { + + private final int obtained; + private final int max; + + private final String color; + + public ProgressBar(int obtained, int max) { + this(obtained, max, "teal"); + } + + public ProgressBar(int obtained, int max, String color) { + this.obtained = obtained; + this.max = max; + this.color = color; + } + + public String toHtml() { + double percentage = obtained * 1.0 / max; + int percentageRounded = (int) percentage; + + return "
    " + + obtained + " / " + max + " (" + Formatters.percentage().apply(percentage) + ")" + + "
    "; + } + +} \ No newline at end of file diff --git a/Plan/src/main/java/com/djrapitops/plan/utilities/html/pages/NetworkPage.java b/Plan/src/main/java/com/djrapitops/plan/utilities/html/pages/NetworkPage.java index 5ea0ce2e5..0f46ab045 100644 --- a/Plan/src/main/java/com/djrapitops/plan/utilities/html/pages/NetworkPage.java +++ b/Plan/src/main/java/com/djrapitops/plan/utilities/html/pages/NetworkPage.java @@ -10,6 +10,7 @@ import com.djrapitops.plan.data.store.keys.NetworkKeys; import com.djrapitops.plan.system.file.PlanFiles; import com.djrapitops.plan.system.info.server.properties.ServerProperties; import com.djrapitops.plan.utilities.formatting.PlaceholderReplacer; +import com.djrapitops.plan.utilities.html.structure.AnalysisPluginsTabContentCreator; import static com.djrapitops.plan.data.store.keys.NetworkKeys.*; @@ -55,6 +56,14 @@ public class NetworkPage implements Page { SERVERS_TAB ); + // TODO Fix + String[] content = AnalysisPluginsTabContentCreator.createContent(networkContainer.getUnsafe(NetworkKeys.PLAYERS_MUTATOR), null); + String nav = content[0]; + String tabs = content[1]; + + placeholderReplacer.put("navPluginsTabs", nav); + placeholderReplacer.put("tabsPlugins", tabs); + return placeholderReplacer.apply(files.readCustomizableResourceFlat("web/network.html")); } catch (Exception e) { throw new ParseException(e); diff --git a/Plan/src/main/java/com/djrapitops/plan/utilities/html/structure/AnalysisPluginsTabContentCreator.java b/Plan/src/main/java/com/djrapitops/plan/utilities/html/structure/AnalysisPluginsTabContentCreator.java index 0bcfd3e1e..c63e4b220 100644 --- a/Plan/src/main/java/com/djrapitops/plan/utilities/html/structure/AnalysisPluginsTabContentCreator.java +++ b/Plan/src/main/java/com/djrapitops/plan/utilities/html/structure/AnalysisPluginsTabContentCreator.java @@ -55,7 +55,7 @@ public class AnalysisPluginsTabContentCreator { com.djrapitops.plan.data.store.containers.AnalysisContainer analysisContainer ) { PlayersMutator mutator = analysisContainer.getValue(AnalysisKeys.PLAYERS_MUTATOR).orElse(new PlayersMutator(new ArrayList<>())); - + if (mutator.all().isEmpty()) { return new String[]{"
  • No Data
  • ", ""}; } @@ -143,6 +143,7 @@ public class AnalysisPluginsTabContentCreator { errorHandler.log(L.WARN, this.getClass(), e); } finally { timings.end(DebugChannels.ANALYSIS, "Source " + source.getSourcePlugin()); + source.setAnalysisData(null); } }); return containers; diff --git a/Plan/src/main/resources/bungee.yml b/Plan/src/main/resources/bungee.yml index 3d5049815..b165e7633 100644 --- a/Plan/src/main/resources/bungee.yml +++ b/Plan/src/main/resources/bungee.yml @@ -1,4 +1,9 @@ name: Plan author: Rsl1122 main: com.djrapitops.plan.PlanBungee -version: 4.4.3 \ No newline at end of file +version: 4.4.7 +softdepend: +- AdvancedBan +- LiteBans +- LuckPerms +- ViaVersion \ No newline at end of file diff --git a/Plan/src/main/resources/bungeeconfig.yml b/Plan/src/main/resources/bungeeconfig.yml index f7bb88844..c59c5cb6a 100644 --- a/Plan/src/main/resources/bungeeconfig.yml +++ b/Plan/src/main/resources/bungeeconfig.yml @@ -66,6 +66,9 @@ Data: LogUnknownCommands: false CombineCommandAliases: true Geolocations: true + Ping: + ServerEnableDelaySeconds: 300 + PlayerLoginDelaySeconds: 30 KeepInactivePlayerDataForDays: 180 # ----------------------------------------------------- Customization: @@ -144,4 +147,9 @@ Servers: Example: WebServerPort: 8034 ServerName: Example - ThemeBase: Default \ No newline at end of file + ThemeBase: Default +# ----------------------------------------------------- +Plugins: + BuyCraft: + # http://help.buycraft.net/article/36-where-to-find-the-secret-key + Secret: "-" \ No newline at end of file diff --git a/Plan/src/main/resources/config.yml b/Plan/src/main/resources/config.yml index f8e27d9ed..0344bb904 100644 --- a/Plan/src/main/resources/config.yml +++ b/Plan/src/main/resources/config.yml @@ -80,6 +80,9 @@ Data: LogUnknownCommands: false CombineCommandAliases: true Geolocations: true + Ping: + ServerEnableDelaySeconds: 300 + PlayerLoginDelaySeconds: 30 KeepInactivePlayerDataForDays: 180 # ----------------------------------------------------- Customization: diff --git a/Plan/src/main/resources/locale/locale_DE.txt b/Plan/src/main/resources/locale/locale_DE.txt index 5e0c30a3e..26a694b20 100644 --- a/Plan/src/main/resources/locale/locale_DE.txt +++ b/Plan/src/main/resources/locale/locale_DE.txt @@ -17,10 +17,10 @@ Cmd Header - Players || > §2Spieler Cmd Header - Search || > §2${0} Ergebnisse für §f${1}§2: Cmd Header - Servers || > §2Server Cmd Header - Web Users || > §2${0} Accounts -Cmd Info - Bungee Connection || §2Verbuunden mit Bungee: §f${0} +Cmd Info - Bungee Connection || §2Verbunden mit Bungee: §f${0} Cmd Info - Database || §2Genutzte Datenbank: §f${0} Cmd Info - Reload Complete || §aReload erfolgreich. -Cmd Info - Reload Failed || §cBeim Reload ist etwas schief gelaufen, ein Neustart wird empfohlen. +Cmd Info - Reload Failed || §cBeim Reload ist etwas schief gelaufen. Es wird empfohlen, den Server neuzustarten. Cmd Info - Update || §2Update verfügbar: §f${0} Cmd Info - Version || §2Version: §f${0} Cmd Notify - No WebUser || Möglicherweise hast du keinen Account. Erstelle einen mit /plan register @@ -29,58 +29,58 @@ Cmd Qinspect - Activity Index || §2Aktivitätsindex: §f${0 Cmd Qinspect - Deaths || §2Tode: §f${0} Cmd Qinspect - Geolocation || §2Eingeloggt aus: §f${0} Cmd Qinspect - Last Seen || §2Zuletzt gesehen: §f${0} -Cmd Qinspect - Longest Session || §2Längste Sitzung: §f${0} +Cmd Qinspect - Longest Session || §2Längste Session: §f${0} Cmd Qinspect - Mob Kills || §2Getötete Mobs: §f${0} Cmd Qinspect - Player Kills || §2Getötete Spieler: §f${0} Cmd Qinspect - Playtime || §Spielzeit: §f${0} Cmd Qinspect - Registered || §2Registrierung: §f${0} Cmd Qinspect - Times Kicked || §2Kicks: §f${0} -Cmd Setup - Allowed || §aSet-up ist nun erlaubt. -Cmd Setup - Bad Request || §eVerbindung hergestellt, der empfangende Server ist kein Bungee-Server. Nutze stattdessen die Bungee-Adresse. -Cmd Setup - Disallowed || §cSet-up ist nun nicht mehr erlaubt. -Cmd Setup - Forbidden || §eVerbindung hergestellt, aber der Bungee-Server hat den Set-up-Modus ausgeschaltet - nutze '/planbungee setup' um ihn zu aktivieren. -Cmd Setup - Gateway Error || §eVerbindung hergestellt, aber der Bungee-Server konnte sich nicht mit diesem Server verbinden (Wurde der aktuelle Server neugestartet?). Nutze /plan m con & /planbungee con zum debuggen. +Cmd Setup - Allowed || §aSetupmodus wurde aktiviert. +Cmd Setup - Bad Request || §eVerbindung hergestellt. Der empfangende Server ist kein Bungeecordserver. Nutze stattdessen die Bungeecord-Adresse. +Cmd Setup - Disallowed || §cSet-up wurde deaktiviert. +Cmd Setup - Forbidden || §eVerbindung hergestellt aber der Bungeecordserver hat den Setupmodus nicht aktiviert. Nutze '/planbungee setup' um ihn zu aktivieren. +Cmd Setup - Gateway Error || §eVerbindung hergestellt, aber der Bungeecordserver konnte sich nicht mit diesem Server verbinden (Wurde der aktuelle Server neugestartet?). Nutze /plan m con & /planbungee con zum debuggen. Cmd Setup - Generic Fail || §eVerbindung fehlgeschlagen: ${0} -Cmd Setup - Internal Error || §eVerbindung hergestellt. ${0}, eventuelle Fehler kannst du dem Error-Log auf der Debugseite des empfangenden Servers einsehen. +Cmd Setup - Internal Error || §eVerbindung hergestellt. ${0}, eventuelle Fehler kannst du dem Error-Log auf der Debugseite des empfangenden Servers entnehmen. Cmd Setup - Success || §aVerbindung erfolgreich, Plan startet eventuell in ein paar Sekunden neu. Cmd Setup - Unauthorized || §eVerbindung erfolgreich, aber der empfangende Server hat diesen Server nicht autorisiert. Auf dem Plan-Discord bekommst du Hilfe. Cmd Setup - Url mistake || §cNutze die gesamte Adresse (Beginnend mit http:// oder https://) - Diese kannst du dem Bungee enable log entnehmen. Cmd Setup - WebServer not Enabled || §cWebServer ist auf diesem Server deaktiviert! Dies sollte beim Start aktiviert werden! -Cmd SUCCESS - Feature disabled || §a'${0}' wurde bist zum nächsten Reload des Plugins deaktiviert. +Cmd SUCCESS - Feature disabled || §a'${0}' wurde bis zum nächsten Reload des Plugins deaktiviert. Cmd SUCCESS - WebUser register || §aNeuer Account (${0}) erfolgreich hinzugefügt! -Cmd Update - Cancel Success || §aAbbruch erfolgreich. +Cmd Update - Cancel Success || §aErfolgreich abgebrochen. Cmd Update - Cancelled || §cUpdate abgebrochen. Cmd Update - Change log || Change Log v${0}: -Cmd Update - Fail Cacnel || §cAuf einem Server war das Update nicht erfolgreich, breche auf allen anderen Servern die Updates ab... +Cmd Update - Fail Cacnel || §cAuf einem Server war das Update nicht erfolgreich. Das Update wird auf allen Servern abgebrochen Cmd Update - Fail Force Notify || §e${0} wurde nicht geupdated, -force wurde angegeben, fahre mit Updates fort. -Cmd Update - Fail Not Online || §cNicht alle Server sind online oder erreichbar, erreichbare Server kannst du mit /plan update -u -force updaten -Cmd Update - Notify Cancel || §aDu kannst das Update auf Servern, die noch nicht neugestartet wurden verwerfen mit: /plan update cancel. -Cmd Update - Online Check || Ãœberprüfe, ob alle Server online sind. +Cmd Update - Fail Not Online || §cNicht alle Server sind online oder erreichbar. Erreichbare Server kannst du mit /plan update -u -force updaten +Cmd Update - Notify Cancel || §aDu kannst das Update auf Servern, die noch nicht neugestartet wurden, verwerfen mit: /plan update cancel. +Cmd Update - Online Check || Ãœberprüfe ob alle Server online sind. Cmd Update - Scheduled || §aUpdate für ${0} geplant. Cmd Update - Url mismatch || §cDie Download-URL beginnt nicht mit ${0} und ist evtl. nicht vertrauenswürdig. Du kannst diese Version manuell hier downloaden (direkter Download): Cmd Web - Permission Levels || >\§70: Zugriff auf alle Seiten\§71: Zugriff auf '/players' Und alle Spielerseiten\§72: Zugriff auf alle Spielerseiten mit dem gleichen Username wie der Web-Account\§73+: Keine Berechtigung Command Help - /plan analyze || Server-Ãœbersicht Command Help - /plan dev || Entwicklungsmodus-Befehl -Command Help - /plan help || Zeigt eine Befehlsliste -Command Help - /plan info || Zeigt die Version von Plan -Command Help - /plan inspect || Zeigt eine Spielerseite +Command Help - /plan help || Zeigt eine Befehlsliste an +Command Help - /plan info || Zeigt die Version von Plan an +Command Help - /plan inspect || Zeigt eine Spielerseite an Command Help - /plan manage || Verwaltet die Plan-Datenbank Command Help - /plan manage backup || Erstellt ein Backup der Datenbank -Command Help - /plan manage clear || Leer die Datenbank +Command Help - /plan manage clear || Datenbank leeren Command Help - /plan manage con || Debug Server-Bungee Verbindungen Command Help - /plan manage disable || Schalte eine Funktion temporär aus Command Help - /plan manage hotswap || Ändere die Datenbank schnell -Command Help - /plan manage import || Importiere Daten -Command Help - /plan manage move || Bewege die Daten zwischen Datenbanken +Command Help - /plan manage import || Daten importieren +Command Help - /plan manage move || Bewege die Daten zwischen den Datenbanken Command Help - /plan manage remove || Entferne die Daten eines Spielers Command Help - /plan manage restore || Spiele ein Backup ein Command Help - /plan manage setup || Stelle die Server-Bungee-Verbindung her Command Help - /plan network || Netzwerk-Seite Command Help - /plan players || Spieler-Seite -Command Help - /plan qinspect || Zeige Spielerinfo im Spiel +Command Help - /plan qinspect || Zeigt die Spielerinfo im Spiel Command Help - /plan register || Registriere einen Account -Command Help - /plan reload || Starte Plan neu -Command Help - /plan search || Suche nach einem Spieler +Command Help - /plan reload || Plan neuladen +Command Help - /plan search || Nach einem Spieler suchen Command Help - /plan servers || Liste die Server in der Datenbank auf Command Help - /plan update || Zeige das Änderungsprotokoll oder update den Server Command Help - /plan web check || Infos über einen Account @@ -92,11 +92,11 @@ Command Help - /planbungee con || Debug Bungee-Server Verbindun Command Help - /planbungee disable || Deaktiviert das Plugin temporär Command Help - /planbungee setup || Schaltet Setup-Modus an oder aus Database - Apply Patch || Wende Patch an: ${0}.. -Database - Patches Applied || Alle Datenbankpatches wurden erfolgreich angewandt. -Database - Patches Applied Already || Alle Datenbankpatches wurden bereits angewandt. +Database - Patches Applied || Alle Datenbankpatches wurden erfolgreich angewendet. +Database - Patches Applied Already || Alle Datenbankpatches wurden bereits angewendet. Database MySQL - Launch Options Error || Startoptionen sind falsch, nutze Voreinstellungen (${0}) Database Notify - Clean || Daten von ${0} Spielern gelöscht. -Database Notify - SQLite No WAL || SQLite WAL auf dieser Serverversion nicht unterstützt, nutze Voreinstellungen. Dies beeinträchtigt möglicherweise die Serverperformance. +Database Notify - SQLite No WAL || SQLite WAL wird auf dieser Serverversion nicht unterstützt, nutze Voreinstellungen. Dies beeinträchtigt möglicherweise die Serverperformance. Disable || Player Analytics ausgeschaltet. Disable - Processing || Verarbeite kritische unverarbeitete Aufgaben. (${0}) Disable - Processing Complete || Verarbeitung komplett. @@ -104,15 +104,15 @@ Disable - WebServer || Webserver deaktiviert. Enable || Player Analytics angeschaltet. Enable - Database || ${0}-dDatenbankverbindung hergestellt. Enable - Notify Address Confirmation || Versichere dich, dass die Adresse auf DIESEN Server verweist: ${0} -Enable - Notify Empty IP || IP in der server.properties ist leer & AlternativeIP ist nicht in Verwendung. Falsche Links werden verwendet! +Enable - Notify Empty IP || IP in der server.properties ist leer & AlternativeIP ist nicht in Verwendung. Es werden falsche Links verwendet! Enable - Notify Geolocations disabled || Geolocation wird nicht aufgezeichnet (Data.Geolocations: false) -Enable - Notify Geolocations Internet Required || Plan braucht Internetzugang um die GeoLite2 Geolocation Datenbank runterzuladen. -Enable - Notify Webserver disabled || WebServer wurde nicht initialisiert. (WebServer.DisableWebServer: true) +Enable - Notify Geolocations Internet Required || Plan braucht einen Internetzugang um die GeoLite2 Geolocation Datenbank runterzuladen. +Enable - Notify Webserver disabled || WebServer wurde nicht geladen. (WebServer.DisableWebServer: true) Enable - WebServer || Webserver läuft auf PORT ${0} (${1}) Enable FAIL - Database || ${0}-Datenbankverbindung fehlgeschlagen: ${1} -Enable FAIL - Database Patch || Datenbank-Patch ist fehlgeschlagen, Plugin wurde deaktiviert. Bitte melde dies. +Enable FAIL - Database Patch || Datenbank-Patch ist fehlgeschlagen. Plugin wurde deaktiviert. Wir bitten dich, uns diesen Vorfall mitzuteilen. Enable FAIL - GeoDB Write || Etwas ist beim Speichern der GeoLite2 Geolocation Datenbank fehlgeschlagen -Enable FAIL - WebServer (Bungee) || Webserver ist nicht initialisiert! +Enable FAIL - WebServer (Bungee) || Webserver ist nicht geladen Enable FAIL - Wrong Database Type || ${0} ist keine gültige Datenbank HTML - ACTIVITY_INDEX || Aktivitätsindex HTML - ALL || Gesamt @@ -166,7 +166,7 @@ HTML - NAV_COMMAND_USAGE || Befehlsverwendung HTML - NAV_GEOLOCATIONS || Geolocations HTML - NAV_INFORMATION || Information HTML - NAV_NETWORK_PLAYERS || Netzwerk Spieler -HTML - NAV_ONLINE_ACTIVITY || Online Aktivität +HTML - NAV_ONLINE_ACTIVITY || Onlineaktivität HTML - NAV_OVERVIEW || Ãœbersicht HTML - NAV_PERFORMANCE || Performance HTML - NAV_PLAYERS || Spieler @@ -205,7 +205,7 @@ HTML - PUNCHCARD || LOCHKARTE HTML - RECENT_LOGINS || Letzte LOGINS HTML - REGISTERED || REGISTRIERT HTML - REGISTERED_TEXT || Registriert -HTML - REGULAR || REGELMÄSSIG +HTML - REGULAR || REGELMÄSSIGE HTML - SEEN_NICKNAMES || Registrierte Nicknames HTML - SERVER || Server HTML - SERVER_ANALYSIS || Server Analyse @@ -221,8 +221,8 @@ HTML - SESSIONS || Sessions HTML - TIME || Zeit HTML - TIMES_KICKED || Mal gekickt HTML - TIMES_USED || Mal benutzt -HTML - TOTAL_ACTIVE_TEXT || Gesamt Aktiv -HTML - TOTAL_AFK || Gesamt AFK +HTML - TOTAL_ACTIVE_TEXT || Gesamte Aktive Spielzeit +HTML - TOTAL_AFK || Gesamte AFK-Zeit HTML - TOTAL_PLAYERS || Gesamte Spieler HTML - TOTAL_PLAYTIME || Gesamte Spielzeit HTML - UNIQUE || EINZIGARTIG @@ -252,7 +252,7 @@ HTML ERRORS - NOT_FOUND_404 || Nicht gefunden. HTML ERRORS - NOT_PLAYED_404 || Der Spieler war nie auf dem Server. HTML ERRORS - PAGE_NOT_FOUND_404 || Diese Seite existiert nicht. HTML ERRORS - UNAUTHORIZED_401 || Unautorisiert -HTML ERRORS - UNKNOWN_PAGE_404 || Stelle sicher, dass du einen Link benutzt, der von einem Command generiert wurde. Beispielsweise:

    /player/PlayerName
    /server/ServerName

    +HTML ERRORS - UNKNOWN_PAGE_404 || Stelle sicher, dass du einen Link benutzt, der von einem Befehl generiert wurde. Beispielsweise:

    /player/PlayerName
    /server/ServerName

    HTML ERRORS - UUID_404 || Die UUID des Spielers wurde nicht in der Datenbank gefunden. In Depth Help - /plan ? || > §2Hauptbefehl\ Zugriff auf Unterbefehle und Hilfe\ §2/plan §fListe Unterbefehle\ §2/plan ? §fAusführliche Hilfe In Depth Help - /plan analyze ? || > §2Analysebefehl\ Aktualisiert die Serverseite und stellt einen Zugriffslink bereit. diff --git a/Plan/src/main/resources/plugin.yml b/Plan/src/main/resources/plugin.yml index 196fb25b3..f83a18fa4 100644 --- a/Plan/src/main/resources/plugin.yml +++ b/Plan/src/main/resources/plugin.yml @@ -1,7 +1,7 @@ name: Plan author: Rsl1122 main: com.djrapitops.plan.Plan -version: 4.4.3 +version: 4.4.7 softdepend: - EssentialsX - Towny @@ -20,6 +20,8 @@ softdepend: - Kingdoms - RedProtect - AdvancedBan +- LuckPerms +- DiscordSRV commands: plan: @@ -51,13 +53,13 @@ permissions: plan.?: description: Help command default: true - plan.inspect: + plan.inspect.base: description: Allows you to check your player data. default: true plan.inspect.other: description: Allows you to check other players' player data. default: op - plan.qinspect: + plan.qinspect.base: description: Allows you to check your player data. default: op plan.qinspect.other: @@ -88,13 +90,13 @@ permissions: default: op plan.webmanage: description: Manage the webusers, delete, list, check - default: op; + default: op plan.basic: children: plan.?: true - plan.inspect: true - plan.qinspect: true + plan.inspect.base: true + plan.qinspect.base: true plan.advanced: childer: plan.basic: true diff --git a/Plan/src/main/resources/web/network.html b/Plan/src/main/resources/web/network.html index 77dfe6b25..869b50233 100644 --- a/Plan/src/main/resources/web/network.html +++ b/Plan/src/main/resources/web/network.html @@ -130,6 +130,15 @@ Network Players +
  • + + extension + Plugins + +
      + ${navPluginsTabs} +
    +
  • @@ -487,6 +496,7 @@ + ${tabsPlugins} diff --git a/Plan/src/main/resources/web/server.html b/Plan/src/main/resources/web/server.html index 17a79e9e8..5438fa41a 100644 --- a/Plan/src/main/resources/web/server.html +++ b/Plan/src/main/resources/web/server.html @@ -1148,9 +1148,9 @@ - - - + + + diff --git a/Plan/src/test/java/com/djrapitops/plan/data/store/mutators/formatting/TimeAmountFormatterDefaultTest.java b/Plan/src/test/java/com/djrapitops/plan/data/store/mutators/formatting/TimeAmountFormatterDefaultTest.java new file mode 100644 index 000000000..236388d4d --- /dev/null +++ b/Plan/src/test/java/com/djrapitops/plan/data/store/mutators/formatting/TimeAmountFormatterDefaultTest.java @@ -0,0 +1,130 @@ +package com.djrapitops.plan.data.store.mutators.formatting; + +import com.djrapitops.plan.system.settings.Settings; +import com.djrapitops.plugin.api.TimeAmount; +import org.junit.AfterClass; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; +import utilities.Teardown; + +import static org.junit.Assert.assertEquals; + +/** + * Test class for {@link TimeAmountFormatter} that checks extra zeros config example. + * + * @author Rsl1122 + */ +public class TimeAmountFormatterDefaultTest { + + private TimeAmountFormatter timeAmountFormatter; + + @BeforeClass + public static void setUpClass() { + Settings.FORMAT_YEAR.setTemporaryValue("1 year, "); + Settings.FORMAT_YEARS.setTemporaryValue("%years% years, "); + Settings.FORMAT_MONTH.setTemporaryValue("1 month, "); + Settings.FORMAT_MONTHS.setTemporaryValue("%months% months, "); + Settings.FORMAT_DAY.setTemporaryValue("1d "); + Settings.FORMAT_DAYS.setTemporaryValue("%days%d "); + Settings.FORMAT_HOURS.setTemporaryValue("%hours%h "); + Settings.FORMAT_MINUTES.setTemporaryValue("%minutes%m "); + Settings.FORMAT_SECONDS.setTemporaryValue("%seconds%s"); + Settings.FORMAT_ZERO_SECONDS.setTemporaryValue("0s"); + } + + @AfterClass + public static void tearDownClass() { + Teardown.resetSettingsTempValues(); + } + + @Before + public void setUp() { + timeAmountFormatter = new TimeAmountFormatter(); + } + + @Test + public void exampleOne() { + String expected = "1 year, 1 month, 5d 12h 30m 20s"; + + long ms = TimeAmount.DAY.ms() * 400L + + TimeAmount.HOUR.ms() * 12L + + TimeAmount.MINUTE.ms() * 30L + + TimeAmount.SECOND.ms() * 20L; + String result = timeAmountFormatter.apply(ms); + + assertEquals(expected, result); + } + + @Test + public void exampleTwo() { + String expected = "1 year, 1 month, 5d "; + + long ms = TimeAmount.DAY.ms() * 400L; + String result = timeAmountFormatter.apply(ms); + + assertEquals(expected, result); + } + + @Test + public void exampleThree() { + String expected = "12h 20s"; + + long ms = TimeAmount.HOUR.ms() * 12L + + TimeAmount.SECOND.ms() * 20L; + String result = timeAmountFormatter.apply(ms); + + assertEquals(expected, result); + } + + @Test + public void exampleFour() { + String expected = "30m "; + + long ms = TimeAmount.MINUTE.ms() * 30L; + String result = timeAmountFormatter.apply(ms); + + assertEquals(expected, result); + } + + @Test + public void exampleFive() { + String expected = "20s"; + + long ms = TimeAmount.SECOND.ms() * 20L; + String result = timeAmountFormatter.apply(ms); + + assertEquals(expected, result); + } + + @Test + public void exampleZero() { + String expected = "-"; + + long ms = 0L; + String result = timeAmountFormatter.apply(ms); + + assertEquals(expected, result); + } + + @Test + public void exampleOneSecond() { + String expected = "1s"; + + long ms = TimeAmount.SECOND.ms(); + String result = timeAmountFormatter.apply(ms); + + assertEquals(expected, result); + } + + @Test + public void exampleOneMinute() { + String expected = "1m "; + + long ms = TimeAmount.MINUTE.ms(); + String result = timeAmountFormatter.apply(ms); + + assertEquals(expected, result); + } + +} \ No newline at end of file diff --git a/Plan/src/test/java/com/djrapitops/plan/data/store/mutators/formatting/TimeAmountFormatterExtraZerosTest.java b/Plan/src/test/java/com/djrapitops/plan/data/store/mutators/formatting/TimeAmountFormatterExtraZerosTest.java new file mode 100644 index 000000000..7971d5d9f --- /dev/null +++ b/Plan/src/test/java/com/djrapitops/plan/data/store/mutators/formatting/TimeAmountFormatterExtraZerosTest.java @@ -0,0 +1,130 @@ +package com.djrapitops.plan.data.store.mutators.formatting; + +import com.djrapitops.plan.system.settings.Settings; +import com.djrapitops.plugin.api.TimeAmount; +import org.junit.AfterClass; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; +import utilities.Teardown; + +import static org.junit.Assert.assertEquals; + +/** + * Test class for {@link TimeAmountFormatter} that checks extra zeros config example. + * + * @author Rsl1122 + */ +public class TimeAmountFormatterExtraZerosTest { + + private TimeAmountFormatter timeAmountFormatter; + + @BeforeClass + public static void setUpClass() { + Settings.FORMAT_YEAR.setTemporaryValue("1 year, "); + Settings.FORMAT_YEARS.setTemporaryValue("%years% years, "); + Settings.FORMAT_MONTH.setTemporaryValue("1 month, "); + Settings.FORMAT_MONTHS.setTemporaryValue("%months% months, "); + Settings.FORMAT_DAY.setTemporaryValue("1d "); + Settings.FORMAT_DAYS.setTemporaryValue("%days%d "); + Settings.FORMAT_HOURS.setTemporaryValue("%zero%%hours%:"); + Settings.FORMAT_MINUTES.setTemporaryValue("%hours%%zero%%minutes%:"); + Settings.FORMAT_SECONDS.setTemporaryValue("%minutes%%zero%%seconds%"); + Settings.FORMAT_ZERO_SECONDS.setTemporaryValue("00:00:00"); + } + + @AfterClass + public static void tearDownClass() { + Teardown.resetSettingsTempValues(); + } + + @Before + public void setUp() { + timeAmountFormatter = new TimeAmountFormatter(); + } + + @Test + public void exampleOne() { + String expected = "1 year, 1 month, 5d 12:30:20"; + + long ms = TimeAmount.DAY.ms() * 400L + + TimeAmount.HOUR.ms() * 12L + + TimeAmount.MINUTE.ms() * 30L + + TimeAmount.SECOND.ms() * 20L; + String result = timeAmountFormatter.apply(ms); + + assertEquals(expected, result); + } + + @Test + public void exampleTwo() { + String expected = "1 year, 1 month, 5d 00:00:00"; + + long ms = TimeAmount.DAY.ms() * 400L; + String result = timeAmountFormatter.apply(ms); + + assertEquals(expected, result); + } + + @Test + public void exampleThree() { + String expected = "12:00:20"; + + long ms = TimeAmount.HOUR.ms() * 12L + + TimeAmount.SECOND.ms() * 20L; + String result = timeAmountFormatter.apply(ms); + + assertEquals(expected, result); + } + + @Test + public void exampleFour() { + String expected = "00:30:00"; + + long ms = TimeAmount.MINUTE.ms() * 30L; + String result = timeAmountFormatter.apply(ms); + + assertEquals(expected, result); + } + + @Test + public void exampleFive() { + String expected = "00:00:20"; + + long ms = TimeAmount.SECOND.ms() * 20L; + String result = timeAmountFormatter.apply(ms); + + assertEquals(expected, result); + } + + @Test + public void exampleZero() { + String expected = "-"; + + long ms = 0L; + String result = timeAmountFormatter.apply(ms); + + assertEquals(expected, result); + } + + @Test + public void exampleOneSecond() { + String expected = "00:00:01"; + + long ms = TimeAmount.SECOND.ms(); + String result = timeAmountFormatter.apply(ms); + + assertEquals(expected, result); + } + + @Test + public void exampleOneMinute() { + String expected = "00:01:00"; + + long ms = TimeAmount.MINUTE.ms(); + String result = timeAmountFormatter.apply(ms); + + assertEquals(expected, result); + } + +} \ No newline at end of file diff --git a/Plan/src/test/java/com/djrapitops/plan/system/cache/SessionCacheTest.java b/Plan/src/test/java/com/djrapitops/plan/system/cache/SessionCacheTest.java index 1a27c5f90..a10fa50ad 100644 --- a/Plan/src/test/java/com/djrapitops/plan/system/cache/SessionCacheTest.java +++ b/Plan/src/test/java/com/djrapitops/plan/system/cache/SessionCacheTest.java @@ -2,10 +2,7 @@ package com.djrapitops.plan.system.cache; import com.djrapitops.plan.data.container.Session; import com.djrapitops.plan.system.database.databases.Database; -import org.junit.Before; -import org.junit.BeforeClass; -import org.junit.ClassRule; -import org.junit.Test; +import org.junit.*; import org.junit.rules.TemporaryFolder; import utilities.TestConstants; import utilities.mocks.SystemMockUtil; @@ -40,6 +37,11 @@ public class SessionCacheTest { sessionCache.cacheSession(uuid, session); } + @After + public void tearDown() { + SessionCache.clear(); + } + @Test public void testAtomity() { SessionCache reloaded = new SessionCache(database); @@ -47,4 +49,16 @@ public class SessionCacheTest { assertTrue(cachedSession.isPresent()); assertEquals(session, cachedSession.get()); } + + @Test + public void testBungeeReCaching() { + SessionCache cache = new ProxyDataCache(null, null); + cache.cacheSession(uuid, session); + Session expected = new Session(uuid, TestConstants.SERVER_UUID, 0, "", ""); + cache.cacheSession(uuid, expected); + + Optional result = SessionCache.getCachedSession(uuid); + assertTrue(result.isPresent()); + assertEquals(expected, result.get()); + } } \ No newline at end of file diff --git a/Plan/src/test/java/com/djrapitops/plan/system/listeners/bukkit/AFKListenerTest.java b/Plan/src/test/java/com/djrapitops/plan/system/listeners/bukkit/AFKListenerTest.java new file mode 100644 index 000000000..3461abcf4 --- /dev/null +++ b/Plan/src/test/java/com/djrapitops/plan/system/listeners/bukkit/AFKListenerTest.java @@ -0,0 +1,67 @@ +package com.djrapitops.plan.system.listeners.bukkit; + +import com.djrapitops.plan.system.settings.Settings; +import org.bukkit.entity.Player; +import org.bukkit.event.player.PlayerMoveEvent; +import org.junit.AfterClass; +import org.junit.BeforeClass; +import org.junit.Test; +import org.mockito.Mockito; +import utilities.Teardown; +import utilities.TestConstants; + +import java.util.ArrayList; +import java.util.Collection; + +import static org.junit.Assert.assertEquals; +import static org.mockito.Mockito.doAnswer; +import static org.mockito.Mockito.doReturn; + +/** + * Test for {@link AFKListener} + * + * @author Rsl1122 + */ +public class AFKListenerTest { + + @BeforeClass + public static void setUpClass() { + Settings.AFK_THRESHOLD_MINUTES.setTemporaryValue(3); + } + + @AfterClass + public static void tearDownClass() { + Teardown.resetSettingsTempValues(); + } + + @Test + public void testAfkPermissionCallCaching() { + AFKListener afkListener = new AFKListener(); + Collection calls = new ArrayList<>(); + + Player player = mockPlayer(calls); + PlayerMoveEvent event = mockMoveEvent(player); + + afkListener.onMove(event); + assertEquals(1, calls.size()); + afkListener.onMove(event); + assertEquals(1, calls.size()); + } + + private PlayerMoveEvent mockMoveEvent(Player player) { + PlayerMoveEvent event = Mockito.mock(PlayerMoveEvent.class); + doReturn(player).when(event).getPlayer(); + return event; + } + + private Player mockPlayer(Collection calls) { + Player player = Mockito.mock(Player.class); + doReturn(TestConstants.PLAYER_ONE_UUID).when(player).getUniqueId(); + doAnswer(perm -> { + calls.add(true); + return true; + }).when(player).hasPermission(Mockito.anyString()); + return player; + } + +} \ No newline at end of file diff --git a/Plan/src/test/java/com/djrapitops/plan/system/processing/processors/player/PingInsertProcessorTest.java b/Plan/src/test/java/com/djrapitops/plan/system/processing/processors/player/PingInsertProcessorTest.java new file mode 100644 index 000000000..ba7c40523 --- /dev/null +++ b/Plan/src/test/java/com/djrapitops/plan/system/processing/processors/player/PingInsertProcessorTest.java @@ -0,0 +1,66 @@ +package com.djrapitops.plan.system.processing.processors.player; + +import com.djrapitops.plan.data.store.objects.DateObj; +import com.djrapitops.plan.utilities.analysis.Median; +import com.djrapitops.plugin.api.TimeAmount; +import org.junit.Before; +import org.junit.Test; +import utilities.RandomData; +import utilities.TestConstants; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.stream.Collectors; + +import static org.junit.Assert.assertEquals; + +/** + * Test for {@link PingInsertProcessor}. + * + * @author Rsl1122 + */ +public class PingInsertProcessorTest { + + private List> testPing; + + @Before + public void setUp() { + testPing = new ArrayList<>(); + + for (int i = 0; i < TimeAmount.MINUTE.ms(); i += TimeAmount.SECOND.ms() * 2L) { + testPing.add(new DateObj<>(i, RandomData.randomInt(1, 4000))); + } + } + + @Test + public void testMedian() { + List collect = testPing.stream().map(DateObj::getValue).sorted().collect(Collectors.toList()); + System.out.println(collect); + int expected = (int) Median.forInt(collect).calculate(); + int result = new PingInsertProcessor(TestConstants.PLAYER_ONE_UUID, new ArrayList<>()).getMeanValue(testPing); + System.out.println(result); + + assertEquals(expected, result); + } + + @Test + public void testMedianSingleEntry() { + int expected = 50; + int result = new PingInsertProcessor(TestConstants.PLAYER_ONE_UUID, new ArrayList<>()).getMeanValue( + Collections.singletonList(new DateObj<>(0, expected)) + ); + + assertEquals(expected, result); + } + + @Test + public void testMedianEmpty() { + int expected = -1; + int result = new PingInsertProcessor(TestConstants.PLAYER_ONE_UUID, new ArrayList<>()).getMeanValue( + Collections.emptyList() + ); + + assertEquals(expected, result); + } +} \ No newline at end of file diff --git a/Plan/src/test/java/com/djrapitops/plan/utilities/analysis/MedianTest.java b/Plan/src/test/java/com/djrapitops/plan/utilities/analysis/MedianTest.java new file mode 100644 index 000000000..50a0a3656 --- /dev/null +++ b/Plan/src/test/java/com/djrapitops/plan/utilities/analysis/MedianTest.java @@ -0,0 +1,90 @@ +package com.djrapitops.plan.utilities.analysis; + +import org.junit.Test; + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +import static org.junit.Assert.assertEquals; + +/** + * Tests for {@link Median}. + * + * @author Rsl1122 + */ +public class MedianTest { + + @Test + public void simpleOdd() { + List testValues = Arrays.asList(1, 3, 3, 6, 7, 8, 9); + Collections.shuffle(testValues); + double expected = 6; + double result = Median.forInt(testValues).calculate(); + + assertEquals(expected, result, 0.01); + } + + @Test + public void simpleEven() { + List testValues = Arrays.asList(1, 2, 3, 4, 5, 6, 8, 9); + Collections.shuffle(testValues); + double expected = 4.5; + double result = Median.forInt(testValues).calculate(); + + assertEquals(expected, result, 0.01); + } + + @Test + public void empty() { + double expected = -1; + double result = Median.forInt(Collections.emptyList()).calculate(); + + assertEquals(expected, result, 0.01); + } + + @Test + public void singleValue() { + double expected = 50; + double result = Median.forInt(Collections.singletonList((int) expected)).calculate(); + + assertEquals(expected, result, 0.01); + } + + @Test + public void twoValues() { + List testValues = Arrays.asList(1, 2); + double expected = 1.5; + double result = Median.forInt(testValues).calculate(); + + assertEquals(expected, result, 0.01); + } + + @Test + public void overflowOdd() { + List testValues = Arrays.asList(Integer.MIN_VALUE, 2, Integer.MAX_VALUE); + double expected = 2; + double result = Median.forInt(testValues).calculate(); + + assertEquals(expected, result, 0.01); + } + + @Test + public void overflowEven() { + List testValues = Arrays.asList(Integer.MIN_VALUE, Integer.MIN_VALUE, Integer.MAX_VALUE, Integer.MAX_VALUE); + double expected = -0.5; + double result = Median.forInt(testValues).calculate(); + + assertEquals(expected, result, 0.01); + } + + @Test + public void overflowEven2() { + List testValues = Arrays.asList(Integer.MAX_VALUE, Integer.MAX_VALUE, Integer.MAX_VALUE, Integer.MAX_VALUE); + double expected = Integer.MAX_VALUE; + double result = Median.forInt(testValues).calculate(); + + assertEquals(expected, result, 0.01); + } + +} \ No newline at end of file diff --git a/PlanPluginBridge/PlanPluginBridge-4.4.0.jar b/PlanPluginBridge/PlanPluginBridge-4.4.0.jar index fdf51327b..6e1a2c5da 100644 Binary files a/PlanPluginBridge/PlanPluginBridge-4.4.0.jar and b/PlanPluginBridge/PlanPluginBridge-4.4.0.jar differ diff --git a/PlanPluginBridge/install_dependencies.bat b/PlanPluginBridge/install_dependencies.bat new file mode 100644 index 000000000..4558c418a --- /dev/null +++ b/PlanPluginBridge/install_dependencies.bat @@ -0,0 +1 @@ +mvn install:install-file -Dfile="AAC-3.5.0-b1.jar" -DgroupId=me.konsolas -DartifactId=AAC -Dversion=3.5.0 -Dpackaging=jar & mvn install:install-file -Dfile="AdvancedBan-2.1.5.jar" -DgroupId=me.leoko -DartifactId=advancedban -Dversion=2.1.5 -Dpackaging=jar & mvn install:install-file -Dfile="ASkyBlock-3.0.9.4.jar" -DgroupId=com.wasteofplastic.askyblock -DartifactId=ASkyBlock -Dversion=3.0.9.4 -Dpackaging=jar & mvn install:install-file -Dfile="BanManager-5.15.0.jar" -DgroupId=me.confuser -DartifactId=banmanager -Dversion=5.15.0 -Dpackaging=jar & mvn install:install-file -Dfile="EssentialsX-2.15.0.1.jar" -DgroupId=net.ess3 -DartifactId=EssentialsX -Dversion=2.15.0.1 -Dpackaging=jar & mvn install:install-file -Dfile="Factions-2.14.0.jar" -DgroupId=com.massivecraft -DartifactId=factions -Dversion=2.14.0 -Dpackaging=jar & mvn install:install-file -Dfile="MassiveCore-2.14.0.jar" -DgroupId=com.massivecraft -DartifactId=mcore -Dversion=2.14.0 -Dpackaging=jar & mvn install:install-file -Dfile="GriefPreventionPlus-13.3.jar" -DgroupId=net.kaikk.mc -DartifactId=GriefPreventionPlus -Dversion=13.3 -Dpackaging=jar & mvn install:install-file -Dfile="Jobs4.7.4.jar" -DgroupId=com.gamingmesh -DartifactId=jobs -Dversion=4.7.4 -Dpackaging=jar & mvn install:install-file -Dfile="Kingdoms-13.3.40.jar" -DgroupId=org.kingdoms -DartifactId=kingdoms_demo -Dversion=13.3.40 -Dpackaging=jar & mvn install:install-file -Dfile="LiteBansAPI-0.3.jar" -DgroupId=litebans -DartifactId=api -Dversion=0.3 -Dpackaging=jar & mvn install:install-file -Dfile="mcMMO-1.6.0-SNAPSHOT.jar" -DgroupId=com.gmail.nossr50 -DartifactId=mcMMO -Dversion=1.6.0 -Dpackaging=jar & mvn install:install-file -Dfile="ProtocolSupport-4.28.jar" -DgroupId=com.github.ProtocolSupport -DartifactId=ProtocolSupport -Dversion=4.28 -Dpackaging=jar & mvn install:install-file -Dfile="React-6.573.jar" -DgroupId=com.volmit -DartifactId=react -Dversion=6.573 -Dpackaging=jar & mvn install:install-file -Dfile="SuperbVote-0.5.3.jar" -DgroupId=io.minimum -DartifactId=minecraft.superbvote -Dversion=0.5.3 -Dpackaging=jar & mvn install:install-file -Dfile="Towny-0.92.0.0.jar" -DgroupId=com.palmergames -DartifactId=towny -Dversion=0.92.0.0 -Dpackaging=jar & mvn install:install-file -Dfile="Vault-1.6.7.jar" -DgroupId=net.milkbowl.vault -DartifactId=VaultAPI -Dversion=1.6 -Dpackaging=jar \ No newline at end of file diff --git a/PlanPluginBridge/pom.xml b/PlanPluginBridge/pom.xml index cc4e6edba..329b230d8 100644 --- a/PlanPluginBridge/pom.xml +++ b/PlanPluginBridge/pom.xml @@ -41,6 +41,10 @@ + + bungeecord-repo + https://oss.sonatype.org/content/repositories/snapshots + spigot-repo https://hub.spigotmc.org/nexus/content/repositories/snapshots/ @@ -49,12 +53,16 @@ paper-repo https://repo.destroystokyo.com/repository/maven-public/ + + sponge-repo + https://repo.spongepowered.org/maven + vault-repo http://nexus.hc.to/content/repositories/pub_releases - jitpack.io + jitpack.io (GriefPrevention) https://jitpack.io @@ -73,6 +81,14 @@ advanced-achievements-repo https://raw.github.com/PyvesB/AdvancedAchievements/mvn-repo/ + + nucleus-repo + http://repo.drnaylor.co.uk/artifactory/list/minecraft + + + discordsrv-repo + https://ci.scarsz.me/plugin/repository/everything/ + @@ -82,14 +98,20 @@ 4.3.0-SNAPSHOT provided
    - + + org.apache.commons + commons-text + 1.3 + + + + com.destroystokyo.paper paper-api 1.12.2-R0.1-SNAPSHOT provided - org.spigotmc spigot-api @@ -97,6 +119,22 @@ jar provided + + net.md-5 + bungeecord-api + 1.12-SNAPSHOT + jar + provided + + + org.spongepowered + spongeapi + 7.0.0 + jar + provided + + + com.hm.achievement advanced-achievements-api @@ -104,39 +142,21 @@ provided - com.earth2me - essentials - 2.0.1 + com.github.TechFortress + GriefPrevention + 16.8 provided - com.massivecraft - factions - 2.10.0 + me.clip + placeholderapi + 2.9.1 provided - com.gamingmesh - jobs - 4.0.1 - provided - - - com.massivecraft - mcore - 2.10.0 - provided - - - com.gmail.nossr50 - mcMMO - 1.5.07 - provided - - - com.palmergames - towny - 0.91.4.0 + br.net.fabiozumbi12.RedProtect + RedProtect-Spigot + 7.5.3 provided @@ -146,18 +166,74 @@ provided - com.wasteofplastic.askyblock - ASkyBlock - 3.0.6.8 + us.myles + viaversion + 1.5.0 provided - com.github.TechFortress - GriefPrevention - 16.7.1 + io.github.nucleuspowered + nucleus-api + 1.6.0-PR1-S7.0 + provided + + + me.lucko.luckperms + luckperms-api + 4.3 + provided + + + github.scarsz.discordsrv + DiscordSRV + 1.16.4 + provided + + + + + + me.konsolas + AAC + 3.5.0 + provided + + + me.leoko + advancedban + 2.1.5 + provided + + + com.wasteofplastic.askyblock + ASkyBlock + 3.0.9.4 + provided + + + me.confuser + banmanager + 5.15.0 + provided + + + net.ess3 + EssentialsX + 2.15.0.1 + provided + + + com.massivecraft + factions + 2.14.0 + provided + + + com.massivecraft + mcore + 2.14.0 provided - net.kaikk.mc GriefPreventionPlus @@ -165,21 +241,9 @@ provided - litebans - api - 0.2 - provided - - - io.minimum - minecraft.superbvote - 0.4.1 - provided - - - us.myles - viaversion - 1.1.1 + com.gamingmesh + jobs + 4.7.4 provided @@ -189,51 +253,39 @@ provided - me.confuser - banmanager - 5.15.0 + litebans + api + 0.3 + provided + + + com.gmail.nossr50 + mcMMO + 1.6.0 provided - - - - - - com.github.ProtocolSupport ProtocolSupport 4.28 provided - - br.net.fabiozumbi12.RedProtect - RedProtect-Spigot - RELEASE - provided - - - me.konsolas - AAC - 3.3.5 - provided - - - me.clip - placeholderapi - LATEST - provided - com.volmit react - 6.549 + 6.573 provided - me.leoko - advancedban - 2.1.5 + io.minimum + minecraft.superbvote + 0.5.3 + provided + + + com.palmergames + towny + 0.92.0.0 provided
    @@ -245,46 +297,6 @@ maven-install-plugin 2.5.2 - org.jacoco jacoco-maven-plugin diff --git a/PlanPluginBridge/src/main/java/com/djrapitops/pluginbridge/plan/Bridge.java b/PlanPluginBridge/src/main/java/com/djrapitops/pluginbridge/plan/Bridge.java index 809783300..4ac01895d 100644 --- a/PlanPluginBridge/src/main/java/com/djrapitops/pluginbridge/plan/Bridge.java +++ b/PlanPluginBridge/src/main/java/com/djrapitops/pluginbridge/plan/Bridge.java @@ -10,20 +10,26 @@ import com.djrapitops.pluginbridge.plan.advancedban.AdvancedBanHook; import com.djrapitops.pluginbridge.plan.askyblock.ASkyBlockHook; import com.djrapitops.pluginbridge.plan.banmanager.BanManagerHook; import com.djrapitops.pluginbridge.plan.buycraft.BuyCraftHook; +import com.djrapitops.pluginbridge.plan.discordsrv.DiscordSRVHook; import com.djrapitops.pluginbridge.plan.essentials.EssentialsHook; import com.djrapitops.pluginbridge.plan.factions.FactionsHook; import com.djrapitops.pluginbridge.plan.griefprevention.GriefPreventionHook; import com.djrapitops.pluginbridge.plan.griefprevention.plus.GriefPreventionPlusHook; import com.djrapitops.pluginbridge.plan.jobs.JobsHook; import com.djrapitops.pluginbridge.plan.kingdoms.KingdomsHook; -import com.djrapitops.pluginbridge.plan.litebans.LiteBansHook; +import com.djrapitops.pluginbridge.plan.litebans.LiteBansBukkitHook; +import com.djrapitops.pluginbridge.plan.litebans.LiteBansBungeeHook; +import com.djrapitops.pluginbridge.plan.luckperms.LuckPermsHook; import com.djrapitops.pluginbridge.plan.mcmmo.McmmoHook; +import com.djrapitops.pluginbridge.plan.nucleus.NucleusHook; import com.djrapitops.pluginbridge.plan.protocolsupport.ProtocolSupportHook; import com.djrapitops.pluginbridge.plan.redprotect.RedProtectHook; +import com.djrapitops.pluginbridge.plan.sponge.SpongeEconomyHook; import com.djrapitops.pluginbridge.plan.superbvote.SuperbVoteHook; import com.djrapitops.pluginbridge.plan.towny.TownyHook; import com.djrapitops.pluginbridge.plan.vault.VaultHook; -import com.djrapitops.pluginbridge.plan.viaversion.ViaVersionHook; +import com.djrapitops.pluginbridge.plan.viaversion.ViaVersionBukkitHook; +import com.djrapitops.pluginbridge.plan.viaversion.ViaVersionBungeeHook; /** * Manages connection to other plugins. @@ -56,17 +62,34 @@ public class Bridge { private static Hook[] getHooks(HookHandler h) { Hook[] hooks; - if (Check.isBukkitAvailable()) { - hooks = getBukkitHooks(h); - } else { + if (Check.isBungeeAvailable()) { hooks = getBungeeHooks(h); + } else if (Check.isBukkitAvailable()) { + hooks = getBukkitHooks(h); + } else if (Check.isSpongeAvailable()) { + hooks = getSpongeHooks(h); + } else { + return new Hook[0]; } return hooks; } + private static Hook[] getSpongeHooks(HookHandler h) { + return new Hook[]{ + new BuyCraftHook(h), + new LuckPermsHook(h), + new SpongeEconomyHook(h), + new NucleusHook(h) + }; + } + private static Hook[] getBungeeHooks(HookHandler h) { return new Hook[]{ - new AdvancedBanHook(h) + new AdvancedBanHook(h), + new BuyCraftHook(h), + new LiteBansBungeeHook(h), + new LuckPermsHook(h), + new ViaVersionBungeeHook(h) }; } @@ -78,13 +101,15 @@ public class Bridge { new ASkyBlockHook(h), new BanManagerHook(h), new BuyCraftHook(h), + new DiscordSRVHook(h), new EssentialsHook(h), new FactionsHook(h), new GriefPreventionHook(h), new GriefPreventionPlusHook(h), new JobsHook(h), new KingdomsHook(h), - new LiteBansHook(h), + new LiteBansBukkitHook(h), + new LuckPermsHook(h), new McmmoHook(h), new SuperbVoteHook(h), new ProtocolSupportHook(h), @@ -92,7 +117,7 @@ public class Bridge { new RedProtectHook(h), new TownyHook(h), new VaultHook(h), - new ViaVersionHook(h)//, + new ViaVersionBukkitHook(h)//, // new PlaceholderAPIHook(h) }; } diff --git a/PlanPluginBridge/src/main/java/com/djrapitops/pluginbridge/plan/advancedban/AdvancedBanData.java b/PlanPluginBridge/src/main/java/com/djrapitops/pluginbridge/plan/advancedban/AdvancedBanData.java index 31f4bfae2..377083f1a 100644 --- a/PlanPluginBridge/src/main/java/com/djrapitops/pluginbridge/plan/advancedban/AdvancedBanData.java +++ b/PlanPluginBridge/src/main/java/com/djrapitops/pluginbridge/plan/advancedban/AdvancedBanData.java @@ -9,7 +9,6 @@ import com.djrapitops.plan.data.element.AnalysisContainer; import com.djrapitops.plan.data.element.InspectContainer; import com.djrapitops.plan.data.plugin.ContainerSize; import com.djrapitops.plan.data.plugin.PluginData; -import com.djrapitops.plan.data.store.keys.AnalysisKeys; import com.djrapitops.plan.utilities.FormatUtils; import com.djrapitops.plan.utilities.html.Html; import com.djrapitops.plan.utilities.html.HtmlUtils; @@ -17,15 +16,13 @@ import com.djrapitops.plan.utilities.html.icon.Color; import com.djrapitops.plan.utilities.html.icon.Family; import com.djrapitops.plan.utilities.html.icon.Icon; import com.djrapitops.plan.utilities.html.icon.Icons; -import com.djrapitops.plugin.api.utility.log.Log; -import java.util.Collection; -import java.util.HashMap; -import java.util.UUID; import me.leoko.advancedban.manager.PunishmentManager; -import me.leoko.advancedban.manager.UUIDManager; import me.leoko.advancedban.utils.Punishment; import me.leoko.advancedban.utils.PunishmentType; +import java.util.Collection; +import java.util.UUID; + /** * PluginData for AdvancedBan plugin. * @@ -45,59 +42,52 @@ public class AdvancedBanData extends PluginData { return inspectContainer; } - Punishment ban = PunishmentManager.get().getBan(abUuid); - Punishment mute = PunishmentManager.get().getMute(abUuid); - long warnings = PunishmentManager.get().getWarns(abUuid).stream().filter(warning -> !warning.isExpired()).count(); + PunishmentManager punishmentManager = PunishmentManager.get(); + Punishment ban = punishmentManager.getBan(abUuid); + Punishment mute = punishmentManager.getMute(abUuid); + long warnings = punishmentManager.getWarns(abUuid).stream().filter(warning -> !warning.isExpired()).count(); inspectContainer.addValue(getWithIcon("Banned", Icons.BANNED), ban != null ? "Yes" : "No"); - inspectContainer.addValue(getWithIcon("Muted", Icon.called("bell-slash").of(Color.DEEP_ORANGE)), mute != null ? "Yes" : "No"); + if (ban != null) { + addPunishment(inspectContainer, ban, "Permanent ban"); + } + + inspectContainer.addValue(getWithIcon("Muted", Icon.called("bell-slash").of(Color.DEEP_ORANGE)), mute != null ? "Yes" : "No"); + if (mute != null) { + addPunishment(inspectContainer, mute, "Permanent mute"); + } + inspectContainer.addValue(getWithIcon("Warnings", Icon.called("flag").of(Color.YELLOW)), warnings); - if (ban != null) { - String operator = ban.getOperator(); - String link = Html.LINK.parse(PlanAPI.getInstance().getPlayerInspectPageLink(operator), operator); - String reason = HtmlUtils.swapColorsToSpan(ban.getReason()); - long start = ban.getStart(); - String end = FormatUtils.formatTimeStampYear(ban.getEnd()); - - if (ban.getType() == PunishmentType.BAN || ban.getType() == PunishmentType.IP_BAN) { // Permanent - end = "Permanent ban"; - } - - if (operator.equals("CONSOLE")) { - link = "CONSOLE"; - } - - inspectContainer.addValue(" " + getWithIcon("Operator", Icon.called("user").of(Color.RED)), link); - inspectContainer.addValue(" " + getWithIcon("Date", Icon.called("calendar").of(Color.RED).of(Family.REGULAR)), FormatUtils.formatTimeStampYear(start)); - inspectContainer.addValue(" " + getWithIcon("Ends", Icon.called("calendar-check").of(Color.RED).of(Family.REGULAR)), end); - inspectContainer.addValue(" " + getWithIcon("Reason", Icon.called("comment").of(Color.RED).of(Family.REGULAR)), reason); - } - - if (mute != null) { - String operator = mute.getOperator(); - String link = Html.LINK.parse(PlanAPI.getInstance().getPlayerInspectPageLink(operator), operator); - String reason = HtmlUtils.swapColorsToSpan(mute.getReason()); - long start = mute.getStart(); - String end = FormatUtils.formatTimeStampYear(mute.getEnd()); - - if (mute.getType() == PunishmentType.MUTE) { // Permanent - end = "Permanent mute"; - } - - if (operator.equals("CONSOLE")) { - link = "CONSOLE"; - } - - inspectContainer.addValue(" " + getWithIcon("Operator", Icon.called("user").of(Color.DEEP_ORANGE)), link); - inspectContainer.addValue(" " + getWithIcon("Date", Icon.called("calendar").of(Color.DEEP_ORANGE).of(Family.REGULAR)), FormatUtils.formatTimeStampYear(start)); - inspectContainer.addValue(" " + getWithIcon("Ends", Icon.called("calendar-check").of(Color.DEEP_ORANGE).of(Family.REGULAR)), end); - inspectContainer.addValue(" " + getWithIcon("Reason", Icon.called("comment").of(Color.DEEP_ORANGE).of(Family.REGULAR)), reason); - } - return inspectContainer; } + private void addPunishment(InspectContainer inspectContainer, Punishment punishment, String identifier) { + String operator = punishment.getOperator(); + String link = Html.LINK.parse(PlanAPI.getInstance().getPlayerInspectPageLink(operator), operator); + String reason = HtmlUtils.swapColorsToSpan(punishment.getReason()); + String start = FormatUtils.formatTimeStampYear(punishment.getStart()); + String end = FormatUtils.formatTimeStampYear(punishment.getEnd()); + + PunishmentType type = punishment.getType(); + // Permanent + if (type == PunishmentType.BAN + || type == PunishmentType.IP_BAN + || type == PunishmentType.MUTE + ) { + end = identifier; + } + + if (operator.equals("CONSOLE")) { + link = "CONSOLE"; + } + + inspectContainer.addValue(" " + getWithIcon("Operator", Icon.called("user").of(Color.RED)), link); + inspectContainer.addValue(" " + getWithIcon("Date", Icon.called("calendar").of(Color.RED).of(Family.REGULAR)), start); + inspectContainer.addValue(" " + getWithIcon("Ends", Icon.called("calendar-check").of(Color.RED).of(Family.REGULAR)), end); + inspectContainer.addValue(" " + getWithIcon("Reason", Icon.called("comment").of(Color.RED).of(Family.REGULAR)), reason); + } + @Override public AnalysisContainer getServerData(Collection uuids, AnalysisContainer analysisContainer) { return analysisContainer; diff --git a/PlanPluginBridge/src/main/java/com/djrapitops/pluginbridge/plan/askyblock/ASkyBlockData.java b/PlanPluginBridge/src/main/java/com/djrapitops/pluginbridge/plan/askyblock/ASkyBlockData.java index 08516f12d..fc3640f07 100644 --- a/PlanPluginBridge/src/main/java/com/djrapitops/pluginbridge/plan/askyblock/ASkyBlockData.java +++ b/PlanPluginBridge/src/main/java/com/djrapitops/pluginbridge/plan/askyblock/ASkyBlockData.java @@ -6,14 +6,18 @@ package com.djrapitops.pluginbridge.plan.askyblock; import com.djrapitops.plan.data.element.AnalysisContainer; import com.djrapitops.plan.data.element.InspectContainer; +import com.djrapitops.plan.data.element.TableContainer; import com.djrapitops.plan.data.plugin.ContainerSize; import com.djrapitops.plan.data.plugin.PluginData; +import com.djrapitops.plan.utilities.html.graphs.ProgressBar; import com.djrapitops.plan.utilities.html.icon.Color; import com.djrapitops.plan.utilities.html.icon.Family; import com.djrapitops.plan.utilities.html.icon.Icon; +import com.djrapitops.plugin.utilities.Format; import com.wasteofplastic.askyblock.ASkyBlockAPI; import java.util.Collection; +import java.util.Map; import java.util.UUID; /** @@ -35,7 +39,7 @@ public class ASkyBlockData extends PluginData { public InspectContainer getPlayerData(UUID uuid, InspectContainer inspectContainer) { if (api.hasIsland(uuid)) { String islandName = api.getIslandName(uuid); - int level = api.getIslandLevel(uuid); + long level = api.getLongIslandLevel(uuid); int resetsLeft = api.getResetsLeft(uuid); inspectContainer.addValue(getWithIcon("Island Name", Icon.called("street-view").of(Color.GREEN)), islandName); @@ -45,9 +49,39 @@ public class ASkyBlockData extends PluginData { inspectContainer.addValue(getWithIcon("Island Name", Icon.called("street-view").of(Color.GREEN)), "No Island"); } + Map challengeCompletion = api.getChallengeTimes(uuid); + int obtained = (int) challengeCompletion.values().stream().filter(value -> value != 0).count(); + int max = challengeCompletion.size(); + + inspectContainer.addValue(getWithIcon("Challenge Progress", Icon.called("bookmark").of(Color.LIGHT_BLUE)), obtained + " / " + max); + ProgressBar challengeProgress = new ProgressBar(obtained, max, "light-blue"); + inspectContainer.addHtml("challenge-progress", challengeProgress.toHtml()); + + addTable(inspectContainer, challengeCompletion); + return inspectContainer; } + private void addTable(InspectContainer inspectContainer, Map challengeCompletion) { + TableContainer challenges = new TableContainer( + getWithIcon("Challenge", Icon.called("bookmark")), + getWithIcon("Times completed", Icon.called("check")) + ); + challenges.setColor("light-blue"); + challengeCompletion.entrySet().stream() + .sorted((one, two) -> Integer.compare(two.getValue(), one.getValue())) + .forEach(entry -> { + String challenge = new Format(entry.getKey()).capitalize().toString(); + Integer completionTimes = entry.getValue(); + boolean complete = completionTimes > 0; + challenges.addRow( + "" + challenge + "", + completionTimes + ); + }); + inspectContainer.addTable("challenge-table", challenges); + } + @Override public AnalysisContainer getServerData(Collection uuids, AnalysisContainer analysisContainer) { int islandCount = api.getIslandCount(); diff --git a/PlanPluginBridge/src/main/java/com/djrapitops/pluginbridge/plan/discordsrv/DiscordSRVData.java b/PlanPluginBridge/src/main/java/com/djrapitops/pluginbridge/plan/discordsrv/DiscordSRVData.java new file mode 100644 index 000000000..0223833ca --- /dev/null +++ b/PlanPluginBridge/src/main/java/com/djrapitops/pluginbridge/plan/discordsrv/DiscordSRVData.java @@ -0,0 +1,124 @@ +/* + * 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 com.djrapitops.pluginbridge.plan.discordsrv; + +import com.djrapitops.plan.data.element.AnalysisContainer; +import com.djrapitops.plan.data.element.InspectContainer; +import com.djrapitops.plan.data.plugin.ContainerSize; +import com.djrapitops.plan.data.plugin.PluginData; +import com.djrapitops.plan.utilities.FormatUtils; +import com.djrapitops.plan.utilities.html.icon.Color; +import com.djrapitops.plan.utilities.html.icon.Family; +import com.djrapitops.plan.utilities.html.icon.Icon; +import github.scarsz.discordsrv.DiscordSRV; +import github.scarsz.discordsrv.dependencies.jda.core.entities.Member; +import github.scarsz.discordsrv.dependencies.jda.core.entities.Role; +import github.scarsz.discordsrv.dependencies.jda.core.entities.User; +import github.scarsz.discordsrv.util.DiscordUtil; +import java.util.Collection; +import java.util.List; +import java.util.UUID; +import java.util.stream.Collectors; +import org.apache.commons.text.TextStringBuilder; + +/** + * PluginData for DiscordSRV plugin. + * + * @author Vankka + */ +public class DiscordSRVData extends PluginData { + public DiscordSRVData() { + super(ContainerSize.THIRD, "DiscordSRV"); + setPluginIcon(Icon.called("discord").of(Family.BRAND).build()); + } + + @Override + public InspectContainer getPlayerData(UUID uuid, InspectContainer inspectContainer) { + if (!DiscordSRV.isReady) { + return inspectContainer; + } + + String userId = DiscordSRV.getPlugin().getAccountLinkManager().getDiscordId(uuid); + User user = userId != null ? DiscordUtil.getUserById(userId) : null; + + if (user == null) { + return inspectContainer; + } + + inspectContainer.addValue( + getWithIcon("Username", Icon.called("user").of(Family.SOLID).of(Color.CYAN)), + "@" + user.getName() + "#" + user.getDiscriminator() + ); + inspectContainer.addValue( + getWithIcon("Account creation date", Icon.called("plus").of(Family.SOLID).of(Color.BLUE)), + FormatUtils.formatTimeStampYear(user.getCreationTime().toEpochSecond() * 1000L) + ); + + Member member = DiscordSRV.getPlugin().getMainGuild().getMember(user); + + if (member != null) { + addMemberData(member, inspectContainer); + } + + return inspectContainer; + } + + private void addMemberData(Member member, InspectContainer inspectContainer) { + String nickname = member.getNickname(); + + inspectContainer.addValue( + getWithIcon("Nickname", Icon.called("user-ninja").of(Family.SOLID).of(Color.ORANGE)), + nickname != null ? nickname : "None" + ); + inspectContainer.addValue( + getWithIcon("Join Date", Icon.called("plus").of(Family.SOLID).of(Color.GREEN)), + FormatUtils.formatTimeStampYear(member.getJoinDate().toEpochSecond() * 1000L) + ); + + List roles = member.getRoles().stream().map(Role::getName).collect(Collectors.toList()); // Ordered list of role names + if (!roles.isEmpty()) { + inspectContainer.addValue( + getWithIcon("Roles", Icon.called("user-circle").of(Family.SOLID).of(Color.RED)), + new TextStringBuilder().appendWithSeparators(roles, ", ").build() + ); + } + } + + @Override + public AnalysisContainer getServerData(Collection uuids, AnalysisContainer analysisContainer) { + if (!DiscordSRV.isReady) { + return analysisContainer; + } + + int accountsLinked = DiscordSRV.getPlugin().getAccountLinkManager().getLinkedAccounts().size(); + int guildUsers = DiscordSRV.getPlugin().getMainGuild().getMembers().size(); + + analysisContainer.addValue( + getWithIcon("Accounts linked", Icon.called("link").of(Family.SOLID).of(Color.CYAN)), + accountsLinked + ); + analysisContainer.addValue( + getWithIcon("Users in main guild", Icon.called("users").of(Family.SOLID).of(Color.GREEN)), + guildUsers + ); + analysisContainer.addValue( + getWithIcon("Accounts linked / Total players", Icon.called("percentage").of(Family.SOLID).of(Color.TEAL)), + calculatePercentage(accountsLinked, uuids.size()) + "%" + ); + analysisContainer.addValue( + getWithIcon("Accounts linked / Users in main guild", Icon.called("percentage").of(Family.SOLID).of(Color.LIGHT_GREEN)), + calculatePercentage(accountsLinked, guildUsers) + "%" + ); + + return analysisContainer; + } + + private double calculatePercentage(int input1, int input2) { + if (input1 == 0 || input2 == 0) + return 0D; + + return Math.round((double) input1 / input2 * 10000D) / 100D; // 2 decimals + } +} diff --git a/PlanPluginBridge/src/main/java/com/djrapitops/pluginbridge/plan/discordsrv/DiscordSRVHook.java b/PlanPluginBridge/src/main/java/com/djrapitops/pluginbridge/plan/discordsrv/DiscordSRVHook.java new file mode 100644 index 000000000..0c951b7c6 --- /dev/null +++ b/PlanPluginBridge/src/main/java/com/djrapitops/pluginbridge/plan/discordsrv/DiscordSRVHook.java @@ -0,0 +1,26 @@ +/* + * 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 com.djrapitops.pluginbridge.plan.discordsrv; + +import com.djrapitops.plan.data.plugin.HookHandler; +import com.djrapitops.pluginbridge.plan.Hook; + +/** + * Hook for DiscordSRV plugin. + * + * @author Vankka + */ +public class DiscordSRVHook extends Hook { + public DiscordSRVHook(HookHandler hookHandler) { + super("github.scarsz.discordsrv.DiscordSRV", hookHandler); + } + + @Override + public void hook() throws NoClassDefFoundError { + if (enabled) { + addPluginDataSource(new DiscordSRVData()); + } + } +} diff --git a/PlanPluginBridge/src/main/java/com/djrapitops/pluginbridge/plan/factions/FactionsData.java b/PlanPluginBridge/src/main/java/com/djrapitops/pluginbridge/plan/factions/FactionsData.java index b6b63c650..b3e017a88 100644 --- a/PlanPluginBridge/src/main/java/com/djrapitops/pluginbridge/plan/factions/FactionsData.java +++ b/PlanPluginBridge/src/main/java/com/djrapitops/pluginbridge/plan/factions/FactionsData.java @@ -72,7 +72,8 @@ public class FactionsData extends PluginData { if (!factions.isEmpty()) { FactionsAccordion factionsAccordion = new FactionsAccordion( factions, - analysisData.getValue(AnalysisKeys.PLAYERS_MUTATOR).orElse(new PlayersMutator(new ArrayList<>())) + Optional.ofNullable(analysisData).flatMap(c -> c.getValue(AnalysisKeys.PLAYERS_MUTATOR)) + .orElse(new PlayersMutator(new ArrayList<>())) ); analysisContainer.addHtml("factionAccordion", factionsAccordion.toHtml()); diff --git a/PlanPluginBridge/src/main/java/com/djrapitops/pluginbridge/plan/kingdoms/KingdomsData.java b/PlanPluginBridge/src/main/java/com/djrapitops/pluginbridge/plan/kingdoms/KingdomsData.java index 53e97c965..7e842e0cf 100644 --- a/PlanPluginBridge/src/main/java/com/djrapitops/pluginbridge/plan/kingdoms/KingdomsData.java +++ b/PlanPluginBridge/src/main/java/com/djrapitops/pluginbridge/plan/kingdoms/KingdomsData.java @@ -1,4 +1,4 @@ -/* +/* * 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 */ @@ -62,7 +62,8 @@ public class KingdomsData extends PluginData { if (!kingdoms.isEmpty()) { KingdomsAccordion kingdomsAccordion = new KingdomsAccordion( kingdoms, - analysisData.getValue(AnalysisKeys.PLAYERS_MUTATOR).orElse(new PlayersMutator(new ArrayList<>())) + Optional.ofNullable(analysisData).flatMap(c -> c.getValue(AnalysisKeys.PLAYERS_MUTATOR)) + .orElse(new PlayersMutator(new ArrayList<>())) ); analysisContainer.addHtml("kingdomsAccordion", kingdomsAccordion.toHtml()); diff --git a/PlanPluginBridge/src/main/java/com/djrapitops/pluginbridge/plan/litebans/LiteBansHook.java b/PlanPluginBridge/src/main/java/com/djrapitops/pluginbridge/plan/litebans/LiteBansBukkitHook.java similarity index 83% rename from PlanPluginBridge/src/main/java/com/djrapitops/pluginbridge/plan/litebans/LiteBansHook.java rename to PlanPluginBridge/src/main/java/com/djrapitops/pluginbridge/plan/litebans/LiteBansBukkitHook.java index 67dfc0544..516d3241c 100644 --- a/PlanPluginBridge/src/main/java/com/djrapitops/pluginbridge/plan/litebans/LiteBansHook.java +++ b/PlanPluginBridge/src/main/java/com/djrapitops/pluginbridge/plan/litebans/LiteBansBukkitHook.java @@ -5,6 +5,7 @@ import com.djrapitops.plan.system.settings.Settings; import com.djrapitops.plugin.api.utility.log.Log; import com.djrapitops.pluginbridge.plan.Hook; import litebans.api.Database; +import org.bukkit.Bukkit; /** * A Class responsible for hooking to LiteBans and registering data @@ -13,7 +14,7 @@ import litebans.api.Database; * @author Rsl1122 * @since 3.5.0 */ -public class LiteBansHook extends Hook { +public class LiteBansBukkitHook extends Hook { /** * Hooks the plugin and registers it's PluginData objects. @@ -24,7 +25,7 @@ public class LiteBansHook extends Hook { * @throws NoClassDefFoundError when the plugin class can not be found. */ @SuppressWarnings("ResultOfMethodCallIgnored") - public LiteBansHook(HookHandler hookH) { + public LiteBansBukkitHook(HookHandler hookH) { super(hookH); try { Database.get(); @@ -39,7 +40,8 @@ public class LiteBansHook extends Hook { public void hook() throws NoClassDefFoundError { if (enabled) { - LiteBansDatabaseQueries db = new LiteBansDatabaseQueries(); + String tablePrefix = Bukkit.getPluginManager().getPlugin("LiteBans").getConfig().getString("sql.table_prefix"); + LiteBansDatabaseQueries db = new LiteBansDatabaseQueries(tablePrefix); addPluginDataSource(new LiteBansData(db)); } } diff --git a/PlanPluginBridge/src/main/java/com/djrapitops/pluginbridge/plan/litebans/LiteBansBungeeHook.java b/PlanPluginBridge/src/main/java/com/djrapitops/pluginbridge/plan/litebans/LiteBansBungeeHook.java new file mode 100644 index 000000000..3cfe62196 --- /dev/null +++ b/PlanPluginBridge/src/main/java/com/djrapitops/pluginbridge/plan/litebans/LiteBansBungeeHook.java @@ -0,0 +1,69 @@ +package com.djrapitops.pluginbridge.plan.litebans; + +import com.djrapitops.plan.data.plugin.HookHandler; +import com.djrapitops.plan.system.settings.Settings; +import com.djrapitops.plugin.api.utility.log.Log; +import com.djrapitops.pluginbridge.plan.Hook; +import litebans.api.Database; +import net.md_5.bungee.api.ProxyServer; +import net.md_5.bungee.config.Configuration; +import net.md_5.bungee.config.ConfigurationProvider; +import net.md_5.bungee.config.YamlConfiguration; + +import java.io.File; +import java.io.IOException; +import java.util.logging.Level; +import java.util.logging.Logger; + +/** + * A Class responsible for hooking to LiteBans and registering data + * sources. + * + * @author Rsl1122 + * @since 3.5.0 + */ +public class LiteBansBungeeHook extends Hook { + + /** + * Hooks the plugin and registers it's PluginData objects. + *

    + * API#addPluginDataSource uses the same method from HookHandler. + * + * @param hookH HookHandler instance for registering the data sources. + * @throws NoClassDefFoundError when the plugin class can not be found. + */ + @SuppressWarnings("ResultOfMethodCallIgnored") + public LiteBansBungeeHook(HookHandler hookH) { + super(hookH); + try { + Database.get(); + enabled = true; + } catch (NoClassDefFoundError | NoSuchFieldError | NoSuchMethodError | Exception e) { + if (Settings.DEV_MODE.isTrue()) { + Log.toLog(this.getClass(), e); + } + enabled = false; + } + } + + public void hook() throws NoClassDefFoundError { + if (enabled) { + LiteBansDatabaseQueries db = new LiteBansDatabaseQueries(getTablePrefix()); + addPluginDataSource(new LiteBansData(db)); + } + } + + private String getTablePrefix() { + String tablePrefix = "libeans_"; + try { + File litebansDataFolder = ProxyServer.getInstance().getPluginManager().getPlugin("LiteBans").getDataFolder(); + File configFile = new File(litebansDataFolder, "config.yml"); + + Configuration configuration = ConfigurationProvider.getProvider(YamlConfiguration.class).load(configFile); + tablePrefix = configuration.getString("sql.table_prefix"); + } catch (NullPointerException | IOException e) { + Logger.getLogger("Plan").log(Level.WARNING, "Could not get Litebans table prefix, using default (litebans_). " + e.toString()); + } + return tablePrefix; + } +} diff --git a/PlanPluginBridge/src/main/java/com/djrapitops/pluginbridge/plan/litebans/LiteBansData.java b/PlanPluginBridge/src/main/java/com/djrapitops/pluginbridge/plan/litebans/LiteBansData.java index 48cde675c..92dfadfa9 100644 --- a/PlanPluginBridge/src/main/java/com/djrapitops/pluginbridge/plan/litebans/LiteBansData.java +++ b/PlanPluginBridge/src/main/java/com/djrapitops/pluginbridge/plan/litebans/LiteBansData.java @@ -44,7 +44,7 @@ public class LiteBansData extends PluginData implements BanData { public InspectContainer getPlayerData(UUID uuid, InspectContainer inspectContainer) { inspectContainer.addValue(Icon.called("balance-scale").of(Color.RED) + - "Hover over 'What' column entry for offence reasons", ""); + " Hover over 'What' column entry for offence reasons", ""); String what = getWithIcon("Effect", Icon.called("times-circle").of(Family.REGULAR)); String by = getWithIcon("By", Icon.called("gavel")); @@ -179,7 +179,8 @@ public class LiteBansData extends PluginData implements BanData { if (objects.isEmpty()) { table.addRow("No Data"); } else { - Map playerNames = analysisData.getValue(AnalysisKeys.PLAYER_NAMES).orElse(new HashMap<>()); + Map playerNames = Optional.ofNullable(analysisData) + .flatMap(c -> c.getValue(AnalysisKeys.PLAYER_NAMES)).orElse(new HashMap<>()); for (LiteBansDBObj object : objects) { UUID uuid = object.getUuid(); String name = playerNames.getOrDefault(uuid, uuid.toString()); diff --git a/PlanPluginBridge/src/main/java/com/djrapitops/pluginbridge/plan/litebans/LiteBansDatabaseQueries.java b/PlanPluginBridge/src/main/java/com/djrapitops/pluginbridge/plan/litebans/LiteBansDatabaseQueries.java index 3c9f90332..f11cfb0b2 100644 --- a/PlanPluginBridge/src/main/java/com/djrapitops/pluginbridge/plan/litebans/LiteBansDatabaseQueries.java +++ b/PlanPluginBridge/src/main/java/com/djrapitops/pluginbridge/plan/litebans/LiteBansDatabaseQueries.java @@ -5,7 +5,6 @@ import com.djrapitops.plan.system.database.databases.sql.processing.QueryAllStat import com.djrapitops.plan.system.database.databases.sql.processing.QueryStatement; import com.djrapitops.plan.system.database.databases.sql.tables.Table; import litebans.api.Database; -import org.bukkit.Bukkit; import java.sql.PreparedStatement; import java.sql.ResultSet; @@ -30,10 +29,9 @@ public class LiteBansDatabaseQueries extends Table { private final String selectSQL; - public LiteBansDatabaseQueries() { + public LiteBansDatabaseQueries(String tablePrefix) { super("litebans", null); database = Database.get(); - String tablePrefix = Bukkit.getPluginManager().getPlugin("LiteBans").getConfig().getString("sql.table_prefix"); banTable = tablePrefix + "bans"; mutesTable = tablePrefix + "mutes"; warningsTable = tablePrefix + "warnings"; diff --git a/PlanPluginBridge/src/main/java/com/djrapitops/pluginbridge/plan/luckperms/LuckPermsData.java b/PlanPluginBridge/src/main/java/com/djrapitops/pluginbridge/plan/luckperms/LuckPermsData.java new file mode 100644 index 000000000..9c10055ec --- /dev/null +++ b/PlanPluginBridge/src/main/java/com/djrapitops/pluginbridge/plan/luckperms/LuckPermsData.java @@ -0,0 +1,125 @@ +/* + * 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 com.djrapitops.pluginbridge.plan.luckperms; + +import com.djrapitops.plan.data.element.AnalysisContainer; +import com.djrapitops.plan.data.element.InspectContainer; +import com.djrapitops.plan.data.element.TableContainer; +import com.djrapitops.plan.data.plugin.ContainerSize; +import com.djrapitops.plan.data.plugin.PluginData; +import com.djrapitops.plan.utilities.html.icon.Color; +import com.djrapitops.plan.utilities.html.icon.Family; +import com.djrapitops.plan.utilities.html.icon.Icon; +import java.util.*; +import java.util.stream.Collectors; +import me.lucko.luckperms.api.*; +import me.lucko.luckperms.api.caching.MetaData; +import org.apache.commons.text.TextStringBuilder; + +/** + * PluginData for LuckPerms plugin. + * + * @author Vankka + */ +public class LuckPermsData extends PluginData { + private LuckPermsApi api; + + public LuckPermsData(LuckPermsApi api) { + super(ContainerSize.THIRD, "LuckPerms"); + setPluginIcon(Icon.called("exclamation-triangle").of(Color.LIGHT_GREEN).build()); + + this.api = api; + } + + @Override + public InspectContainer getPlayerData(UUID uuid, InspectContainer inspectContainer) { + User user = api.getUser(uuid); + + if (user == null) { + inspectContainer.addValue("Data unavailable", "Could not get user data"); + return inspectContainer; + } + + MetaData metaData = user.getCachedData().getMetaData(Contexts.allowAll()); + String prefix = metaData.getPrefix(); + String suffix = metaData.getSuffix(); + + inspectContainer.addValue(getWithIcon("Primary group", Icon.called("user-friends").of(Family.SOLID)), user.getPrimaryGroup()); + inspectContainer.addValue(getWithIcon("Prefix", Icon.called("file-signature").of(Family.SOLID).of(Color.GREEN)), prefix != null ? prefix : "None"); + inspectContainer.addValue(getWithIcon("Suffix", Icon.called("file-signature").of(Family.SOLID).of(Color.BLUE)),suffix != null ? suffix : "None"); + + if (!metaData.getMeta().isEmpty()) { + TableContainer metaTable = new TableContainer( + getWithIcon("Meta", Icon.called("info-circle").of(Family.SOLID)), + getWithIcon("Value", Icon.called("file-alt").of(Family.SOLID)) + ); + metaData.getMeta().forEach((key, value) -> metaTable.addRow(key, value)); + inspectContainer.addTable("Meta", metaTable); + } + + List groups = user.getPermissions().stream() + .filter(Node::isGroupNode) + .map(Node::getGroupName) + .sorted() + .collect(Collectors.toList()); + + inspectContainer.addValue( + getWithIcon("Groups", Icon.called("user-friends").of(Family.SOLID)), + new TextStringBuilder().appendWithSeparators(groups, ", ").build() + ); + + Set tracks = api.getTracks(); + if (!tracks.isEmpty()) { + TableContainer trackTable = new TableContainer( + getWithIcon("Track", Icon.called("ellipsis-h").of(Family.SOLID)), + getWithIcon("Group", Icon.called("user-friends").of(Family.SOLID)) + ); + for (Track track : tracks) { + // reduce is used to get the last element + String currentGroup = api.getGroups().stream() + .map(this::getGroupDisplayName).filter(groups::contains) + .reduce((first, second) -> second).orElse("None"); + trackTable.addRow(track.getName(), currentGroup); + } + inspectContainer.addTable("Tracks", trackTable); + } + + return inspectContainer; + } + + @Override + public AnalysisContainer getServerData(Collection uuids, AnalysisContainer analysisContainer) { + // There will *always* be atleast 1 group + TableContainer groupTable = new TableContainer( + getWithIcon("Group", Icon.called("user-friends").of(Family.SOLID)), + getWithIcon("Weight", Icon.called("weight-hanging").of(Family.SOLID)), + getWithIcon("Permissions", Icon.called("list").of(Family.SOLID)) + ); + + api.getGroups().stream().sorted(Comparator.comparing(Group::getName)).forEach(group -> { + OptionalInt weight = group.getWeight(); + + groupTable.addRow(getGroupDisplayName(group), weight.isPresent() ? weight.getAsInt() : "None", group.getPermissions().size()); + }); + analysisContainer.addTable("Groups", groupTable); + + Set tracks = api.getTracks(); + if (!tracks.isEmpty()) { + TableContainer trackTable = new TableContainer( + getWithIcon("Track", Icon.called("ellipsis-h").of(Family.SOLID)), + getWithIcon("Size", Icon.called("list").of(Family.SOLID)) + ); + tracks.forEach(track -> trackTable.addRow(track.getName(), track.getSize())); + analysisContainer.addTable("Tracks", trackTable); + } + + return analysisContainer; + } + + private String getGroupDisplayName(Group group) { + String displayName = group.getDisplayName(); + return displayName != null ? displayName : group.getName(); + } +} diff --git a/PlanPluginBridge/src/main/java/com/djrapitops/pluginbridge/plan/luckperms/LuckPermsHook.java b/PlanPluginBridge/src/main/java/com/djrapitops/pluginbridge/plan/luckperms/LuckPermsHook.java new file mode 100644 index 000000000..341ed1853 --- /dev/null +++ b/PlanPluginBridge/src/main/java/com/djrapitops/pluginbridge/plan/luckperms/LuckPermsHook.java @@ -0,0 +1,27 @@ +/* + * 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 com.djrapitops.pluginbridge.plan.luckperms; + +import com.djrapitops.plan.data.plugin.HookHandler; +import com.djrapitops.pluginbridge.plan.Hook; +import me.lucko.luckperms.LuckPerms; + +/** + * Hook for LuckPerms plugin. + * + * @author Vankka + */ +public class LuckPermsHook extends Hook { + public LuckPermsHook(HookHandler hookHandler) { + super("me.lucko.luckperms.LuckPerms", hookHandler); + } + + @Override + public void hook() throws IllegalStateException { + if (enabled) { + addPluginDataSource(new LuckPermsData(LuckPerms.getApi())); + } + } +} diff --git a/PlanPluginBridge/src/main/java/com/djrapitops/pluginbridge/plan/nucleus/NucleusData.java b/PlanPluginBridge/src/main/java/com/djrapitops/pluginbridge/plan/nucleus/NucleusData.java new file mode 100644 index 000000000..02cc603e5 --- /dev/null +++ b/PlanPluginBridge/src/main/java/com/djrapitops/pluginbridge/plan/nucleus/NucleusData.java @@ -0,0 +1,290 @@ +/* + * 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 com.djrapitops.pluginbridge.plan.nucleus; + +import com.djrapitops.plan.api.PlanAPI; +import com.djrapitops.plan.data.element.AnalysisContainer; +import com.djrapitops.plan.data.element.InspectContainer; +import com.djrapitops.plan.data.element.TableContainer; +import com.djrapitops.plan.data.plugin.ContainerSize; +import com.djrapitops.plan.data.plugin.PluginData; +import com.djrapitops.plan.utilities.FormatUtils; +import com.djrapitops.plan.utilities.html.Html; +import com.djrapitops.plan.utilities.html.HtmlUtils; +import com.djrapitops.plan.utilities.html.icon.Color; +import com.djrapitops.plan.utilities.html.icon.Family; +import com.djrapitops.plan.utilities.html.icon.Icon; +import io.github.nucleuspowered.nucleus.api.NucleusAPI; +import io.github.nucleuspowered.nucleus.api.nucleusdata.*; +import io.github.nucleuspowered.nucleus.api.service.*; +import java.time.Duration; +import java.time.Instant; +import java.util.*; +import org.spongepowered.api.Sponge; +import org.spongepowered.api.entity.living.player.User; +import org.spongepowered.api.service.user.UserStorageService; +import org.spongepowered.api.text.Text; +import org.spongepowered.api.text.serializer.TextSerializers; + +/** + * PluginData for Nucleus plugin. + * + * @author Vankka + */ +public class NucleusData extends PluginData { + private UserStorageService userStorageService = null; + + public NucleusData() { + super(ContainerSize.TWO_THIRDS, "Nucleus"); + setPluginIcon(Icon.called("flask").of(Color.DEEP_ORANGE).build()); + + Sponge.getServiceManager().provide(UserStorageService.class).ifPresent(storageService -> userStorageService = storageService); + } + + @Override + public InspectContainer getPlayerData(UUID uuid, InspectContainer inspectContainer) { + User user = getUser(uuid); + + if (user == null) { + inspectContainer.addValue("Data unavailable", "Could not get user data"); + return inspectContainer; + } + + NucleusAPI.getMuteService().ifPresent(muteService -> addMuteData(user, muteService, inspectContainer)); + NucleusAPI.getJailService().ifPresent(jailService -> addJailData(user, jailService, inspectContainer)); + NucleusAPI.getHomeService().ifPresent(homeService -> addHomeData(user, homeService, inspectContainer)); + NucleusAPI.getNoteService().ifPresent(noteService -> addNoteData(user, noteService, inspectContainer)); + NucleusAPI.getWarningService().ifPresent(warningService -> addWarningData(user, warningService, inspectContainer)); + NucleusAPI.getInvulnerabilityService().ifPresent(invulnerabilityService -> addInvulnerabilityData(user, invulnerabilityService, inspectContainer)); + NucleusAPI.getNicknameService().ifPresent(nicknameService -> addNicknameData(user, nicknameService, inspectContainer)); + + return inspectContainer; + } + + @Override + public AnalysisContainer getServerData(Collection uuids, AnalysisContainer analysisContainer) { + NucleusAPI.getWarpService().ifPresent(warpService -> addWarpData(warpService, analysisContainer)); + NucleusAPI.getJailService().ifPresent(jailService -> addJailData(jailService, analysisContainer)); + NucleusAPI.getKitService().ifPresent(kitService -> addKitData(kitService, analysisContainer)); + + return analysisContainer; + } + + private User getUser(UUID uuid) { + if (Sponge.getServer().getPlayer(uuid).isPresent()) { + return Sponge.getServer().getPlayer(uuid).get(); + } else if (userStorageService != null) { + Optional optionalUser = userStorageService.get(uuid); + return optionalUser.orElse(null); + } else { + return null; + } + } + + private String formatTimeStampYear(Instant instant) { + return FormatUtils.formatTimeStampYear(instant.toEpochMilli()); + } + + private String formatTimeStampYear(Duration duration) { + return FormatUtils.formatTimeStampYear(duration.plusMillis(System.currentTimeMillis()).toMillis()); + } + + /* + * Player Data + */ + private void addMuteData(User user, NucleusMuteService muteService, InspectContainer inspectContainer) { + boolean muted = muteService.isMuted(user); + inspectContainer.addValue(getWithIcon("Muted", Icon.called("bell-slash").of(Color.DEEP_ORANGE)), muted ? "Yes" : "No"); + + Optional optionalMuteInfo = muteService.getPlayerMuteInfo(user); + if (muted && optionalMuteInfo.isPresent()) { + MuteInfo muteInfo = optionalMuteInfo.get(); + + String reason = HtmlUtils.swapColorsToSpan(muteInfo.getReason()); + String start = muteInfo.getCreationInstant().map(this::formatTimeStampYear).orElse("Unknown"); + String end = muteInfo.getRemainingTime().map(this::formatTimeStampYear).orElse("Permanent mute"); + String link = "Unknown"; + + User operatorUser = muteInfo.getMuter().map(this::getUser).orElse(null); + if (operatorUser != null) { + String operator = operatorUser.getName(); + link = Html.LINK.parse(PlanAPI.getInstance().getPlayerInspectPageLink(operator), operator); + } + + inspectContainer.addValue(" " + getWithIcon("Operator", Icon.called("user").of(Color.DEEP_ORANGE)), link); + inspectContainer.addValue(" " + getWithIcon("Date", Icon.called("calendar").of(Color.DEEP_ORANGE).of(Family.REGULAR)), start); + inspectContainer.addValue(" " + getWithIcon("Ends", Icon.called("calendar-check").of(Color.DEEP_ORANGE).of(Family.REGULAR)), end); + inspectContainer.addValue(" " + getWithIcon("Reason", Icon.called("comment").of(Color.DEEP_ORANGE).of(Family.REGULAR)), reason); + } + } + + private void addJailData(User user, NucleusJailService jailService, InspectContainer inspectContainer) { + boolean jailed = jailService.isPlayerJailed(user); + inspectContainer.addValue(getWithIcon("Jailed", Icon.called("bars").of(Color.YELLOW).of(Family.SOLID)), jailed ? "Yes" : "No"); + + if (jailed && jailService.getPlayerJailData(user).isPresent()) { + Inmate inmate = jailService.getPlayerJailData(user).get(); + + String reason = inmate.getReason(); + String start = inmate.getCreationInstant().map(this::formatTimeStampYear).orElse("Unknown"); + String end = inmate.getRemainingTime().map(this::formatTimeStampYear).orElse("Permanent jail sentence"); + String link = "Unknown"; + + User operatorUser = inmate.getJailer().map(this::getUser).orElse(null); + if (operatorUser != null) { + String operator = operatorUser.getName(); + link = Html.LINK.parse(PlanAPI.getInstance().getPlayerInspectPageLink(operator), operator); + } + + inspectContainer.addValue(" " + getWithIcon("Operator", Icon.called("user").of(Color.YELLOW)), link); + inspectContainer.addValue(" " + getWithIcon("Date", Icon.called("calendar").of(Color.YELLOW).of(Family.REGULAR)), start); + inspectContainer.addValue(" " + getWithIcon("Ends", Icon.called("calendar-check").of(Color.YELLOW).of(Family.REGULAR)), end); + inspectContainer.addValue(" " + getWithIcon("Reason", Icon.called("comment").of(Color.YELLOW).of(Family.REGULAR)), reason); + inspectContainer.addValue(" " + getWithIcon("Jail", Icon.called("bars").of(Color.YELLOW).of(Family.SOLID)), inmate.getJailName()); + } + } + + private void addHomeData(User user, NucleusHomeService homeService, InspectContainer inspectContainer) { + int homeCount = homeService.getHomeCount(user); + int maxHomes = homeService.getMaximumHomes(user); + + inspectContainer.addValue(" " + getWithIcon("Homes", Icon.called("home").of(Color.GREEN).of(Family.SOLID)), homeCount + "/" + maxHomes); + + List homes = homeService.getHomes(user); + + if (!homes.isEmpty()) { + TableContainer homesTable = new TableContainer(getWithIcon("Home", Icon.called("home").of(Family.SOLID))); + homesTable.setColor("light-green"); + + for (Home home : homes) { + homesTable.addRow(home.getName()); + } + + inspectContainer.addTable("Homes", homesTable); + } + } + + private void addNoteData(User user, NucleusNoteService noteService, InspectContainer inspectContainer) { + List notes = noteService.getNotes(user); + + if (!notes.isEmpty()) { + TableContainer notesTable = new TableContainer( + getWithIcon("Noter", Icon.called("pen").of(Family.SOLID)), + getWithIcon("Note", Icon.called("sticky-note").of(Family.REGULAR)) + ); + + notesTable.setColor("light-blue"); + + for (Note note : notes) { + String noter = "Unknown"; + + User noterUser = note.getNoter().map(this::getUser).orElse(null); + if (noterUser != null) { + noter = noterUser.getName(); + } + + notesTable.addRow(noter, note.getNote()); + } + + inspectContainer.addTable("Notes", notesTable); + } + } + + private void addWarningData(User user, NucleusWarningService warningService, InspectContainer inspectContainer) { + List warnings = warningService.getWarnings(user); + inspectContainer.addValue(getWithIcon("Warning count", Icon.called("flag").of(Color.AMBER)), warnings.size()); + + if (!warnings.isEmpty()) { + TableContainer warningsTable = new TableContainer( + getWithIcon("Warner", Icon.called("exclamation").of(Family.SOLID)), + getWithIcon("Reason", Icon.called("sticky-note").of(Family.SOLID)) + ); + + warningsTable.setColor("amber"); + + for (Warning warning : warnings) { + String warner = "Unknown"; + + User warnerUser = warning.getWarner().map(this::getUser).orElse(null); + if (warnerUser != null) { + warner = warnerUser.getName(); + } + + warningsTable.addRow(warner, warning.getReason()); + } + + inspectContainer.addTable("Warnings", warningsTable); + } + } + + private void addInvulnerabilityData(User user, NucleusInvulnerabilityService invulnerabilityService, InspectContainer inspectContainer) { + boolean invulnerable = invulnerabilityService.isInvulnerable(user); + inspectContainer.addValue(getWithIcon("Invulnerable", Icon.called("crosshairs").of(Color.BLUE).of(Family.SOLID)), invulnerable ? "Yes" : "No"); + } + + private void addNicknameData(User user, NucleusNicknameService nicknameService, InspectContainer inspectContainer) { + Optional nickname = nicknameService.getNickname(user); + + if (nickname.isPresent()) { + String nicknameString = HtmlUtils.swapColorsToSpan(TextSerializers.FORMATTING_CODE.serialize(nickname.get())); + inspectContainer.addValue(" " + getWithIcon("Nickname", Icon.called("id-badge").of(Color.GREEN).of(Family.REGULAR)), nicknameString); + } + } + + /* + * Server Data + */ + private void addWarpData(NucleusWarpService warpService, AnalysisContainer analysisContainer) { + List warps = warpService.getAllWarps(); + analysisContainer.addValue(getWithIcon("Warp count", Icon.called("map-marker-alt").of(Color.BLUE)), warps.size()); + + if (!warps.isEmpty()) { + TableContainer warpsTable = new TableContainer( + getWithIcon("Name", Icon.called("map-marker-alt").of(Family.SOLID)), + getWithIcon("Description", Icon.called("sticky-note").of(Family.REGULAR)), + getWithIcon("Category", Icon.called("list").of(Family.SOLID)) + ); + + for (Warp warp : warps) { + String description = warp.getDescription().map(desc -> HtmlUtils.swapColorsToSpan(TextSerializers.FORMATTING_CODE.serialize(desc))).orElse("None"); + String category = warp.getCategory().orElse("None"); + + warpsTable.addRow(warp.getName(), description, category); + } + + analysisContainer.addTable("Warps", warpsTable); + } + } + + private void addJailData(NucleusJailService jailService, AnalysisContainer analysisContainer) { + Map jails = jailService.getJails(); + analysisContainer.addValue(getWithIcon("Jail count", Icon.called("bars").of(Family.SOLID).of(Color.TEAL)), jails.size()); + + if (!jails.isEmpty()) { + TableContainer jailsTable = new TableContainer(getWithIcon("Jail", Icon.called("bars").of(Family.SOLID))); + + for (String jail : jails.keySet()) { + jailsTable.addRow(jail); + } + + analysisContainer.addTable("Jails", jailsTable); + } + } + + private void addKitData(NucleusKitService kitService, AnalysisContainer analysisContainer) { + Set kits = kitService.getKitNames(); + analysisContainer.addValue(getWithIcon("Kit count", Icon.called("box").of(Family.SOLID)), kits.size()); + + if (!kits.isEmpty()) { + TableContainer kitsTable = new TableContainer(getWithIcon("Kit", Icon.called("box").of(Family.SOLID))); + + for (String kit : kits) { + kitsTable.addRow(kit); + } + + analysisContainer.addTable("Kits", kitsTable); + } + } +} diff --git a/PlanPluginBridge/src/main/java/com/djrapitops/pluginbridge/plan/nucleus/NucleusHook.java b/PlanPluginBridge/src/main/java/com/djrapitops/pluginbridge/plan/nucleus/NucleusHook.java new file mode 100644 index 000000000..d8a9afc36 --- /dev/null +++ b/PlanPluginBridge/src/main/java/com/djrapitops/pluginbridge/plan/nucleus/NucleusHook.java @@ -0,0 +1,26 @@ +/* + * 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 com.djrapitops.pluginbridge.plan.nucleus; + +import com.djrapitops.plan.data.plugin.HookHandler; +import com.djrapitops.pluginbridge.plan.Hook; + +/** + * Hook for AdvancedBan plugin. + * + * @author Vankka + */ +public class NucleusHook extends Hook { + public NucleusHook(HookHandler hookHandler) { + super("io.github.nucleuspowered.nucleus.NucleusPlugin", hookHandler); + } + + @Override + public void hook() throws NoClassDefFoundError { + if (enabled) { + addPluginDataSource(new NucleusData()); + } + } +} diff --git a/PlanPluginBridge/src/main/java/com/djrapitops/pluginbridge/plan/react/ReactDataTask.java b/PlanPluginBridge/src/main/java/com/djrapitops/pluginbridge/plan/react/ReactDataTask.java index 7510e0c88..2a44728a2 100644 --- a/PlanPluginBridge/src/main/java/com/djrapitops/pluginbridge/plan/react/ReactDataTask.java +++ b/PlanPluginBridge/src/main/java/com/djrapitops/pluginbridge/plan/react/ReactDataTask.java @@ -7,8 +7,8 @@ import com.djrapitops.plugin.task.AbsRunnable; import com.volmit.react.React; import com.volmit.react.api.GraphSampleLine; import com.volmit.react.api.SampledType; -import com.volmit.react.util.GMap; import com.volmit.react.util.M; +import com.volmit.react.volume.lang.collections.GMap; import java.util.ArrayList; import java.util.EnumMap; diff --git a/PlanPluginBridge/src/main/java/com/djrapitops/pluginbridge/plan/sponge/SpongeEconomyData.java b/PlanPluginBridge/src/main/java/com/djrapitops/pluginbridge/plan/sponge/SpongeEconomyData.java new file mode 100644 index 000000000..efe27b3a8 --- /dev/null +++ b/PlanPluginBridge/src/main/java/com/djrapitops/pluginbridge/plan/sponge/SpongeEconomyData.java @@ -0,0 +1,97 @@ +package com.djrapitops.pluginbridge.plan.sponge; + +import com.djrapitops.plan.data.element.AnalysisContainer; +import com.djrapitops.plan.data.element.InspectContainer; +import com.djrapitops.plan.data.plugin.ContainerSize; +import com.djrapitops.plan.data.plugin.PluginData; +import com.djrapitops.plan.data.store.keys.AnalysisKeys; +import com.djrapitops.plan.data.store.keys.PlayerKeys; +import com.djrapitops.plan.data.store.mutators.PlayersMutator; +import com.djrapitops.plan.system.cache.DataCache; +import com.djrapitops.plan.utilities.html.icon.Color; +import com.djrapitops.plan.utilities.html.icon.Icon; +import org.spongepowered.api.service.economy.Currency; +import org.spongepowered.api.service.economy.EconomyService; +import org.spongepowered.api.service.economy.account.UniqueAccount; +import java.math.BigDecimal; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.UUID; +import java.util.stream.Collectors; + +/** + * PluginData for Sponge. + * + * @author BrainStone + */ +public class SpongeEconomyData extends PluginData { + private static final Color color = Color.AMBER; + private static final String nameMoneyIcon = "money-bill-wave"; + private static final Icon moneyIcon = Icon.called(nameMoneyIcon).build(); + private static final Icon moneyIconColored = Icon.called(nameMoneyIcon).of(color).build(); + + private final EconomyService economyService; + + public SpongeEconomyData(EconomyService economyService) { + super(ContainerSize.THIRD, "Sponge Economy"); + + this.economyService = economyService; + + setPluginIcon(moneyIconColored); + } + + @Override + public InspectContainer getPlayerData(UUID uuid, InspectContainer inspectContainer) { + String name = DataCache.getInstance().getName(uuid); + + if (name == null) { + return inspectContainer; + } + + Optional uOpt = economyService.getOrCreateAccount(uuid); + + if (!uOpt.isPresent()) { + return inspectContainer; + } + + UniqueAccount acc = uOpt.get(); + + for(Currency currency : economyService.getCurrencies()) { + BigDecimal balance = acc.getBalance(currency); + inspectContainer.addValue(getWithIcon(currency.getName(), moneyIconColored), currency.format(balance).toPlain()); + } + + return inspectContainer; + } + + @Override + public AnalysisContainer getServerData(Collection uuids, AnalysisContainer analysisContainer) { + List players = uuids.stream().map(economyService::getOrCreateAccount) + .filter(Optional::isPresent).map(Optional::get).collect(Collectors.toList()); + + for(Currency currency : economyService.getCurrencies()) { + addCurrencyToContainer(currency, players, analysisContainer); + } + + return analysisContainer; + } + + private void addCurrencyToContainer(Currency currency, List players, AnalysisContainer analysisContainer) { + BigDecimal totalBalance = BigDecimal.ZERO; + Map playerBalances = new HashMap<>(); + + for (UniqueAccount player : players) { + BigDecimal balance = player.getBalance(currency); + + totalBalance = totalBalance.add(balance); + playerBalances.put(player.getUniqueId(), currency.format(balance).toPlain()); + } + + analysisContainer.addValue(getWithIcon("Total Server Balance " + currency.getName(), moneyIconColored), currency.format(totalBalance).toPlain()); + analysisContainer.addPlayerTableValues(getWithIcon("Balance " + currency.getName(), moneyIcon), playerBalances); + } +} diff --git a/PlanPluginBridge/src/main/java/com/djrapitops/pluginbridge/plan/sponge/SpongeEconomyHook.java b/PlanPluginBridge/src/main/java/com/djrapitops/pluginbridge/plan/sponge/SpongeEconomyHook.java new file mode 100644 index 000000000..c65c198c8 --- /dev/null +++ b/PlanPluginBridge/src/main/java/com/djrapitops/pluginbridge/plan/sponge/SpongeEconomyHook.java @@ -0,0 +1,33 @@ +package com.djrapitops.pluginbridge.plan.sponge; + +import com.djrapitops.plan.data.plugin.HookHandler; +import com.djrapitops.pluginbridge.plan.Hook; +import org.spongepowered.api.Sponge; +import org.spongepowered.api.service.economy.EconomyService; +import java.util.Optional; + +/** + * A Class responsible for hooking to Sponge and registering 1 data sources + * + * @author BrainStone + * @since 4.4.6 + */ +public class SpongeEconomyHook extends Hook { + public SpongeEconomyHook(HookHandler hookHandler) { + super("org.spongepowered.api.Sponge", hookHandler); + + try { + Optional serviceOpt = Sponge.getServiceManager().provide(EconomyService.class); + enabled = serviceOpt.isPresent(); + } catch(NoClassDefFoundError e) { + enabled = false; + } + } + + @Override + public void hook() { + if (enabled) { + addPluginDataSource(new SpongeEconomyData(Sponge.getServiceManager().provide(EconomyService.class).get())); + } + } +} diff --git a/PlanPluginBridge/src/main/java/com/djrapitops/pluginbridge/plan/towny/TownyData.java b/PlanPluginBridge/src/main/java/com/djrapitops/pluginbridge/plan/towny/TownyData.java index bacdd88ec..83c902822 100644 --- a/PlanPluginBridge/src/main/java/com/djrapitops/pluginbridge/plan/towny/TownyData.java +++ b/PlanPluginBridge/src/main/java/com/djrapitops/pluginbridge/plan/towny/TownyData.java @@ -97,7 +97,8 @@ public class TownyData extends PluginData { TownsAccordion townsAccordion = new TownsAccordion( towns, - analysisData.getValue(AnalysisKeys.PLAYERS_MUTATOR).orElse(new PlayersMutator(new ArrayList<>())) + Optional.ofNullable(analysisData).flatMap(c -> c.getValue(AnalysisKeys.PLAYERS_MUTATOR)) + .orElse(new PlayersMutator(new ArrayList<>())) ); analysisContainer.addHtml("townAccordion", townsAccordion.toHtml()); diff --git a/PlanPluginBridge/src/main/java/com/djrapitops/pluginbridge/plan/vault/VaultEcoData.java b/PlanPluginBridge/src/main/java/com/djrapitops/pluginbridge/plan/vault/VaultEcoData.java index 175672de1..92c5047fc 100644 --- a/PlanPluginBridge/src/main/java/com/djrapitops/pluginbridge/plan/vault/VaultEcoData.java +++ b/PlanPluginBridge/src/main/java/com/djrapitops/pluginbridge/plan/vault/VaultEcoData.java @@ -9,7 +9,6 @@ import com.djrapitops.plan.data.element.InspectContainer; import com.djrapitops.plan.data.plugin.ContainerSize; import com.djrapitops.plan.data.plugin.PluginData; import com.djrapitops.plan.data.store.keys.AnalysisKeys; -import com.djrapitops.plan.data.store.mutators.PlayersMutator; import com.djrapitops.plan.system.cache.DataCache; import com.djrapitops.plan.utilities.FormatUtils; import com.djrapitops.plan.utilities.html.icon.Color; @@ -50,10 +49,11 @@ public class VaultEcoData extends PluginData { @Override public AnalysisContainer getServerData(Collection collection, AnalysisContainer analysisContainer) { - List offlinePlayers = analysisData.getValue(AnalysisKeys.PLAYERS_MUTATOR) - .map(PlayersMutator::all).orElse(new ArrayList<>()) - .stream().map(FakeOfflinePlayer::new) - .collect(Collectors.toList()); + List offlinePlayers = Optional.ofNullable(analysisData) + .flatMap(c -> c.getValue(AnalysisKeys.PLAYERS_MUTATOR)) + .map(mutator -> mutator.all().stream().map(FakeOfflinePlayer::new) + .collect(Collectors.toList())) + .orElse(new ArrayList<>()); Map balances = new HashMap<>(); double totalBalance = 0.0; diff --git a/PlanPluginBridge/src/main/java/com/djrapitops/pluginbridge/plan/vault/VaultPermData.java b/PlanPluginBridge/src/main/java/com/djrapitops/pluginbridge/plan/vault/VaultPermData.java index 1e9d83dce..e2701c6b2 100644 --- a/PlanPluginBridge/src/main/java/com/djrapitops/pluginbridge/plan/vault/VaultPermData.java +++ b/PlanPluginBridge/src/main/java/com/djrapitops/pluginbridge/plan/vault/VaultPermData.java @@ -9,7 +9,6 @@ import com.djrapitops.plan.data.element.InspectContainer; import com.djrapitops.plan.data.plugin.ContainerSize; import com.djrapitops.plan.data.plugin.PluginData; import com.djrapitops.plan.data.store.keys.AnalysisKeys; -import com.djrapitops.plan.data.store.mutators.PlayersMutator; import com.djrapitops.plan.system.cache.DataCache; import com.djrapitops.pluginbridge.plan.FakeOfflinePlayer; import net.milkbowl.vault.permission.Permission; @@ -49,10 +48,11 @@ public class VaultPermData extends PluginData { @Override public AnalysisContainer getServerData(Collection collection, AnalysisContainer analysisContainer) { - List offlinePlayers = analysisData.getValue(AnalysisKeys.PLAYERS_MUTATOR) - .map(PlayersMutator::all).orElse(new ArrayList<>()) - .stream().map(FakeOfflinePlayer::new) - .collect(Collectors.toList()); + List offlinePlayers = Optional.ofNullable(analysisData) + .flatMap(c -> c.getValue(AnalysisKeys.PLAYERS_MUTATOR)) + .map(mutator -> mutator.all().stream().map(FakeOfflinePlayer::new) + .collect(Collectors.toList())) + .orElse(new ArrayList<>()); Map groups = new HashMap<>(); for (FakeOfflinePlayer p : offlinePlayers) { diff --git a/PlanPluginBridge/src/main/java/com/djrapitops/pluginbridge/plan/viaversion/PlayerVersionListener.java b/PlanPluginBridge/src/main/java/com/djrapitops/pluginbridge/plan/viaversion/BukkitPlayerVersionListener.java similarity index 92% rename from PlanPluginBridge/src/main/java/com/djrapitops/pluginbridge/plan/viaversion/PlayerVersionListener.java rename to PlanPluginBridge/src/main/java/com/djrapitops/pluginbridge/plan/viaversion/BukkitPlayerVersionListener.java index da82d60f8..ca41fbd7e 100644 --- a/PlanPluginBridge/src/main/java/com/djrapitops/pluginbridge/plan/viaversion/PlayerVersionListener.java +++ b/PlanPluginBridge/src/main/java/com/djrapitops/pluginbridge/plan/viaversion/BukkitPlayerVersionListener.java @@ -24,11 +24,11 @@ import java.util.UUID; * @author Rsl1122 * @since 3.5.0 */ -public class PlayerVersionListener implements Listener { +public class BukkitPlayerVersionListener implements Listener { private ViaAPI viaAPI; - public PlayerVersionListener(ViaAPI viaAPI) { + public BukkitPlayerVersionListener(ViaAPI viaAPI) { this.viaAPI = viaAPI; } diff --git a/PlanPluginBridge/src/main/java/com/djrapitops/pluginbridge/plan/viaversion/BungeePlayerVersionListener.java b/PlanPluginBridge/src/main/java/com/djrapitops/pluginbridge/plan/viaversion/BungeePlayerVersionListener.java new file mode 100644 index 000000000..382fedc1d --- /dev/null +++ b/PlanPluginBridge/src/main/java/com/djrapitops/pluginbridge/plan/viaversion/BungeePlayerVersionListener.java @@ -0,0 +1,46 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package com.djrapitops.pluginbridge.plan.viaversion; + +import com.djrapitops.plan.api.exceptions.database.DBOpException; +import com.djrapitops.plan.system.database.databases.Database; +import com.djrapitops.plan.system.database.databases.sql.SQLDB; +import com.djrapitops.plan.system.processing.Processing; +import com.djrapitops.plugin.api.utility.log.Log; +import net.md_5.bungee.api.event.PostLoginEvent; +import net.md_5.bungee.api.plugin.Listener; +import net.md_5.bungee.event.EventHandler; +import us.myles.ViaVersion.api.ViaAPI; + +import java.util.UUID; + +/** + * Class responsible for listening join events for Version protocol. + * + * @author Rsl1122 + * @since 3.5.0 + */ +public class BungeePlayerVersionListener implements Listener { + + private ViaAPI viaAPI; + + public BungeePlayerVersionListener(ViaAPI viaAPI) { + this.viaAPI = viaAPI; + } + + @EventHandler + public void onJoin(PostLoginEvent event) { + UUID uuid = event.getPlayer().getUniqueId(); + int playerVersion = viaAPI.getPlayerVersion(uuid); + Processing.submitNonCritical(() -> { + try { + new ProtocolTable((SQLDB) Database.getActive()).saveProtocolVersion(uuid, playerVersion); + } catch (DBOpException e) { + Log.toLog(this.getClass(), e); + } + }); + } +} diff --git a/PlanPluginBridge/src/main/java/com/djrapitops/pluginbridge/plan/viaversion/Protocol.java b/PlanPluginBridge/src/main/java/com/djrapitops/pluginbridge/plan/viaversion/Protocol.java index b99764fb7..a98417036 100644 --- a/PlanPluginBridge/src/main/java/com/djrapitops/pluginbridge/plan/viaversion/Protocol.java +++ b/PlanPluginBridge/src/main/java/com/djrapitops/pluginbridge/plan/viaversion/Protocol.java @@ -25,6 +25,8 @@ public class Protocol { */ public static String getMCVersion(int protocolVersion) { switch (protocolVersion) { + case 401: + return "1.13.1"; case 390: case 391: case 392: diff --git a/PlanPluginBridge/src/main/java/com/djrapitops/pluginbridge/plan/viaversion/ViaVersionHook.java b/PlanPluginBridge/src/main/java/com/djrapitops/pluginbridge/plan/viaversion/ViaVersionBukkitHook.java similarity index 89% rename from PlanPluginBridge/src/main/java/com/djrapitops/pluginbridge/plan/viaversion/ViaVersionHook.java rename to PlanPluginBridge/src/main/java/com/djrapitops/pluginbridge/plan/viaversion/ViaVersionBukkitHook.java index bcaaa9f12..4db2dc27e 100644 --- a/PlanPluginBridge/src/main/java/com/djrapitops/pluginbridge/plan/viaversion/ViaVersionHook.java +++ b/PlanPluginBridge/src/main/java/com/djrapitops/pluginbridge/plan/viaversion/ViaVersionBukkitHook.java @@ -16,7 +16,7 @@ import us.myles.ViaVersion.api.ViaAPI; * @author Rsl1122 * @since 3.1.0 */ -public class ViaVersionHook extends Hook { +public class ViaVersionBukkitHook extends Hook { /** * Hooks the plugin and registers it's PluginData objects. @@ -25,7 +25,7 @@ public class ViaVersionHook extends Hook { * * @param hookH HookHandler instance for registering the data sources. */ - public ViaVersionHook(HookHandler hookH) { + public ViaVersionBukkitHook(HookHandler hookH) { super("us.myles.ViaVersion.ViaVersionPlugin", hookH); } @@ -42,7 +42,7 @@ public class ViaVersionHook extends Hook { Log.toLog(this.getClass().getName(), e); return; } - plan.registerListener(new PlayerVersionListener(api)); + plan.registerListener(new BukkitPlayerVersionListener(api)); addPluginDataSource(new ViaVersionData(table)); } } \ No newline at end of file diff --git a/PlanPluginBridge/src/main/java/com/djrapitops/pluginbridge/plan/viaversion/ViaVersionBungeeHook.java b/PlanPluginBridge/src/main/java/com/djrapitops/pluginbridge/plan/viaversion/ViaVersionBungeeHook.java new file mode 100644 index 000000000..41addff02 --- /dev/null +++ b/PlanPluginBridge/src/main/java/com/djrapitops/pluginbridge/plan/viaversion/ViaVersionBungeeHook.java @@ -0,0 +1,48 @@ +package com.djrapitops.pluginbridge.plan.viaversion; + +import com.djrapitops.plan.PlanBungee; +import com.djrapitops.plan.api.exceptions.database.DBException; +import com.djrapitops.plan.data.plugin.HookHandler; +import com.djrapitops.plan.system.database.databases.Database; +import com.djrapitops.plan.system.database.databases.sql.SQLDB; +import com.djrapitops.plugin.api.utility.log.Log; +import com.djrapitops.pluginbridge.plan.Hook; +import us.myles.ViaVersion.api.Via; +import us.myles.ViaVersion.api.ViaAPI; + +/** + * A Class responsible for hooking to ViaVersion and registering data sources. + * + * @author Rsl1122 + * @since 3.1.0 + */ +public class ViaVersionBungeeHook extends Hook { + + /** + * Hooks the plugin and registers it's PluginData objects. + *

    + * API#addPluginDataSource uses the same method from HookHandler. + * + * @param hookH HookHandler instance for registering the data sources. + */ + public ViaVersionBungeeHook(HookHandler hookH) { + super("us.myles.ViaVersion.BungeePlugin", hookH); + } + + public void hook() throws NoClassDefFoundError { + if (!enabled) { + return; + } + PlanBungee plan = PlanBungee.getInstance(); + ViaAPI api = Via.getAPI(); + ProtocolTable table = new ProtocolTable((SQLDB) Database.getActive()); + try { + table.createTable(); + } catch (DBException e) { + Log.toLog(this.getClass().getName(), e); + return; + } + plan.registerListener(new BungeePlayerVersionListener(api)); + addPluginDataSource(new ViaVersionData(table)); + } +} \ No newline at end of file diff --git a/docs/main/java/com/djrapitops/plan/Permissions.html b/docs/main/java/com/djrapitops/plan/Permissions.html index 2ed30f1c4..549a9c7fc 100644 --- a/docs/main/java/com/djrapitops/plan/Permissions.html +++ b/docs/main/java/com/djrapitops/plan/Permissions.html @@ -430,7 +430,7 @@ not permitted.)

    Returns the permission node in plugin.yml.
    Returns:
    -
    permission node eg. plan.inspect
    +
    permission node eg. plan.inspect.base
    @@ -444,7 +444,7 @@ not permitted.)
    Returns:
    -
    permission node eg. plan.inspect
    +
    permission node eg. plan.inspect.base
    diff --git a/versions.txt b/versions.txt index 26ed37777..8890ce64b 100644 --- a/versions.txt +++ b/versions.txt @@ -1,3 +1,9 @@ +REL|4.4.7|https://github.com/Rsl1122/Plan-PlayerAnalytics/releases/download/4.4.7-REL/Plan-4.4.7-bukkit-bungee.jar|https://github.com/Rsl1122/Plan-PlayerAnalytics/releases/tag/4.4.7-REL +REL|4.4.6|https://github.com/Rsl1122/Plan-PlayerAnalytics/releases/download/4.4.6-REL/Plan-4.4.6-bukkit-bungee.jar|https://github.com/Rsl1122/Plan-PlayerAnalytics/releases/tag/4.4.6-REL +DEV|4.4.5-b2|https://github.com/Rsl1122/Plan-PlayerAnalytics/releases/download/4.4.6-DEV2/Plan-4.4.5-b2-bukkit-bungee.jar|https://github.com/Rsl1122/Plan-PlayerAnalytics/releases/tag/4.4.6-DEV2 +REL|4.4.5|https://github.com/Rsl1122/Plan-PlayerAnalytics/releases/download/4.4.5-RELEASE/Plan-4.4.5.Bukkit.Bungee.jar|https://github.com/Rsl1122/Plan-PlayerAnalytics/releases/tag/4.4.5-RELEASE +REL|4.4.4|https://github.com/Rsl1122/Plan-PlayerAnalytics/releases/download/4.4.4-REL/Plan-4.4.4.jar|https://www.spigotmc.org/resources/plan-player-analytics.32536/update?update=240584 +DEV|4.4.3-b3|https://github.com/Rsl1122/Plan-PlayerAnalytics/releases/download/4.4.4-DEV3/Plan-4.4.3-b3.jar|https://github.com/Rsl1122/Plan-PlayerAnalytics/releases/tag/4.4.4-DEV3 REL|4.4.3|https://github.com/Rsl1122/Plan-PlayerAnalytics/releases/download/4.4.3/Plan-4.4.3.jar|https://www.spigotmc.org/resources/plan-player-analytics.32536/update?update=238545 REL|4.4.2|https://github.com/Rsl1122/Plan-PlayerAnalytics/releases/download/4.4.2/Plan-4.4.2.jar|https://www.spigotmc.org/resources/plan-player-analytics.32536/update?update=237951 REL|4.4.1|https://github.com/Rsl1122/Plan-PlayerAnalytics/releases/download/4.4.1/Plan-4.4.1.jar|https://www.spigotmc.org/resources/plan-player-analytics.32536/update?update=234420