diff --git a/Plan/dependency-reduced-pom.xml b/Plan/dependency-reduced-pom.xml index 7b3d46293..4a214d000 100644 --- a/Plan/dependency-reduced-pom.xml +++ b/Plan/dependency-reduced-pom.xml @@ -306,10 +306,6 @@ guice com.google.inject - - caffeine - com.github.ben-manes.caffeine - guava com.github.ben-manes.caffeine diff --git a/Plan/pom.xml b/Plan/pom.xml index 9f16f2e25..316381126 100644 --- a/Plan/pom.xml +++ b/Plan/pom.xml @@ -122,6 +122,13 @@ 2.9.0 + + + com.github.ben-manes.caffeine + caffeine + 2.6.2 + + org.bstats @@ -139,6 +146,13 @@ 1.2 + + + com.googlecode.htmlcompressor + htmlcompressor + 1.5.2 + + org.mockito diff --git a/Plan/src/main/java/com/djrapitops/plan/PlanSponge.java b/Plan/src/main/java/com/djrapitops/plan/PlanSponge.java index 543b60759..aa5dc60fd 100644 --- a/Plan/src/main/java/com/djrapitops/plan/PlanSponge.java +++ b/Plan/src/main/java/com/djrapitops/plan/PlanSponge.java @@ -25,7 +25,7 @@ import org.spongepowered.api.plugin.Plugin; import java.io.File; import java.io.InputStream; -@Plugin(id = "plan", name = "Plan", version = "4.4.5", description = "Player Analytics Plugin by Rsl1122", authors = {"Rsl1122"}) +@Plugin(id = "plan", name = "Plan", version = "4.4.6", description = "Player Analytics Plugin by Rsl1122", authors = {"Rsl1122"}) public class PlanSponge extends SpongePlugin implements PlanPlugin { @Inject 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 d11aeb46c..d871e35c0 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 @@ -62,9 +62,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(); @@ -101,7 +101,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") ); @@ -126,7 +126,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("?")) ) @@ -169,22 +169,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)) ); @@ -212,7 +212,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) ).count() @@ -224,13 +224,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) @@ -260,8 +260,8 @@ public class AnalysisContainer extends DataContainer { private void addSessionSuppliers() { Key sessionAccordion = new Key<>(SessionAccordion.class, "SESSION_ACCORDION"); - putSupplier(serverNames, () -> Database.getActive().fetch().getServerNames()); - putSupplier(sessionAccordion, () -> SessionAccordion.forServer( + putCachingSupplier(serverNames, () -> Database.getActive().fetch().getServerNames()); + putCachingSupplier(sessionAccordion, () -> SessionAccordion.forServer( getUnsafe(AnalysisKeys.SESSIONS_MUTATOR).all(), getSupplier(serverNames), () -> getUnsafe(AnalysisKeys.PLAYER_NAMES) @@ -301,13 +301,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)) ); @@ -320,7 +320,7 @@ public class AnalysisContainer extends DataContainer { private void addGraphSuppliers() { Key worldPie = new Key<>(WorldPie.class, "WORLD_PIE"); - putSupplier(worldPie, () -> new WorldPie(serverContainer.getValue(ServerKeys.WORLD_TIMES).orElse(new WorldTimes(new HashMap<>())))); + putCachingSupplier(worldPie, () -> new WorldPie(serverContainer.getValue(ServerKeys.WORLD_TIMES).orElse(new WorldTimes(new HashMap<>())))); putSupplier(AnalysisKeys.WORLD_PIE_SERIES, () -> getUnsafe(worldPie).toHighChartsSeries()); putSupplier(AnalysisKeys.GM_PIE_SERIES, () -> getUnsafe(worldPie).toHighChartsDrilldown()); putSupplier(AnalysisKeys.PLAYERS_ONLINE_SERIES, () -> @@ -335,12 +335,12 @@ public class AnalysisContainer extends DataContainer { new WorldMap(getUnsafe(AnalysisKeys.PLAYERS_MUTATOR).getGeolocations()).toHighChartsSeries() ); Key geolocationBarChart = new Key<>(GeolocationBarGraph.class, "GEOLOCATION_BAR_CHART"); - putSupplier(geolocationBarChart, () -> new GeolocationBarGraph(getUnsafe(AnalysisKeys.PLAYERS_MUTATOR))); + putCachingSupplier(geolocationBarChart, () -> new 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, () -> new PingGraph( + putCachingSupplier(pingGraph, () -> new PingGraph( PingMutator.forContainer(serverContainer).mutateToByMinutePings().all() )); putSupplier(AnalysisKeys.AVG_PING_SERIES, () -> getUnsafe(pingGraph).toAvgSeries()); @@ -353,9 +353,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))); + putCachingSupplier(AnalysisKeys.ACTIVITY_DATA, () -> getUnsafe(AnalysisKeys.PLAYERS_MUTATOR).toActivityDataMap(getUnsafe(AnalysisKeys.ANALYSIS_TIME))); Key activityStackGraph = new Key<>(ActivityStackGraph.class, "ACTIVITY_STACK_GRAPH"); - putSupplier(activityStackGraph, () -> new ActivityStackGraph(getUnsafe(AnalysisKeys.ACTIVITY_DATA))); + putCachingSupplier(activityStackGraph, () -> new 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, () -> @@ -376,17 +376,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))); putSupplier(AnalysisKeys.TPS_SPIKE_MONTH, () -> getUnsafe(tpsMonth).lowTpsSpikeCount()); putSupplier(AnalysisKeys.AVG_TPS_MONTH, () -> getUnsafe(tpsMonth).averageTPS()); @@ -416,7 +416,7 @@ public class AnalysisContainer extends DataContainer { private void addServerHealth() { Key healthInformation = new Key<>(HealthInformation.class, "HEALTH_INFORMATION"); - putSupplier(healthInformation, () -> new HealthInformation(this)); + putCachingSupplier(healthInformation, () -> new HealthInformation(this)); putSupplier(AnalysisKeys.HEALTH_INDEX, () -> getUnsafe(healthInformation).getServerHealth()); putSupplier(AnalysisKeys.HEALTH_NOTES, () -> getUnsafe(healthInformation).toHtml()); } @@ -424,13 +424,13 @@ 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, () -> + putCachingSupplier(navAndTabs, () -> AnalysisPluginsTabContentCreator.createContent( getUnsafe(AnalysisKeys.PLAYERS_MUTATOR), this ) ); - putSupplier(AnalysisKeys.BAN_DATA, () -> new ServerBanDataReader().readBanDataForContainer(this)); + putCachingSupplier(AnalysisKeys.BAN_DATA, () -> new ServerBanDataReader().readBanDataForContainer(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 96d7ca388..b47d3ee03 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 dab7aae04..5bc3e758d 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 @@ -46,7 +46,7 @@ public class NetworkContainer extends DataContainer { this.bungeeContainer = bungeeContainer; serverContainers = new HashMap<>(); - putSupplier(NetworkKeys.PLAYERS_MUTATOR, () -> PlayersMutator.forContainer(bungeeContainer)); + putCachingSupplier(NetworkKeys.PLAYERS_MUTATOR, () -> PlayersMutator.forContainer(bungeeContainer)); addConstants(); addPlayerInformation(); @@ -55,9 +55,9 @@ public class NetworkContainer extends DataContainer { private void addNetworkHealth() { Key healthInformation = new Key<>(NetworkHealthInformation.class, "HEALTH_INFORMATION"); - putSupplier(healthInformation, () -> new NetworkHealthInformation(this)); - putSupplier(NetworkKeys.HEALTH_INDEX, () -> getUnsafe(healthInformation).getServerHealth()); - putSupplier(NetworkKeys.HEALTH_NOTES, () -> getUnsafe(healthInformation).toHtml()); + putCachingSupplier(healthInformation, () -> new NetworkHealthInformation(this)); + putCachingSupplier(NetworkKeys.HEALTH_INDEX, () -> getUnsafe(healthInformation).getServerHealth()); + putCachingSupplier(NetworkKeys.HEALTH_NOTES, () -> getUnsafe(healthInformation).toHtml()); } public void putAnalysisContainer(AnalysisContainer analysisContainer) { @@ -90,7 +90,7 @@ public class NetworkContainer extends DataContainer { putRawData(NetworkKeys.VERSION, PlanPlugin.getInstance().getVersion()); putSupplier(NetworkKeys.TIME_ZONE, MiscUtils::getTimeZoneOffsetHours); - putSupplier(NetworkKeys.NETWORK_NAME, () -> + putCachingSupplier(NetworkKeys.NETWORK_NAME, () -> Check.isBungeeAvailable() ? Settings.BUNGEE_NETWORK_NAME.toString() : bungeeContainer.getValue(ServerKeys.NAME).orElse("Plan") @@ -146,22 +146,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)) ); diff --git a/Plan/src/main/java/com/djrapitops/plan/data/store/mutators/formatting/TimeAmountFormatter.java b/Plan/src/main/java/com/djrapitops/plan/data/store/mutators/formatting/TimeAmountFormatter.java index e758a55cf..014309a7b 100644 --- a/Plan/src/main/java/com/djrapitops/plan/data/store/mutators/formatting/TimeAmountFormatter.java +++ b/Plan/src/main/java/com/djrapitops/plan/data/store/mutators/formatting/TimeAmountFormatter.java @@ -57,20 +57,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); } } @@ -89,13 +82,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/system/afk/AFKTracker.java b/Plan/src/main/java/com/djrapitops/plan/system/afk/AFKTracker.java index d9c7f85a6..0dd7a512e 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 @@ -65,4 +65,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/GeolocationCache.java b/Plan/src/main/java/com/djrapitops/plan/system/cache/GeolocationCache.java index 3e7a752dc..0dbe32cb0 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 @@ -16,6 +16,7 @@ import com.maxmind.geoip2.record.Country; 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; @@ -147,8 +148,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(getInstance().geolocationDB.getAbsoluteFile())) { + try ( + InputStream in = downloadSite.openStream(); + GZIPInputStream gzipIn = new GZIPInputStream(in); + ReadableByteChannel rbc = Channels.newChannel(gzipIn); + FileOutputStream fos = new FileOutputStream(getInstance().geolocationDB.getAbsoluteFile()) + ) { fos.getChannel().transferFrom(rbc, 0, Long.MAX_VALUE); } } 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 30e957451..7868a8873 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 @@ -14,9 +14,9 @@ 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 com.djrapitops.plugin.api.TimeAmount; import java.util.*; +import java.util.concurrent.TimeUnit; public class SQLFetchOps extends SQLOps implements FetchOperations { @@ -27,7 +27,7 @@ public class SQLFetchOps extends SQLOps implements FetchOperations { @Override public NetworkContainer getNetworkContainer() { NetworkContainer networkContainer = new NetworkContainer(getBungeeServerContainer()); - networkContainer.putSupplier(NetworkKeys.BUKKIT_SERVERS, () -> getBukkitServers().values()); + networkContainer.putCachingSupplier(NetworkKeys.BUKKIT_SERVERS, () -> getBukkitServers().values()); return networkContainer; } @@ -38,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); @@ -58,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(); @@ -71,8 +71,8 @@ public class SQLFetchOps extends SQLOps implements FetchOperations { } return null; }); - container.putSupplier(ServerKeys.RECENT_PEAK_PLAYERS, () -> { - long twoDaysAgo = System.currentTimeMillis() - (TimeAmount.DAY.ms() * 2L); + container.putCachingSupplier(ServerKeys.RECENT_PEAK_PLAYERS, () -> { + long twoDaysAgo = System.currentTimeMillis() - TimeUnit.DAYS.toMillis(2); Optional lastPeak = tpsTable.getPeakPlayerCount(serverUUID, twoDaysAgo); if (lastPeak.isPresent()) { TPS peak = lastPeak.get(); @@ -81,22 +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, () -> { + container.putCachingSupplier(ServerKeys.OPERATORS, () -> PlayersMutator.forContainer(container).operators()); + container.putCachingSupplier(ServerKeys.SESSIONS, () -> { List sessions = PlayersMutator.forContainer(container).getSessions(); if (serverUUID.equals(ServerInfo.getServerUUID())) { sessions.addAll(SessionCache.getActiveSessions().values()); } return sessions; }); - 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.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; } @@ -129,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; @@ -143,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( @@ -187,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; @@ -285,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/Patch.java b/Plan/src/main/java/com/djrapitops/plan/system/database/databases/sql/patches/Patch.java index e832d00f3..cb4fcbb28 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; @@ -29,13 +30,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 @@ -48,11 +52,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 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/TPSTable.java b/Plan/src/main/java/com/djrapitops/plan/system/database/databases/sql/tables/TPSTable.java index 0cc1773f1..661f6db8d 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 @@ -151,9 +151,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(); @@ -161,13 +164,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/listeners/bukkit/AFKListener.java b/Plan/src/main/java/com/djrapitops/plan/system/listeners/bukkit/AFKListener.java index 6f1246cc6..ac7d9ec0d 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 @@ -41,7 +41,10 @@ public class AFKListener implements Listener { UUID uuid = player.getUniqueId(); long time = System.currentTimeMillis(); - boolean ignored = ignorePermissionInfo.getOrDefault(uuid, 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); 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 c28f0f4a8..5daaf4d84 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 @@ -63,6 +63,10 @@ public class PlayerOnlineListener implements Listener { return; } UUID uuid = event.getPlayer().getUniqueId(); + if (AFKListener.AFK_TRACKER.isAfk(uuid)) { + return; + } + Processing.submit(new KickProcessor(uuid)); } catch (Exception e) { Log.toLog(this.getClass(), e); 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 d0f4c478c..c7f5897f5 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 @@ -2,6 +2,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.listeners.bukkit.AFKListener; import com.djrapitops.plan.system.processing.Processing; import com.djrapitops.plan.system.processing.processors.info.NetworkPageUpdateProcessor; import com.djrapitops.plan.system.processing.processors.info.PlayerPageUpdateProcessor; @@ -52,6 +53,9 @@ public class SpongePlayerListener { public void onKick(KickPlayerEvent event) { try { UUID uuid = event.getTargetEntity().getUniqueId(); + if (AFKListener.AFK_TRACKER.isAfk(uuid)) { + return; + } Processing.submit(new KickProcessor(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 3483cd1aa..baa310fc9 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 com.djrapitops.plan.system.settings.Settings; 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.function.Function; import java.util.stream.Collectors; /** @@ -71,10 +73,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 35a039693..ce9e9df9a 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 @@ -9,6 +9,7 @@ import com.djrapitops.plan.system.locale.lang.PluginLang; import com.djrapitops.plugin.StaticHolder; import com.djrapitops.plugin.api.utility.log.Log; import com.djrapitops.plugin.utilities.Verify; +import com.google.common.util.concurrent.ThreadFactoryBuilder; import java.util.List; import java.util.concurrent.*; @@ -23,8 +24,8 @@ public class Processing implements SubSystem { public Processing(Supplier locale) { this.locale = locale; - 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()); saveInstance(nonCriticalExecutor); saveInstance(criticalExecutor); saveInstance(this); diff --git a/Plan/src/main/java/com/djrapitops/plan/system/processing/importing/UserImportData.java b/Plan/src/main/java/com/djrapitops/plan/system/processing/importing/UserImportData.java index b4ce14a3d..37cf6d2f2 100644 --- a/Plan/src/main/java/com/djrapitops/plan/system/processing/importing/UserImportData.java +++ b/Plan/src/main/java/com/djrapitops/plan/system/processing/importing/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/processing/processors/info/PlayerPageUpdateProcessor.java b/Plan/src/main/java/com/djrapitops/plan/system/processing/processors/info/PlayerPageUpdateProcessor.java index e6cbe7cf7..8081c66bd 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,11 +1,7 @@ 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; @@ -19,20 +15,6 @@ public class PlayerPageUpdateProcessor implements Runnable { @Override public void run() { - if (!InfoSystem.getInstance().getConnectionSystem().isServerAvailable() || Check.isBungeeAvailable()) { - RunnableFactory.createNew("Generate Inspect page: " + uuid, new AbsRunnable() { - @Override - public void run() { - try { - - WebExceptionLogger.logIfOccurs(PlayerPageUpdateProcessor.class, - () -> InfoSystem.getInstance().generateAndCachePlayerPage(uuid) - ); - } finally { - cancel(); - } - } - }).runTaskLaterAsynchronously(TimeAmount.SECOND.ticks() * 5); - } + ResponseCache.clearResponse(PageId.PLAYER.of(uuid)); } } 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/tasks/BukkitTaskSystem.java b/Plan/src/main/java/com/djrapitops/plan/system/tasks/BukkitTaskSystem.java index b99dde0c8..1a769c767 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 @@ -8,7 +8,7 @@ import com.djrapitops.plan.Plan; import com.djrapitops.plan.system.settings.Settings; import com.djrapitops.plan.system.tasks.server.BukkitTPSCountTimer; import com.djrapitops.plan.system.tasks.server.PaperTPSCountTimer; -import com.djrapitops.plan.system.tasks.server.PingCountTimer; +import com.djrapitops.plan.system.tasks.server.PingCountTimerBukkit; import com.djrapitops.plugin.api.Check; import com.djrapitops.plugin.api.TimeAmount; import com.djrapitops.plugin.task.RunnableFactory; @@ -33,11 +33,11 @@ public class BukkitTaskSystem extends ServerTaskSystem { public void enable() { super.enable(); try { - PingCountTimer pingCountTimer = new PingCountTimer(); + PingCountTimerBukkit pingCountTimer = new PingCountTimerBukkit(); ((Plan) plugin).registerListener(pingCountTimer); long startDelay = TimeAmount.SECOND.ticks() * (long) Settings.PING_SERVER_ENABLE_DELAY.getNumber(); RunnableFactory.createNew("PingCountTimer", pingCountTimer) - .runTaskTimer(startDelay, PingCountTimer.PING_INTERVAL); + .runTaskTimer(startDelay, PingCountTimerBukkit.PING_INTERVAL); } 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 ad6506e46..a5cf91ab2 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 @@ -9,8 +9,10 @@ import com.djrapitops.plan.system.settings.Settings; import com.djrapitops.plan.system.tasks.bungee.BungeeTPSCountTimer; import com.djrapitops.plan.system.tasks.bungee.EnableConnectionTask; import com.djrapitops.plan.system.tasks.server.NetworkPageRefreshTask; +import com.djrapitops.plan.system.tasks.server.PingCountTimerBungee; 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 Bungee. @@ -38,5 +40,10 @@ public class BungeeTaskSystem extends TaskSystem { if (Settings.ANALYSIS_EXPORT.isTrue()) { registerTask(new HtmlExport(plugin)).runTaskAsynchronously(); } + 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 1bdd02b12..a8bf2bbf5 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 @@ -34,7 +34,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 7b0017108..229e1dc5f 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 @@ -1,20 +1,37 @@ package com.djrapitops.plan.system.tasks; import com.djrapitops.plan.PlanSponge; +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.api.TimeAmount; +import com.djrapitops.plugin.task.RunnableFactory; import org.spongepowered.api.Sponge; import org.spongepowered.api.scheduler.Task; public class SpongeTaskSystem extends ServerTaskSystem { + private final PlanSponge planSponge; + public SpongeTaskSystem(PlanSponge plugin) { super(plugin, new SpongeTPSCountTimer(plugin)); + this.planSponge = plugin; + } + + @Override + public void enable() { + super.enable(); + PingCountTimerSponge pingCountTimer = new PingCountTimerSponge(); + planSponge.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(); - for (Task task : Sponge.getScheduler().getScheduledTasks(plugin)) { + for (Task task : Sponge.getScheduler().getScheduledTasks(planSponge)) { task.cancel(); } } 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 90% 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 37c0b728b..2da73a083 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 @@ -53,23 +53,23 @@ import java.util.*; * * @author games647 */ -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"); @@ -80,12 +80,12 @@ public class PingCountTimer extends AbsRunnable implements Listener { localPing = lookup.findGetter(entityPlayer, "ping", Integer.TYPE); } catch (NoSuchMethodException | IllegalAccessException | NoSuchFieldException reflectiveEx) { - Log.toLog(PingCountTimer.class, reflectiveEx); + Log.toLog(PingCountTimerBukkit.class, reflectiveEx); } } - getHandleMethod = localHandle; - pingField = localPing; + GET_HANDLE_METHOD = localHandle; + PING_FIELD = localPing; } private final Map>> playerHistory = new HashMap<>(); @@ -133,7 +133,7 @@ public class PingCountTimer extends AbsRunnable implements Listener { } private int getPing(Player player) { - if (pingMethodAvailable) { + if (PING_METHOD_AVAILABLE) { return player.spigot().getPing(); } @@ -142,8 +142,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) { 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/webserver/WebServer.java b/Plan/src/main/java/com/djrapitops/plan/system/webserver/WebServer.java index c8d90b4e3..d2b4a4a87 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.StaticHolder; import com.djrapitops.plugin.api.Check; import com.djrapitops.plugin.api.utility.log.Log; import com.djrapitops.plugin.utilities.Verify; +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; @@ -27,9 +28,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.*; import java.util.function.Supplier; /** @@ -108,7 +107,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; @@ -199,12 +202,26 @@ public class WebServer implements SubSystem { @Override public void disable() { if (server != null) { + shutdown(); Log.info(locale.get().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 0456b76ba..349a209c4 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 @@ -92,8 +92,10 @@ public abstract class Response { ? getContent() : locale.replaceMatchingLanguage(getContent()); - 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 d8d2ecd9e..78aa8810a 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.settings.theme.Theme; -import com.djrapitops.plan.system.webserver.response.Response; +import com.djrapitops.plan.system.webserver.response.pages.PageResponse; import com.djrapitops.plan.utilities.MiscUtils; import com.djrapitops.plan.utilities.file.FileUtil; import com.djrapitops.plugin.api.utility.log.Log; @@ -20,7 +20,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 644f7697f..9d3655458 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 @@ -7,7 +7,6 @@ import com.djrapitops.plan.system.info.InfoSystem; import com.djrapitops.plan.system.processing.Processing; import com.djrapitops.plan.system.webserver.cache.PageId; import com.djrapitops.plan.system.webserver.cache.ResponseCache; -import com.djrapitops.plan.system.webserver.response.Response; import com.djrapitops.plan.system.webserver.response.errors.NotFoundResponse; import com.djrapitops.plan.utilities.html.pages.AnalysisPage; import com.djrapitops.plugin.api.utility.log.Log; @@ -18,7 +17,7 @@ import java.util.UUID; * @author Rsl1122 * @since 3.5.2 */ -public class AnalysisPageResponse extends Response { +public class AnalysisPageResponse extends PageResponse { public static AnalysisPageResponse refreshNow(UUID serverUUID) { Processing.submitNonCritical(() -> { 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 bf41399f1..d201f4657 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 @@ -3,20 +3,20 @@ package com.djrapitops.plan.system.webserver.response.pages; import com.djrapitops.plan.system.settings.theme.Theme; import com.djrapitops.plan.system.webserver.cache.PageId; import com.djrapitops.plan.system.webserver.cache.ResponseCache; -import com.djrapitops.plan.system.webserver.response.Response; import com.djrapitops.plan.system.webserver.response.errors.ErrorResponse; import com.djrapitops.plan.system.webserver.response.pages.parts.InspectPagePluginsContent; 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; @@ -49,4 +49,18 @@ public class InspectPageResponse extends Response { refreshPage.replacePlaceholders(); return new InspectPageResponse(null, refreshPage.getContent()); } + + @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 5dda443a6..e2316491c 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 @@ -3,7 +3,6 @@ package com.djrapitops.plan.system.webserver.response.pages; import com.djrapitops.plan.api.exceptions.ParseException; import com.djrapitops.plan.data.store.containers.NetworkContainer; import com.djrapitops.plan.system.database.databases.Database; -import com.djrapitops.plan.system.webserver.response.Response; import com.djrapitops.plan.utilities.html.pages.NetworkPage; /** @@ -11,7 +10,7 @@ import com.djrapitops.plan.utilities.html.pages.NetworkPage; * * @author Rsl1122 */ -public class NetworkPageResponse extends Response { +public class NetworkPageResponse extends PageResponse { public NetworkPageResponse() throws ParseException { super.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 2b805db9f..d076bb0dd 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,7 +1,6 @@ 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.system.webserver.response.errors.InternalErrorResponse; import com.djrapitops.plan.utilities.html.pages.PlayersPage; import com.djrapitops.plugin.api.utility.log.Log; @@ -10,7 +9,7 @@ import com.djrapitops.plugin.api.utility.log.Log; * @author Rsl1122 * @since 3.5.2 */ -public class PlayersPageResponse extends Response { +public class PlayersPageResponse extends PageResponse { public PlayersPageResponse() { super.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 d58abe252..bcba4c45c 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/html/structure/AnalysisPluginsTabContentCreator.java b/Plan/src/main/java/com/djrapitops/plan/utilities/html/structure/AnalysisPluginsTabContentCreator.java index c8094884a..50d6ee6cc 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 @@ -118,6 +118,7 @@ public class AnalysisPluginsTabContentCreator { Log.toLog(AnalysisPluginsTabContentCreator.class, e); } finally { Benchmark.stop("Analysis", "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 0b1e9b6f6..048b498cc 100644 --- a/Plan/src/main/resources/bungee.yml +++ b/Plan/src/main/resources/bungee.yml @@ -1,4 +1,4 @@ name: Plan author: Rsl1122 main: com.djrapitops.plan.PlanBungee -version: 4.4.5 \ No newline at end of file +version: 4.4.6 \ No newline at end of file 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 55c0b3fe7..56ef3675e 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.5 +version: 4.4.6 softdepend: - EssentialsX - Towny @@ -51,13 +51,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: @@ -93,8 +93,8 @@ permissions: 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/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/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/PlanPluginBridge/PlanPluginBridge-4.4.0.jar b/PlanPluginBridge/PlanPluginBridge-4.4.0.jar index c39acfcea..25b269757 100644 Binary files a/PlanPluginBridge/PlanPluginBridge-4.4.0.jar and b/PlanPluginBridge/PlanPluginBridge-4.4.0.jar differ diff --git a/PlanPluginBridge/pom.xml b/PlanPluginBridge/pom.xml index eb7c024dc..a4f68f61d 100644 --- a/PlanPluginBridge/pom.xml +++ b/PlanPluginBridge/pom.xml @@ -53,6 +53,10 @@ 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 @@ -77,6 +81,10 @@ advanced-achievements-repo https://raw.github.com/PyvesB/AdvancedAchievements/mvn-repo/ + + nucleus-repo + http://repo.drnaylor.co.uk/artifactory/list/minecraft + @@ -108,6 +116,13 @@ jar provided
+ + org.spongepowered + spongeapi + 7.0.0 + jar + provided + @@ -146,6 +161,12 @@ 1.5.0 provided + + io.github.nucleuspowered + nucleus-api + 1.6.0-PR1-S7.0 + provided + 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 6659fdb50..2adf0dfbd 100644 --- a/PlanPluginBridge/src/main/java/com/djrapitops/pluginbridge/plan/Bridge.java +++ b/PlanPluginBridge/src/main/java/com/djrapitops/pluginbridge/plan/Bridge.java @@ -19,8 +19,10 @@ import com.djrapitops.pluginbridge.plan.kingdoms.KingdomsHook; import com.djrapitops.pluginbridge.plan.litebans.LiteBansBukkitHook; import com.djrapitops.pluginbridge.plan.litebans.LiteBansBungeeHook; 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; @@ -72,7 +74,9 @@ public class Bridge { private static Hook[] getSpongeHooks(HookHandler h) { return new Hook[]{ - new BuyCraftHook(h) + new BuyCraftHook(h), + new SpongeEconomyHook(h), + new NucleusHook(h) }; } 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/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/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