From fd5e02e1c9f3bfb002d024d8fb0e42fa01a003c2 Mon Sep 17 00:00:00 2001 From: Rsl1122 Date: Thu, 30 Aug 2018 16:11:34 +0300 Subject: [PATCH 01/31] Reduced html page size by compressing result html #685 --- Plan/pom.xml | 7 +++++ .../response/errors/ErrorResponse.java | 4 +-- .../response/pages/AnalysisPageResponse.java | 3 +-- .../response/pages/InspectPageResponse.java | 3 +-- .../response/pages/NetworkPageResponse.java | 3 +-- .../response/pages/PageResponse.java | 27 +++++++++++++++++++ .../response/pages/PlayersPageResponse.java | 3 +-- .../parts/InspectPagePluginsContent.java | 4 +-- .../pages/parts/NetworkPageContent.java | 4 +-- 9 files changed, 44 insertions(+), 14 deletions(-) create mode 100644 Plan/src/main/java/com/djrapitops/plan/system/webserver/response/pages/PageResponse.java diff --git a/Plan/pom.xml b/Plan/pom.xml index 9f16f2e25..ea29ad87b 100644 --- a/Plan/pom.xml +++ b/Plan/pom.xml @@ -139,6 +139,13 @@ 1.2 + + + com.googlecode.htmlcompressor + htmlcompressor + 1.5.2 + + org.mockito 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..926f474ee 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,7 +3,6 @@ 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; @@ -16,7 +15,7 @@ import java.util.UUID; * @author Rsl1122 * @since 3.5.2 */ -public class InspectPageResponse extends Response { +public class InspectPageResponse extends PageResponse { private final UUID 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; From c30650c0ee5ada9f9aa5d8e5114c05cad66f8861 Mon Sep 17 00:00:00 2001 From: Rsl1122 Date: Thu, 30 Aug 2018 16:36:14 +0300 Subject: [PATCH 02/31] Changed ResponseCache to use caffeine (5 minute invalidation) #685 --- Plan/dependency-reduced-pom.xml | 4 --- Plan/pom.xml | 7 ++++ .../system/webserver/cache/ResponseCache.java | 35 +++++++++---------- 3 files changed, 23 insertions(+), 23 deletions(-) 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 ea29ad87b..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 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); } } From 446aec2a0a5ee7b8f5ed204c7f2ff61adabc96c1 Mon Sep 17 00:00:00 2001 From: Rsl1122 Date: Thu, 30 Aug 2018 17:18:57 +0300 Subject: [PATCH 03/31] Reproduced issue #693 in a test --- .../listeners/bukkit/AFKListenerTest.java | 67 +++++++++++++++++++ 1 file changed, 67 insertions(+) create mode 100644 Plan/src/test/java/com/djrapitops/plan/system/listeners/bukkit/AFKListenerTest.java 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 From 0bb2922329eff32828547544a34e6be37db54fe2 Mon Sep 17 00:00:00 2001 From: Rsl1122 Date: Thu, 30 Aug 2018 17:22:33 +0300 Subject: [PATCH 04/31] Fixed #693 --- .../djrapitops/plan/system/listeners/bukkit/AFKListener.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) 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); From be2be530cab4e7a51caa7c3d81205af952c11d48 Mon Sep 17 00:00:00 2001 From: Rsl1122 Date: Thu, 30 Aug 2018 17:43:43 +0300 Subject: [PATCH 05/31] Reproduced #710 with a test --- .../TimeAmountFormatterExtraZerosTest.java | 130 ++++++++++++++++++ 1 file changed, 130 insertions(+) create mode 100644 Plan/src/test/java/com/djrapitops/plan/data/store/mutators/formatting/TimeAmountFormatterExtraZerosTest.java 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..2b1e7809b --- /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 "; + + 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 From bee9966450e458bc6cc622aa8975d148352b9dc0 Mon Sep 17 00:00:00 2001 From: Rsl1122 Date: Thu, 30 Aug 2018 17:52:50 +0300 Subject: [PATCH 06/31] Ignored exception in #715 --- .../formatting/TimeAmountFormatter.java | 34 +++++++++---------- .../system/tasks/LogsFolderCleanTask.java | 6 +++- 2 files changed, 22 insertions(+), 18 deletions(-) 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..51471b492 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) { + 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/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. */ + } } } From 08d1067bd8c1dcf050fde64389e90703bfcdd258 Mon Sep 17 00:00:00 2001 From: Rsl1122 Date: Thu, 30 Aug 2018 18:02:24 +0300 Subject: [PATCH 07/31] Fixed #710 --- .../formatting/TimeAmountFormatter.java | 2 +- .../TimeAmountFormatterDefaultTest.java | 130 ++++++++++++++++++ .../TimeAmountFormatterExtraZerosTest.java | 2 +- 3 files changed, 132 insertions(+), 2 deletions(-) create mode 100644 Plan/src/test/java/com/djrapitops/plan/data/store/mutators/formatting/TimeAmountFormatterDefaultTest.java 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 51471b492..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 @@ -83,7 +83,7 @@ public class TimeAmountFormatter implements Formatter { } private void appendSeconds(StringBuilder builder, long seconds, long minutes, long hours, String fHours, String fMinutes, String fSeconds) { - if (seconds != 0) { + 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)) { 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 index 2b1e7809b..7971d5d9f 100644 --- 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 @@ -58,7 +58,7 @@ public class TimeAmountFormatterExtraZerosTest { @Test public void exampleTwo() { - String expected = "1 year, 1 month, 5d "; + String expected = "1 year, 1 month, 5d 00:00:00"; long ms = TimeAmount.DAY.ms() * 400L; String result = timeAmountFormatter.apply(ms); From 333936cfa8682ac3dd3d88cdc01d319293ba6c9e Mon Sep 17 00:00:00 2001 From: Rsl1122 Date: Thu, 30 Aug 2018 18:10:16 +0300 Subject: [PATCH 08/31] Implemented afk check on kick event #705 Note about AFK ignore perm: - Players kicked by afk that have plan.ignore.afk will be counted as "real" kicks. --- .../com/djrapitops/plan/system/afk/AFKTracker.java | 10 ++++++++++ .../system/listeners/bukkit/PlayerOnlineListener.java | 4 ++++ .../system/listeners/sponge/SpongePlayerListener.java | 4 ++++ 3 files changed, 18 insertions(+) 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/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); From 8a4b23649d205454ef045ed1c6b9e9dc8f5e0e0a Mon Sep 17 00:00:00 2001 From: BrainStone Date: Fri, 31 Aug 2018 18:47:04 +0200 Subject: [PATCH 09/31] PingCountTimerSponge --- .../plan/system/tasks/BukkitTaskSystem.java | 6 +- .../plan/system/tasks/SpongeTaskSystem.java | 18 +++ ...ntTimer.java => PingCountTimerBukkit.java} | 4 +- .../tasks/server/PingCountTimerSponge.java | 110 ++++++++++++++++++ 4 files changed, 133 insertions(+), 5 deletions(-) rename Plan/src/main/java/com/djrapitops/plan/system/tasks/server/{PingCountTimer.java => PingCountTimerBukkit.java} (97%) create mode 100644 Plan/src/main/java/com/djrapitops/plan/system/tasks/server/PingCountTimerSponge.java 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/SpongeTaskSystem.java b/Plan/src/main/java/com/djrapitops/plan/system/tasks/SpongeTaskSystem.java index 7b0017108..9b13ed881 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,7 +1,11 @@ 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; @@ -10,6 +14,20 @@ public class SpongeTaskSystem extends ServerTaskSystem { public SpongeTaskSystem(PlanSponge plugin) { super(plugin, new SpongeTPSCountTimer(plugin)); } + + @Override + public void enable() { + super.enable(); + try { + PingCountTimerSponge pingCountTimer = new PingCountTimerSponge(); + Sponge.getEventManager().registerListeners(plugin, pingCountTimer); + long startDelay = TimeAmount.SECOND.ticks() * (long) Settings.PING_SERVER_ENABLE_DELAY.getNumber(); + RunnableFactory.createNew("PingCountTimer", pingCountTimer) + .runTaskTimer(startDelay, PingCountTimerSponge.PING_INTERVAL); + } catch (ExceptionInInitializerError | NoClassDefFoundError ignore) { + // Running CraftBukkit + } + } @Override public void disable() { 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 97% 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..97c818546 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,7 +53,7 @@ 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 @@ -80,7 +80,7 @@ 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); } } 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(); + } +} From 81992a5676b2209a81941804f1837eebdd76e159 Mon Sep 17 00:00:00 2001 From: BrainStone Date: Sat, 1 Sep 2018 18:01:10 +0200 Subject: [PATCH 10/31] Using correct way to register listener --- .../plan/system/tasks/SpongeTaskSystem.java | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) 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 9b13ed881..05ab2d0f7 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 @@ -18,15 +18,11 @@ public class SpongeTaskSystem extends ServerTaskSystem { @Override public void enable() { super.enable(); - try { - PingCountTimerSponge pingCountTimer = new PingCountTimerSponge(); - Sponge.getEventManager().registerListeners(plugin, pingCountTimer); - long startDelay = TimeAmount.SECOND.ticks() * (long) Settings.PING_SERVER_ENABLE_DELAY.getNumber(); - RunnableFactory.createNew("PingCountTimer", pingCountTimer) - .runTaskTimer(startDelay, PingCountTimerSponge.PING_INTERVAL); - } catch (ExceptionInInitializerError | NoClassDefFoundError ignore) { - // Running CraftBukkit - } + PingCountTimerSponge pingCountTimer = new PingCountTimerSponge(); + ((PlanSponge) plugin).registerListener(pingCountTimer); + long startDelay = TimeAmount.SECOND.ticks() * (long) Settings.PING_SERVER_ENABLE_DELAY.getNumber(); + RunnableFactory.createNew("PingCountTimer", pingCountTimer) + .runTaskTimer(startDelay, PingCountTimerSponge.PING_INTERVAL); } @Override From 7cce16813397f196e48be3c0c045ff26a0ab85ab Mon Sep 17 00:00:00 2001 From: BrainStone Date: Sat, 1 Sep 2018 18:15:07 +0200 Subject: [PATCH 11/31] No need for a cast --- .../com/djrapitops/plan/system/tasks/SpongeTaskSystem.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) 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 05ab2d0f7..4865ffcf9 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 @@ -10,16 +10,19 @@ import org.spongepowered.api.Sponge; import org.spongepowered.api.scheduler.Task; public class SpongeTaskSystem extends ServerTaskSystem { + + private final PlanSponge plugin; public SpongeTaskSystem(PlanSponge plugin) { super(plugin, new SpongeTPSCountTimer(plugin)); + this.plugin = plugin; } @Override public void enable() { super.enable(); PingCountTimerSponge pingCountTimer = new PingCountTimerSponge(); - ((PlanSponge) plugin).registerListener(pingCountTimer); + plugin.registerListener(pingCountTimer); long startDelay = TimeAmount.SECOND.ticks() * (long) Settings.PING_SERVER_ENABLE_DELAY.getNumber(); RunnableFactory.createNew("PingCountTimer", pingCountTimer) .runTaskTimer(startDelay, PingCountTimerSponge.PING_INTERVAL); From 00cdd6e251a96544da5f9cb3ee7e5d9fb9fb84b3 Mon Sep 17 00:00:00 2001 From: Yannick Schinko Date: Sat, 1 Sep 2018 21:59:09 +0200 Subject: [PATCH 12/31] Bungee pingAdded PinCoutTimerBungee (#717) * Implemented Bungee ping measurement * Removed unused import * Removed second unused import --- .../plan/system/tasks/BungeeTaskSystem.java | 7 ++ .../tasks/server/PingCountTimerBungee.java | 111 ++++++++++++++++++ 2 files changed, 118 insertions(+) create mode 100644 Plan/src/main/java/com/djrapitops/plan/system/tasks/server/PingCountTimerBungee.java 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/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(); + } +} From 2783841e4b0d527d0af78ce5b6ed91c45819e260 Mon Sep 17 00:00:00 2001 From: Vankka Date: Wed, 5 Sep 2018 20:22:35 +0300 Subject: [PATCH 13/31] Nucleus PluginData (#723) Implements Nucleus PluginData in #583 --- PlanPluginBridge/pom.xml | 21 ++ .../djrapitops/pluginbridge/plan/Bridge.java | 4 +- .../plan/nucleus/NucleusData.java | 290 ++++++++++++++++++ .../plan/nucleus/NucleusHook.java | 26 ++ 4 files changed, 340 insertions(+), 1 deletion(-) create mode 100644 PlanPluginBridge/src/main/java/com/djrapitops/pluginbridge/plan/nucleus/NucleusData.java create mode 100644 PlanPluginBridge/src/main/java/com/djrapitops/pluginbridge/plan/nucleus/NucleusHook.java 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..cdc1e6ce9 100644 --- a/PlanPluginBridge/src/main/java/com/djrapitops/pluginbridge/plan/Bridge.java +++ b/PlanPluginBridge/src/main/java/com/djrapitops/pluginbridge/plan/Bridge.java @@ -19,6 +19,7 @@ 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.superbvote.SuperbVoteHook; @@ -72,7 +73,8 @@ public class Bridge { private static Hook[] getSpongeHooks(HookHandler h) { return new Hook[]{ - new BuyCraftHook(h) + new BuyCraftHook(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()); + } + } +} From 67712f9b26d2997699cfdb4507778ad5243ec6a3 Mon Sep 17 00:00:00 2001 From: Yannick Schinko Date: Thu, 6 Sep 2018 22:19:43 +0200 Subject: [PATCH 14/31] Implemented support for sponge economy (#721) * Implement basic economy support for Sponge * Implemented global server stats --- .../djrapitops/pluginbridge/plan/Bridge.java | 2 + .../plan/sponge/SpongeEconomyData.java | 97 +++++++++++++++++++ .../plan/sponge/SpongeEconomyHook.java | 33 +++++++ 3 files changed, 132 insertions(+) create mode 100644 PlanPluginBridge/src/main/java/com/djrapitops/pluginbridge/plan/sponge/SpongeEconomyData.java create mode 100644 PlanPluginBridge/src/main/java/com/djrapitops/pluginbridge/plan/sponge/SpongeEconomyHook.java 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 cdc1e6ce9..2adf0dfbd 100644 --- a/PlanPluginBridge/src/main/java/com/djrapitops/pluginbridge/plan/Bridge.java +++ b/PlanPluginBridge/src/main/java/com/djrapitops/pluginbridge/plan/Bridge.java @@ -22,6 +22,7 @@ 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; @@ -74,6 +75,7 @@ public class Bridge { private static Hook[] getSpongeHooks(HookHandler h) { return new Hook[]{ new BuyCraftHook(h), + new SpongeEconomyHook(h), new NucleusHook(h) }; } 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())); + } + } +} From 4475c44c222d47e047c2c0d96b80c94172e0eb7e Mon Sep 17 00:00:00 2001 From: Brycey92 Date: Thu, 6 Sep 2018 16:34:33 -0400 Subject: [PATCH 15/31] Fixed plan.inspect and plan.qinspect permission nodes for Sponge --- .../com/djrapitops/plan/system/settings/Permissions.java | 8 ++++---- Plan/src/main/resources/plugin.yml | 8 ++++---- docs/main/java/com/djrapitops/plan/Permissions.html | 4 ++-- 3 files changed, 10 insertions(+), 10 deletions(-) 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/resources/plugin.yml b/Plan/src/main/resources/plugin.yml index 55c0b3fe7..4b1b783fd 100644 --- a/Plan/src/main/resources/plugin.yml +++ b/Plan/src/main/resources/plugin.yml @@ -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/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
From 0fceaaf9e11736a600b930682b7fe9166dc729b6 Mon Sep 17 00:00:00 2001 From: Rsl1122 Date: Sat, 8 Sep 2018 17:00:24 +0300 Subject: [PATCH 16/31] Updated PluginBridge jar --- PlanPluginBridge/PlanPluginBridge-4.4.0.jar | Bin 156141 -> 169161 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/PlanPluginBridge/PlanPluginBridge-4.4.0.jar b/PlanPluginBridge/PlanPluginBridge-4.4.0.jar index c39acfceac167d6b506b941bceb939e4417443ae..25b26975718cde71b772644ae2520761400953a7 100644 GIT binary patch delta 20838 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 delta 8188 zcmZWucRW?^AHVn5``Q^9nQ^78gk+YH5tn2(l&r|6bj?Bt7l+8sj!ImSNKp||p|Z+I zl#G_I*6+FJNZo$@(d#+y_xm%RanAF(9}g>-=kGGHS(?(&GGi$Jq@9IQ*(7PCoQ1Bb zNMYdyR|rRP$10+Oa;!2sc!xDW23Q3Wr!pQ+J_gZ4G_>$lSBQ_40PR3UBlJ>megMQm zOYx3!W3*DFS|28oDGl=<&6(i;)BLPpB8~pm7y&h{~k!e#0^-czY;h>r;2i`blT{^h<+D3I6)@`O!UZrM!B02 z8T4yPqvBO53(5b9(dGe4y6TUjApdW z#(o+zGCK8ur8b3O(SUn7EBkx&cNP)lY}TpVQA4#miT4g(SorV5(tqa7LrwRIV?REt zMO<1jE@0^Ew)9NFWigl)2pKyEL>g{@Q`5F>6Q!4?Q`Od=>Ak(zB#afsM;@@c(f0AcUP<>IsWWb;Zo z8}V=xflF7foF9?#duerYr75e*V^&vnrrxgeYA{*5_?A`EsYkVM9vxAuU7mQi*$D5J zy?G~NM?#*~yeOgMY3C1DgGXJ%7qt&A#+HBX3zZKRk8=KGY`61i>ST6eo|Q78utKb*mt1A&1 z+etqfyk98Yj(cT#n)l97<0f&XiH!L&*~uKn+Z{FnHwgt6Q=Uy-=j(~nj?jPmdZBP!`OYPRjHoMlk7DC2u) z5ST8Do!Fr~Blp_qK-e8imZo{j@r-Ty8$Z7G;&+;6v8=b67%lbuWd64D9qF80jq`vs zYfFJ!;M2fkw|cBr*}rB6zbz@_5->HDAHDX8z)@_%QmA&!#@cMQK`%-8)1k|*qbFs} z^n27zh)lL&(VaHLq}toXth8-1Uoxahz4S%)KNg4y{r-f^yn*WnOa-RupF{^O?-(`b z!SCOHSVUODZZ>q7@J_UOtNO^0#s2VKng8|?!aSaB<>1C5z7!!zZFd$BR54nN*vKJBR9moJp`%J}V;^4*<2>1C6} zA(k_Jj8Yrfu8*qh{|N8ZS8e&0^4Ox8rhF6aGxB&8;ohwf>j8zBGcr9|tdF}S)UPs$ z3x;0cN>~|GKRYQo^K_V~w5Xz!=56>*rM*AdoiHvNT2UrjAig89hj{PBM5z6QgG7RE z+M!jE_O1WnvnR% z;>euPB%)I3FZg-?KGS)7T+3Our^gqAlWsUDB}o&>DM6|>t%v$QC%-ee=yX?iu19=s z{?KacTSnW*Otr1^jj+?)2VeYzd7B|gPD|h&0ZcT66^UqHXeQVvd?Ol@!tvKxk-^hIbNT)L4l>=R8 z@)A1qrQjuB&K_2{V`9J%@76d`s3TDa1!h8*T1AcQ`JRuu+wmCKKTNg_O|?~aoHT3_ z&EIt8XyX<>_`F@8uRfmWtW2_@IpF1bD}HPCjjbo7_NsX&4m0&B7DX=|YcGmZNF;OB zDCix!mE>CzXWlu?G{k!?dda-~v*G*0w{jMvHhtMTX4+U77oOP6xSj7>{t~v`%uv|% zblhV5CU#}*Wy}1Dy43)gx1^XEttysZ*xv;Sa(|l5+SWz*@US8m6?_VZUT>9Wl+a6c zJT42p)xt9MP^vXs8@c(eU(Q0Wxty{s=-^QW9@Tzp;ALl{+{8Ee9?wl;F_?Ndj}t3P z`dPtQXWQ`=i^Q_LZ-?H*RoyjcbjRmSs3U=4Srjpt8A1_}xo_w|czy~^jAlh1MYgTl zAYyVnJ(dy4jGWGi)j&vcKQ}fMi9$BpfW1r4PDv_O!K77%7nBC7a6TtihEm0Fl5It? zM!b}~$b~A{DbzS_J2noPGnrcx>w<(O2O44vQC7eR+k%FBVU1Nqu^U0y5Oky%iY?$o z?)>`!i584>7|a}~M)o3i{$dW66ZVOL1j&l$v0O;aK{iOi2BY3?=V14sDKi#e8zCg+ zsO1 z!HZTb6RMlOhpj+uQ}1KXqL^S0_8L;^kb4KP)+qbp&2KO7-(&Bhl?<0p;%;S-(LPXT zX~O$(&=&HC1*{orpuLQ}i=2ddT1X3r%z#9nA@v5FgaVoj4oHa{frD0&V?Yk)f^H&J z5_yL(WQC@kEDCX>ScxnofF{oM>#E z>~AfqPVUMWd;ZIu1wj?V%gJIxhOEmVajd)otS1+%IvQ{bQ4R#^xA; z^5!PK+)RwyJT?4qu=)JN?)%e4E|0|6pim2j!Y7(hjU@vrA+b ztk|cf3uMn>Y6s<&idb_}9<+7Eac`S=kxq;sO6af_eAU^Lpj9g%T6mlI`L$QT=b`%+ z*xuns*sk0*-KUEe%Q3ZzqPr?(X^oGsylc(4Y$-Kx;pyim7yr9};h!29%$hU0_DpVv zR#n`sctT#$Nj@z9%A!Rlk6HQFrbZGjm9rOiKBU>J&_DM*EsD|o7$KDR>$p}`QiAvi zqF?o(Qir~$HrtmRCFSl;MwKK*RZ-Tn`d-1`7Tt^ovc*oj+m&3~mmcL*U&uUe+pzPR zEr%N?@5MRa`6E^JolopZQu4KLrgvUr%?q2E7E#=D_lb>QLbXK*W9Bw^Qkt*YhIVN0 zLBsk-1OwF?+TGnTwXX)WP70ZYUm4Pfb2lg6+}uer^uNU=>4F5)4A^-TkKEde+<<>kW(Ne z&lWo?>;5631ox`T7T@U4taaYSO-Q1E#dk8@+cv~MCw{PwkMwkOZ^Oo+S6?s;-WrY( zro^7=G}854R?j^{a`TDGoks{wi^G+TF^1leT0x>_lhsOv(Q%t62{tjWF?TqJeMVb` zaH^M&y&IbwWIn=TTVH-$PIaZ>CB~svar@59j@Rnk)+Q1ahsU4LWp#W#%37h?hP#J< zdOa-i{)Pv)8XpRK>RPcaUG{N{y3#(PSY-Z!ND!1+s;*0)GA6{yyfrHgW{l_9bkw9x z%v{f2kk+PC_GS#NnoC#vouRV*^Ewxk#4SX2H|4sXCNt|c>K!`HCoVy*Vk=oKl`W%v z@>SQM`B-NIYn<`I!~C|xN>(XiF=i|l!4-ZjU&V@aEU=MB#pXs?jrqz`3&&Eok%(>& zvxsbGSJFC*mt}LOiqt>oWV)8jwT@i!ZYiq{I_Dc~zDa|mRoYPS!M9S6F^r~{=c)Px zUg1;yXp8-f$nK+(z1}RM-yZubFJ39KIUN_fr#p9UXr)?Xq59;Avn{)QR*spahmB-W z6039VxZcd*%FvV$OC{{TAi5f2hzgdRTIOliBi0$gEeJ`7Prq39+t1&a<{s+_l-tU5n2(b&fH4OzH3s zc=r)6_1fb*ALok*^Lb=6?CL-M#au3{v)!@Mb!RBpj z{VXbPG0*J8g`RT{n}jPmY`$^gvg6g7iXT;*W^T5HC!0Us$KZQjvUBA$o?n=EXA{dk z#Y|j6i36kdI}Cp!?qQmBS)9Z6vJ)ZZ?6B1b3u4oBwBff;Dcy041C#d{N}L%7Zt~ds zO_Ze8)otHWsjg8X_eKA1MQ(R^*Zt>LUI=fx>a&OL$f10nE~Y6HZ@ZYdJjMG5S8kFz zYTk4YNnJZXx|-s2K~qk^YOm7#Ab)f}*QN*0tQO7&M$wP16q^-Uj$UvSYZ-XS7u5cF z{~jXkUg9N!-n%rW{)^KpR<09-4exj1tTgpBKUD7+=C^}dzBTM8{1PP$GR?&F%slWG z8Gfr=_(uJJx9>CKd@Ge_l9BdVmOK6L1S{M(k4WzSXl_+_DSswHCNtjJ%(?%}dG}3D z3bDz}zJ|T5vCrmdyH5(g4W0??(9{sGx0MMe6HcBaF7;ni{t%{KADxgW+1mAl=NO^v zeo&5sqn_9Ej?*3|-=xVrVuFh#{q#PvUea1<7s!q=HoGcmntLlH{(6)6rOSjQ|16_X z=t_?6GeLPNtwQD}w}wZKP3hrBx3+8$S=F^X;{KyrOV)WnZM%7v*mwPJ!!lQDFIF=N zHb3#hjPMZZ9LMau-TV16=5lsUzZ;$_X?}LI;>mK0i>lk|^qW&EBTtrbT5cg_2}`!G zH;Oxv7_E}!P!!YHhGS6m+^CGg$SFcLxQmW5$FMO{js}3 zDwO&NHgttnkbVqob`(O}J8UjzTAn}`dK}V4SrCYTj<&c%Xu}LfT&1}Gwg8Vts3eDC ztK$J}M<2&Lz+;97Bt;UX=Y*?W!8|G6u`{r)2P6q2^;v``#PY`&Qlls7LK0}&0%0g?MNvjYOnwhnKt|A!w3;w8SSv8sXxEcb|axkPUJ<0SH#EH9`o! zeF8!XWVnYy81Vr@19nP*Sj8ySu!e7F?*u5~MkviPq)UnZR}vX31eD5fM+(G^_|5hK z<}G3fIP0?p{-S`Ak^od9{gR}I%Tw39e;S88MhVP|Opk)b8-Ulh6@l ze(wvlbI71tP@h++El5j!iD~j8RBAz;-bX? zKIsMyT|ofEL&|exhAjiu!nyjR720@Yt z>J!7Zj#2gTg4l62Z_0M6VqT&^K8~(x6Q(kAT$_P^RQJ$P0rX zROqo53;|4gDd`#wo@Cy^KpHK>!T7|r2=Ns1WBD4%NaCpEfM-M2j^aM!RK?riMCon% zcV?R!)-ANj%zxHm{dX%kywq!R{u#CMG1QBqNMNL%<#`Nh?x$3r7OdLqv)YEdNa)Odj^R?KYNAYN1<46v}o8XqHwK;E|KQrzQcGDQ_CbfmxKi+?mq-v42L-3GyovX zRSI*J4lXCUKa>AZxAUqPIj;kuPP{ zkAc^n%>J(`fFI^0uBraC0se1~Mc_yxWP&8qO9aV0P^JLtaNl+;E36m+aU)@_w7jvC z1P4SMe1eRrlBrc&*K@;HBG!({tq9;sLt~v{frTR>E+opfNI>NpP$*S6I&y6_b1A6K z9&{meq98_iDiRW)7-U8P24lU>h$9I(|M$(97wq0iutG*u38~w_`=TLRBvxfKu&vuq z0aW4d(Ga>xA~6891>enR@GbK7gm9Dwg*4(;cH51QT;T8tf$s?QNq%tRFa2!H+IIjK z3tZ`iQB&ZGk6qiu<%d5O4uUiXK$<(L(o8&q&ggf0gYVBZ^``^!uhE`JY9kKV=~O-lwY|(g z2b|WBsCgE6@f^g1IDH$^5!eW(J_4?CH7ci0dDKR9FipbRQSgidPThW0 zQ+VFebzT8Mb>ab=YmSXXi7f}qk)VypZxj;>KqC+1(jjsA_0kggySv$631Af0A8?pozjh%8 t+o+1s|LgOuTVOCHk%@@KVW>cf5=s@WNQb!C126#?r!5#v7^N+W`5$xK+OGfr From ac3e9a969cd7f9ae7c07deb2c61e01a19e519576 Mon Sep 17 00:00:00 2001 From: Rsl1122 Date: Sat, 8 Sep 2018 17:11:29 +0300 Subject: [PATCH 17/31] Fixes Last Peak time not showing up #726 --- .../database/databases/sql/operation/SQLFetchOps.java | 4 ++-- .../system/database/databases/sql/tables/TPSTable.java | 10 +++++++--- 2 files changed, 9 insertions(+), 5 deletions(-) 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..51a3ebeaa 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 { @@ -72,7 +72,7 @@ public class SQLFetchOps extends SQLOps implements FetchOperations { return null; }); container.putSupplier(ServerKeys.RECENT_PEAK_PLAYERS, () -> { - long twoDaysAgo = System.currentTimeMillis() - (TimeAmount.DAY.ms() * 2L); + long twoDaysAgo = System.currentTimeMillis() - TimeUnit.DAYS.toMillis(2); Optional lastPeak = tpsTable.getPeakPlayerCount(serverUUID, twoDaysAgo); if (lastPeak.isPresent()) { TPS peak = lastPeak.get(); 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())) From 2c5a97d0b1b7c2ccb95e89551b0b57e22c2f82a5 Mon Sep 17 00:00:00 2001 From: Rsl1122 Date: Sat, 8 Sep 2018 17:15:50 +0300 Subject: [PATCH 18/31] Fixed MySQL with multiple plan databases returns wrong table count #724 --- .../plan/system/database/databases/sql/patches/Patch.java | 6 +++++- .../databases/sql/patches/VersionTableRemovalPatch.java | 2 +- 2 files changed, 6 insertions(+), 2 deletions(-) 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..39ed66ed7 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 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 From 795479658226959ed900a31d1a9b6122e19056ac Mon Sep 17 00:00:00 2001 From: Rsl1122 Date: Sat, 8 Sep 2018 17:36:52 +0300 Subject: [PATCH 19/31] Attempt to reduce memory usage with selective caching in DataContainer Reduced use of CachingSupplier in every DataContainer to reduce the amount of String variables ending up in heap for a longer period of time. Impacts #685 --- .../store/containers/AnalysisContainer.java | 62 +++++++++---------- .../data/store/containers/DataContainer.java | 7 +++ .../store/containers/NetworkContainer.java | 22 +++---- .../databases/sql/operation/SQLFetchOps.java | 54 ++++++++-------- 4 files changed, 76 insertions(+), 69 deletions(-) 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/system/database/databases/sql/operation/SQLFetchOps.java b/Plan/src/main/java/com/djrapitops/plan/system/database/databases/sql/operation/SQLFetchOps.java index 51a3ebeaa..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 @@ -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,7 +71,7 @@ public class SQLFetchOps extends SQLOps implements FetchOperations { } return null; }); - container.putSupplier(ServerKeys.RECENT_PEAK_PLAYERS, () -> { + container.putCachingSupplier(ServerKeys.RECENT_PEAK_PLAYERS, () -> { long twoDaysAgo = System.currentTimeMillis() - TimeUnit.DAYS.toMillis(2); Optional lastPeak = tpsTable.getPeakPlayerCount(serverUUID, twoDaysAgo); if (lastPeak.isPresent()) { @@ -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( From 3781115311e104946d1cfd3ff173ed97d4892a6d Mon Sep 17 00:00:00 2001 From: Rsl1122 Date: Sat, 8 Sep 2018 17:43:38 +0300 Subject: [PATCH 20/31] Reduce memory usage with changes to player page refreshing Old way: Pre-render & cache the html for a player page when the player joins, leaves or switches server New way: Render & cache the html when requested. Remove the rendered html from cache if the player joins, leaves or switches server This should prevent server from crashing if a bunch of bots join Impacts #685 --- .../info/PlayerPageUpdateProcessor.java | 24 +++---------------- 1 file changed, 3 insertions(+), 21 deletions(-) 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)); } } From 18be484c0d1ac7f5b1e9921801c7faa6be37953a Mon Sep 17 00:00:00 2001 From: Rsl1122 Date: Sat, 8 Sep 2018 17:57:24 +0300 Subject: [PATCH 21/31] Update versions.txt --- versions.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/versions.txt b/versions.txt index 8f575e18b..f175248c3 100644 --- a/versions.txt +++ b/versions.txt @@ -1,3 +1,4 @@ +DEV|4.4.5-b2|https://github.com/Rsl1122/Plan-PlayerAnalytics/releases/download/4.4.6-DEV2/Plan-4.4.5-b2-bukkit-bungee.jar|https://github.com/Rsl1122/Plan-PlayerAnalytics/releases/tag/4.4.6-DEV2 REL|4.4.5|https://github.com/Rsl1122/Plan-PlayerAnalytics/releases/download/4.4.5-RELEASE/Plan-4.4.5.Bukkit.Bungee.jar|https://github.com/Rsl1122/Plan-PlayerAnalytics/releases/tag/4.4.5-RELEASE REL|4.4.4|https://github.com/Rsl1122/Plan-PlayerAnalytics/releases/download/4.4.4-REL/Plan-4.4.4.jar|https://www.spigotmc.org/resources/plan-player-analytics.32536/update?update=240584 DEV|4.4.3-b3|https://github.com/Rsl1122/Plan-PlayerAnalytics/releases/download/4.4.4-DEV3/Plan-4.4.3-b3.jar|https://github.com/Rsl1122/Plan-PlayerAnalytics/releases/tag/4.4.4-DEV3 From 26f64990d3f6b10d7f8231cd79916b2d02fbba69 Mon Sep 17 00:00:00 2001 From: Rsl1122 Date: Sun, 9 Sep 2018 13:50:38 +0300 Subject: [PATCH 22/31] Fixed new code smells in development branch - static final class variable names in PingCountTimerBukkit - plugin field hiding protected variable in SpongeTaskSystem - InspectPageResponse not overriding equals method --- .../plan/system/tasks/SpongeTaskSystem.java | 10 +++++----- .../tasks/server/PingCountTimerBukkit.java | 20 +++++++++---------- .../response/pages/InspectPageResponse.java | 15 ++++++++++++++ 3 files changed, 30 insertions(+), 15 deletions(-) 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 4865ffcf9..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 @@ -10,19 +10,19 @@ import org.spongepowered.api.Sponge; import org.spongepowered.api.scheduler.Task; public class SpongeTaskSystem extends ServerTaskSystem { - - private final PlanSponge plugin; + + private final PlanSponge planSponge; public SpongeTaskSystem(PlanSponge plugin) { super(plugin, new SpongeTPSCountTimer(plugin)); - this.plugin = plugin; + this.planSponge = plugin; } @Override public void enable() { super.enable(); PingCountTimerSponge pingCountTimer = new PingCountTimerSponge(); - plugin.registerListener(pingCountTimer); + planSponge.registerListener(pingCountTimer); long startDelay = TimeAmount.SECOND.ticks() * (long) Settings.PING_SERVER_ENABLE_DELAY.getNumber(); RunnableFactory.createNew("PingCountTimer", pingCountTimer) .runTaskTimer(startDelay, PingCountTimerSponge.PING_INTERVAL); @@ -31,7 +31,7 @@ public class SpongeTaskSystem extends ServerTaskSystem { @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/PingCountTimerBukkit.java b/Plan/src/main/java/com/djrapitops/plan/system/tasks/server/PingCountTimerBukkit.java index 97c818546..2da73a083 100644 --- a/Plan/src/main/java/com/djrapitops/plan/system/tasks/server/PingCountTimerBukkit.java +++ b/Plan/src/main/java/com/djrapitops/plan/system/tasks/server/PingCountTimerBukkit.java @@ -59,17 +59,17 @@ public class PingCountTimerBukkit extends AbsRunnable implements Listener { //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"); @@ -84,8 +84,8 @@ public class PingCountTimerBukkit extends AbsRunnable implements Listener { } } - getHandleMethod = localHandle; - pingField = localPing; + GET_HANDLE_METHOD = localHandle; + PING_FIELD = localPing; } private final Map>> playerHistory = new HashMap<>(); @@ -133,7 +133,7 @@ public class PingCountTimerBukkit 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 PingCountTimerBukkit 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/webserver/response/pages/InspectPageResponse.java b/Plan/src/main/java/com/djrapitops/plan/system/webserver/response/pages/InspectPageResponse.java index 926f474ee..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 @@ -9,6 +9,7 @@ import org.apache.commons.text.StringSubstitutor; import java.util.HashMap; import java.util.Map; +import java.util.Objects; import java.util.UUID; /** @@ -48,4 +49,18 @@ public class InspectPageResponse extends PageResponse { 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); + } } From d28e20bf9a5b1af9bde5f2d03da1a6b8405c9738 Mon Sep 17 00:00:00 2001 From: Rsl1122 Date: Sun, 9 Sep 2018 17:14:20 +0300 Subject: [PATCH 23/31] Implemented equals & hashCode for UserImportData --- .../processing/importing/UserImportData.java | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) 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); + } } From f846bd5b0eda6ef907bc8a7f48fb32acb165c22b Mon Sep 17 00:00:00 2001 From: Rsl1122 Date: Sat, 15 Sep 2018 10:03:35 +0300 Subject: [PATCH 24/31] AnalysisContainer no longer held in memory by PluginData objects. Since PluginData objects are more persistent than AnalysisContainer, objects related to the each analysis can not be freed until the next analysis has been performed, because a reference was held in each PluginData object. Change: set the reference to point to null in a finally block after getServerData call. AnalysisContainer can now be freed by GC at any time Affected issues: #685 --- .../html/structure/AnalysisPluginsTabContentCreator.java | 1 + 1 file changed, 1 insertion(+) 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; From 3ae0855ef5d1837fb0c47c2471ced766d35aa5c4 Mon Sep 17 00:00:00 2001 From: Rsl1122 Date: Sat, 15 Sep 2018 10:38:05 +0300 Subject: [PATCH 25/31] Named Plan ExecutorService pools, Fixed WebServer thread leak on reload WebServer ThreadPoolExecutor was never shutdown, as it was assumed HTTPServer.shutdown() would perform that. In extreme cases 250 reloads could lead to a OutOfMemoryException due to Heap size allocation for threads not being possible. Change: Shut down ThreadPoolExecutor manually. --- .../plan/system/processing/Processing.java | 5 ++-- .../plan/system/webserver/WebServer.java | 27 +++++++++++++++---- 2 files changed, 25 insertions(+), 7 deletions(-) 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/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"; } From 764a1c661cbaad5fac6d06862d36a0d4fdd1d913 Mon Sep 17 00:00:00 2001 From: Rsl1122 Date: Sat, 15 Sep 2018 14:38:42 +0300 Subject: [PATCH 26/31] Fixed a possible buffer memory leak in GeolocationCache on first enable http://www.evanjones.ca/java-native-leak-bug.html "TL;DR: Always close GZIPInputStream and GZIPOutputStream since they use native memory via zlib." --- .../djrapitops/plan/system/cache/GeolocationCache.java | 9 +++++++-- .../plan/system/webserver/response/Response.java | 6 ++++-- 2 files changed, 11 insertions(+), 4 deletions(-) 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/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) { From ef9bf00ddf3c09fed16a70f1e6e0845e6c5f6e5f Mon Sep 17 00:00:00 2001 From: Sprungente Date: Sat, 15 Sep 2018 17:56:37 +0200 Subject: [PATCH 27/31] Improved DE Locale (#730) (Sprungente) --- Plan/src/main/resources/locale/locale_DE.txt | 74 ++++++++++---------- 1 file changed, 37 insertions(+), 37 deletions(-) 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. From cbb45bf49dc1394f0fd1c020d96d06454443ecb0 Mon Sep 17 00:00:00 2001 From: Rsl1122 Date: Sun, 23 Sep 2018 17:39:15 +0300 Subject: [PATCH 28/31] [Fix] Locale lang group replacement changes Changed the Lang that was being used to replace things on the html, now some of the Lang is no longer used. (CmdHelpLang, CommandLang, etc) GenericLang caused page breakage due to wrong replacements, so it is no longer used for replacement Affected issues: Fixes #706 --- .../djrapitops/plan/system/locale/Locale.java | 24 +++++++++++++++---- 1 file changed, 19 insertions(+), 5 deletions(-) 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(); From d1d27534222f3a358ac8208caf0b15aa5d529c8b Mon Sep 17 00:00:00 2001 From: Rsl1122 Date: Sun, 23 Sep 2018 17:51:36 +0300 Subject: [PATCH 29/31] [Fix] Fix MySQL query for "Has Column" MySQL query for has column did not take database name into account. This means that a database with multiple Plan databases would provide wrong results for the Patch system, leading to patch failiure Affected issues: Fixes #732 --- .../plan/system/database/databases/sql/patches/Patch.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) 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 39ed66ed7..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 @@ -52,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 From 836bf28348a9c4fff9a6ad48058ce20ba1a15119 Mon Sep 17 00:00:00 2001 From: Rsl1122 Date: Sun, 23 Sep 2018 17:52:33 +0300 Subject: [PATCH 30/31] [V] Version bump to 4.4.6 --- Plan/src/main/java/com/djrapitops/plan/PlanSponge.java | 2 +- Plan/src/main/resources/bungee.yml | 2 +- Plan/src/main/resources/plugin.yml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) 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/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/plugin.yml b/Plan/src/main/resources/plugin.yml index 4b1b783fd..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 From e72b89571ee6029dc86d455ad53b0718a741ad77 Mon Sep 17 00:00:00 2001 From: Rsl1122 Date: Sun, 23 Sep 2018 18:04:03 +0300 Subject: [PATCH 31/31] [Fix] Order of Registration to PluginData values Old addValue method added the values in an arbitrary label order, Order of adding makes more sense and gives developers more control. --- .../plan/data/element/InspectContainer.java | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) 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("
"); }