From 10756061a32127af308c73aa597705d6af370128 Mon Sep 17 00:00:00 2001 From: Matt Date: Wed, 25 Jun 2025 02:17:02 -0400 Subject: [PATCH 1/2] fix: a few bugs. and update BStats stuff. --- .../base/timers/AbstractPlayerTeleporter.java | 4 + ...{Streamline.java => StreamlineBungee.java} | 6 +- .../base/events/BungeeBaseListener.java | 4 +- .../base/runnables/PlayerChecker.java | 4 +- .../base/runnables/PlayerTeleporter.java | 6 +- .../java/net/streamline/metrics/Metrics.java | 418 +++++++++-------- .../net/streamline/platform/Messenger.java | 18 +- .../platform/commands/ProperCommand.java | 6 +- .../platform/listeners/PlatformListener.java | 4 +- .../messaging/ProxyPluginMessenger.java | 4 +- .../platform/savables/UserManager.java | 32 +- bungee/src/main/resources/plugin.yml | 2 +- ...{Streamline.java => StreamlineSpigot.java} | 6 +- .../net/streamline/base/TenSecondTimer.java | 2 +- .../base/runnables/PlayerChecker.java | 4 +- .../java/net/streamline/metrics/Metrics.java | 431 +++++++++-------- .../net/streamline/platform/Messenger.java | 4 +- .../platform/commands/ProperCommand.java | 6 +- .../commands/StreamlineSpigotCommand.java | 4 +- .../platform/listeners/PaperListener.java | 18 +- .../platform/listeners/PlatformListener.java | 8 +- .../messaging/ProxyPluginMessenger.java | 6 +- .../platform/savables/ConsoleHolder.java | 4 +- .../platform/savables/UserManager.java | 18 +- spigot/src/main/resources/plugin.yml | 2 +- .../streamline/base/StreamlineVelocity.java | 4 +- .../java/net/streamline/metrics/Metrics.java | 435 ++++++++++-------- 27 files changed, 807 insertions(+), 653 deletions(-) rename bungee/src/main/java/net/streamline/base/{Streamline.java => StreamlineBungee.java} (75%) rename spigot/src/main/java/net/streamline/base/{Streamline.java => StreamlineSpigot.java} (76%) diff --git a/api/src/main/java/net/streamline/api/base/timers/AbstractPlayerTeleporter.java b/api/src/main/java/net/streamline/api/base/timers/AbstractPlayerTeleporter.java index 1613e948..95e1ae01 100644 --- a/api/src/main/java/net/streamline/api/base/timers/AbstractPlayerTeleporter.java +++ b/api/src/main/java/net/streamline/api/base/timers/AbstractPlayerTeleporter.java @@ -102,6 +102,10 @@ public void run() { private static AtomicReference>> atomicTicketsPending = new AtomicReference<>(null); public CompletableFuture> getTicketsPending() { + if (GivenConfigs.getMainDatabase() == null) { + return CompletableFuture.completedFuture(new ConcurrentSkipListSet<>()); + } + if (getAtomicTicketsPending().get() == null) { getAtomicTicketsPending().set(GivenConfigs.getMainDatabase().pullAllTPTickets()); } diff --git a/bungee/src/main/java/net/streamline/base/Streamline.java b/bungee/src/main/java/net/streamline/base/StreamlineBungee.java similarity index 75% rename from bungee/src/main/java/net/streamline/base/Streamline.java rename to bungee/src/main/java/net/streamline/base/StreamlineBungee.java index 9277e94d..7c28355b 100644 --- a/bungee/src/main/java/net/streamline/base/Streamline.java +++ b/bungee/src/main/java/net/streamline/base/StreamlineBungee.java @@ -7,7 +7,7 @@ import net.streamline.metrics.Metrics; import net.streamline.platform.BasePlugin; -public class Streamline extends BasePlugin { +public class StreamlineBungee extends BasePlugin { @Getter @Setter private static ServerPusher serverPusher; @@ -21,10 +21,12 @@ public void enable() { e.printStackTrace(); } - Metrics metrics = new Metrics(this, 16973); + Metrics metrics = new Metrics(this, 26272); metrics.addCustomChart(new Metrics.SimplePie("plugin_version", () -> getDescription().getVersion())); metrics.addCustomChart(new Metrics.SimplePie("modules_loaded_count", () -> String.valueOf(ModuleManager.getLoadedModules().size()))); metrics.addCustomChart(new Metrics.SimplePie("modules_enabled_count", () -> String.valueOf(ModuleManager.getEnabledModules().size()))); + metrics.addCustomChart(new Metrics.SingleLineChart("total_modules_loaded", () -> ModuleManager.getLoadedModules().size())); + metrics.addCustomChart(new Metrics.SingleLineChart("total_modules_enabled", () -> ModuleManager.getEnabledModules().size())); } @Override diff --git a/bungee/src/main/java/net/streamline/base/events/BungeeBaseListener.java b/bungee/src/main/java/net/streamline/base/events/BungeeBaseListener.java index b4acc4c3..42fde7d0 100644 --- a/bungee/src/main/java/net/streamline/base/events/BungeeBaseListener.java +++ b/bungee/src/main/java/net/streamline/base/events/BungeeBaseListener.java @@ -3,7 +3,7 @@ import gg.drak.thebase.events.processing.BaseProcessor; import net.md_5.bungee.api.connection.ProxiedPlayer; import net.streamline.api.base.listeners.BaseListener; -import net.streamline.base.Streamline; +import net.streamline.base.StreamlineBungee; import singularity.events.player.updates.properties.PlayerIPUpdateEvent; import singularity.utils.MessageUtils; @@ -14,7 +14,7 @@ public BungeeBaseListener() { @BaseProcessor public void onPlayerIPUpdateEvent(PlayerIPUpdateEvent event) { - ProxiedPlayer player = Streamline.getPlayer(event.getPlayerUuid()); + ProxiedPlayer player = StreamlineBungee.getPlayer(event.getPlayerUuid()); if (player == null) { MessageUtils.logWarning("PlayerIPUpdateEvent: Player is null!"); return; diff --git a/bungee/src/main/java/net/streamline/base/runnables/PlayerChecker.java b/bungee/src/main/java/net/streamline/base/runnables/PlayerChecker.java index cd18b4bd..c8fd0f24 100644 --- a/bungee/src/main/java/net/streamline/base/runnables/PlayerChecker.java +++ b/bungee/src/main/java/net/streamline/base/runnables/PlayerChecker.java @@ -1,7 +1,7 @@ package net.streamline.base.runnables; import net.md_5.bungee.api.connection.Server; -import net.streamline.base.Streamline; +import net.streamline.base.StreamlineBungee; import net.streamline.platform.savables.UserManager; import singularity.data.players.CosmicPlayer; import singularity.scheduler.BaseRunnable; @@ -14,7 +14,7 @@ public PlayerChecker() { @Override public void run() { - Streamline.getPlayersByUUID().forEach((uuid, player) -> { + StreamlineBungee.getPlayersByUUID().forEach((uuid, player) -> { if (UserUtils.isLoaded(player.getUniqueId().toString())) return; CosmicPlayer streamPlayer = UserUtils.getOrCreatePlayer(player.getUniqueId().toString()); diff --git a/bungee/src/main/java/net/streamline/base/runnables/PlayerTeleporter.java b/bungee/src/main/java/net/streamline/base/runnables/PlayerTeleporter.java index e3dfeddc..a3d403ab 100644 --- a/bungee/src/main/java/net/streamline/base/runnables/PlayerTeleporter.java +++ b/bungee/src/main/java/net/streamline/base/runnables/PlayerTeleporter.java @@ -5,7 +5,7 @@ import net.md_5.bungee.api.connection.ProxiedPlayer; import net.md_5.bungee.api.event.ServerConnectEvent; import net.streamline.api.base.timers.AbstractPlayerTeleporter; -import net.streamline.base.Streamline; +import net.streamline.base.StreamlineBungee; import singularity.data.teleportation.TPTicket; import singularity.utils.MessageUtils; @@ -43,7 +43,7 @@ private void processTicket(TPTicket ticket) { return; } - ProxiedPlayer player = Streamline.getInstance().getProxy().getPlayer(UUID.fromString(ticket.getIdentifier())); + ProxiedPlayer player = StreamlineBungee.getInstance().getProxy().getPlayer(UUID.fromString(ticket.getIdentifier())); if (player == null) { clearTicket(ticket, 2); return; @@ -57,7 +57,7 @@ private void processTicket(TPTicket ticket) { } private void teleportPlayerAsync(ProxiedPlayer player, String server) { - ServerInfo targetServer = Streamline.getInstance().getProxy().getServerInfo(server); + ServerInfo targetServer = StreamlineBungee.getInstance().getProxy().getServerInfo(server); if (targetServer != null) { ServerConnectRequest request = ServerConnectRequest.builder() .target(targetServer) diff --git a/bungee/src/main/java/net/streamline/metrics/Metrics.java b/bungee/src/main/java/net/streamline/metrics/Metrics.java index 2ca768f4..37c870bd 100644 --- a/bungee/src/main/java/net/streamline/metrics/Metrics.java +++ b/bungee/src/main/java/net/streamline/metrics/Metrics.java @@ -1,3 +1,17 @@ +/* + * This Metrics class was auto-generated and can be copied into your project if you are + * not using a build tool like Gradle or Maven for dependency management. + * + * IMPORTANT: You are not allowed to modify this class, except changing the package. + * + * Disallowed modifications include but are not limited to: + * - Remove the option for users to opt-out + * - Change the frequency for data submission + * - Obfuscate the code (every obfuscator should allow you to make an exception for specific files) + * - Reformat the code (if you use a linter, add an exception) + * + * Violations will result in a ban of your plugin and account from bStats. + */ package net.streamline.metrics; import java.io.BufferedReader; @@ -17,8 +31,8 @@ import java.util.Set; import java.util.UUID; import java.util.concurrent.Callable; -import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.ScheduledThreadPoolExecutor; import java.util.concurrent.TimeUnit; import java.util.function.BiConsumer; import java.util.function.Consumer; @@ -66,20 +80,21 @@ public Metrics(Plugin plugin, int serviceId) { return; } metricsBase = - new MetricsBase( - "bungeecord", - serverUUID, - serviceId, - enabled, - this::appendPlatformData, - this::appendServiceData, - null, - () -> true, - (message, error) -> this.plugin.getLogger().log(Level.WARNING, message, error), - (message) -> this.plugin.getLogger().log(Level.INFO, message), - logErrors, - logSentData, - logResponseStatusText); + new MetricsBase( + "bungeecord", + serverUUID, + serviceId, + enabled, + this::appendPlatformData, + this::appendServiceData, + null, + () -> true, + (message, error) -> this.plugin.getLogger().log(Level.WARNING, message, error), + (message) -> this.plugin.getLogger().log(Level.INFO, message), + logErrors, + logSentData, + logResponseStatusText, + false); } /** Loads the bStats configuration. */ @@ -89,20 +104,20 @@ private void loadConfig() throws IOException { File configFile = new File(bStatsFolder, "config.yml"); if (!configFile.exists()) { writeFile( - configFile, - "# bStats (https://bStats.org) collects some basic information for plugin authors, like how", - "# many people use their plugin and their total player count. It's recommended to keep bStats", - "# enabled, but if you're not comfortable with this, you can turn this setting off. There is no", - "# performance penalty associated with having metrics enabled, and data sent to bStats is fully", - "# anonymous.", - "enabled: true", - "serverUuid: \"" + UUID.randomUUID() + "\"", - "logFailedRequests: false", - "logSentData: false", - "logResponseStatusText: false"); + configFile, + "# bStats (https://bStats.org) collects some basic information for plugin authors, like how", + "# many people use their plugin and their total player count. It's recommended to keep bStats", + "# enabled, but if you're not comfortable with this, you can turn this setting off. There is no", + "# performance penalty associated with having metrics enabled, and data sent to bStats is fully", + "# anonymous.", + "enabled: true", + "serverUuid: \"" + UUID.randomUUID() + "\"", + "logFailedRequests: false", + "logSentData: false", + "logResponseStatusText: false"); } Configuration configuration = - ConfigurationProvider.getProvider(YamlConfiguration.class).load(configFile); + ConfigurationProvider.getProvider(YamlConfiguration.class).load(configFile); // Load configuration enabled = configuration.getBoolean("enabled", true); serverUUID = configuration.getString("serverUuid"); @@ -120,6 +135,11 @@ private void writeFile(File file, String... lines) throws IOException { } } + /** Shuts down the underlying scheduler service. */ + public void shutdown() { + metricsBase.shutdown(); + } + /** * Adds a custom chart. * @@ -134,6 +154,7 @@ private void appendPlatformData(JsonObjectBuilder builder) { builder.appendField("managedServers", plugin.getProxy().getServers().size()); builder.appendField("onlineMode", plugin.getProxy().getConfig().isOnlineMode() ? 1 : 0); builder.appendField("bungeecordVersion", plugin.getProxy().getVersion()); + builder.appendField("bungeecordName", plugin.getProxy().getName()); builder.appendField("javaVersion", System.getProperty("java.version")); builder.appendField("osName", System.getProperty("os.name")); builder.appendField("osArch", System.getProperty("os.arch")); @@ -148,13 +169,12 @@ private void appendServiceData(JsonObjectBuilder builder) { public static class MetricsBase { /** The version of the Metrics class. */ - public static final String METRICS_VERSION = "3.0.0"; - - private static final ScheduledExecutorService scheduler = - Executors.newScheduledThreadPool(1, task -> new Thread(task, "bStats-Metrics")); + public static final String METRICS_VERSION = "3.1.0"; private static final String REPORT_URL = "https://bStats.org/api/v2/data/%s"; + private final ScheduledExecutorService scheduler; + private final String platform; private final String serverUuid; @@ -203,21 +223,37 @@ public static class MetricsBase { * @param logErrors Whether or not errors should be logged. * @param logSentData Whether or not the sent data should be logged. * @param logResponseStatusText Whether or not the response status text should be logged. + * @param skipRelocateCheck Whether or not the relocate check should be skipped. */ public MetricsBase( - String platform, - String serverUuid, - int serviceId, - boolean enabled, - Consumer appendPlatformDataConsumer, - Consumer appendServiceDataConsumer, - Consumer submitTaskConsumer, - Supplier checkServiceEnabledSupplier, - BiConsumer errorLogger, - Consumer infoLogger, - boolean logErrors, - boolean logSentData, - boolean logResponseStatusText) { + String platform, + String serverUuid, + int serviceId, + boolean enabled, + Consumer appendPlatformDataConsumer, + Consumer appendServiceDataConsumer, + Consumer submitTaskConsumer, + Supplier checkServiceEnabledSupplier, + BiConsumer errorLogger, + Consumer infoLogger, + boolean logErrors, + boolean logSentData, + boolean logResponseStatusText, + boolean skipRelocateCheck) { + ScheduledThreadPoolExecutor scheduler = + new ScheduledThreadPoolExecutor( + 1, + task -> { + Thread thread = new Thread(task, "bStats-Metrics"); + thread.setDaemon(true); + return thread; + }); + // We want delayed tasks (non-periodic) that will execute in the future to be + // cancelled when the scheduler is shutdown. + // Otherwise, we risk preventing the server from shutting down even when + // MetricsBase#shutdown() is called + scheduler.setExecuteExistingDelayedTasksAfterShutdownPolicy(false); + this.scheduler = scheduler; this.platform = platform; this.serverUuid = serverUuid; this.serviceId = serviceId; @@ -231,9 +267,12 @@ public MetricsBase( this.logErrors = logErrors; this.logSentData = logSentData; this.logResponseStatusText = logResponseStatusText; - checkRelocation(); + if (!skipRelocateCheck) { + checkRelocation(); + } if (enabled) { - // WARNING: Removing the option to opt-out will get your plugin banned from bStats + // WARNING: Removing the option to opt-out will get your plugin banned from + // bStats startSubmitting(); } } @@ -242,32 +281,37 @@ public void addCustomChart(CustomChart chart) { this.customCharts.add(chart); } + public void shutdown() { + scheduler.shutdown(); + } + private void startSubmitting() { final Runnable submitTask = - () -> { - if (!enabled || !checkServiceEnabledSupplier.get()) { - // Submitting data or service is disabled - scheduler.shutdown(); - return; - } - if (submitTaskConsumer != null) { - submitTaskConsumer.accept(this::submitData); - } else { - this.submitData(); - } - }; - // Many servers tend to restart at a fixed time at xx:00 which causes an uneven distribution - // of requests on the - // bStats backend. To circumvent this problem, we introduce some randomness into the initial - // and second delay. - // WARNING: You must not modify and part of this Metrics class, including the submit delay or - // frequency! - // WARNING: Modifying this code will get your plugin banned on bStats. Just don't do it! + () -> { + if (!enabled || !checkServiceEnabledSupplier.get()) { + // Submitting data or service is disabled + scheduler.shutdown(); + return; + } + if (submitTaskConsumer != null) { + submitTaskConsumer.accept(this::submitData); + } else { + this.submitData(); + } + }; + // Many servers tend to restart at a fixed time at xx:00 which causes an uneven + // distribution of requests on the + // bStats backend. To circumvent this problem, we introduce some randomness into + // the initial and second delay. + // WARNING: You must not modify and part of this Metrics class, including the + // submit delay or frequency! + // WARNING: Modifying this code will get your plugin banned on bStats. Just + // don't do it! long initialDelay = (long) (1000 * 60 * (3 + Math.random() * 3)); long secondDelay = (long) (1000 * 60 * (Math.random() * 30)); scheduler.schedule(submitTask, initialDelay, TimeUnit.MILLISECONDS); scheduler.scheduleAtFixedRate( - submitTask, initialDelay + secondDelay, 1000 * 60 * 30, TimeUnit.MILLISECONDS); + submitTask, initialDelay + secondDelay, 1000 * 60 * 30, TimeUnit.MILLISECONDS); } private void submitData() { @@ -276,10 +320,10 @@ private void submitData() { final JsonObjectBuilder serviceJsonBuilder = new JsonObjectBuilder(); appendServiceDataConsumer.accept(serviceJsonBuilder); JsonObjectBuilder.JsonObject[] chartData = - customCharts.stream() - .map(customChart -> customChart.getRequestJsonObject(errorLogger, logErrors)) - .filter(Objects::nonNull) - .toArray(JsonObjectBuilder.JsonObject[]::new); + customCharts.stream() + .map(customChart -> customChart.getRequestJsonObject(errorLogger, logErrors)) + .filter(Objects::nonNull) + .toArray(JsonObjectBuilder.JsonObject[]::new); serviceJsonBuilder.appendField("id", serviceId); serviceJsonBuilder.appendField("customCharts", chartData); baseJsonBuilder.appendField("service", serviceJsonBuilder.build()); @@ -287,17 +331,17 @@ private void submitData() { baseJsonBuilder.appendField("metricsVersion", METRICS_VERSION); JsonObjectBuilder.JsonObject data = baseJsonBuilder.build(); scheduler.execute( - () -> { - try { - // Send the data - sendData(data); - } catch (Exception e) { - // Something went wrong! :( - if (logErrors) { - errorLogger.accept("Could not submit bStats metrics data", e); - } - } - }); + () -> { + try { + // Send the data + sendData(data); + } catch (Exception e) { + // Something went wrong! :( + if (logErrors) { + errorLogger.accept("Could not submit bStats metrics data", e); + } + } + }); } private void sendData(JsonObjectBuilder.JsonObject data) throws Exception { @@ -321,7 +365,7 @@ private void sendData(JsonObjectBuilder.JsonObject data) throws Exception { } StringBuilder builder = new StringBuilder(); try (BufferedReader bufferedReader = - new BufferedReader(new InputStreamReader(connection.getInputStream()))) { + new BufferedReader(new InputStreamReader(connection.getInputStream()))) { String line; while ((line = bufferedReader.readLine()) != null) { builder.append(line); @@ -336,17 +380,17 @@ private void sendData(JsonObjectBuilder.JsonObject data) throws Exception { private void checkRelocation() { // You can use the property to disable the check in your test environment if (System.getProperty("bstats.relocatecheck") == null - || !System.getProperty("bstats.relocatecheck").equals("false")) { - // Maven's Relocate is clever and changes strings, too. So we have to use this little - // "trick" ... :D + || !System.getProperty("bstats.relocatecheck").equals("false")) { + // Maven's Relocate is clever and changes strings, too. So we have to use this + // little "trick" ... :D final String defaultPackage = - new String(new byte[] {'o', 'r', 'g', '.', 'b', 's', 't', 'a', 't', 's'}); + new String(new byte[] {'o', 'r', 'g', '.', 'b', 's', 't', 'a', 't', 's'}); final String examplePackage = - new String(new byte[] {'y', 'o', 'u', 'r', '.', 'p', 'a', 'c', 'k', 'a', 'g', 'e'}); - // We want to make sure no one just copy & pastes the example and uses the wrong package - // names + new String(new byte[] {'y', 'o', 'u', 'r', '.', 'p', 'a', 'c', 'k', 'a', 'g', 'e'}); + // We want to make sure no one just copy & pastes the example and uses the wrong + // package names if (MetricsBase.class.getPackage().getName().startsWith(defaultPackage) - || MetricsBase.class.getPackage().getName().startsWith(examplePackage)) { + || MetricsBase.class.getPackage().getName().startsWith(examplePackage)) { throw new IllegalStateException("bStats Metrics class has not been relocated correctly!"); } } @@ -370,6 +414,72 @@ private static byte[] compress(final String str) throws IOException { } } + public static class AdvancedBarChart extends CustomChart { + + private final Callable> callable; + + /** + * Class constructor. + * + * @param chartId The id of the chart. + * @param callable The callable which is used to request the chart data. + */ + public AdvancedBarChart(String chartId, Callable> callable) { + super(chartId); + this.callable = callable; + } + + @Override + protected JsonObjectBuilder.JsonObject getChartData() throws Exception { + JsonObjectBuilder valuesBuilder = new JsonObjectBuilder(); + Map map = callable.call(); + if (map == null || map.isEmpty()) { + // Null = skip the chart + return null; + } + boolean allSkipped = true; + for (Map.Entry entry : map.entrySet()) { + if (entry.getValue().length == 0) { + // Skip this invalid + continue; + } + allSkipped = false; + valuesBuilder.appendField(entry.getKey(), entry.getValue()); + } + if (allSkipped) { + // Null = skip the chart + return null; + } + return new JsonObjectBuilder().appendField("values", valuesBuilder.build()).build(); + } + } + + public static class SimplePie extends CustomChart { + + private final Callable callable; + + /** + * Class constructor. + * + * @param chartId The id of the chart. + * @param callable The callable which is used to request the chart data. + */ + public SimplePie(String chartId, Callable callable) { + super(chartId); + this.callable = callable; + } + + @Override + protected JsonObjectBuilder.JsonObject getChartData() throws Exception { + String value = callable.call(); + if (value == null || value.isEmpty()) { + // Null = skip the chart + return null; + } + return new JsonObjectBuilder().appendField("value", value).build(); + } + } + public static class DrilldownPie extends CustomChart { private final Callable>> callable; @@ -414,9 +524,9 @@ public JsonObjectBuilder.JsonObject getChartData() throws Exception { } } - public static class AdvancedPie extends CustomChart { + public static class SingleLineChart extends CustomChart { - private final Callable> callable; + private final Callable callable; /** * Class constructor. @@ -424,33 +534,19 @@ public static class AdvancedPie extends CustomChart { * @param chartId The id of the chart. * @param callable The callable which is used to request the chart data. */ - public AdvancedPie(String chartId, Callable> callable) { + public SingleLineChart(String chartId, Callable callable) { super(chartId); this.callable = callable; } @Override protected JsonObjectBuilder.JsonObject getChartData() throws Exception { - JsonObjectBuilder valuesBuilder = new JsonObjectBuilder(); - Map map = callable.call(); - if (map == null || map.isEmpty()) { - // Null = skip the chart - return null; - } - boolean allSkipped = true; - for (Map.Entry entry : map.entrySet()) { - if (entry.getValue() == 0) { - // Skip this invalid - continue; - } - allSkipped = false; - valuesBuilder.appendField(entry.getKey(), entry.getValue()); - } - if (allSkipped) { + int value = callable.call(); + if (value == 0) { // Null = skip the chart return null; } - return new JsonObjectBuilder().appendField("values", valuesBuilder.build()).build(); + return new JsonObjectBuilder().appendField("value", value).build(); } } @@ -494,7 +590,7 @@ protected JsonObjectBuilder.JsonObject getChartData() throws Exception { } } - public static class SimpleBarChart extends CustomChart { + public static class AdvancedPie extends CustomChart { private final Callable> callable; @@ -504,7 +600,7 @@ public static class SimpleBarChart extends CustomChart { * @param chartId The id of the chart. * @param callable The callable which is used to request the chart data. */ - public SimpleBarChart(String chartId, Callable> callable) { + public AdvancedPie(String chartId, Callable> callable) { super(chartId); this.callable = callable; } @@ -517,8 +613,18 @@ protected JsonObjectBuilder.JsonObject getChartData() throws Exception { // Null = skip the chart return null; } + boolean allSkipped = true; for (Map.Entry entry : map.entrySet()) { - valuesBuilder.appendField(entry.getKey(), new int[] {entry.getValue()}); + if (entry.getValue() == 0) { + // Skip this invalid + continue; + } + allSkipped = false; + valuesBuilder.appendField(entry.getKey(), entry.getValue()); + } + if (allSkipped) { + // Null = skip the chart + return null; } return new JsonObjectBuilder().appendField("values", valuesBuilder.build()).build(); } @@ -536,7 +642,7 @@ protected CustomChart(String chartId) { } public JsonObjectBuilder.JsonObject getRequestJsonObject( - BiConsumer errorLogger, boolean logErrors) { + BiConsumer errorLogger, boolean logErrors) { JsonObjectBuilder builder = new JsonObjectBuilder(); builder.appendField("chartId", chartId); try { @@ -558,35 +664,9 @@ public JsonObjectBuilder.JsonObject getRequestJsonObject( protected abstract JsonObjectBuilder.JsonObject getChartData() throws Exception; } - public static class SimplePie extends CustomChart { - - private final Callable callable; - - /** - * Class constructor. - * - * @param chartId The id of the chart. - * @param callable The callable which is used to request the chart data. - */ - public SimplePie(String chartId, Callable callable) { - super(chartId); - this.callable = callable; - } - - @Override - protected JsonObjectBuilder.JsonObject getChartData() throws Exception { - String value = callable.call(); - if (value == null || value.isEmpty()) { - // Null = skip the chart - return null; - } - return new JsonObjectBuilder().appendField("value", value).build(); - } - } - - public static class AdvancedBarChart extends CustomChart { + public static class SimpleBarChart extends CustomChart { - private final Callable> callable; + private final Callable> callable; /** * Class constructor. @@ -594,7 +674,7 @@ public static class AdvancedBarChart extends CustomChart { * @param chartId The id of the chart. * @param callable The callable which is used to request the chart data. */ - public AdvancedBarChart(String chartId, Callable> callable) { + public SimpleBarChart(String chartId, Callable> callable) { super(chartId); this.callable = callable; } @@ -602,54 +682,18 @@ public AdvancedBarChart(String chartId, Callable> callable) { @Override protected JsonObjectBuilder.JsonObject getChartData() throws Exception { JsonObjectBuilder valuesBuilder = new JsonObjectBuilder(); - Map map = callable.call(); + Map map = callable.call(); if (map == null || map.isEmpty()) { // Null = skip the chart return null; } - boolean allSkipped = true; - for (Map.Entry entry : map.entrySet()) { - if (entry.getValue().length == 0) { - // Skip this invalid - continue; - } - allSkipped = false; - valuesBuilder.appendField(entry.getKey(), entry.getValue()); - } - if (allSkipped) { - // Null = skip the chart - return null; + for (Map.Entry entry : map.entrySet()) { + valuesBuilder.appendField(entry.getKey(), new int[] {entry.getValue()}); } return new JsonObjectBuilder().appendField("values", valuesBuilder.build()).build(); } } - public static class SingleLineChart extends CustomChart { - - private final Callable callable; - - /** - * Class constructor. - * - * @param chartId The id of the chart. - * @param callable The callable which is used to request the chart data. - */ - public SingleLineChart(String chartId, Callable callable) { - super(chartId); - this.callable = callable; - } - - @Override - protected JsonObjectBuilder.JsonObject getChartData() throws Exception { - int value = callable.call(); - if (value == 0) { - // Null = skip the chart - return null; - } - return new JsonObjectBuilder().appendField("value", value).build(); - } - } - /** * An extremely simple JSON builder. * @@ -731,9 +775,9 @@ public JsonObjectBuilder appendField(String key, String[] values) { throw new IllegalArgumentException("JSON values must not be null"); } String escapedValues = - Arrays.stream(values) - .map(value -> "\"" + escape(value) + "\"") - .collect(Collectors.joining(",")); + Arrays.stream(values) + .map(value -> "\"" + escape(value) + "\"") + .collect(Collectors.joining(",")); appendFieldUnescaped(key, "[" + escapedValues + "]"); return this; } @@ -750,7 +794,7 @@ public JsonObjectBuilder appendField(String key, int[] values) { throw new IllegalArgumentException("JSON values must not be null"); } String escapedValues = - Arrays.stream(values).mapToObj(String::valueOf).collect(Collectors.joining(",")); + Arrays.stream(values).mapToObj(String::valueOf).collect(Collectors.joining(",")); appendFieldUnescaped(key, "[" + escapedValues + "]"); return this; } @@ -767,7 +811,7 @@ public JsonObjectBuilder appendField(String key, JsonObject[] values) { throw new IllegalArgumentException("JSON values must not be null"); } String escapedValues = - Arrays.stream(values).map(JsonObject::toString).collect(Collectors.joining(",")); + Arrays.stream(values).map(JsonObject::toString).collect(Collectors.joining(",")); appendFieldUnescaped(key, "[" + escapedValues + "]"); return this; } diff --git a/bungee/src/main/java/net/streamline/platform/Messenger.java b/bungee/src/main/java/net/streamline/platform/Messenger.java index 28caf678..aa094854 100644 --- a/bungee/src/main/java/net/streamline/platform/Messenger.java +++ b/bungee/src/main/java/net/streamline/platform/Messenger.java @@ -10,7 +10,7 @@ import net.md_5.bungee.api.connection.ProxiedPlayer; import net.md_5.bungee.chat.ComponentSerializer; import net.streamline.api.SLAPI; -import net.streamline.base.Streamline; +import net.streamline.base.StreamlineBungee; import singularity.data.console.CosmicSender; import singularity.data.players.CosmicPlayer; import singularity.interfaces.IMessenger; @@ -63,19 +63,19 @@ public void sendMessage(@Nullable CommandSender to, CosmicSender other, String m public void sendMessage(@Nullable CosmicSender to, String message) { if (to == null) return; - if (to instanceof CosmicPlayer) sendMessage(Streamline.getPlayer(to.getUuid()), message); + if (to instanceof CosmicPlayer) sendMessage(StreamlineBungee.getPlayer(to.getUuid()), message); else sendMessage(ProxyServer.getInstance().getConsole(), message); } public void sendMessage(@Nullable CosmicSender to, String otherUUID, String message) { if (to == null) return; - if (to instanceof CosmicPlayer) sendMessage(Streamline.getPlayer(to.getUuid()), otherUUID, message); + if (to instanceof CosmicPlayer) sendMessage(StreamlineBungee.getPlayer(to.getUuid()), otherUUID, message); else sendMessage(ProxyServer.getInstance().getConsole(), otherUUID, message); } public void sendMessage(@Nullable CosmicSender to, CosmicSender other, String message) { if (to == null || other == null) return; - if (to instanceof CosmicPlayer) sendMessage(Streamline.getPlayer(to.getUuid()), other, message); + if (to instanceof CosmicPlayer) sendMessage(StreamlineBungee.getPlayer(to.getUuid()), other, message); else sendMessage(ProxyServer.getInstance().getConsole(), other, message); } @@ -120,30 +120,30 @@ public void sendMessageRaw(CommandSender to, CosmicSender other, String message) public void sendMessageRaw(@Nullable CosmicSender to, String message) { if (to == null) return; - if (to instanceof CosmicPlayer) sendMessageRaw(Streamline.getPlayer(to.getUuid()), message); + if (to instanceof CosmicPlayer) sendMessageRaw(StreamlineBungee.getPlayer(to.getUuid()), message); else sendMessageRaw(ProxyServer.getInstance().getConsole(), message); } public void sendMessageRaw(@Nullable CosmicSender to, String otherUUID, String message) { if (to == null) return; - if (to instanceof CosmicPlayer) sendMessageRaw(Streamline.getPlayer(to.getUuid()), otherUUID, message); + if (to instanceof CosmicPlayer) sendMessageRaw(StreamlineBungee.getPlayer(to.getUuid()), otherUUID, message); else sendMessageRaw(ProxyServer.getInstance().getConsole(), otherUUID, message); } public void sendMessageRaw(@Nullable CosmicSender to, CosmicSender other, String message) { if (to == null || other == null) return; - if (to instanceof CosmicPlayer) sendMessageRaw(Streamline.getPlayer(to.getUuid()), other, message); + if (to instanceof CosmicPlayer) sendMessageRaw(StreamlineBungee.getPlayer(to.getUuid()), other, message); else sendMessageRaw(ProxyServer.getInstance().getConsole(), other, message); } public void sendTitle(CosmicSender player, CosmicTitle title) { - ProxiedPlayer p = Streamline.getPlayer(player.getUuid()); + ProxiedPlayer p = StreamlineBungee.getPlayer(player.getUuid()); if (p == null) { MessageUtils.logInfo("Could not send a title to a player because player is null!"); return; } - p.sendTitle(Streamline.getInstance().getProxy().createTitle() + p.sendTitle(StreamlineBungee.getInstance().getProxy().createTitle() .title(codedText(title.getMain())) .subTitle(codedText(title.getSub())) .fadeIn((int) title.getFadeIn()) diff --git a/bungee/src/main/java/net/streamline/platform/commands/ProperCommand.java b/bungee/src/main/java/net/streamline/platform/commands/ProperCommand.java index f39bb0fb..b8ed24c1 100644 --- a/bungee/src/main/java/net/streamline/platform/commands/ProperCommand.java +++ b/bungee/src/main/java/net/streamline/platform/commands/ProperCommand.java @@ -4,7 +4,7 @@ import net.md_5.bungee.api.CommandSender; import net.md_5.bungee.api.plugin.Command; import net.md_5.bungee.api.plugin.TabExecutor; -import net.streamline.base.Streamline; +import net.streamline.base.StreamlineBungee; import singularity.command.CosmicCommand; import singularity.data.console.CosmicSender; import singularity.interfaces.IProperCommand; @@ -44,10 +44,10 @@ public Iterable onTabComplete(CommandSender sender, String[] args) { } public void register() { - Streamline.getInstance().getProxy().getPluginManager().registerCommand(Streamline.getInstance(), this); + StreamlineBungee.getInstance().getProxy().getPluginManager().registerCommand(StreamlineBungee.getInstance(), this); } public void unregister() { - Streamline.getInstance().getProxy().getPluginManager().unregisterCommand(this); + StreamlineBungee.getInstance().getProxy().getPluginManager().unregisterCommand(this); } } diff --git a/bungee/src/main/java/net/streamline/platform/listeners/PlatformListener.java b/bungee/src/main/java/net/streamline/platform/listeners/PlatformListener.java index c79b7f76..44568642 100644 --- a/bungee/src/main/java/net/streamline/platform/listeners/PlatformListener.java +++ b/bungee/src/main/java/net/streamline/platform/listeners/PlatformListener.java @@ -26,7 +26,7 @@ import singularity.modules.ModuleUtils; import singularity.objects.CosmicFavicon; import singularity.objects.PingedResponse; -import net.streamline.base.Streamline; +import net.streamline.base.StreamlineBungee; import net.streamline.platform.Messenger; import net.streamline.platform.events.ProperEvent; import net.streamline.platform.savables.UserManager; @@ -131,7 +131,7 @@ public void onChat(ChatEvent event) { CosmicPlayer streamPlayer = UserUtils.getOrCreatePlayer(player.getUniqueId().toString()); CosmicChatEvent chatEvent = new CosmicChatEvent(streamPlayer, event.getMessage()); - Streamline.getInstance().fireEvent(chatEvent, true); + StreamlineBungee.getInstance().fireEvent(chatEvent, true); if (chatEvent.isCanceled()) { event.setCancelled(true); return; diff --git a/bungee/src/main/java/net/streamline/platform/messaging/ProxyPluginMessenger.java b/bungee/src/main/java/net/streamline/platform/messaging/ProxyPluginMessenger.java index d83b3fcc..fd83f364 100644 --- a/bungee/src/main/java/net/streamline/platform/messaging/ProxyPluginMessenger.java +++ b/bungee/src/main/java/net/streamline/platform/messaging/ProxyPluginMessenger.java @@ -2,7 +2,7 @@ import net.md_5.bungee.api.ProxyServer; import net.md_5.bungee.api.connection.ProxiedPlayer; -import net.streamline.base.Streamline; +import net.streamline.base.StreamlineBungee; import singularity.data.players.CosmicPlayer; import singularity.messages.ProxyMessenger; import singularity.messages.events.ProxyMessageInEvent; @@ -14,7 +14,7 @@ public class ProxyPluginMessenger implements ProxyMessenger { @Override public void sendMessage(ProxiedMessage message) { - if (Streamline.getInstance().getOnlinePlayers().isEmpty()) return; + if (StreamlineBungee.getInstance().getOnlinePlayers().isEmpty()) return; CosmicPlayer carrier = message.getCarrier(); if (carrier == null) { diff --git a/bungee/src/main/java/net/streamline/platform/savables/UserManager.java b/bungee/src/main/java/net/streamline/platform/savables/UserManager.java index 37def53a..2389e266 100644 --- a/bungee/src/main/java/net/streamline/platform/savables/UserManager.java +++ b/bungee/src/main/java/net/streamline/platform/savables/UserManager.java @@ -8,7 +8,7 @@ import net.md_5.bungee.api.event.ServerConnectEvent; import net.streamline.api.SLAPI; import net.streamline.api.permissions.LuckPermsHandler; -import net.streamline.base.Streamline; +import net.streamline.base.StreamlineBungee; import net.streamline.platform.BasePlugin; import net.streamline.platform.Messenger; import singularity.configs.given.GivenConfigs; @@ -60,7 +60,7 @@ public String getUsername(CommandSender sender) { public String getUsername(String uuid) { if (uuid.equals(GivenConfigs.getMainConfig().getConsoleDiscriminator())) return GivenConfigs.getMainConfig().getConsoleName(); else { - ProxiedPlayer player = Streamline.getPlayer(uuid); + ProxiedPlayer player = StreamlineBungee.getPlayer(uuid); if (player == null) return null; return getUsername(player); } @@ -97,11 +97,11 @@ public boolean runAs(CosmicSender user, boolean bypass, String command) { CommandSender source; if (user instanceof CosmicPlayer) { CosmicPlayer player = (CosmicPlayer) user; - source = Streamline.getPlayer(player.getUuid()); + source = StreamlineBungee.getPlayer(player.getUuid()); } else { - source = Streamline.getInstance().getProxy().getConsole(); - Streamline.getInstance().getProxy().getPluginManager().dispatchCommand(source, command); + source = StreamlineBungee.getInstance().getProxy().getConsole(); + StreamlineBungee.getInstance().getProxy().getPluginManager().dispatchCommand(source, command); return true; } CosmicPlayer player = (CosmicPlayer) user; @@ -114,7 +114,7 @@ public boolean runAs(CosmicSender user, boolean bypass, String command) { return false; } } - Streamline.getInstance().getProxy().getPluginManager().dispatchCommand(source, command); + StreamlineBungee.getInstance().getProxy().getPluginManager().dispatchCommand(source, command); if (bypass && !already) { if (LuckPermsHandler.hasLuckPerms()) { LuckPermsHandler.removePermission(player.getUuid(), "*"); @@ -129,7 +129,7 @@ public boolean runAs(CosmicSender user, boolean bypass, String command) { public ConcurrentSkipListSet getUsersOn(String server) { ConcurrentSkipListSet r = new ConcurrentSkipListSet<>(); - Streamline.getInstance().getProxy().getServers().values().forEach(a -> { + StreamlineBungee.getInstance().getProxy().getServers().values().forEach(a -> { a.getPlayers().forEach(b -> { CosmicPlayer player = getOrCreatePlayer(b); if (player == null) return; @@ -144,9 +144,9 @@ public ConcurrentSkipListSet getUsersOn(String server) { public void connect(CosmicPlayer user, String server) { if (! user.isOnline()) return; - ProxiedPlayer player = Streamline.getPlayer(user.getUuid()); + ProxiedPlayer player = StreamlineBungee.getPlayer(user.getUuid()); if (player == null) return; - ServerInfo serverInfo = Streamline.getInstance().getProxy().getServerInfo(server); + ServerInfo serverInfo = StreamlineBungee.getInstance().getProxy().getServerInfo(server); if (serverInfo == null) { MessageUtils.logWarning("Tried to send a user with uuid of '" + user.getUuid() + "' to server '" + server + "', but it does not exist!"); @@ -159,7 +159,7 @@ public void connect(CosmicPlayer user, String server) { @Override public void sendUserResourcePack(CosmicPlayer user, CosmicResourcePack pack) { if (! user.isOnline()) return; - ProxiedPlayer p = Streamline.getPlayer(user.getUuid()); + ProxiedPlayer p = StreamlineBungee.getPlayer(user.getUuid()); if (p == null) return; SLAPI.getInstance().getProxyMessenger().sendMessage(ResourcePackMessageBuilder.build(user, true, user, pack)); @@ -167,7 +167,7 @@ public void sendUserResourcePack(CosmicPlayer user, CosmicResourcePack pack) { @Override public String parsePlayerIP(String uuid) { - ProxiedPlayer player = Streamline.getPlayer(uuid); + ProxiedPlayer player = StreamlineBungee.getPlayer(uuid); if (player == null) return MainMessagesHandler.MESSAGES.DEFAULTS.IS_NULL.get(); InetSocketAddress address = (InetSocketAddress) player.getSocketAddress(); @@ -181,21 +181,21 @@ public String parsePlayerIP(String uuid) { @Override public double getPlayerPing(String uuid) { - ProxiedPlayer player = Streamline.getPlayer(uuid); + ProxiedPlayer player = StreamlineBungee.getPlayer(uuid); if (player == null) return 0d; return player.getPing(); } @Override public void kick(CosmicPlayer user, String message) { - ProxiedPlayer player = Streamline.getInstance().getProxy().getPlayer(user.getUuid()); + ProxiedPlayer player = StreamlineBungee.getInstance().getProxy().getPlayer(user.getUuid()); if (player == null) return; player.disconnect(Messenger.getInstance().codedText(message)); } @Override public ProxiedPlayer getPlayer(String uuid) { - return Streamline.getPlayer(uuid); + return StreamlineBungee.getPlayer(uuid); } @Override @@ -241,13 +241,13 @@ public String getDisplayName(String uuid) { @Override public void teleport(CosmicPlayer player, CosmicLocation location) { if (! player.isOnline()) return; - ProxiedPlayer p = Streamline.getPlayer(player.getUuid()); + ProxiedPlayer p = StreamlineBungee.getPlayer(player.getUuid()); if (p == null) return; CosmicServer server = location.getServer(); String serverName = server.getIdentifier(); - ServerInfo info = Streamline.getInstance().getProxy().getServerInfo(serverName); + ServerInfo info = StreamlineBungee.getInstance().getProxy().getServerInfo(serverName); if (info == null) return; p.connect(info, ServerConnectEvent.Reason.PLUGIN); diff --git a/bungee/src/main/resources/plugin.yml b/bungee/src/main/resources/plugin.yml index 0b1cb1af..4ebbbb89 100644 --- a/bungee/src/main/resources/plugin.yml +++ b/bungee/src/main/resources/plugin.yml @@ -1,6 +1,6 @@ name: '${name}' version: '${version}' -main: 'net.streamline.base.Streamline' +main: 'net.streamline.base.StreamlineBungee' authors: - MrDrakify website: https://github.com/Streamline-Essentials diff --git a/spigot/src/main/java/net/streamline/base/Streamline.java b/spigot/src/main/java/net/streamline/base/StreamlineSpigot.java similarity index 76% rename from spigot/src/main/java/net/streamline/base/Streamline.java rename to spigot/src/main/java/net/streamline/base/StreamlineSpigot.java index 95e9e162..85e4078b 100644 --- a/spigot/src/main/java/net/streamline/base/Streamline.java +++ b/spigot/src/main/java/net/streamline/base/StreamlineSpigot.java @@ -7,7 +7,7 @@ import net.streamline.platform.commands.StreamlineSpigotCommand; import singularity.modules.ModuleManager; -public class Streamline extends BasePlugin { +public class StreamlineSpigot extends BasePlugin { @Getter @Setter private static StreamlineSpigotCommand streamlineSpigotCommand; @@ -20,10 +20,12 @@ public void enable() { e.printStackTrace(); } - Metrics metrics = new Metrics(this, 16972); + Metrics metrics = new Metrics(this, 26273); metrics.addCustomChart(new Metrics.SimplePie("plugin_version", () -> getDescription().getVersion())); metrics.addCustomChart(new Metrics.SimplePie("modules_loaded_count", () -> String.valueOf(ModuleManager.getLoadedModules().size()))); metrics.addCustomChart(new Metrics.SimplePie("modules_enabled_count", () -> String.valueOf(ModuleManager.getEnabledModules().size()))); + metrics.addCustomChart(new Metrics.SingleLineChart("total_modules_loaded", () -> ModuleManager.getLoadedModules().size())); + metrics.addCustomChart(new Metrics.SingleLineChart("total_modules_enabled", () -> ModuleManager.getEnabledModules().size())); streamlineSpigotCommand = new StreamlineSpigotCommand(); } diff --git a/spigot/src/main/java/net/streamline/base/TenSecondTimer.java b/spigot/src/main/java/net/streamline/base/TenSecondTimer.java index f007c176..0ba48db1 100644 --- a/spigot/src/main/java/net/streamline/base/TenSecondTimer.java +++ b/spigot/src/main/java/net/streamline/base/TenSecondTimer.java @@ -22,7 +22,7 @@ public class TenSecondTimer implements Runnable { public TenSecondTimer(Player player) { this.player = player; - this.task = Streamline.getScheduler().runTaskTimerAsynchronously(this, 20 * 2, 20 * 2); + this.task = StreamlineSpigot.getScheduler().runTaskTimerAsynchronously(this, 20 * 2, 20 * 2); } @Override diff --git a/spigot/src/main/java/net/streamline/base/runnables/PlayerChecker.java b/spigot/src/main/java/net/streamline/base/runnables/PlayerChecker.java index 1d435c23..a222de30 100644 --- a/spigot/src/main/java/net/streamline/base/runnables/PlayerChecker.java +++ b/spigot/src/main/java/net/streamline/base/runnables/PlayerChecker.java @@ -1,6 +1,6 @@ package net.streamline.base.runnables; -import net.streamline.base.Streamline; +import net.streamline.base.StreamlineSpigot; import net.streamline.platform.savables.UserManager; import singularity.data.players.CosmicPlayer; import singularity.scheduler.BaseRunnable; @@ -13,7 +13,7 @@ public PlayerChecker() { @Override public void run() { - Streamline.getPlayersByUUID().forEach((uuid, player) -> { + StreamlineSpigot.getPlayersByUUID().forEach((uuid, player) -> { if (UserUtils.isLoaded(player.getUniqueId().toString())) return; CosmicPlayer streamPlayer = UserUtils.getOrCreatePlayer(player.getUniqueId().toString()); diff --git a/spigot/src/main/java/net/streamline/metrics/Metrics.java b/spigot/src/main/java/net/streamline/metrics/Metrics.java index 697b689d..3509acf8 100644 --- a/spigot/src/main/java/net/streamline/metrics/Metrics.java +++ b/spigot/src/main/java/net/streamline/metrics/Metrics.java @@ -1,3 +1,17 @@ +/* + * This Metrics class was auto-generated and can be copied into your project if you are + * not using a build tool like Gradle or Maven for dependency management. + * + * IMPORTANT: You are not allowed to modify this class, except changing the package. + * + * Disallowed modifications include but are not limited to: + * - Remove the option for users to opt-out + * - Change the frequency for data submission + * - Obfuscate the code (every obfuscator should allow you to make an exception for specific files) + * - Reformat the code (if you use a linter, add an exception) + * + * Violations will result in a ban of your plugin and account from bStats. + */ package net.streamline.metrics; import java.io.BufferedReader; @@ -17,8 +31,8 @@ import java.util.Set; import java.util.UUID; import java.util.concurrent.Callable; -import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.ScheduledThreadPoolExecutor; import java.util.concurrent.TimeUnit; import java.util.function.BiConsumer; import java.util.function.Consumer; @@ -31,9 +45,9 @@ import org.bukkit.configuration.file.YamlConfiguration; import org.bukkit.entity.Player; import org.bukkit.plugin.Plugin; -import org.bukkit.plugin.java.JavaPlugin; public class Metrics { + private final Plugin plugin; private final MetricsBase metricsBase; @@ -45,7 +59,7 @@ public class Metrics { * @param serviceId The id of the service. It can be found at What is my plugin id? */ - public Metrics(JavaPlugin plugin, int serviceId) { + public Metrics(Plugin plugin, int serviceId) { this.plugin = plugin; // Get the config file File bStatsFolder = new File(plugin.getDataFolder().getParentFile(), "bStats"); @@ -59,14 +73,14 @@ public Metrics(JavaPlugin plugin, int serviceId) { config.addDefault("logResponseStatusText", false); // Inform the server owners about bStats config - .options() - .header( - "bStats (https://bStats.org) collects some basic information for plugin authors, like how\n" - + "many people use their plugin and their total player count. It's recommended to keep bStats\n" - + "enabled, but if you're not comfortable with this, you can turn this setting off. There is no\n" - + "performance penalty associated with having metrics enabled, and data sent to bStats is fully\n" - + "anonymous.") - .copyDefaults(true); + .options() + .header( + "bStats (https://bStats.org) collects some basic information for plugin authors, like how\n" + + "many people use their plugin and their total player count. It's recommended to keep bStats\n" + + "enabled, but if you're not comfortable with this, you can turn this setting off. There is no\n" + + "performance penalty associated with having metrics enabled, and data sent to bStats is fully\n" + + "anonymous.") + .copyDefaults(true); try { config.save(configFile); } catch (IOException ignored) { @@ -78,21 +92,41 @@ public Metrics(JavaPlugin plugin, int serviceId) { boolean logErrors = config.getBoolean("logFailedRequests", false); boolean logSentData = config.getBoolean("logSentData", false); boolean logResponseStatusText = config.getBoolean("logResponseStatusText", false); + boolean isFolia = false; + try { + isFolia = Class.forName("io.papermc.paper.threadedregions.RegionizedServer") != null; + } catch (Exception e) { + } metricsBase = - new MetricsBase( - "bukkit", - serverUUID, - serviceId, - enabled, - this::appendPlatformData, - this::appendServiceData, - submitDataTask -> Bukkit.getScheduler().runTask(plugin, submitDataTask), - plugin::isEnabled, - (message, error) -> this.plugin.getLogger().log(Level.WARNING, message, error), - (message) -> this.plugin.getLogger().log(Level.INFO, message), - logErrors, - logSentData, - logResponseStatusText); + new // See https://github.com/Bastian/bstats-metrics/pull/126 + // See https://github.com/Bastian/bstats-metrics/pull/126 + // See https://github.com/Bastian/bstats-metrics/pull/126 + // See https://github.com/Bastian/bstats-metrics/pull/126 + // See https://github.com/Bastian/bstats-metrics/pull/126 + // See https://github.com/Bastian/bstats-metrics/pull/126 + // See https://github.com/Bastian/bstats-metrics/pull/126 + MetricsBase( + "bukkit", + serverUUID, + serviceId, + enabled, + this::appendPlatformData, + this::appendServiceData, + isFolia + ? null + : submitDataTask -> Bukkit.getScheduler().runTask(plugin, submitDataTask), + plugin::isEnabled, + (message, error) -> this.plugin.getLogger().log(Level.WARNING, message, error), + (message) -> this.plugin.getLogger().log(Level.INFO, message), + logErrors, + logSentData, + logResponseStatusText, + false); + } + + /** Shuts down the underlying scheduler service. */ + public void shutdown() { + metricsBase.shutdown(); } /** @@ -127,8 +161,8 @@ private int getPlayerAmount() { // org.bukkit.Bukkit.getOnlinePlayers()Ljava/util/Collection; Method onlinePlayersMethod = Class.forName("org.bukkit.Server").getMethod("getOnlinePlayers"); return onlinePlayersMethod.getReturnType().equals(Collection.class) - ? ((Collection) onlinePlayersMethod.invoke(Bukkit.getServer())).size() - : ((Player[]) onlinePlayersMethod.invoke(Bukkit.getServer())).length; + ? ((Collection) onlinePlayersMethod.invoke(Bukkit.getServer())).size() + : ((Player[]) onlinePlayersMethod.invoke(Bukkit.getServer())).length; } catch (Exception e) { // Just use the new method if the reflection failed return Bukkit.getOnlinePlayers().size(); @@ -138,13 +172,12 @@ private int getPlayerAmount() { public static class MetricsBase { /** The version of the Metrics class. */ - public static final String METRICS_VERSION = "3.0.0"; - - private static final ScheduledExecutorService scheduler = - Executors.newScheduledThreadPool(1, task -> new Thread(task, "bStats-Metrics")); + public static final String METRICS_VERSION = "3.1.0"; private static final String REPORT_URL = "https://bStats.org/api/v2/data/%s"; + private final ScheduledExecutorService scheduler; + private final String platform; private final String serverUuid; @@ -193,21 +226,37 @@ public static class MetricsBase { * @param logErrors Whether or not errors should be logged. * @param logSentData Whether or not the sent data should be logged. * @param logResponseStatusText Whether or not the response status text should be logged. + * @param skipRelocateCheck Whether or not the relocate check should be skipped. */ public MetricsBase( - String platform, - String serverUuid, - int serviceId, - boolean enabled, - Consumer appendPlatformDataConsumer, - Consumer appendServiceDataConsumer, - Consumer submitTaskConsumer, - Supplier checkServiceEnabledSupplier, - BiConsumer errorLogger, - Consumer infoLogger, - boolean logErrors, - boolean logSentData, - boolean logResponseStatusText) { + String platform, + String serverUuid, + int serviceId, + boolean enabled, + Consumer appendPlatformDataConsumer, + Consumer appendServiceDataConsumer, + Consumer submitTaskConsumer, + Supplier checkServiceEnabledSupplier, + BiConsumer errorLogger, + Consumer infoLogger, + boolean logErrors, + boolean logSentData, + boolean logResponseStatusText, + boolean skipRelocateCheck) { + ScheduledThreadPoolExecutor scheduler = + new ScheduledThreadPoolExecutor( + 1, + task -> { + Thread thread = new Thread(task, "bStats-Metrics"); + thread.setDaemon(true); + return thread; + }); + // We want delayed tasks (non-periodic) that will execute in the future to be + // cancelled when the scheduler is shutdown. + // Otherwise, we risk preventing the server from shutting down even when + // MetricsBase#shutdown() is called + scheduler.setExecuteExistingDelayedTasksAfterShutdownPolicy(false); + this.scheduler = scheduler; this.platform = platform; this.serverUuid = serverUuid; this.serviceId = serviceId; @@ -221,9 +270,12 @@ public MetricsBase( this.logErrors = logErrors; this.logSentData = logSentData; this.logResponseStatusText = logResponseStatusText; - checkRelocation(); + if (!skipRelocateCheck) { + checkRelocation(); + } if (enabled) { - // WARNING: Removing the option to opt-out will get your plugin banned from bStats + // WARNING: Removing the option to opt-out will get your plugin banned from + // bStats startSubmitting(); } } @@ -232,32 +284,37 @@ public void addCustomChart(CustomChart chart) { this.customCharts.add(chart); } + public void shutdown() { + scheduler.shutdown(); + } + private void startSubmitting() { final Runnable submitTask = - () -> { - if (!enabled || !checkServiceEnabledSupplier.get()) { - // Submitting data or service is disabled - scheduler.shutdown(); - return; - } - if (submitTaskConsumer != null) { - submitTaskConsumer.accept(this::submitData); - } else { - this.submitData(); - } - }; - // Many servers tend to restart at a fixed time at xx:00 which causes an uneven distribution - // of requests on the - // bStats backend. To circumvent this problem, we introduce some randomness into the initial - // and second delay. - // WARNING: You must not modify and part of this Metrics class, including the submit delay or - // frequency! - // WARNING: Modifying this code will get your plugin banned on bStats. Just don't do it! + () -> { + if (!enabled || !checkServiceEnabledSupplier.get()) { + // Submitting data or service is disabled + scheduler.shutdown(); + return; + } + if (submitTaskConsumer != null) { + submitTaskConsumer.accept(this::submitData); + } else { + this.submitData(); + } + }; + // Many servers tend to restart at a fixed time at xx:00 which causes an uneven + // distribution of requests on the + // bStats backend. To circumvent this problem, we introduce some randomness into + // the initial and second delay. + // WARNING: You must not modify and part of this Metrics class, including the + // submit delay or frequency! + // WARNING: Modifying this code will get your plugin banned on bStats. Just + // don't do it! long initialDelay = (long) (1000 * 60 * (3 + Math.random() * 3)); long secondDelay = (long) (1000 * 60 * (Math.random() * 30)); scheduler.schedule(submitTask, initialDelay, TimeUnit.MILLISECONDS); scheduler.scheduleAtFixedRate( - submitTask, initialDelay + secondDelay, 1000 * 60 * 30, TimeUnit.MILLISECONDS); + submitTask, initialDelay + secondDelay, 1000 * 60 * 30, TimeUnit.MILLISECONDS); } private void submitData() { @@ -266,10 +323,10 @@ private void submitData() { final JsonObjectBuilder serviceJsonBuilder = new JsonObjectBuilder(); appendServiceDataConsumer.accept(serviceJsonBuilder); JsonObjectBuilder.JsonObject[] chartData = - customCharts.stream() - .map(customChart -> customChart.getRequestJsonObject(errorLogger, logErrors)) - .filter(Objects::nonNull) - .toArray(JsonObjectBuilder.JsonObject[]::new); + customCharts.stream() + .map(customChart -> customChart.getRequestJsonObject(errorLogger, logErrors)) + .filter(Objects::nonNull) + .toArray(JsonObjectBuilder.JsonObject[]::new); serviceJsonBuilder.appendField("id", serviceId); serviceJsonBuilder.appendField("customCharts", chartData); baseJsonBuilder.appendField("service", serviceJsonBuilder.build()); @@ -277,17 +334,17 @@ private void submitData() { baseJsonBuilder.appendField("metricsVersion", METRICS_VERSION); JsonObjectBuilder.JsonObject data = baseJsonBuilder.build(); scheduler.execute( - () -> { - try { - // Send the data - sendData(data); - } catch (Exception e) { - // Something went wrong! :( - if (logErrors) { - errorLogger.accept("Could not submit bStats metrics data", e); - } - } - }); + () -> { + try { + // Send the data + sendData(data); + } catch (Exception e) { + // Something went wrong! :( + if (logErrors) { + errorLogger.accept("Could not submit bStats metrics data", e); + } + } + }); } private void sendData(JsonObjectBuilder.JsonObject data) throws Exception { @@ -311,7 +368,7 @@ private void sendData(JsonObjectBuilder.JsonObject data) throws Exception { } StringBuilder builder = new StringBuilder(); try (BufferedReader bufferedReader = - new BufferedReader(new InputStreamReader(connection.getInputStream()))) { + new BufferedReader(new InputStreamReader(connection.getInputStream()))) { String line; while ((line = bufferedReader.readLine()) != null) { builder.append(line); @@ -326,17 +383,17 @@ private void sendData(JsonObjectBuilder.JsonObject data) throws Exception { private void checkRelocation() { // You can use the property to disable the check in your test environment if (System.getProperty("bstats.relocatecheck") == null - || !System.getProperty("bstats.relocatecheck").equals("false")) { - // Maven's Relocate is clever and changes strings, too. So we have to use this little - // "trick" ... :D + || !System.getProperty("bstats.relocatecheck").equals("false")) { + // Maven's Relocate is clever and changes strings, too. So we have to use this + // little "trick" ... :D final String defaultPackage = - new String(new byte[] {'o', 'r', 'g', '.', 'b', 's', 't', 'a', 't', 's'}); + new String(new byte[] {'o', 'r', 'g', '.', 'b', 's', 't', 'a', 't', 's'}); final String examplePackage = - new String(new byte[] {'y', 'o', 'u', 'r', '.', 'p', 'a', 'c', 'k', 'a', 'g', 'e'}); - // We want to make sure no one just copy & pastes the example and uses the wrong package - // names + new String(new byte[] {'y', 'o', 'u', 'r', '.', 'p', 'a', 'c', 'k', 'a', 'g', 'e'}); + // We want to make sure no one just copy & pastes the example and uses the wrong + // package names if (MetricsBase.class.getPackage().getName().startsWith(defaultPackage) - || MetricsBase.class.getPackage().getName().startsWith(examplePackage)) { + || MetricsBase.class.getPackage().getName().startsWith(examplePackage)) { throw new IllegalStateException("bStats Metrics class has not been relocated correctly!"); } } @@ -360,6 +417,72 @@ private static byte[] compress(final String str) throws IOException { } } + public static class AdvancedBarChart extends CustomChart { + + private final Callable> callable; + + /** + * Class constructor. + * + * @param chartId The id of the chart. + * @param callable The callable which is used to request the chart data. + */ + public AdvancedBarChart(String chartId, Callable> callable) { + super(chartId); + this.callable = callable; + } + + @Override + protected JsonObjectBuilder.JsonObject getChartData() throws Exception { + JsonObjectBuilder valuesBuilder = new JsonObjectBuilder(); + Map map = callable.call(); + if (map == null || map.isEmpty()) { + // Null = skip the chart + return null; + } + boolean allSkipped = true; + for (Map.Entry entry : map.entrySet()) { + if (entry.getValue().length == 0) { + // Skip this invalid + continue; + } + allSkipped = false; + valuesBuilder.appendField(entry.getKey(), entry.getValue()); + } + if (allSkipped) { + // Null = skip the chart + return null; + } + return new JsonObjectBuilder().appendField("values", valuesBuilder.build()).build(); + } + } + + public static class SimplePie extends CustomChart { + + private final Callable callable; + + /** + * Class constructor. + * + * @param chartId The id of the chart. + * @param callable The callable which is used to request the chart data. + */ + public SimplePie(String chartId, Callable callable) { + super(chartId); + this.callable = callable; + } + + @Override + protected JsonObjectBuilder.JsonObject getChartData() throws Exception { + String value = callable.call(); + if (value == null || value.isEmpty()) { + // Null = skip the chart + return null; + } + return new JsonObjectBuilder().appendField("value", value).build(); + } + } + public static class DrilldownPie extends CustomChart { private final Callable>> callable; @@ -404,9 +527,9 @@ public JsonObjectBuilder.JsonObject getChartData() throws Exception { } } - public static class AdvancedPie extends CustomChart { + public static class SingleLineChart extends CustomChart { - private final Callable> callable; + private final Callable callable; /** * Class constructor. @@ -414,33 +537,19 @@ public static class AdvancedPie extends CustomChart { * @param chartId The id of the chart. * @param callable The callable which is used to request the chart data. */ - public AdvancedPie(String chartId, Callable> callable) { + public SingleLineChart(String chartId, Callable callable) { super(chartId); this.callable = callable; } @Override protected JsonObjectBuilder.JsonObject getChartData() throws Exception { - JsonObjectBuilder valuesBuilder = new JsonObjectBuilder(); - Map map = callable.call(); - if (map == null || map.isEmpty()) { - // Null = skip the chart - return null; - } - boolean allSkipped = true; - for (Map.Entry entry : map.entrySet()) { - if (entry.getValue() == 0) { - // Skip this invalid - continue; - } - allSkipped = false; - valuesBuilder.appendField(entry.getKey(), entry.getValue()); - } - if (allSkipped) { + int value = callable.call(); + if (value == 0) { // Null = skip the chart return null; } - return new JsonObjectBuilder().appendField("values", valuesBuilder.build()).build(); + return new JsonObjectBuilder().appendField("value", value).build(); } } @@ -484,7 +593,7 @@ protected JsonObjectBuilder.JsonObject getChartData() throws Exception { } } - public static class SimpleBarChart extends CustomChart { + public static class AdvancedPie extends CustomChart { private final Callable> callable; @@ -494,7 +603,7 @@ public static class SimpleBarChart extends CustomChart { * @param chartId The id of the chart. * @param callable The callable which is used to request the chart data. */ - public SimpleBarChart(String chartId, Callable> callable) { + public AdvancedPie(String chartId, Callable> callable) { super(chartId); this.callable = callable; } @@ -507,8 +616,18 @@ protected JsonObjectBuilder.JsonObject getChartData() throws Exception { // Null = skip the chart return null; } + boolean allSkipped = true; for (Map.Entry entry : map.entrySet()) { - valuesBuilder.appendField(entry.getKey(), new int[] {entry.getValue()}); + if (entry.getValue() == 0) { + // Skip this invalid + continue; + } + allSkipped = false; + valuesBuilder.appendField(entry.getKey(), entry.getValue()); + } + if (allSkipped) { + // Null = skip the chart + return null; } return new JsonObjectBuilder().appendField("values", valuesBuilder.build()).build(); } @@ -526,7 +645,7 @@ protected CustomChart(String chartId) { } public JsonObjectBuilder.JsonObject getRequestJsonObject( - BiConsumer errorLogger, boolean logErrors) { + BiConsumer errorLogger, boolean logErrors) { JsonObjectBuilder builder = new JsonObjectBuilder(); builder.appendField("chartId", chartId); try { @@ -548,35 +667,9 @@ public JsonObjectBuilder.JsonObject getRequestJsonObject( protected abstract JsonObjectBuilder.JsonObject getChartData() throws Exception; } - public static class SimplePie extends CustomChart { - - private final Callable callable; - - /** - * Class constructor. - * - * @param chartId The id of the chart. - * @param callable The callable which is used to request the chart data. - */ - public SimplePie(String chartId, Callable callable) { - super(chartId); - this.callable = callable; - } - - @Override - protected JsonObjectBuilder.JsonObject getChartData() throws Exception { - String value = callable.call(); - if (value == null || value.isEmpty()) { - // Null = skip the chart - return null; - } - return new JsonObjectBuilder().appendField("value", value).build(); - } - } - - public static class AdvancedBarChart extends CustomChart { + public static class SimpleBarChart extends CustomChart { - private final Callable> callable; + private final Callable> callable; /** * Class constructor. @@ -584,7 +677,7 @@ public static class AdvancedBarChart extends CustomChart { * @param chartId The id of the chart. * @param callable The callable which is used to request the chart data. */ - public AdvancedBarChart(String chartId, Callable> callable) { + public SimpleBarChart(String chartId, Callable> callable) { super(chartId); this.callable = callable; } @@ -592,54 +685,18 @@ public AdvancedBarChart(String chartId, Callable> callable) { @Override protected JsonObjectBuilder.JsonObject getChartData() throws Exception { JsonObjectBuilder valuesBuilder = new JsonObjectBuilder(); - Map map = callable.call(); + Map map = callable.call(); if (map == null || map.isEmpty()) { // Null = skip the chart return null; } - boolean allSkipped = true; - for (Map.Entry entry : map.entrySet()) { - if (entry.getValue().length == 0) { - // Skip this invalid - continue; - } - allSkipped = false; - valuesBuilder.appendField(entry.getKey(), entry.getValue()); - } - if (allSkipped) { - // Null = skip the chart - return null; + for (Map.Entry entry : map.entrySet()) { + valuesBuilder.appendField(entry.getKey(), new int[] {entry.getValue()}); } return new JsonObjectBuilder().appendField("values", valuesBuilder.build()).build(); } } - public static class SingleLineChart extends CustomChart { - - private final Callable callable; - - /** - * Class constructor. - * - * @param chartId The id of the chart. - * @param callable The callable which is used to request the chart data. - */ - public SingleLineChart(String chartId, Callable callable) { - super(chartId); - this.callable = callable; - } - - @Override - protected JsonObjectBuilder.JsonObject getChartData() throws Exception { - int value = callable.call(); - if (value == 0) { - // Null = skip the chart - return null; - } - return new JsonObjectBuilder().appendField("value", value).build(); - } - } - /** * An extremely simple JSON builder. * @@ -721,9 +778,9 @@ public JsonObjectBuilder appendField(String key, String[] values) { throw new IllegalArgumentException("JSON values must not be null"); } String escapedValues = - Arrays.stream(values) - .map(value -> "\"" + escape(value) + "\"") - .collect(Collectors.joining(",")); + Arrays.stream(values) + .map(value -> "\"" + escape(value) + "\"") + .collect(Collectors.joining(",")); appendFieldUnescaped(key, "[" + escapedValues + "]"); return this; } @@ -740,7 +797,7 @@ public JsonObjectBuilder appendField(String key, int[] values) { throw new IllegalArgumentException("JSON values must not be null"); } String escapedValues = - Arrays.stream(values).mapToObj(String::valueOf).collect(Collectors.joining(",")); + Arrays.stream(values).mapToObj(String::valueOf).collect(Collectors.joining(",")); appendFieldUnescaped(key, "[" + escapedValues + "]"); return this; } @@ -757,7 +814,7 @@ public JsonObjectBuilder appendField(String key, JsonObject[] values) { throw new IllegalArgumentException("JSON values must not be null"); } String escapedValues = - Arrays.stream(values).map(JsonObject::toString).collect(Collectors.joining(",")); + Arrays.stream(values).map(JsonObject::toString).collect(Collectors.joining(",")); appendFieldUnescaped(key, "[" + escapedValues + "]"); return this; } diff --git a/spigot/src/main/java/net/streamline/platform/Messenger.java b/spigot/src/main/java/net/streamline/platform/Messenger.java index c6406ea6..899fbe11 100644 --- a/spigot/src/main/java/net/streamline/platform/Messenger.java +++ b/spigot/src/main/java/net/streamline/platform/Messenger.java @@ -9,7 +9,7 @@ import net.streamline.api.SLAPI; import singularity.data.console.CosmicSender; import singularity.data.players.CosmicPlayer; -import net.streamline.base.Streamline; +import net.streamline.base.StreamlineSpigot; import net.streamline.platform.savables.UserManager; import org.bukkit.Bukkit; import org.bukkit.command.CommandSender; @@ -146,7 +146,7 @@ public void sendMessageRaw(@Nullable CosmicSender to, CosmicSender other, String @Override public void sendTitle(CosmicSender player, CosmicTitle title) { - Player p = Streamline.getPlayer(player.getUuid()); + Player p = StreamlineSpigot.getPlayer(player.getUuid()); if (p == null) { MessageUtils.logInfo("Could not send a title to a player because player is null!"); return; diff --git a/spigot/src/main/java/net/streamline/platform/commands/ProperCommand.java b/spigot/src/main/java/net/streamline/platform/commands/ProperCommand.java index ce299705..70857464 100644 --- a/spigot/src/main/java/net/streamline/platform/commands/ProperCommand.java +++ b/spigot/src/main/java/net/streamline/platform/commands/ProperCommand.java @@ -1,7 +1,7 @@ package net.streamline.platform.commands; import lombok.Getter; -import net.streamline.base.Streamline; +import net.streamline.base.StreamlineSpigot; import net.streamline.platform.savables.UserManager; import org.bukkit.command.Command; import org.bukkit.command.CommandSender; @@ -64,7 +64,7 @@ public List onTabComplete(@NotNull CommandSender sender, @NotNull Comman public void register() { try { - Streamline.registerCommands(this); + StreamlineSpigot.registerCommands(this); } catch(Exception e) { e.printStackTrace(); } @@ -72,7 +72,7 @@ public void register() { public void unregister() { try { - Streamline.unregisterCommands(getParent().getBase()); + StreamlineSpigot.unregisterCommands(getParent().getBase()); } catch(Exception e) { e.printStackTrace(); } diff --git a/spigot/src/main/java/net/streamline/platform/commands/StreamlineSpigotCommand.java b/spigot/src/main/java/net/streamline/platform/commands/StreamlineSpigotCommand.java index acd9c62b..81074d7d 100644 --- a/spigot/src/main/java/net/streamline/platform/commands/StreamlineSpigotCommand.java +++ b/spigot/src/main/java/net/streamline/platform/commands/StreamlineSpigotCommand.java @@ -3,7 +3,7 @@ import gg.drak.thebase.utils.StringUtils; import host.plas.bou.commands.CommandContext; import host.plas.bou.commands.SimplifiedCommand; -import net.streamline.base.Streamline; +import net.streamline.base.StreamlineSpigot; import net.streamline.platform.savables.UserManager; import org.jetbrains.annotations.Nullable; import singularity.command.CommandHandler; @@ -14,7 +14,7 @@ public class StreamlineSpigotCommand extends SimplifiedCommand { public StreamlineSpigotCommand() { - super("streamlinespigot", Streamline.getInstance()); + super("streamlinespigot", StreamlineSpigot.getInstance()); } @Override diff --git a/spigot/src/main/java/net/streamline/platform/listeners/PaperListener.java b/spigot/src/main/java/net/streamline/platform/listeners/PaperListener.java index 13d8cf8d..b577d14e 100644 --- a/spigot/src/main/java/net/streamline/platform/listeners/PaperListener.java +++ b/spigot/src/main/java/net/streamline/platform/listeners/PaperListener.java @@ -3,7 +3,7 @@ import com.destroystokyo.paper.event.server.PaperServerListPingEvent; import com.destroystokyo.paper.profile.PlayerProfile; import host.plas.bou.utils.ClassHelper; -import net.streamline.base.Streamline; +import net.streamline.base.StreamlineSpigot; import net.streamline.platform.Messenger; import org.bukkit.Bukkit; import org.bukkit.entity.Player; @@ -22,8 +22,8 @@ public class PaperListener implements Listener { public PaperListener() { if (! ClassHelper.isPaper()) return; - Bukkit.getPluginManager().registerEvents(this, Streamline.getInstance()); - Streamline.getInstance().logInfo("PaperListener registered."); + Bukkit.getPluginManager().registerEvents(this, StreamlineSpigot.getInstance()); + StreamlineSpigot.getInstance().logInfo("PaperListener registered."); } @EventHandler @@ -49,8 +49,8 @@ public void onPing(PaperServerListPingEvent event) { try { response = new PingedResponse(protocol, players, event.getMotd()); } catch (Throwable e) { - Streamline.getInstance().logWarning("Failed to create PingedResponse: " + e.getMessage()); - Streamline.getInstance().logWarning(e.getStackTrace()); + StreamlineSpigot.getInstance().logWarning("Failed to create PingedResponse: " + e.getMessage()); + StreamlineSpigot.getInstance().logWarning(e.getStackTrace()); return; } @@ -79,8 +79,8 @@ public void onPing(PaperServerListPingEvent event) { event.getPlayerSample().addAll(playerSample); } catch (Throwable e) { - Streamline.getInstance().logWarning("Failed to set player sample: " + e.getMessage()); - Streamline.getInstance().logWarning(e.getStackTrace()); + StreamlineSpigot.getInstance().logWarning("Failed to set player sample: " + e.getMessage()); + StreamlineSpigot.getInstance().logWarning(e.getStackTrace()); } event.setMaxPlayers(pingReceivedEvent.getResponse().getPlayers().getMax()); @@ -92,8 +92,8 @@ public void onPing(PaperServerListPingEvent event) { CachedServerIcon icon = Bukkit.loadServerIcon(favicon.getImage()); event.setServerIcon(icon); } catch (Throwable e) { - Streamline.getInstance().logWarning("Failed to set server icon: " + e.getMessage()); - Streamline.getInstance().logWarning(e.getStackTrace()); + StreamlineSpigot.getInstance().logWarning("Failed to set server icon: " + e.getMessage()); + StreamlineSpigot.getInstance().logWarning(e.getStackTrace()); } } diff --git a/spigot/src/main/java/net/streamline/platform/listeners/PlatformListener.java b/spigot/src/main/java/net/streamline/platform/listeners/PlatformListener.java index 4b52899a..2ca4f740 100644 --- a/spigot/src/main/java/net/streamline/platform/listeners/PlatformListener.java +++ b/spigot/src/main/java/net/streamline/platform/listeners/PlatformListener.java @@ -7,7 +7,7 @@ import lombok.Getter; import lombok.Setter; import net.streamline.api.SLAPI; -import net.streamline.base.Streamline; +import net.streamline.base.StreamlineSpigot; import net.streamline.base.TenSecondTimer; import net.streamline.platform.Messenger; import net.streamline.platform.events.ProperEvent; @@ -158,7 +158,7 @@ public void onChat(AsyncPlayerChatEvent event) { CosmicPlayer streamPlayer = UserUtils.getOrCreatePlayer(player.getUniqueId().toString()); CosmicChatEvent chatEvent = new CosmicChatEvent(streamPlayer, event.getMessage()); - Streamline.getInstance().fireEvent(chatEvent, true); + StreamlineSpigot.getInstance().fireEvent(chatEvent, true); if (chatEvent.isCanceled()) { event.setCancelled(true); return; @@ -235,8 +235,8 @@ public void onPing(ServerListPingEvent event) { try { response = new PingedResponse(protocol, players, event.getMotd()); } catch (Throwable e) { - Streamline.getInstance().logWarning("Failed to create PingedResponse: " + e.getMessage()); - Streamline.getInstance().logWarning(e.getStackTrace()); + StreamlineSpigot.getInstance().logWarning("Failed to create PingedResponse: " + e.getMessage()); + StreamlineSpigot.getInstance().logWarning(e.getStackTrace()); return; } diff --git a/spigot/src/main/java/net/streamline/platform/messaging/ProxyPluginMessenger.java b/spigot/src/main/java/net/streamline/platform/messaging/ProxyPluginMessenger.java index 9dadebf8..557d4284 100644 --- a/spigot/src/main/java/net/streamline/platform/messaging/ProxyPluginMessenger.java +++ b/spigot/src/main/java/net/streamline/platform/messaging/ProxyPluginMessenger.java @@ -1,7 +1,7 @@ package net.streamline.platform.messaging; import singularity.data.players.CosmicPlayer; -import net.streamline.base.Streamline; +import net.streamline.base.StreamlineSpigot; import org.bukkit.Bukkit; import org.bukkit.entity.Player; import singularity.messages.ProxyMessenger; @@ -16,7 +16,7 @@ public class ProxyPluginMessenger implements ProxyMessenger { @Override public void sendMessage(ProxiedMessage message) { - if (Streamline.getInstance().getProxy().getOnlinePlayers().isEmpty()) { + if (StreamlineSpigot.getInstance().getProxy().getOnlinePlayers().isEmpty()) { ProxiedMessageManager.pendMessage(message); return; } @@ -39,7 +39,7 @@ public void sendMessage(ProxiedMessage message) { return; } - player.sendPluginMessage(Streamline.getInstance(), message.getMainChannel(), message.read()); + player.sendPluginMessage(StreamlineSpigot.getInstance(), message.getMainChannel(), message.read()); } @Override diff --git a/spigot/src/main/java/net/streamline/platform/savables/ConsoleHolder.java b/spigot/src/main/java/net/streamline/platform/savables/ConsoleHolder.java index ac158bb4..c178551b 100644 --- a/spigot/src/main/java/net/streamline/platform/savables/ConsoleHolder.java +++ b/spigot/src/main/java/net/streamline/platform/savables/ConsoleHolder.java @@ -3,7 +3,7 @@ import host.plas.bou.commands.Sender; import lombok.Getter; import lombok.Setter; -import net.streamline.base.Streamline; +import net.streamline.base.StreamlineSpigot; import org.bukkit.Bukkit; import org.bukkit.command.CommandSender; import singularity.interfaces.audiences.IConsoleHolder; @@ -47,7 +47,7 @@ public void sendConsoleMessageNonNull(String message) { @Override public void sendLogMessage(String message) { - Streamline.getInstance().logInfo(message); + StreamlineSpigot.getInstance().logInfo(message); } @Override diff --git a/spigot/src/main/java/net/streamline/platform/savables/UserManager.java b/spigot/src/main/java/net/streamline/platform/savables/UserManager.java index 7a66abdd..005ec21f 100644 --- a/spigot/src/main/java/net/streamline/platform/savables/UserManager.java +++ b/spigot/src/main/java/net/streamline/platform/savables/UserManager.java @@ -2,7 +2,7 @@ import lombok.Getter; import net.streamline.api.permissions.LuckPermsHandler; -import net.streamline.base.Streamline; +import net.streamline.base.StreamlineSpigot; import net.streamline.platform.BasePlugin; import net.streamline.platform.Messenger; import org.bukkit.Bukkit; @@ -58,7 +58,7 @@ public String getUsername(CommandSender sender) { public String getUsername(String uuid) { if (uuid.equals(GivenConfigs.getMainConfig().getConsoleDiscriminator())) return GivenConfigs.getMainConfig().getConsoleName(); else { - Player player = Streamline.getPlayer(uuid); + Player player = StreamlineSpigot.getPlayer(uuid); if (player == null) return null; return getUsername(player); } @@ -94,7 +94,7 @@ public String parsePlayerIP(Player player) { public boolean runAs(CosmicSender player, boolean bypass, String command) { CommandSender source; if (! player.isConsole()) { - source = Streamline.getPlayer(player.getUuid()); + source = StreamlineSpigot.getPlayer(player.getUuid()); } else { source = Bukkit.getConsoleSender(); @@ -141,7 +141,7 @@ public void connect(CosmicPlayer user, String server) { @Override public void sendUserResourcePack(CosmicPlayer user, CosmicResourcePack pack) { if (! user.isOnline()) return; - Player p = Streamline.getPlayer(user.getUuid()); + Player p = StreamlineSpigot.getPlayer(user.getUuid()); if (p == null) return; // p.setResourcePack(pack.getUrl(), pack.getHash(), pack.getPrompt(), pack.isForce()); @@ -150,7 +150,7 @@ public void sendUserResourcePack(CosmicPlayer user, CosmicResourcePack pack) { @Override public String parsePlayerIP(String uuid) { - Player player = Streamline.getPlayer(uuid); + Player player = StreamlineSpigot.getPlayer(uuid); if (player == null) return MainMessagesHandler.MESSAGES.DEFAULTS.IS_NULL.get(); InetSocketAddress address = player.getAddress(); @@ -164,21 +164,21 @@ public String parsePlayerIP(String uuid) { @Override public double getPlayerPing(String uuid) { - Player player = Streamline.getPlayer(uuid); + Player player = StreamlineSpigot.getPlayer(uuid); if (player == null) return 0d; return player.getPing(); } @Override public void kick(CosmicPlayer user, String message) { - Player player = Streamline.getInstance().getProxy().getPlayer(user.getUuid()); + Player player = StreamlineSpigot.getInstance().getProxy().getPlayer(user.getUuid()); if (player == null) return; player.kickPlayer(Messenger.getInstance().codedString(message)); } @Override public Player getPlayer(String uuid) { - return Streamline.getPlayer(uuid); + return StreamlineSpigot.getPlayer(uuid); } @Override @@ -213,7 +213,7 @@ public String getDisplayName(String uuid) { @Override public void teleport(CosmicPlayer player, CosmicLocation location) { if (! player.isOnline()) return; - Player p = Streamline.getPlayer(player.getUuid()); + Player p = StreamlineSpigot.getPlayer(player.getUuid()); if (p == null) return; WorldPosition pos = location.getPosition(); diff --git a/spigot/src/main/resources/plugin.yml b/spigot/src/main/resources/plugin.yml index 65600d70..5c5d9c01 100644 --- a/spigot/src/main/resources/plugin.yml +++ b/spigot/src/main/resources/plugin.yml @@ -1,6 +1,6 @@ name: '${name}' version: '${version}' -main: 'net.streamline.base.Streamline' +main: 'net.streamline.base.StreamlineSpigot' authors: [ Drak ] website: https://github.com/Streamline-Essentials diff --git a/velocity/src/main/java/net/streamline/base/StreamlineVelocity.java b/velocity/src/main/java/net/streamline/base/StreamlineVelocity.java index 641cc7ed..e28d7295 100644 --- a/velocity/src/main/java/net/streamline/base/StreamlineVelocity.java +++ b/velocity/src/main/java/net/streamline/base/StreamlineVelocity.java @@ -105,10 +105,12 @@ public void enable() { e.printStackTrace(); } - Metrics metrics = getMetricsFactory().make(this, 16971); + Metrics metrics = getMetricsFactory().make(this, 26274); metrics.addCustomChart(new Metrics.SimplePie("plugin_version", () -> getProxy().getPluginManager().getPlugin("streamlinecore").get().getDescription().getVersion().get())); metrics.addCustomChart(new Metrics.SimplePie("modules_loaded_count", () -> String.valueOf(ModuleManager.getLoadedModules().size()))); metrics.addCustomChart(new Metrics.SimplePie("modules_enabled_count", () -> String.valueOf(ModuleManager.getEnabledModules().size()))); + metrics.addCustomChart(new Metrics.SingleLineChart("total_modules_loaded", () -> ModuleManager.getLoadedModules().size())); + metrics.addCustomChart(new Metrics.SingleLineChart("total_modules_enabled", () -> ModuleManager.getEnabledModules().size())); } // public Map getInstalledModulesCount() { diff --git a/velocity/src/main/java/net/streamline/metrics/Metrics.java b/velocity/src/main/java/net/streamline/metrics/Metrics.java index 0236cfa4..44c6fa0f 100644 --- a/velocity/src/main/java/net/streamline/metrics/Metrics.java +++ b/velocity/src/main/java/net/streamline/metrics/Metrics.java @@ -1,3 +1,17 @@ +/* + * This Metrics class was auto-generated and can be copied into your project if you are + * not using a build tool like Gradle or Maven for dependency management. + * + * IMPORTANT: You are not allowed to modify this class, except changing the package. + * + * Disallowed modifications include but are not limited to: + * - Remove the option for users to opt-out + * - Change the frequency for data submission + * - Obfuscate the code (every obfuscator should allow you to make an exception for specific files) + * - Reformat the code (if you use a linter, add an exception) + * + * Violations will result in a ban of your plugin and account from bStats. + */ package net.streamline.metrics; import com.google.inject.Inject; @@ -27,8 +41,8 @@ import java.util.Set; import java.util.UUID; import java.util.concurrent.Callable; -import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.ScheduledThreadPoolExecutor; import java.util.concurrent.TimeUnit; import java.util.function.BiConsumer; import java.util.function.Consumer; @@ -81,13 +95,13 @@ public Metrics make(Object plugin, int serviceId) { private MetricsBase metricsBase; private Metrics( - Object plugin, ProxyServer server, Logger logger, Path dataDirectory, int serviceId) { + Object plugin, ProxyServer server, Logger logger, Path dataDirectory, int serviceId) { pluginContainer = - server - .getPluginManager() - .fromInstance(plugin) - .orElseThrow( - () -> new IllegalArgumentException("The provided instance is not a plugin")); + server + .getPluginManager() + .fromInstance(plugin) + .orElseThrow( + () -> new IllegalArgumentException("The provided instance is not a plugin")); this.server = server; File configFile = dataDirectory.getParent().resolve("bStats").resolve("config.txt").toFile(); MetricsConfig config; @@ -98,34 +112,40 @@ private Metrics( return; } metricsBase = - new MetricsBase( - "velocity", - config.getServerUUID(), - serviceId, - config.isEnabled(), - this::appendPlatformData, - this::appendServiceData, - task -> server.getScheduler().buildTask(plugin, task).schedule(), - () -> true, - logger::warn, - logger::info, - config.isLogErrorsEnabled(), - config.isLogSentDataEnabled(), - config.isLogResponseStatusTextEnabled()); + new MetricsBase( + "velocity", + config.getServerUUID(), + serviceId, + config.isEnabled(), + this::appendPlatformData, + this::appendServiceData, + task -> server.getScheduler().buildTask(plugin, task).schedule(), + () -> true, + logger::warn, + logger::info, + config.isLogErrorsEnabled(), + config.isLogSentDataEnabled(), + config.isLogResponseStatusTextEnabled(), + false); if (!config.didExistBefore()) { // Send an info message when the bStats config file gets created for the first time logger.info( - "Velocity and some of its plugins collect metrics and send them to bStats (https://bStats.org)."); + "Velocity and some of its plugins collect metrics and send them to bStats (https://bStats.org)."); logger.info( - "bStats collects some basic information for plugin authors, like how many people use"); + "bStats collects some basic information for plugin authors, like how many people use"); logger.info( - "their plugin and their total player count. It's recommend to keep bStats enabled, but"); + "their plugin and their total player count. It's recommend to keep bStats enabled, but"); logger.info( - "if you're not comfortable with this, you can opt-out by editing the config.txt file in"); + "if you're not comfortable with this, you can opt-out by editing the config.txt file in"); logger.info("the '/plugins/bStats/' folder and setting enabled to false."); } } + /** Shuts down the underlying scheduler service. */ + public void shutdown() { + metricsBase.shutdown(); + } + /** * Adds a custom chart. * @@ -153,19 +173,18 @@ private void appendPlatformData(JsonObjectBuilder builder) { private void appendServiceData(JsonObjectBuilder builder) { builder.appendField( - "pluginVersion", pluginContainer.getDescription().getVersion().orElse("unknown")); + "pluginVersion", pluginContainer.getDescription().getVersion().orElse("unknown")); } public static class MetricsBase { /** The version of the Metrics class. */ - public static final String METRICS_VERSION = "3.0.0"; - - private static final ScheduledExecutorService scheduler = - Executors.newScheduledThreadPool(1, task -> new Thread(task, "bStats-Metrics")); + public static final String METRICS_VERSION = "3.1.0"; private static final String REPORT_URL = "https://bStats.org/api/v2/data/%s"; + private final ScheduledExecutorService scheduler; + private final String platform; private final String serverUuid; @@ -214,21 +233,37 @@ public static class MetricsBase { * @param logErrors Whether or not errors should be logged. * @param logSentData Whether or not the sent data should be logged. * @param logResponseStatusText Whether or not the response status text should be logged. + * @param skipRelocateCheck Whether or not the relocate check should be skipped. */ public MetricsBase( - String platform, - String serverUuid, - int serviceId, - boolean enabled, - Consumer appendPlatformDataConsumer, - Consumer appendServiceDataConsumer, - Consumer submitTaskConsumer, - Supplier checkServiceEnabledSupplier, - BiConsumer errorLogger, - Consumer infoLogger, - boolean logErrors, - boolean logSentData, - boolean logResponseStatusText) { + String platform, + String serverUuid, + int serviceId, + boolean enabled, + Consumer appendPlatformDataConsumer, + Consumer appendServiceDataConsumer, + Consumer submitTaskConsumer, + Supplier checkServiceEnabledSupplier, + BiConsumer errorLogger, + Consumer infoLogger, + boolean logErrors, + boolean logSentData, + boolean logResponseStatusText, + boolean skipRelocateCheck) { + ScheduledThreadPoolExecutor scheduler = + new ScheduledThreadPoolExecutor( + 1, + task -> { + Thread thread = new Thread(task, "bStats-Metrics"); + thread.setDaemon(true); + return thread; + }); + // We want delayed tasks (non-periodic) that will execute in the future to be + // cancelled when the scheduler is shutdown. + // Otherwise, we risk preventing the server from shutting down even when + // MetricsBase#shutdown() is called + scheduler.setExecuteExistingDelayedTasksAfterShutdownPolicy(false); + this.scheduler = scheduler; this.platform = platform; this.serverUuid = serverUuid; this.serviceId = serviceId; @@ -242,9 +277,12 @@ public MetricsBase( this.logErrors = logErrors; this.logSentData = logSentData; this.logResponseStatusText = logResponseStatusText; - checkRelocation(); + if (!skipRelocateCheck) { + checkRelocation(); + } if (enabled) { - // WARNING: Removing the option to opt-out will get your plugin banned from bStats + // WARNING: Removing the option to opt-out will get your plugin banned from + // bStats startSubmitting(); } } @@ -253,32 +291,37 @@ public void addCustomChart(CustomChart chart) { this.customCharts.add(chart); } + public void shutdown() { + scheduler.shutdown(); + } + private void startSubmitting() { final Runnable submitTask = - () -> { - if (!enabled || !checkServiceEnabledSupplier.get()) { - // Submitting data or service is disabled - scheduler.shutdown(); - return; - } - if (submitTaskConsumer != null) { - submitTaskConsumer.accept(this::submitData); - } else { - this.submitData(); - } - }; - // Many servers tend to restart at a fixed time at xx:00 which causes an uneven distribution - // of requests on the - // bStats backend. To circumvent this problem, we introduce some randomness into the initial - // and second delay. - // WARNING: You must not modify and part of this Metrics class, including the submit delay or - // frequency! - // WARNING: Modifying this code will get your plugin banned on bStats. Just don't do it! + () -> { + if (!enabled || !checkServiceEnabledSupplier.get()) { + // Submitting data or service is disabled + scheduler.shutdown(); + return; + } + if (submitTaskConsumer != null) { + submitTaskConsumer.accept(this::submitData); + } else { + this.submitData(); + } + }; + // Many servers tend to restart at a fixed time at xx:00 which causes an uneven + // distribution of requests on the + // bStats backend. To circumvent this problem, we introduce some randomness into + // the initial and second delay. + // WARNING: You must not modify and part of this Metrics class, including the + // submit delay or frequency! + // WARNING: Modifying this code will get your plugin banned on bStats. Just + // don't do it! long initialDelay = (long) (1000 * 60 * (3 + Math.random() * 3)); long secondDelay = (long) (1000 * 60 * (Math.random() * 30)); scheduler.schedule(submitTask, initialDelay, TimeUnit.MILLISECONDS); scheduler.scheduleAtFixedRate( - submitTask, initialDelay + secondDelay, 1000 * 60 * 30, TimeUnit.MILLISECONDS); + submitTask, initialDelay + secondDelay, 1000 * 60 * 30, TimeUnit.MILLISECONDS); } private void submitData() { @@ -287,10 +330,10 @@ private void submitData() { final JsonObjectBuilder serviceJsonBuilder = new JsonObjectBuilder(); appendServiceDataConsumer.accept(serviceJsonBuilder); JsonObjectBuilder.JsonObject[] chartData = - customCharts.stream() - .map(customChart -> customChart.getRequestJsonObject(errorLogger, logErrors)) - .filter(Objects::nonNull) - .toArray(JsonObjectBuilder.JsonObject[]::new); + customCharts.stream() + .map(customChart -> customChart.getRequestJsonObject(errorLogger, logErrors)) + .filter(Objects::nonNull) + .toArray(JsonObjectBuilder.JsonObject[]::new); serviceJsonBuilder.appendField("id", serviceId); serviceJsonBuilder.appendField("customCharts", chartData); baseJsonBuilder.appendField("service", serviceJsonBuilder.build()); @@ -298,17 +341,17 @@ private void submitData() { baseJsonBuilder.appendField("metricsVersion", METRICS_VERSION); JsonObjectBuilder.JsonObject data = baseJsonBuilder.build(); scheduler.execute( - () -> { - try { - // Send the data - sendData(data); - } catch (Exception e) { - // Something went wrong! :( - if (logErrors) { - errorLogger.accept("Could not submit bStats metrics data", e); - } - } - }); + () -> { + try { + // Send the data + sendData(data); + } catch (Exception e) { + // Something went wrong! :( + if (logErrors) { + errorLogger.accept("Could not submit bStats metrics data", e); + } + } + }); } private void sendData(JsonObjectBuilder.JsonObject data) throws Exception { @@ -332,7 +375,7 @@ private void sendData(JsonObjectBuilder.JsonObject data) throws Exception { } StringBuilder builder = new StringBuilder(); try (BufferedReader bufferedReader = - new BufferedReader(new InputStreamReader(connection.getInputStream()))) { + new BufferedReader(new InputStreamReader(connection.getInputStream()))) { String line; while ((line = bufferedReader.readLine()) != null) { builder.append(line); @@ -347,17 +390,17 @@ private void sendData(JsonObjectBuilder.JsonObject data) throws Exception { private void checkRelocation() { // You can use the property to disable the check in your test environment if (System.getProperty("bstats.relocatecheck") == null - || !System.getProperty("bstats.relocatecheck").equals("false")) { - // Maven's Relocate is clever and changes strings, too. So we have to use this little - // "trick" ... :D + || !System.getProperty("bstats.relocatecheck").equals("false")) { + // Maven's Relocate is clever and changes strings, too. So we have to use this + // little "trick" ... :D final String defaultPackage = - new String(new byte[] {'o', 'r', 'g', '.', 'b', 's', 't', 'a', 't', 's'}); + new String(new byte[] {'o', 'r', 'g', '.', 'b', 's', 't', 'a', 't', 's'}); final String examplePackage = - new String(new byte[] {'y', 'o', 'u', 'r', '.', 'p', 'a', 'c', 'k', 'a', 'g', 'e'}); - // We want to make sure no one just copy & pastes the example and uses the wrong package - // names + new String(new byte[] {'y', 'o', 'u', 'r', '.', 'p', 'a', 'c', 'k', 'a', 'g', 'e'}); + // We want to make sure no one just copy & pastes the example and uses the wrong + // package names if (MetricsBase.class.getPackage().getName().startsWith(defaultPackage) - || MetricsBase.class.getPackage().getName().startsWith(examplePackage)) { + || MetricsBase.class.getPackage().getName().startsWith(examplePackage)) { throw new IllegalStateException("bStats Metrics class has not been relocated correctly!"); } } @@ -381,6 +424,72 @@ private static byte[] compress(final String str) throws IOException { } } + public static class AdvancedBarChart extends CustomChart { + + private final Callable> callable; + + /** + * Class constructor. + * + * @param chartId The id of the chart. + * @param callable The callable which is used to request the chart data. + */ + public AdvancedBarChart(String chartId, Callable> callable) { + super(chartId); + this.callable = callable; + } + + @Override + protected JsonObjectBuilder.JsonObject getChartData() throws Exception { + JsonObjectBuilder valuesBuilder = new JsonObjectBuilder(); + Map map = callable.call(); + if (map == null || map.isEmpty()) { + // Null = skip the chart + return null; + } + boolean allSkipped = true; + for (Map.Entry entry : map.entrySet()) { + if (entry.getValue().length == 0) { + // Skip this invalid + continue; + } + allSkipped = false; + valuesBuilder.appendField(entry.getKey(), entry.getValue()); + } + if (allSkipped) { + // Null = skip the chart + return null; + } + return new JsonObjectBuilder().appendField("values", valuesBuilder.build()).build(); + } + } + + public static class SimplePie extends CustomChart { + + private final Callable callable; + + /** + * Class constructor. + * + * @param chartId The id of the chart. + * @param callable The callable which is used to request the chart data. + */ + public SimplePie(String chartId, Callable callable) { + super(chartId); + this.callable = callable; + } + + @Override + protected JsonObjectBuilder.JsonObject getChartData() throws Exception { + String value = callable.call(); + if (value == null || value.isEmpty()) { + // Null = skip the chart + return null; + } + return new JsonObjectBuilder().appendField("value", value).build(); + } + } + public static class DrilldownPie extends CustomChart { private final Callable>> callable; @@ -425,9 +534,9 @@ public JsonObjectBuilder.JsonObject getChartData() throws Exception { } } - public static class AdvancedPie extends CustomChart { + public static class SingleLineChart extends CustomChart { - private final Callable> callable; + private final Callable callable; /** * Class constructor. @@ -435,33 +544,19 @@ public static class AdvancedPie extends CustomChart { * @param chartId The id of the chart. * @param callable The callable which is used to request the chart data. */ - public AdvancedPie(String chartId, Callable> callable) { + public SingleLineChart(String chartId, Callable callable) { super(chartId); this.callable = callable; } @Override protected JsonObjectBuilder.JsonObject getChartData() throws Exception { - JsonObjectBuilder valuesBuilder = new JsonObjectBuilder(); - Map map = callable.call(); - if (map == null || map.isEmpty()) { - // Null = skip the chart - return null; - } - boolean allSkipped = true; - for (Map.Entry entry : map.entrySet()) { - if (entry.getValue() == 0) { - // Skip this invalid - continue; - } - allSkipped = false; - valuesBuilder.appendField(entry.getKey(), entry.getValue()); - } - if (allSkipped) { + int value = callable.call(); + if (value == 0) { // Null = skip the chart return null; } - return new JsonObjectBuilder().appendField("values", valuesBuilder.build()).build(); + return new JsonObjectBuilder().appendField("value", value).build(); } } @@ -505,7 +600,7 @@ protected JsonObjectBuilder.JsonObject getChartData() throws Exception { } } - public static class SimpleBarChart extends CustomChart { + public static class AdvancedPie extends CustomChart { private final Callable> callable; @@ -515,7 +610,7 @@ public static class SimpleBarChart extends CustomChart { * @param chartId The id of the chart. * @param callable The callable which is used to request the chart data. */ - public SimpleBarChart(String chartId, Callable> callable) { + public AdvancedPie(String chartId, Callable> callable) { super(chartId); this.callable = callable; } @@ -528,8 +623,18 @@ protected JsonObjectBuilder.JsonObject getChartData() throws Exception { // Null = skip the chart return null; } + boolean allSkipped = true; for (Map.Entry entry : map.entrySet()) { - valuesBuilder.appendField(entry.getKey(), new int[] {entry.getValue()}); + if (entry.getValue() == 0) { + // Skip this invalid + continue; + } + allSkipped = false; + valuesBuilder.appendField(entry.getKey(), entry.getValue()); + } + if (allSkipped) { + // Null = skip the chart + return null; } return new JsonObjectBuilder().appendField("values", valuesBuilder.build()).build(); } @@ -547,7 +652,7 @@ protected CustomChart(String chartId) { } public JsonObjectBuilder.JsonObject getRequestJsonObject( - BiConsumer errorLogger, boolean logErrors) { + BiConsumer errorLogger, boolean logErrors) { JsonObjectBuilder builder = new JsonObjectBuilder(); builder.appendField("chartId", chartId); try { @@ -569,35 +674,9 @@ public JsonObjectBuilder.JsonObject getRequestJsonObject( protected abstract JsonObjectBuilder.JsonObject getChartData() throws Exception; } - public static class SimplePie extends CustomChart { - - private final Callable callable; - - /** - * Class constructor. - * - * @param chartId The id of the chart. - * @param callable The callable which is used to request the chart data. - */ - public SimplePie(String chartId, Callable callable) { - super(chartId); - this.callable = callable; - } - - @Override - protected JsonObjectBuilder.JsonObject getChartData() throws Exception { - String value = callable.call(); - if (value == null || value.isEmpty()) { - // Null = skip the chart - return null; - } - return new JsonObjectBuilder().appendField("value", value).build(); - } - } - - public static class AdvancedBarChart extends CustomChart { + public static class SimpleBarChart extends CustomChart { - private final Callable> callable; + private final Callable> callable; /** * Class constructor. @@ -605,7 +684,7 @@ public static class AdvancedBarChart extends CustomChart { * @param chartId The id of the chart. * @param callable The callable which is used to request the chart data. */ - public AdvancedBarChart(String chartId, Callable> callable) { + public SimpleBarChart(String chartId, Callable> callable) { super(chartId); this.callable = callable; } @@ -613,54 +692,18 @@ public AdvancedBarChart(String chartId, Callable> callable) { @Override protected JsonObjectBuilder.JsonObject getChartData() throws Exception { JsonObjectBuilder valuesBuilder = new JsonObjectBuilder(); - Map map = callable.call(); + Map map = callable.call(); if (map == null || map.isEmpty()) { // Null = skip the chart return null; } - boolean allSkipped = true; - for (Map.Entry entry : map.entrySet()) { - if (entry.getValue().length == 0) { - // Skip this invalid - continue; - } - allSkipped = false; - valuesBuilder.appendField(entry.getKey(), entry.getValue()); - } - if (allSkipped) { - // Null = skip the chart - return null; + for (Map.Entry entry : map.entrySet()) { + valuesBuilder.appendField(entry.getKey(), new int[] {entry.getValue()}); } return new JsonObjectBuilder().appendField("values", valuesBuilder.build()).build(); } } - public static class SingleLineChart extends CustomChart { - - private final Callable callable; - - /** - * Class constructor. - * - * @param chartId The id of the chart. - * @param callable The callable which is used to request the chart data. - */ - public SingleLineChart(String chartId, Callable callable) { - super(chartId); - this.callable = callable; - } - - @Override - protected JsonObjectBuilder.JsonObject getChartData() throws Exception { - int value = callable.call(); - if (value == 0) { - // Null = skip the chart - return null; - } - return new JsonObjectBuilder().appendField("value", value).build(); - } - } - /** * An extremely simple JSON builder. * @@ -742,9 +785,9 @@ public JsonObjectBuilder appendField(String key, String[] values) { throw new IllegalArgumentException("JSON values must not be null"); } String escapedValues = - Arrays.stream(values) - .map(value -> "\"" + escape(value) + "\"") - .collect(Collectors.joining(",")); + Arrays.stream(values) + .map(value -> "\"" + escape(value) + "\"") + .collect(Collectors.joining(",")); appendFieldUnescaped(key, "[" + escapedValues + "]"); return this; } @@ -761,7 +804,7 @@ public JsonObjectBuilder appendField(String key, int[] values) { throw new IllegalArgumentException("JSON values must not be null"); } String escapedValues = - Arrays.stream(values).mapToObj(String::valueOf).collect(Collectors.joining(",")); + Arrays.stream(values).mapToObj(String::valueOf).collect(Collectors.joining(",")); appendFieldUnescaped(key, "[" + escapedValues + "]"); return this; } @@ -778,7 +821,7 @@ public JsonObjectBuilder appendField(String key, JsonObject[] values) { throw new IllegalArgumentException("JSON values must not be null"); } String escapedValues = - Arrays.stream(values).map(JsonObject::toString).collect(Collectors.joining(",")); + Arrays.stream(values).map(JsonObject::toString).collect(Collectors.joining(",")); appendFieldUnescaped(key, "[" + escapedValues + "]"); return this; } @@ -944,13 +987,13 @@ private void setupConfig() throws IOException { private void writeConfig() throws IOException { List configContent = new ArrayList<>(); configContent.add( - "# bStats (https://bStats.org) collects some basic information for plugin authors, like"); + "# bStats (https://bStats.org) collects some basic information for plugin authors, like"); configContent.add( - "# how many people use their plugin and their total player count. It's recommended to keep"); + "# how many people use their plugin and their total player count. It's recommended to keep"); configContent.add( - "# bStats enabled, but if you're not comfortable with this, you can turn this setting off."); + "# bStats enabled, but if you're not comfortable with this, you can turn this setting off."); configContent.add( - "# There is no performance penalty associated with having metrics enabled, and data sent to"); + "# There is no performance penalty associated with having metrics enabled, and data sent to"); configContent.add("# bStats is fully anonymous."); configContent.add("enabled=" + defaultEnabled); configContent.add("server-uuid=" + UUID.randomUUID().toString()); @@ -971,7 +1014,7 @@ private void readConfig() throws IOException { logErrors = getConfigValue("log-errors", lines).map("true"::equals).orElse(false); logSentData = getConfigValue("log-sent-data", lines).map("true"::equals).orElse(false); logResponseStatusText = - getConfigValue("log-response-status-text", lines).map("true"::equals).orElse(false); + getConfigValue("log-response-status-text", lines).map("true"::equals).orElse(false); } /** @@ -983,9 +1026,9 @@ private void readConfig() throws IOException { */ private Optional getConfigValue(String key, List lines) { return lines.stream() - .filter(line -> line.startsWith(key + "=")) - .map(line -> line.replaceFirst(Pattern.quote(key + "="), "")) - .findFirst(); + .filter(line -> line.startsWith(key + "=")) + .map(line -> line.replaceFirst(Pattern.quote(key + "="), "")) + .findFirst(); } /** @@ -999,7 +1042,7 @@ private List readFile(File file) throws IOException { return null; } try (FileReader fileReader = new FileReader(file); - BufferedReader bufferedReader = new BufferedReader(fileReader)) { + BufferedReader bufferedReader = new BufferedReader(fileReader)) { return bufferedReader.lines().collect(Collectors.toList()); } } @@ -1016,7 +1059,7 @@ private void writeFile(File file, List lines) throws IOException { file.createNewFile(); } try (FileWriter fileWriter = new FileWriter(file); - BufferedWriter bufferedWriter = new BufferedWriter(fileWriter)) { + BufferedWriter bufferedWriter = new BufferedWriter(fileWriter)) { for (String line : lines) { bufferedWriter.write(line); bufferedWriter.newLine(); From 1d4c514dff0794284709872c2d7cea8e862d850e Mon Sep 17 00:00:00 2001 From: Matt Date: Wed, 25 Jun 2025 06:04:36 -0400 Subject: [PATCH 2/2] update: code clean up --- .../streamline/apib/depends/PAPIDepend.java | 2 +- .../base/runnables/PlayerChecker.java | 3 +- .../net/streamline/platform/BasePlugin.java | 11 +- .../net/streamline/platform/Messenger.java | 5 +- .../platform/commands/ProperCommand.java | 12 +- .../platform/listeners/PlatformListener.java | 39 +++- .../platform/savables/UserManager.java | 15 +- .../main/java/singularity/Singularity.java | 4 + .../singularity/command/CommandExecution.java | 2 +- .../data/console/CosmicSender.java | 57 ++++- .../data/players/CosmicPlayer.java | 4 +- ...enderEvent.java => CosmicSenderEvent.java} | 4 +- .../players/events/CreatePlayerEvent.java | 19 ++ .../players/events/CreateSenderEvent.java | 2 +- .../players/events/DeletePlayerEvent.java | 13 ++ .../players/events/DeleteSenderEvent.java | 2 +- .../data/players/events/LoadPlayerEvent.java | 13 ++ ...mSenderEvent.java => LoadSenderEvent.java} | 4 +- .../data/players/events/SavePlayerEvent.java | 13 ++ ...derSaveEvent.java => SaveSenderEvent.java} | 4 +- .../players/events/UnloadPlayerEvent.java | 13 ++ .../players/events/UnloadSenderEvent.java | 9 + .../events/UnloadStreamSenderEvent.java | 9 - .../players/events/UserNameUpdateEvent.java | 2 +- .../data/players/location/CosmicLocation.java | 6 + .../singularity/database/CoreDBOperator.java | 197 +++++++++++++++++- .../java/singularity/database/Statements.java | 15 ++ .../events/server/CosmicChatEvent.java | 4 +- .../events/server/KickedFromServerEvent.java | 4 +- .../singularity/events/server/LoginEvent.java | 4 +- .../events/server/LogoutEvent.java | 4 +- .../events/server/world/BlockBreakEvent.java | 16 ++ .../events/server/world/BlockEvent.java | 16 ++ .../events/server/world/BlockPlaceEvent.java | 16 ++ .../interfaces/ISingularityExtension.java | 2 + .../singularity/interfaces/IUserManager.java | 5 +- .../java/singularity/loading/Loadable.java | 55 ++++- .../PlayerLocationMessageBuilder.java | 6 +- .../builders/ProxyParseMessageBuilder.java | 6 +- .../builders/ServerConnectMessageBuilder.java | 6 +- .../java/singularity/modules/ModuleUtils.java | 4 +- .../objects/world/CosmicBlock.java | 77 +++++++ .../singularity/permissions/MetaValue.java | 4 +- .../java/singularity/utils/MessageUtils.java | 18 +- .../java/singularity/utils/UserUtils.java | 172 +++++++++++---- .../java/singularity/utils/UuidUtils.java | 50 +++++ .../net/streamline/base/TenSecondTimer.java | 6 +- .../base/runnables/PlayerChecker.java | 3 +- .../net/streamline/platform/BasePlugin.java | 9 +- .../net/streamline/platform/Messenger.java | 5 +- .../platform/commands/ProperCommand.java | 12 +- .../commands/StreamlineSpigotCommand.java | 4 +- .../platform/listeners/PlatformListener.java | 76 ++++++- .../platform/savables/UserManager.java | 16 +- .../base/runnables/PlayerChecker.java | 3 +- .../net/streamline/platform/BasePlugin.java | 9 +- .../net/streamline/platform/Messenger.java | 5 +- .../platform/commands/ProperCommand.java | 12 +- .../platform/listeners/PlatformListener.java | 40 +++- .../platform/savables/UserManager.java | 14 +- 60 files changed, 1014 insertions(+), 148 deletions(-) rename singularity-api/src/main/java/singularity/data/players/events/{StreamSenderEvent.java => CosmicSenderEvent.java} (83%) create mode 100644 singularity-api/src/main/java/singularity/data/players/events/CreatePlayerEvent.java create mode 100644 singularity-api/src/main/java/singularity/data/players/events/DeletePlayerEvent.java create mode 100644 singularity-api/src/main/java/singularity/data/players/events/LoadPlayerEvent.java rename singularity-api/src/main/java/singularity/data/players/events/{LoadStreamSenderEvent.java => LoadSenderEvent.java} (50%) create mode 100644 singularity-api/src/main/java/singularity/data/players/events/SavePlayerEvent.java rename singularity-api/src/main/java/singularity/data/players/events/{SenderSaveEvent.java => SaveSenderEvent.java} (52%) create mode 100644 singularity-api/src/main/java/singularity/data/players/events/UnloadPlayerEvent.java create mode 100644 singularity-api/src/main/java/singularity/data/players/events/UnloadSenderEvent.java delete mode 100644 singularity-api/src/main/java/singularity/data/players/events/UnloadStreamSenderEvent.java create mode 100644 singularity-api/src/main/java/singularity/events/server/world/BlockBreakEvent.java create mode 100644 singularity-api/src/main/java/singularity/events/server/world/BlockEvent.java create mode 100644 singularity-api/src/main/java/singularity/events/server/world/BlockPlaceEvent.java create mode 100644 singularity-api/src/main/java/singularity/objects/world/CosmicBlock.java diff --git a/backend-api/src/main/java/net/streamline/apib/depends/PAPIDepend.java b/backend-api/src/main/java/net/streamline/apib/depends/PAPIDepend.java index d88eef3a..de4ed28f 100644 --- a/backend-api/src/main/java/net/streamline/apib/depends/PAPIDepend.java +++ b/backend-api/src/main/java/net/streamline/apib/depends/PAPIDepend.java @@ -60,7 +60,7 @@ public boolean register() { @Override public @Nullable String onRequest(OfflinePlayer player, @NotNull String params) { - CosmicPlayer streamPlayer = ModuleUtils.getOrCreatePlayer(player.getUniqueId().toString()); + CosmicPlayer streamPlayer = ModuleUtils.getOrCreatePlayer(player.getUniqueId().toString()).orElse(null); if (streamPlayer == null) return MainMessagesHandler.MESSAGES.DEFAULTS.PLACEHOLDERS.IS_NULL.get(); String toParse; if (params.startsWith("!")) toParse = "%" + params.substring("!".length()) + "%"; diff --git a/bungee/src/main/java/net/streamline/base/runnables/PlayerChecker.java b/bungee/src/main/java/net/streamline/base/runnables/PlayerChecker.java index c8fd0f24..094b163a 100644 --- a/bungee/src/main/java/net/streamline/base/runnables/PlayerChecker.java +++ b/bungee/src/main/java/net/streamline/base/runnables/PlayerChecker.java @@ -17,7 +17,8 @@ public void run() { StreamlineBungee.getPlayersByUUID().forEach((uuid, player) -> { if (UserUtils.isLoaded(player.getUniqueId().toString())) return; - CosmicPlayer streamPlayer = UserUtils.getOrCreatePlayer(player.getUniqueId().toString()); + CosmicPlayer streamPlayer = UserUtils.getOrCreatePlayer(player.getUniqueId().toString()).orElse(null); + if (streamPlayer == null) return; streamPlayer.setCurrentIp(UserManager.getInstance().parsePlayerIP(player)); streamPlayer.setCurrentName(player.getName()); diff --git a/bungee/src/main/java/net/streamline/platform/BasePlugin.java b/bungee/src/main/java/net/streamline/platform/BasePlugin.java index d21cf1b1..619dd3da 100644 --- a/bungee/src/main/java/net/streamline/platform/BasePlugin.java +++ b/bungee/src/main/java/net/streamline/platform/BasePlugin.java @@ -184,7 +184,9 @@ public static void registerListener(Listener listener) { ConcurrentSkipListSet players = new ConcurrentSkipListSet<>(); for (ProxiedPlayer player : onlinePlayers()) { - players.add(getUserManager().getOrCreatePlayer(player)); + CosmicPlayer cosmicPlayer = getUserManager().getOrCreatePlayer(player).orElse(null); + if (cosmicPlayer == null) continue; + players.add(cosmicPlayer); } return players; @@ -212,6 +214,11 @@ public ConcurrentSkipListSet getOnlinePlayerNames() { return r; } + @Override + public boolean isOfflineMode() { + return ! getInstance().getProxy().getConfig().isOnlineMode(); + } + @Override public long getConnectionThrottle() { return getInstance().getProxy().getConfig().getThrottle(); @@ -324,7 +331,7 @@ public void sendResourcePack(CosmicResourcePack resourcePack, String uuid) { public void sendResourcePack(CosmicResourcePack resourcePack, ProxiedPlayer player) { if (player == null) return; - CosmicPlayer streamPlayer = getUserManager().getOrCreatePlayer(player); + CosmicPlayer streamPlayer = getUserManager().getOrCreatePlayer(player).orElse(null); if (streamPlayer == null) return; ResourcePackMessageBuilder.build(streamPlayer, true, streamPlayer, resourcePack).send(); diff --git a/bungee/src/main/java/net/streamline/platform/Messenger.java b/bungee/src/main/java/net/streamline/platform/Messenger.java index aa094854..377bf768 100644 --- a/bungee/src/main/java/net/streamline/platform/Messenger.java +++ b/bungee/src/main/java/net/streamline/platform/Messenger.java @@ -212,7 +212,10 @@ public BaseComponent[] codedText(String from) { } public String replaceAllPlayerBungee(CommandSender sender, String of) { - CosmicSender s = UserManager.getInstance().getOrCreateSender(sender); + CosmicSender s = UserManager.getInstance().getOrCreateSender(sender).orElse(null); + if (s == null) { + return of; + } return MessageUtils.replaceAllPlayerBungee(s, of); } diff --git a/bungee/src/main/java/net/streamline/platform/commands/ProperCommand.java b/bungee/src/main/java/net/streamline/platform/commands/ProperCommand.java index b8ed24c1..2c28af2a 100644 --- a/bungee/src/main/java/net/streamline/platform/commands/ProperCommand.java +++ b/bungee/src/main/java/net/streamline/platform/commands/ProperCommand.java @@ -26,7 +26,11 @@ public ProperCommand(CosmicCommand parent) { @Override public void execute(@NotNull CommandSender sender, @NotNull String[] args) { - CosmicSender s = UserManager.getInstance().getOrCreateSender(sender); + CosmicSender s = UserManager.getInstance().getOrCreateSender(sender).orElse(null); + if (s == null) { + MessageUtils.logWarning("Command execution failed: Sender is not a CosmicSender."); + return; + } parent.baseRun(s, args); } @@ -36,7 +40,11 @@ public Iterable onTabComplete(CommandSender sender, String[] args) { if (args == null) args = new String[] { "" }; if (args.length < 1) args = new String[] { "" }; - CosmicSender s = UserManager.getInstance().getOrCreateSender(sender); + CosmicSender s = UserManager.getInstance().getOrCreateSender(sender).orElse(null); + if (s == null) { + MessageUtils.logWarning("Tab completion failed: Sender is not a CosmicSender."); + return new ArrayList<>(); + } ConcurrentSkipListSet r = parent.baseTabComplete(s, args); diff --git a/bungee/src/main/java/net/streamline/platform/listeners/PlatformListener.java b/bungee/src/main/java/net/streamline/platform/listeners/PlatformListener.java index 44568642..e2ee1d83 100644 --- a/bungee/src/main/java/net/streamline/platform/listeners/PlatformListener.java +++ b/bungee/src/main/java/net/streamline/platform/listeners/PlatformListener.java @@ -59,7 +59,8 @@ public void onPreJoin(PreLoginEvent event) { uuid = optional.get(); } - CosmicPlayer streamPlayer = UserUtils.getOrCreatePlayer(uuid); + CosmicPlayer streamPlayer = UserUtils.getOrCreatePlayer(uuid).orElse(null); + if (streamPlayer == null) return; streamPlayer.setCurrentName(name); // streamPlayer.getLocation().setServerName(proxiedPlayer.getServer().getInfo().getName()); @@ -91,7 +92,11 @@ public void onJoin(PostLoginEvent event) { UuidManager.cachePlayer(player.getUniqueId().toString(), player.getName(), UserManager.getInstance().parsePlayerIP(player)); - CosmicPlayer streamPlayer = UserUtils.getOrCreatePlayer(player.getUniqueId().toString()); + CosmicPlayer streamPlayer = UserUtils.getOrCreatePlayer(player.getUniqueId().toString()).orElse(null); + if (streamPlayer == null) { + MessageUtils.logWarning("Failed to create CosmicPlayer for " + player.getName() + " (" + player.getUniqueId() + ")"); + return; + } streamPlayer.setCurrentIp(UserManager.getInstance().parsePlayerIP(player)); streamPlayer.setCurrentName(player.getName()); @@ -106,7 +111,11 @@ public void onJoin(PostLoginEvent event) { public void onLeave(PlayerDisconnectEvent event) { ProxiedPlayer player = event.getPlayer(); - CosmicPlayer streamPlayer = UserUtils.getOrCreatePlayer(player.getUniqueId().toString()); + CosmicPlayer streamPlayer = UserUtils.getOrCreatePlayer(player.getUniqueId().toString()).orElse(null); + if (streamPlayer == null) { + MessageUtils.logWarning("Failed to create CosmicPlayer for " + player.getName() + " (" + player.getUniqueId() + ") on disconnect."); + return; + } LogoutEvent logoutEvent = new LogoutEvent(streamPlayer); ModuleUtils.fireEvent(logoutEvent); @@ -119,7 +128,11 @@ public void onLeave(PlayerDisconnectEvent event) { public void onServerSwitch(ServerConnectedEvent event) { ProxiedPlayer player = event.getPlayer(); - CosmicPlayer streamPlayer = UserUtils.getOrCreatePlayer(player.getUniqueId().toString()); + CosmicPlayer streamPlayer = UserUtils.getOrCreatePlayer(player.getUniqueId().toString()).orElse(null); + if (streamPlayer == null) { + MessageUtils.logWarning("Failed to create CosmicPlayer for " + player.getName() + " (" + player.getUniqueId() + ") on server switch."); + return; + } streamPlayer.setServerName(event.getServer().getInfo().getName()); } @@ -128,7 +141,11 @@ public void onServerSwitch(ServerConnectedEvent event) { public void onChat(ChatEvent event) { ProxiedPlayer player = (ProxiedPlayer) event.getSender(); - CosmicPlayer streamPlayer = UserUtils.getOrCreatePlayer(player.getUniqueId().toString()); + CosmicPlayer streamPlayer = UserUtils.getOrCreatePlayer(player.getUniqueId().toString()).orElse(null); + if (streamPlayer == null) { + MessageUtils.logWarning("Failed to create CosmicPlayer for " + player.getName() + " (" + player.getUniqueId() + ") on chat event."); + return; + } CosmicChatEvent chatEvent = new CosmicChatEvent(streamPlayer, event.getMessage()); StreamlineBungee.getInstance().fireEvent(chatEvent, true); @@ -151,7 +168,11 @@ public void onPluginMessage(PluginMessageEvent event) { String tag = event.getTag(); if (event.getData() == null) return; - CosmicPlayer streamPlayer = UserUtils.getOrCreatePlayer(player.getUniqueId().toString()); + CosmicPlayer streamPlayer = UserUtils.getOrCreatePlayer(player.getUniqueId().toString()).orElse(null); + if (streamPlayer == null) { + MessageUtils.logWarning("Failed to create CosmicPlayer for " + player.getName() + " (" + player.getUniqueId() + ") on plugin message event."); + return; + } try { ProxiedMessage messageIn = new ProxiedMessage(streamPlayer, false, event.getData(), tag); @@ -250,7 +271,11 @@ public void onServerKick(ServerKickEvent event) { toName = "none"; } - CosmicPlayer streamPlayer = UserUtils.getOrCreatePlayer(player.getUniqueId().toString()); + CosmicPlayer streamPlayer = UserUtils.getOrCreatePlayer(player.getUniqueId().toString()).orElse(null); + if (streamPlayer == null) { + MessageUtils.logWarning("Failed to create CosmicPlayer for " + player.getName() + " (" + player.getUniqueId() + ") on server kick event."); + return; + } KickedFromServerEvent kickedFromServerEvent = new KickedFromServerEvent(streamPlayer, fromName, kickedReason, toName); ModuleUtils.fireEvent(kickedFromServerEvent); diff --git a/bungee/src/main/java/net/streamline/platform/savables/UserManager.java b/bungee/src/main/java/net/streamline/platform/savables/UserManager.java index 2389e266..8e9c5333 100644 --- a/bungee/src/main/java/net/streamline/platform/savables/UserManager.java +++ b/bungee/src/main/java/net/streamline/platform/savables/UserManager.java @@ -25,6 +25,7 @@ import java.net.InetSocketAddress; import java.net.SocketAddress; +import java.util.Optional; import java.util.concurrent.ConcurrentSkipListMap; import java.util.concurrent.ConcurrentSkipListSet; @@ -37,17 +38,17 @@ public UserManager() { } @Override - public CosmicPlayer getOrCreatePlayer(ProxiedPlayer player) { + public Optional getOrCreatePlayer(ProxiedPlayer player) { return UserUtils.getOrCreatePlayer(player.getUniqueId().toString()); } @Override - public CosmicSender getOrCreateSender(CommandSender sender) { + public Optional getOrCreateSender(CommandSender sender) { if (isConsole(sender)) { - return UserUtils.getConsole(); + return Optional.ofNullable(UserUtils.getConsole()); } else { ProxiedPlayer player = (ProxiedPlayer) sender; - return getOrCreatePlayer(player); + return getOrCreatePlayer(player).map(s -> s); } } @@ -131,7 +132,7 @@ public ConcurrentSkipListSet getUsersOn(String server) { StreamlineBungee.getInstance().getProxy().getServers().values().forEach(a -> { a.getPlayers().forEach(b -> { - CosmicPlayer player = getOrCreatePlayer(b); + CosmicPlayer player = getOrCreatePlayer(b).orElse(null); if (player == null) return; if (player.isOnline() && player.getServerName().equals(server)) r.add(player); }); @@ -204,7 +205,9 @@ public ConcurrentSkipListMap ensurePlayers() { for (ProxiedPlayer player : BasePlugin.onlinePlayers()) { if (UserUtils.isLoaded(player.getUniqueId().toString())) { - r.put(player.getUniqueId().toString(), getOrCreatePlayer(player)); + CosmicPlayer cosmicPlayer = getOrCreatePlayer(player).orElse(null); + if (cosmicPlayer == null) continue; + r.put(player.getUniqueId().toString(), cosmicPlayer); } } diff --git a/singularity-api/src/main/java/singularity/Singularity.java b/singularity-api/src/main/java/singularity/Singularity.java index e6033f04..2f1a87b7 100644 --- a/singularity-api/src/main/java/singularity/Singularity.java +++ b/singularity-api/src/main/java/singularity/Singularity.java @@ -312,4 +312,8 @@ public static

RealPlayer

getPlayerFromUuid(String uuid) { public static void sendConsoleMessage(String message) { getInstance().getConsoleHolder().sendConsoleMessage(message); } + + public static boolean isOfflineMode() { + return getInstance().getPlatform().isOfflineMode(); + } } diff --git a/singularity-api/src/main/java/singularity/command/CommandExecution.java b/singularity-api/src/main/java/singularity/command/CommandExecution.java index 6a9fa549..9ae4f9aa 100644 --- a/singularity-api/src/main/java/singularity/command/CommandExecution.java +++ b/singularity-api/src/main/java/singularity/command/CommandExecution.java @@ -36,7 +36,7 @@ public Optional getSender() { String name = split[1]; return UserUtils.getOrCreateSenderByName(name); } else if (classifier.equals("u")) { - return Optional.of(UserUtils.getOrCreateSender(split[1])); + return UserUtils.getOrCreateSender(split[1]); } else { return UserUtils.getOrCreateSenderByName(split[1]); } diff --git a/singularity-api/src/main/java/singularity/data/console/CosmicSender.java b/singularity-api/src/main/java/singularity/data/console/CosmicSender.java index c2906244..be709e7a 100644 --- a/singularity-api/src/main/java/singularity/data/console/CosmicSender.java +++ b/singularity-api/src/main/java/singularity/data/console/CosmicSender.java @@ -5,6 +5,8 @@ import singularity.Singularity; import singularity.configs.given.GivenConfigs; import singularity.data.players.CosmicPlayer; +import singularity.data.players.events.CreatePlayerEvent; +import singularity.data.players.events.CreateSenderEvent; import singularity.data.players.location.CosmicLocation; import singularity.data.players.meta.SenderMeta; import singularity.data.players.permissions.SenderPermissions; @@ -13,6 +15,7 @@ import singularity.loading.Loadable; import singularity.modules.ModuleUtils; import singularity.text.UsersReplacements; +import singularity.utils.MessageUtils; import singularity.utils.UserUtils; import java.util.Date; @@ -52,14 +55,19 @@ public void setIdentifier(String identifier) { private boolean proxyTouched; @Setter - private boolean loadComplete = false; + private boolean fullyLoaded = false; + + @Setter + private boolean temporary = false; @Setter private UsersReplacements replacements; - public CosmicSender(String uuid) { + public CosmicSender(String uuid, boolean temporary) { this.uuid = uuid; + this.temporary = temporary; + this.firstJoinDate = new Date(); this.lastJoinDate = new Date(); @@ -75,6 +83,10 @@ public CosmicSender(String uuid) { this.proxyTouched = Singularity.isProxy(); } + public CosmicSender(String uuid) { + this(uuid, false); + } + public CosmicSender() { this( GivenConfigs.getMainConfig().getConsoleDiscriminator() @@ -96,10 +108,12 @@ public void setCurrentNameAsProper() { setCurrentName(Singularity.getInstance().getUserManager().getUsername(getUuid())); } - public void save() { - // Do nothing + @Override + public void save(boolean async) { + UserUtils.saveSender(this, async); } + @Override public void load() { if (this instanceof CosmicPlayer) { UserUtils.loadPlayer((CosmicPlayer) this); @@ -109,10 +123,17 @@ public void load() { UserUtils.loadSender(this); } + @Override public void unload() { UserUtils.unloadSender(this); } + @Override + public void saveAndUnload(boolean async) { + save(async); + unload(); + } + public boolean isLoaded() { return UserUtils.isLoaded(getUuid()); } @@ -122,11 +143,16 @@ public void ensureLoaded() { } @Override - public CosmicPlayer augment(CompletableFuture> future) { - loadComplete = false; + public CosmicPlayer augment(CompletableFuture> future, boolean isGet) { + fullyLoaded = false; + + future.whenComplete((optional, error) -> { + if (error != null) { + MessageUtils.logWarning("Failed to augment CosmicSender for UUID: " + getUuid(), error); + this.fullyLoaded = true; + return; + } - CompletableFuture.runAsync(() -> { - Optional optional = future.join(); if (optional.isPresent()) { CosmicPlayer sender = optional.get(); @@ -143,10 +169,23 @@ public CosmicPlayer augment(CompletableFuture> future) { augmentMore(sender); setCurrentNameAsProper(); // might need to be forced... need to check this... + } else { + if (! isGet) { + this.temporary = false; + if (this instanceof CosmicPlayer) new CreatePlayerEvent((CosmicPlayer) this).fire(); + else new CreateSenderEvent(this).fire(); + MessageUtils.logInfo("Created new CosmicPlayer for UUID: " + getUuid() + " (Console: " + isConsole() + ")"); + + this.save(); + } else { + unload(); + fullyLoaded = true; + return; + } } ensureLoaded(); - loadComplete = true; + fullyLoaded = true; }); return (CosmicPlayer) this; diff --git a/singularity-api/src/main/java/singularity/data/players/CosmicPlayer.java b/singularity-api/src/main/java/singularity/data/players/CosmicPlayer.java index 05e743d2..ab985be5 100644 --- a/singularity-api/src/main/java/singularity/data/players/CosmicPlayer.java +++ b/singularity-api/src/main/java/singularity/data/players/CosmicPlayer.java @@ -4,7 +4,7 @@ import lombok.Setter; import singularity.Singularity; import singularity.data.console.CosmicSender; -import singularity.data.players.events.SenderSaveEvent; +import singularity.data.players.events.SaveSenderEvent; import singularity.data.players.location.CosmicLocation; import singularity.data.players.location.PlayerWorld; import singularity.data.server.CosmicServer; @@ -87,7 +87,7 @@ public void save() { getDatabase().savePlayer(this); - new SenderSaveEvent(this).fire(); + new SaveSenderEvent(this).fire(); } @Override diff --git a/singularity-api/src/main/java/singularity/data/players/events/StreamSenderEvent.java b/singularity-api/src/main/java/singularity/data/players/events/CosmicSenderEvent.java similarity index 83% rename from singularity-api/src/main/java/singularity/data/players/events/StreamSenderEvent.java rename to singularity-api/src/main/java/singularity/data/players/events/CosmicSenderEvent.java index 1386123d..e56cca31 100644 --- a/singularity-api/src/main/java/singularity/data/players/events/StreamSenderEvent.java +++ b/singularity-api/src/main/java/singularity/data/players/events/CosmicSenderEvent.java @@ -7,10 +7,10 @@ import singularity.events.CosmicEvent; @Getter @Setter -public class StreamSenderEvent extends CosmicEvent { +public class CosmicSenderEvent extends CosmicEvent { private CosmicSender sender; - public StreamSenderEvent(CosmicSender sender) { + public CosmicSenderEvent(CosmicSender sender) { this.sender = sender; } diff --git a/singularity-api/src/main/java/singularity/data/players/events/CreatePlayerEvent.java b/singularity-api/src/main/java/singularity/data/players/events/CreatePlayerEvent.java new file mode 100644 index 00000000..3f7f7bfe --- /dev/null +++ b/singularity-api/src/main/java/singularity/data/players/events/CreatePlayerEvent.java @@ -0,0 +1,19 @@ +package singularity.data.players.events; + +import singularity.data.players.CosmicPlayer; + +import java.util.Date; + +public class CreatePlayerEvent extends CreateSenderEvent { + public CreatePlayerEvent(CosmicPlayer player) { + super(player); + } + + public CosmicPlayer getPlayer() { + return (CosmicPlayer) super.getSender(); + } + + public Date getCreationDate() { + return getPlayer().getFirstJoinDate(); + } +} diff --git a/singularity-api/src/main/java/singularity/data/players/events/CreateSenderEvent.java b/singularity-api/src/main/java/singularity/data/players/events/CreateSenderEvent.java index a898d8ad..10525ce8 100644 --- a/singularity-api/src/main/java/singularity/data/players/events/CreateSenderEvent.java +++ b/singularity-api/src/main/java/singularity/data/players/events/CreateSenderEvent.java @@ -2,7 +2,7 @@ import singularity.data.console.CosmicSender; -public class CreateSenderEvent extends StreamSenderEvent { +public class CreateSenderEvent extends CosmicSenderEvent { public CreateSenderEvent(CosmicSender player) { super(player); } diff --git a/singularity-api/src/main/java/singularity/data/players/events/DeletePlayerEvent.java b/singularity-api/src/main/java/singularity/data/players/events/DeletePlayerEvent.java new file mode 100644 index 00000000..875cf706 --- /dev/null +++ b/singularity-api/src/main/java/singularity/data/players/events/DeletePlayerEvent.java @@ -0,0 +1,13 @@ +package singularity.data.players.events; + +import singularity.data.players.CosmicPlayer; + +public class DeletePlayerEvent extends DeleteSenderEvent { + public DeletePlayerEvent(CosmicPlayer player) { + super(player); + } + + public CosmicPlayer getPlayer() { + return (CosmicPlayer) super.getSender(); + } +} diff --git a/singularity-api/src/main/java/singularity/data/players/events/DeleteSenderEvent.java b/singularity-api/src/main/java/singularity/data/players/events/DeleteSenderEvent.java index a011cb0c..52d9600d 100644 --- a/singularity-api/src/main/java/singularity/data/players/events/DeleteSenderEvent.java +++ b/singularity-api/src/main/java/singularity/data/players/events/DeleteSenderEvent.java @@ -2,7 +2,7 @@ import singularity.data.console.CosmicSender; -public class DeleteSenderEvent extends StreamSenderEvent { +public class DeleteSenderEvent extends CosmicSenderEvent { public DeleteSenderEvent(CosmicSender player) { super(player); } diff --git a/singularity-api/src/main/java/singularity/data/players/events/LoadPlayerEvent.java b/singularity-api/src/main/java/singularity/data/players/events/LoadPlayerEvent.java new file mode 100644 index 00000000..c14da191 --- /dev/null +++ b/singularity-api/src/main/java/singularity/data/players/events/LoadPlayerEvent.java @@ -0,0 +1,13 @@ +package singularity.data.players.events; + +import singularity.data.players.CosmicPlayer; + +public class LoadPlayerEvent extends LoadSenderEvent { + public LoadPlayerEvent(CosmicPlayer player) { + super(player); + } + + public CosmicPlayer getPlayer() { + return (CosmicPlayer) super.getSender(); + } +} diff --git a/singularity-api/src/main/java/singularity/data/players/events/LoadStreamSenderEvent.java b/singularity-api/src/main/java/singularity/data/players/events/LoadSenderEvent.java similarity index 50% rename from singularity-api/src/main/java/singularity/data/players/events/LoadStreamSenderEvent.java rename to singularity-api/src/main/java/singularity/data/players/events/LoadSenderEvent.java index ce43d798..0dafc571 100644 --- a/singularity-api/src/main/java/singularity/data/players/events/LoadStreamSenderEvent.java +++ b/singularity-api/src/main/java/singularity/data/players/events/LoadSenderEvent.java @@ -2,8 +2,8 @@ import singularity.data.console.CosmicSender; -public class LoadStreamSenderEvent extends StreamSenderEvent { - public LoadStreamSenderEvent(CosmicSender player) { +public class LoadSenderEvent extends CosmicSenderEvent { + public LoadSenderEvent(CosmicSender player) { super(player); } } diff --git a/singularity-api/src/main/java/singularity/data/players/events/SavePlayerEvent.java b/singularity-api/src/main/java/singularity/data/players/events/SavePlayerEvent.java new file mode 100644 index 00000000..e34befb1 --- /dev/null +++ b/singularity-api/src/main/java/singularity/data/players/events/SavePlayerEvent.java @@ -0,0 +1,13 @@ +package singularity.data.players.events; + +import singularity.data.players.CosmicPlayer; + +public class SavePlayerEvent extends SaveSenderEvent { + public SavePlayerEvent(CosmicPlayer player) { + super(player); + } + + public CosmicPlayer getPlayer() { + return (CosmicPlayer) super.getSender(); + } +} diff --git a/singularity-api/src/main/java/singularity/data/players/events/SenderSaveEvent.java b/singularity-api/src/main/java/singularity/data/players/events/SaveSenderEvent.java similarity index 52% rename from singularity-api/src/main/java/singularity/data/players/events/SenderSaveEvent.java rename to singularity-api/src/main/java/singularity/data/players/events/SaveSenderEvent.java index 5fc24017..5005530b 100644 --- a/singularity-api/src/main/java/singularity/data/players/events/SenderSaveEvent.java +++ b/singularity-api/src/main/java/singularity/data/players/events/SaveSenderEvent.java @@ -2,8 +2,8 @@ import singularity.data.console.CosmicSender; -public class SenderSaveEvent extends StreamSenderEvent { - public SenderSaveEvent(CosmicSender sender) { +public class SaveSenderEvent extends CosmicSenderEvent { + public SaveSenderEvent(CosmicSender sender) { super(sender); } } diff --git a/singularity-api/src/main/java/singularity/data/players/events/UnloadPlayerEvent.java b/singularity-api/src/main/java/singularity/data/players/events/UnloadPlayerEvent.java new file mode 100644 index 00000000..65c3ce88 --- /dev/null +++ b/singularity-api/src/main/java/singularity/data/players/events/UnloadPlayerEvent.java @@ -0,0 +1,13 @@ +package singularity.data.players.events; + +import singularity.data.players.CosmicPlayer; + +public class UnloadPlayerEvent extends UnloadSenderEvent { + public UnloadPlayerEvent(CosmicPlayer player) { + super(player); + } + + public CosmicPlayer getPlayer() { + return (CosmicPlayer) super.getSender(); + } +} diff --git a/singularity-api/src/main/java/singularity/data/players/events/UnloadSenderEvent.java b/singularity-api/src/main/java/singularity/data/players/events/UnloadSenderEvent.java new file mode 100644 index 00000000..693949ea --- /dev/null +++ b/singularity-api/src/main/java/singularity/data/players/events/UnloadSenderEvent.java @@ -0,0 +1,9 @@ +package singularity.data.players.events; + +import singularity.data.console.CosmicSender; + +public class UnloadSenderEvent extends CosmicSenderEvent { + public UnloadSenderEvent(CosmicSender user) { + super(user); + } +} diff --git a/singularity-api/src/main/java/singularity/data/players/events/UnloadStreamSenderEvent.java b/singularity-api/src/main/java/singularity/data/players/events/UnloadStreamSenderEvent.java deleted file mode 100644 index 78f59e0c..00000000 --- a/singularity-api/src/main/java/singularity/data/players/events/UnloadStreamSenderEvent.java +++ /dev/null @@ -1,9 +0,0 @@ -package singularity.data.players.events; - -import singularity.data.players.CosmicPlayer; - -public class UnloadStreamSenderEvent extends StreamSenderEvent { - public UnloadStreamSenderEvent(CosmicPlayer user) { - super(user); - } -} diff --git a/singularity-api/src/main/java/singularity/data/players/events/UserNameUpdateEvent.java b/singularity-api/src/main/java/singularity/data/players/events/UserNameUpdateEvent.java index 17acb824..52cfcf9a 100644 --- a/singularity-api/src/main/java/singularity/data/players/events/UserNameUpdateEvent.java +++ b/singularity-api/src/main/java/singularity/data/players/events/UserNameUpdateEvent.java @@ -5,7 +5,7 @@ import singularity.data.players.CosmicPlayer; @Getter @Setter -public class UserNameUpdateEvent extends StreamSenderEvent { +public class UserNameUpdateEvent extends CosmicSenderEvent { private String changeTo; private final String changeFrom; diff --git a/singularity-api/src/main/java/singularity/data/players/location/CosmicLocation.java b/singularity-api/src/main/java/singularity/data/players/location/CosmicLocation.java index 72236f8f..239c5b9f 100644 --- a/singularity-api/src/main/java/singularity/data/players/location/CosmicLocation.java +++ b/singularity-api/src/main/java/singularity/data/players/location/CosmicLocation.java @@ -130,4 +130,10 @@ public String asString() { public void teleport(CosmicPlayer otherPlayer) { Singularity.getInstance().getUserManager().teleport(otherPlayer, this); } + + public double distance(CosmicLocation other) { + return Math.sqrt(Math.pow(getX() - other.getX(), 2) + + Math.pow(getY() - other.getY(), 2) + + Math.pow(getZ() - other.getZ(), 2)); + } } diff --git a/singularity-api/src/main/java/singularity/database/CoreDBOperator.java b/singularity-api/src/main/java/singularity/database/CoreDBOperator.java index e788db18..f7da6c71 100644 --- a/singularity-api/src/main/java/singularity/database/CoreDBOperator.java +++ b/singularity-api/src/main/java/singularity/database/CoreDBOperator.java @@ -14,7 +14,10 @@ import lombok.Getter; import lombok.Setter; import singularity.configs.given.GivenConfigs; +import singularity.data.console.CosmicSender; import singularity.data.players.CosmicPlayer; +import singularity.data.players.events.SavePlayerEvent; +import singularity.data.players.events.SaveSenderEvent; import singularity.data.players.location.PlayerRotation; import singularity.data.players.location.PlayerWorld; import singularity.data.players.location.WorldPosition; @@ -60,9 +63,15 @@ public void ensureTables() { public void savePlayer(CosmicPlayer player, boolean async) { if (async) { - CompletableFuture.runAsync(() -> savePlayerAsync(player).join()); + CompletableFuture.runAsync(() -> { + savePlayerAsync(player).join(); + + new SavePlayerEvent(player).fire(); + }); } else { savePlayerAsync(player).join(); + + new SavePlayerEvent(player).fire(); } } @@ -99,7 +108,7 @@ public boolean isPlayerTouched(String uuid) { return atomicBoolean.get(); } - + private CompletableFuture savePlayerAsync(CosmicPlayer player) { return CompletableFuture.supplyAsync(() -> { String s1 = Statements.getStatement(Statements.StatementType.PUSH_PLAYER_MAIN, this.getConnectorSet()); @@ -215,6 +224,144 @@ private CompletableFuture savePlayerAsync(CosmicPlayer player) { }); } + public void saveSender(CosmicSender sender, boolean async) { + if (sender instanceof CosmicPlayer) { + savePlayer((CosmicPlayer) sender, async); + return; + } + + if (async) { + CompletableFuture.runAsync(() -> { + saveSenderAsync(sender).join(); + + new SaveSenderEvent(sender).fire(); + }); + } else { + saveSenderAsync(sender).join(); + + new SaveSenderEvent(sender).fire(); + } + } + + public void saveSender(CosmicSender sender) { + saveSender(sender, true); + } + + private CompletableFuture saveSenderAsync(CosmicSender sender) { + return CompletableFuture.supplyAsync(() -> { + String s1 = Statements.getStatement(Statements.StatementType.PUSH_PLAYER_MAIN, this.getConnectorSet()); + if (s1 == null) return false; + if (s1.isBlank() || s1.isEmpty()) return false; + + this.execute(s1, stmt -> { + try { + stmt.setString(1, sender.getUuid()); + stmt.setLong(2, sender.getFirstJoinDate().getTime()); + stmt.setLong(3, sender.getLastJoinDate().getTime()); + stmt.setString(4, sender.getCurrentName()); + stmt.setString(5, "--null"); + stmt.setLong(6, sender.getPlaySeconds()); + stmt.setBoolean(7, sender.isProxyTouched()); + + // Repeat everything except the Uuid parameter for MySql. + if (getType() == DatabaseType.MYSQL) { + stmt.setLong(8, sender.getFirstJoinDate().getTime()); + stmt.setLong(9, sender.getLastJoinDate().getTime()); + stmt.setString(10, sender.getCurrentName()); + stmt.setString(11, "--null"); + stmt.setLong(12, sender.getPlaySeconds()); + stmt.setBoolean(13, sender.isProxyTouched()); + } + } catch (Exception e) { + e.printStackTrace(); + } + }); + + if (sender.getMeta() != null) { + s1 = Statements.getStatement(Statements.StatementType.PUSH_PLAYER_META, this.getConnectorSet()); + if (s1 == null) return false; + if (s1.isBlank() || s1.isEmpty()) return false; + + this.execute(s1, stmt -> { + try { + stmt.setString(1, sender.getUuid()); + stmt.setString(2, sender.getMeta().getNickname()); + stmt.setString(3, sender.getMeta().getPrefix()); + stmt.setString(4, sender.getMeta().getSuffix()); + + // Repeat everything except the Uuid parameter (which is the first parameter) for MySql. + if (getType() == DatabaseType.MYSQL) { + stmt.setString(5, sender.getMeta().getNickname()); + stmt.setString(6, sender.getMeta().getPrefix()); + stmt.setString(7, sender.getMeta().getSuffix()); + } + } catch (Exception e) { + e.printStackTrace(); + } + }); + } + + if (true) { + s1 = Statements.getStatement(Statements.StatementType.PUSH_PLAYER_LOCATION, this.getConnectorSet()); + if (s1 == null) return false; + if (s1.isBlank() || s1.isEmpty()) return false; + + this.execute(s1, stmt -> { + try { + stmt.setString(1, sender.getUuid()); + stmt.setString(2, "--null"); + stmt.setString(3, "--null"); + stmt.setDouble(4, 0d); + stmt.setDouble(5, 0d); + stmt.setDouble(6, 0d); + stmt.setFloat(7, 0f); + stmt.setFloat(8, 0f); + + // Repeat everything except the Uuid parameter for MySql. + if (getType() == DatabaseType.MYSQL) { + stmt.setString(9, "--null"); + stmt.setString(10, "--null"); + stmt.setDouble(11, 0d); + stmt.setDouble(12, 0d); + stmt.setDouble(13, 0d); + stmt.setFloat(14, 0f); + stmt.setFloat(15, 0f); + } + } catch (Exception e) { + e.printStackTrace(); + } + }); + } + + if (sender.getPermissions() != null) { + s1 = Statements.getStatement(Statements.StatementType.PUSH_PLAYER_PERMISSIONS, this.getConnectorSet()); + if (s1 == null) return false; + if (s1.isBlank() || s1.isEmpty()) return false; + + s1 = s1.replace("%uuid%", sender.getUuid()); + s1 = s1.replace("%bypassing_permissions%", String.valueOf(sender.getPermissions().isBypassingPermissions())); + + this.execute(s1, stmt -> { + try { + stmt.setString(1, sender.getUuid()); + stmt.setBoolean(2, sender.getPermissions().isBypassingPermissions()); + + // Repeat everything except the Uuid parameter for MySql. + if (getType() == DatabaseType.MYSQL) { + stmt.setBoolean(3, sender.getPermissions().isBypassingPermissions()); + } + } catch (Exception e) { + e.printStackTrace(); + } + }); + } + + DefaultUpdaters.getPlayerUpdater().update(sender.getUuid()); + + return true; + }); + } + public CompletableFuture> loadPlayer(String uuid) { CompletableFuture> future = getLoadingPlayers().getIfPresent(uuid); if (future == null || future.isDone()) { @@ -774,4 +921,50 @@ public CompletableFuture> pullAllTPTickets() { return tickets; }); } + + public void delete(String uuid) { + delete(uuid, true); + } + + public void delete(String uuid, boolean async) { + if (async) { + CompletableFuture.runAsync(() -> deletePlayer(uuid).join()); + } else { + deletePlayer(uuid).join(); + } + } + + public CompletableFuture deletePlayer(String uuid) { + return CompletableFuture.supplyAsync(() -> { + ensureUsable(); + + String s1 = Statements.getStatement(Statements.StatementType.DROP_PLAYER, this.getConnectorSet()); + if (s1.isBlank()) return false; + + AtomicBoolean success = new AtomicBoolean(false); + this.executeQuery(s1, stmt -> { + try { + stmt.setString(1, uuid); + stmt.setString(2, uuid); + stmt.setString(3, uuid); + stmt.setString(4, uuid); + stmt.setString(5, uuid); + } catch (Exception e) { + e.printStackTrace(); + } + }, rs -> { + try { + if (rs.next()) { + success.set(true); + } + } catch (Exception e) { + e.printStackTrace(); + } + }); + + if (success.get()) MessageUtils.logDebug("Deleted player data for " + uuid + "."); + + return success.get(); + }); + } } diff --git a/singularity-api/src/main/java/singularity/database/Statements.java b/singularity-api/src/main/java/singularity/database/Statements.java index 7f48efdb..acafc786 100644 --- a/singularity-api/src/main/java/singularity/database/Statements.java +++ b/singularity-api/src/main/java/singularity/database/Statements.java @@ -154,6 +154,13 @@ public enum MySQL { CHECK_UPDATE("SELECT ServerUuid, PostDate FROM `%table_prefix%update` WHERE Type = ? AND Identifier = ?;"), CLEAR_UPDATE("DELETE FROM `%table_prefix%update` WHERE Type = ? AND Identifier = ?;"), CLEAR_TP_TICKET("DELETE FROM `%table_prefix%tp_tickets` WHERE Uuid = ?;"), + DROP_PLAYER( + "DELETE FROM `%table_prefix%players` WHERE Uuid = ?;;" + + "DELETE FROM `%table_prefix%player_meta` WHERE Uuid = ?;;" + + "DELETE FROM `%table_prefix%player_location` WHERE Uuid = ?;;" + + "DELETE FROM `%table_prefix%player_permissions` WHERE Uuid = ?;;" + + "DELETE FROM `%table_prefix%uuid_info` WHERE Uuid = ?;;" + ), ; private final String statement; @@ -313,6 +320,13 @@ public enum SQLite { CHECK_UPDATE("SELECT ServerUuid, PostDate FROM `%table_prefix%update` WHERE Type = ? AND Identifier = ?;"), CLEAR_UPDATE("DELETE FROM `%table_prefix%update` WHERE Type = ? AND Identifier = ?;"), CLEAR_TP_TICKET("DELETE FROM `%table_prefix%tp_tickets` WHERE Uuid = ?;"), + DROP_PLAYER( + "DELETE FROM `%table_prefix%players` WHERE Uuid = ?;;" + + "DELETE FROM `%table_prefix%player_meta` WHERE Uuid = ?;;" + + "DELETE FROM `%table_prefix%player_location` WHERE Uuid = ?;;" + + "DELETE FROM `%table_prefix%player_permissions` WHERE Uuid = ?;;" + + "DELETE FROM `%table_prefix%uuid_info` WHERE Uuid = ?;;" + ), ; private final String statement; @@ -351,6 +365,7 @@ public enum StatementType { CHECK_UPDATE, CLEAR_UPDATE, CLEAR_TP_TICKET, + DROP_PLAYER, ; } diff --git a/singularity-api/src/main/java/singularity/events/server/CosmicChatEvent.java b/singularity-api/src/main/java/singularity/events/server/CosmicChatEvent.java index cba8f11c..4c4e54a1 100644 --- a/singularity-api/src/main/java/singularity/events/server/CosmicChatEvent.java +++ b/singularity-api/src/main/java/singularity/events/server/CosmicChatEvent.java @@ -3,10 +3,10 @@ import lombok.Getter; import lombok.Setter; import singularity.data.players.CosmicPlayer; -import singularity.data.players.events.StreamSenderEvent; +import singularity.data.players.events.CosmicSenderEvent; @Getter -public class CosmicChatEvent extends StreamSenderEvent { +public class CosmicChatEvent extends CosmicSenderEvent { private final String originalMessage; @Setter private String message; diff --git a/singularity-api/src/main/java/singularity/events/server/KickedFromServerEvent.java b/singularity-api/src/main/java/singularity/events/server/KickedFromServerEvent.java index 0ef4ec9b..61a7bd26 100644 --- a/singularity-api/src/main/java/singularity/events/server/KickedFromServerEvent.java +++ b/singularity-api/src/main/java/singularity/events/server/KickedFromServerEvent.java @@ -3,11 +3,11 @@ import lombok.Getter; import lombok.Setter; import singularity.data.players.CosmicPlayer; -import singularity.data.players.events.StreamSenderEvent; +import singularity.data.players.events.CosmicSenderEvent; @Setter @Getter -public class KickedFromServerEvent extends StreamSenderEvent { +public class KickedFromServerEvent extends CosmicSenderEvent { private String fromServer; private String reason; private String toServer; diff --git a/singularity-api/src/main/java/singularity/events/server/LoginEvent.java b/singularity-api/src/main/java/singularity/events/server/LoginEvent.java index f321bd52..ec1461b0 100644 --- a/singularity-api/src/main/java/singularity/events/server/LoginEvent.java +++ b/singularity-api/src/main/java/singularity/events/server/LoginEvent.java @@ -1,9 +1,9 @@ package singularity.events.server; import singularity.data.players.CosmicPlayer; -import singularity.data.players.events.StreamSenderEvent; +import singularity.data.players.events.CosmicSenderEvent; -public class LoginEvent extends StreamSenderEvent { +public class LoginEvent extends CosmicSenderEvent { public LoginEvent(CosmicPlayer player) { super(player); } diff --git a/singularity-api/src/main/java/singularity/events/server/LogoutEvent.java b/singularity-api/src/main/java/singularity/events/server/LogoutEvent.java index 21a29848..ce2d55f9 100644 --- a/singularity-api/src/main/java/singularity/events/server/LogoutEvent.java +++ b/singularity-api/src/main/java/singularity/events/server/LogoutEvent.java @@ -1,9 +1,9 @@ package singularity.events.server; import singularity.data.players.CosmicPlayer; -import singularity.data.players.events.StreamSenderEvent; +import singularity.data.players.events.CosmicSenderEvent; -public class LogoutEvent extends StreamSenderEvent { +public class LogoutEvent extends CosmicSenderEvent { public LogoutEvent(CosmicPlayer player) { super(player); } diff --git a/singularity-api/src/main/java/singularity/events/server/world/BlockBreakEvent.java b/singularity-api/src/main/java/singularity/events/server/world/BlockBreakEvent.java new file mode 100644 index 00000000..61849cf2 --- /dev/null +++ b/singularity-api/src/main/java/singularity/events/server/world/BlockBreakEvent.java @@ -0,0 +1,16 @@ +package singularity.events.server.world; + +import lombok.Getter; +import lombok.Setter; +import singularity.data.console.CosmicSender; +import singularity.objects.world.CosmicBlock; + +@Getter @Setter +public class BlockBreakEvent extends BlockEvent { + private CosmicSender player; + + public BlockBreakEvent(CosmicSender player, CosmicBlock block) { + super(block); + this.player = player; + } +} diff --git a/singularity-api/src/main/java/singularity/events/server/world/BlockEvent.java b/singularity-api/src/main/java/singularity/events/server/world/BlockEvent.java new file mode 100644 index 00000000..191baa3e --- /dev/null +++ b/singularity-api/src/main/java/singularity/events/server/world/BlockEvent.java @@ -0,0 +1,16 @@ +package singularity.events.server.world; + +import lombok.Getter; +import lombok.Setter; +import singularity.events.CosmicEvent; +import singularity.objects.world.CosmicBlock; + +@Getter @Setter +public class BlockEvent extends CosmicEvent { + private CosmicBlock block; + + public BlockEvent(CosmicBlock block) { + super(); + this.block = block; + } +} diff --git a/singularity-api/src/main/java/singularity/events/server/world/BlockPlaceEvent.java b/singularity-api/src/main/java/singularity/events/server/world/BlockPlaceEvent.java new file mode 100644 index 00000000..4f93d0f6 --- /dev/null +++ b/singularity-api/src/main/java/singularity/events/server/world/BlockPlaceEvent.java @@ -0,0 +1,16 @@ +package singularity.events.server.world; + +import lombok.Getter; +import lombok.Setter; +import singularity.data.console.CosmicSender; +import singularity.objects.world.CosmicBlock; + +@Getter @Setter +public class BlockPlaceEvent extends BlockEvent { + private CosmicSender player; + + public BlockPlaceEvent(CosmicSender player, CosmicBlock block) { + super(block); + this.player = player; + } +} diff --git a/singularity-api/src/main/java/singularity/interfaces/ISingularityExtension.java b/singularity-api/src/main/java/singularity/interfaces/ISingularityExtension.java index 50aa7523..5171454f 100644 --- a/singularity-api/src/main/java/singularity/interfaces/ISingularityExtension.java +++ b/singularity-api/src/main/java/singularity/interfaces/ISingularityExtension.java @@ -64,4 +64,6 @@ enum ServerType { ClassLoader getMainClassLoader(); String getName(); + + boolean isOfflineMode(); } diff --git a/singularity-api/src/main/java/singularity/interfaces/IUserManager.java b/singularity-api/src/main/java/singularity/interfaces/IUserManager.java index d1ac9da3..0cff3bbe 100644 --- a/singularity-api/src/main/java/singularity/interfaces/IUserManager.java +++ b/singularity-api/src/main/java/singularity/interfaces/IUserManager.java @@ -5,13 +5,14 @@ import singularity.data.players.location.CosmicLocation; import singularity.objects.CosmicResourcePack; +import java.util.Optional; import java.util.concurrent.ConcurrentSkipListMap; import java.util.concurrent.ConcurrentSkipListSet; public interface IUserManager { - CosmicPlayer getOrCreatePlayer(P player); + Optional getOrCreatePlayer(P player); - CosmicSender getOrCreateSender(C sender); + Optional getOrCreateSender(C sender); String getUsername(String uuid); diff --git a/singularity-api/src/main/java/singularity/loading/Loadable.java b/singularity-api/src/main/java/singularity/loading/Loadable.java index 3cffee65..20b437b3 100644 --- a/singularity-api/src/main/java/singularity/loading/Loadable.java +++ b/singularity-api/src/main/java/singularity/loading/Loadable.java @@ -4,9 +4,60 @@ import java.util.Optional; import java.util.concurrent.CompletableFuture; +import java.util.function.Consumer; public interface Loadable extends Identifiable { - void save(); + boolean isFullyLoaded(); - L augment(CompletableFuture> loader); + void setFullyLoaded(boolean fullyLoaded); + + void save(boolean async); + + default void save() { + save(true); + } + + L augment(CompletableFuture> loader, boolean isGet); + + default L augment(CompletableFuture> loader) { + return augment(loader, false); + } + + void unload(); + + void load(); + + boolean isLoaded(); + + void saveAndUnload(boolean async); + + default void saveAndUnload() { + saveAndUnload(true); + } + + default Loadable waitUntilFullyLoaded() { + while (! isFullyLoaded()) { + Thread.onSpinWait(); + } + + return this; + } + + default > T waitUntilFullyLoadedTyped() { + try { + return (T) waitUntilFullyLoaded(); + } catch (Throwable e) { + e.printStackTrace(); + + return null; + } + } + + default void onceFullyLoaded(Consumer> consumer) { + consumer.accept(waitUntilFullyLoaded()); + } + + default > void onceFullyLoadedTyped(Consumer consumer) { + consumer.accept(waitUntilFullyLoadedTyped()); + } } diff --git a/singularity-api/src/main/java/singularity/messages/builders/PlayerLocationMessageBuilder.java b/singularity-api/src/main/java/singularity/messages/builders/PlayerLocationMessageBuilder.java index c9e20c64..e43b4280 100644 --- a/singularity-api/src/main/java/singularity/messages/builders/PlayerLocationMessageBuilder.java +++ b/singularity-api/src/main/java/singularity/messages/builders/PlayerLocationMessageBuilder.java @@ -47,7 +47,11 @@ public static void handle(ProxiedMessage in) { float yaw = Float.parseFloat(in.getString("yaw")); float pitch = Float.parseFloat(in.getString("pitch")); - CosmicPlayer player = ModuleUtils.getOrCreatePlayer(uuid); + CosmicPlayer player = ModuleUtils.getOrCreatePlayer(uuid).orElse(null); + if (player == null) { + MessageUtils.logWarning("PlayerLocationMessageBuilder received for invalid player '" + uuid + "'."); + return; + } CosmicServer cosmicServer = new CosmicServer(server); CosmicLocation location; diff --git a/singularity-api/src/main/java/singularity/messages/builders/ProxyParseMessageBuilder.java b/singularity-api/src/main/java/singularity/messages/builders/ProxyParseMessageBuilder.java index 90b89065..7db0c7a1 100644 --- a/singularity-api/src/main/java/singularity/messages/builders/ProxyParseMessageBuilder.java +++ b/singularity-api/src/main/java/singularity/messages/builders/ProxyParseMessageBuilder.java @@ -48,7 +48,11 @@ public static void handle(ProxiedMessage in) { String key = in.getString(ReturnableMessage.getKey()); // MessageUtils.logInfo("ProxiedMessage in > uuid = '" + uuid + "', parse = '" + parse + "', key = '" + key + "'."); - CosmicSender sender = UserUtils.getOrCreateSender(uuid); + CosmicSender sender = UserUtils.getOrCreateSender(uuid).orElse(null); + if (sender == null) { + MessageUtils.logWarning("Could not find CosmicSender for UUID '" + uuid + "'."); + return; + } String parsed = ModuleUtils.replacePlaceholders(sender, parse); diff --git a/singularity-api/src/main/java/singularity/messages/builders/ServerConnectMessageBuilder.java b/singularity-api/src/main/java/singularity/messages/builders/ServerConnectMessageBuilder.java index b4602086..4cfa222c 100644 --- a/singularity-api/src/main/java/singularity/messages/builders/ServerConnectMessageBuilder.java +++ b/singularity-api/src/main/java/singularity/messages/builders/ServerConnectMessageBuilder.java @@ -43,7 +43,11 @@ public static void handle(ProxiedMessage messageIn) { String uuid = messageIn.getString("user_uuid"); - CosmicPlayer player = ModuleUtils.getOrCreatePlayer(uuid); + CosmicPlayer player = ModuleUtils.getOrCreatePlayer(uuid).orElse(null); + if (player == null) { + MessageUtils.logWarning("Failed to find player with UUID '" + uuid + "' for ServerConnectMessageBuilder."); + return; + } Singularity.getInstance().getUserManager().connect(player, messageIn.getString("identifier")); } diff --git a/singularity-api/src/main/java/singularity/modules/ModuleUtils.java b/singularity-api/src/main/java/singularity/modules/ModuleUtils.java index aee6b4e6..dd30da6e 100644 --- a/singularity-api/src/main/java/singularity/modules/ModuleUtils.java +++ b/singularity-api/src/main/java/singularity/modules/ModuleUtils.java @@ -219,11 +219,11 @@ public static boolean userExists(String uuid) { return UserUtils.userExists(uuid); } - public static CosmicPlayer getOrCreatePlayer(String uuid) { + public static Optional getOrCreatePlayer(String uuid) { return UserUtils.getOrCreatePlayer(uuid); } - public static CosmicSender getOrCreateSender(String uuid) { + public static Optional getOrCreateSender(String uuid) { return UserUtils.getOrCreateSender(uuid); } diff --git a/singularity-api/src/main/java/singularity/objects/world/CosmicBlock.java b/singularity-api/src/main/java/singularity/objects/world/CosmicBlock.java new file mode 100644 index 00000000..a2ec26b9 --- /dev/null +++ b/singularity-api/src/main/java/singularity/objects/world/CosmicBlock.java @@ -0,0 +1,77 @@ +package singularity.objects.world; + +import lombok.Getter; +import lombok.Setter; +import org.jetbrains.annotations.NotNull; +import singularity.data.players.location.PlayerWorld; +import singularity.data.players.location.WorldPosition; + +@Getter @Setter +public class CosmicBlock implements Comparable { + private PlayerWorld world; + private WorldPosition location; + + private String type; + + public CosmicBlock(PlayerWorld world, WorldPosition location, String type) { + this.world = world; + this.location = location; + this.type = type; + } + + public double getX() { + return location.getX(); + } + + public double getY() { + return location.getY(); + } + + public double getZ() { + return location.getZ(); + } + + public String getWorldName() { + return world.getIdentifier(); + } + + public CosmicBlock setX(double x) { + this.location.setX(x); + return this; + } + + public CosmicBlock setY(double y) { + this.location.setY(y); + return this; + } + + public CosmicBlock setZ(double z) { + this.location.setZ(z); + return this; + } + + public CosmicBlock setWorld(PlayerWorld world) { + this.world = world; + return this; + } + + public CosmicBlock setWorld(String worldName) { + this.world = new PlayerWorld(worldName); + return this; + } + + public double distance(CosmicBlock other) { + if (other == null) return -1d; + + return getLocation().distance(other.getLocation()); + } + + @Override + public int compareTo(@NotNull CosmicBlock o) { + if (this.world != o.world) { + return this.world.compareTo(o.world); + } else { + return this.location.compareTo(o.location); + } + } +} diff --git a/singularity-api/src/main/java/singularity/permissions/MetaValue.java b/singularity-api/src/main/java/singularity/permissions/MetaValue.java index 48c8184c..9f200f27 100644 --- a/singularity-api/src/main/java/singularity/permissions/MetaValue.java +++ b/singularity-api/src/main/java/singularity/permissions/MetaValue.java @@ -6,6 +6,8 @@ import singularity.data.players.CosmicPlayer; import singularity.utils.UserUtils; +import java.util.Optional; + @Getter @Setter public class MetaValue implements Identified { private String identifier; @@ -23,7 +25,7 @@ public MetaValue(String identifier, MetaKey key, String value, long expiration, this.priority = priority; } - public CosmicPlayer getOn() { + public Optional getOn() { return UserUtils.getOrCreatePlayer(identifier); } } diff --git a/singularity-api/src/main/java/singularity/utils/MessageUtils.java b/singularity-api/src/main/java/singularity/utils/MessageUtils.java index 382248ac..3bd346c3 100644 --- a/singularity-api/src/main/java/singularity/utils/MessageUtils.java +++ b/singularity-api/src/main/java/singularity/utils/MessageUtils.java @@ -196,13 +196,21 @@ public static void logDebug(ModuleLike module, StackTraceElement[] elements) { } public static void sendMessage(String to, String message) { - CosmicSender user = UserUtils.getOrCreateSender(to); + CosmicSender user = UserUtils.getOrCreateSender(to).orElse(null); + if (user == null) { + logWarning("Tried to send message to " + to + " but they are null."); + return; + } Singularity.getInstance().getMessenger().sendMessage(user, message); } public static void sendMessage(@Nullable String to, String otherUUID, String message) { - CosmicSender user = UserUtils.getOrCreateSender(to); + CosmicSender user = UserUtils.getOrCreateSender(to).orElse(null); + if (user == null) { + logWarning("Tried to send message to " + to + " but they are null."); + return; + } Singularity.getInstance().getMessenger().sendMessage(user, replaceAllPlayerBungee(otherUUID, message)); } @@ -225,7 +233,11 @@ public static String replaceAllPlayerBungee(CosmicSender user, String of) { } public static String replaceAllPlayerBungee(String to, String of) { - CosmicSender user = UserUtils.getOrCreateSender(to); + CosmicSender user = UserUtils.getOrCreateSender(to).orElse(null); + if (user == null) { + logWarning("Tried to replace placeholders for " + to + " but they are null."); + return of; + } return replaceAllPlayerBungee(user, of); } diff --git a/singularity-api/src/main/java/singularity/utils/UserUtils.java b/singularity-api/src/main/java/singularity/utils/UserUtils.java index c5b64fdb..f08d5424 100644 --- a/singularity-api/src/main/java/singularity/utils/UserUtils.java +++ b/singularity-api/src/main/java/singularity/utils/UserUtils.java @@ -1,24 +1,22 @@ package singularity.utils; +import gg.drak.thebase.async.AsyncUtils; import lombok.Getter; import lombok.Setter; import singularity.Singularity; import singularity.configs.given.GivenConfigs; import singularity.configs.given.MainMessagesHandler; -import singularity.data.console.CosmicSender; +import singularity.data.console.*; import singularity.data.players.CosmicPlayer; -import singularity.data.players.events.CreateSenderEvent; +import singularity.data.players.events.*; import singularity.data.players.location.CosmicLocation; import singularity.data.teleportation.TPTicket; import singularity.data.uuid.UuidManager; import singularity.modules.ModuleUtils; -import singularity.data.players.events.LoadStreamSenderEvent; import singularity.permissions.MetaValue; import singularity.permissions.PermissionUtil; import java.util.Optional; -import java.util.UUID; -import java.util.concurrent.CompletableFuture; import java.util.concurrent.ConcurrentSkipListMap; import java.util.concurrent.ConcurrentSkipListSet; @@ -87,7 +85,10 @@ public static CosmicSender loadSender(CosmicSender sender) { if (isLoaded(sender.getUuid())) unloadSender(sender.getUuid()); // unload the sender if it's already loaded getLoadedSenders().put(sender.getUuid(), sender); - ModuleUtils.fireEvent(new LoadStreamSenderEvent(sender)); + + if (sender instanceof CosmicPlayer) new LoadPlayerEvent((CosmicPlayer) sender).fire(); + else ModuleUtils.fireEvent(new LoadSenderEvent(sender)); + return sender; } @@ -95,8 +96,30 @@ public static void unloadSender(CosmicSender user) { unloadSender(user.getUuid()); } + public static void saveSender(CosmicSender sender, boolean async) { + if (sender == null) return; + + Singularity.getMainDatabase().saveSender(sender, async); + } + public static void unloadSender(String uuid) { - getLoadedSenders().remove(uuid); + CosmicSender sender = getLoadedSenders().remove(uuid); + if (sender == null) return; + + if (sender instanceof CosmicPlayer) new UnloadPlayerEvent((CosmicPlayer) sender).fire(); + else new UnloadSenderEvent(sender).fire(); + } + + public static void deleteSender(String uuid) { + CosmicSender sender = getOrGetSender(uuid).orElse(null); + if (sender == null) return; + + AsyncUtils.executeAsync(() -> { + Singularity.getMainDatabase().delete(uuid, false); + + if (sender instanceof CosmicPlayer) new DeletePlayerEvent((CosmicPlayer) sender).fire(); + else new DeleteSenderEvent(sender).fire(); + }); } public static boolean isLoaded(String uuid) { @@ -129,7 +152,7 @@ public static boolean userExists(String uuid) { public static Optional getSender(String uuid) { if (uuid == null) return Optional.empty(); - if (uuid.equals(CosmicSender.getConsoleDiscriminator())) return Optional.of(getConsole()); + if (UuidUtils.isConsole(uuid)) return Optional.of(getConsole()); CosmicSender sender = getLoadedSenders().get(uuid); if (sender == null) return Optional.empty(); @@ -146,7 +169,7 @@ public static CosmicPlayer loadPlayer(CosmicPlayer player) { return (CosmicPlayer) loadSender(player); } - public static CosmicPlayer getOrCreatePlayer(CosmicSender sender) { + public static Optional getOrCreatePlayer(CosmicSender sender) { return getOrCreatePlayer(sender.getUuid()); } @@ -168,37 +191,78 @@ public static CosmicPlayer createPlayer(String uuid) { return player; } - public static CosmicPlayer createAndAugmentPlayer(String uuid) { - CompletableFuture> loader = Singularity.getMainDatabase().loadPlayer(uuid); + public static Optional getOrCreateSender(String uuid) { + Optional optional = getOrGetSender(uuid); + if (optional.isPresent()) return optional; + + if (isConsole(uuid)) return Optional.ofNullable(getConsole()); + if (! UuidUtils.isValidPlayerUUID(uuid)) return Optional.empty(); + + CosmicSender sender = createSender(uuid); + sender.load(); + + sender.augment(Singularity.getMainDatabase().loadPlayer(uuid), false); + + return Optional.of(sender); + } + + public static Optional getOrCreatePlayer(String uuid) { + Optional sender = getOrCreateSender(uuid); + if (sender.isPresent()) { + if (sender.get() instanceof CosmicPlayer) return sender.map(s -> (CosmicPlayer) s); + } + + Optional optional = getOrGetPlayer(uuid); + if (optional.isPresent()) return optional; + + if (! UuidUtils.isValidPlayerUUID(uuid)) return Optional.empty(); CosmicPlayer player = createPlayer(uuid); - player.augment(loader); + player.load(); - loadPlayer(player); + player.augment(Singularity.getMainDatabase().loadPlayer(uuid), false); - return player; + return Optional.of(player); } - public static CosmicSender getOrCreateSender(String uuid) { - Optional user = getSender(uuid); - if (user.isPresent()) return user.get(); + public static CosmicSender createTemporarySender(String uuid) { + return new CosmicSender(uuid, true); + } - if (uuid.equals(CosmicSender.getConsoleDiscriminator())) { - return getConsole(); - } + public static Optional getOrGetSender(String uuid) { + if (uuid == null || uuid.isEmpty()) return Optional.empty(); - return createAndAugmentPlayer(uuid); + if (UuidUtils.isConsole(uuid)) return Optional.of(getConsole()); + + Optional optional = getSender(uuid); + if (optional.isPresent()) return optional; + + if (! UuidUtils.isValidPlayerUUID(uuid)) return Optional.empty(); + + CosmicSender sender = createTemporarySender(uuid); + sender.load(); + + sender.augment(Singularity.getMainDatabase().loadPlayer(uuid), true); + + return Optional.of(sender); } - public static CosmicPlayer getOrCreatePlayer(String uuid) { - CosmicSender sender = getOrCreateSender(uuid); - if (sender instanceof CosmicPlayer) return (CosmicPlayer) sender; + public static Optional getOrGetPlayer(String uuid) { + Optional optional = getOrGetSender(uuid); + if (optional.isEmpty()) return Optional.empty(); - return createAndAugmentPlayer(uuid); + CosmicSender sender = optional.get(); + if (sender instanceof CosmicPlayer) { + return Optional.of((CosmicPlayer) sender); + } + + return Optional.empty(); } public static boolean isConsole(String uuid) { - return uuid.equals(GivenConfigs.getMainConfig().getConsoleDiscriminator()); + if (getConsole() != null) return getConsole().getIdentifier().equals(uuid); + + return UuidUtils.isConsole(uuid); } public static String getOffOnFormatted(CosmicSender stat) { @@ -239,15 +303,6 @@ public static String getAbsolute(CosmicSender stat) { return stat.getCurrentName(); } - public static boolean isValidUuid(String possibleUUID) { - try { - UUID.fromString(possibleUUID); - return true; - } catch (Exception e) { - return false; - } - } - public static String getPrefix(CosmicSender user) { if (! (user instanceof CosmicPlayer)) return user.getMeta().getPrefix(); CosmicPlayer player = (CosmicPlayer) user; @@ -290,16 +345,59 @@ public static Optional getUUIDFromName(String name) { return UuidManager.getUuidFromName(name); } + public static Optional getOrGetSenderByName(String name) { + Optional uuid = getUUIDFromName(name); + if (uuid.isEmpty()) return Optional.empty(); + + return getOrGetSender(uuid.get()); + } + + public static Optional getOrGetPlayerByName(String name) { + Optional sender = getOrGetSenderByName(name); + if (sender.isEmpty()) return Optional.empty(); + if (sender.get() instanceof CosmicPlayer) { + return Optional.of((CosmicPlayer) sender.get()); + } + + return Optional.empty(); + } + public static Optional getOrCreateSenderByName(String name) { + Optional optional = getOrGetSenderByName(name); + if (optional.isPresent()) return optional; + Optional uuid = getUUIDFromName(name); - return uuid.map(UserUtils::getOrCreateSender); + if (uuid.isEmpty()) return Optional.empty(); + + CosmicSender sender = createSender(uuid.get()); + sender.load(); + + sender.augment(Singularity.getMainDatabase().loadPlayer(uuid.get()), false); + + return Optional.of(sender); } public static Optional getOrCreatePlayerByName(String name) { + Optional optional = getOrGetPlayerByName(name); + if (optional.isPresent()) return optional; + Optional uuid = getUUIDFromName(name); if (uuid.isEmpty()) return Optional.empty(); - return Optional.of(getOrCreatePlayer(uuid.get())); + CosmicPlayer player = createPlayer(uuid.get()); + player.load(); + + player.augment(Singularity.getMainDatabase().loadPlayer(uuid.get()), false); + + return Optional.of(player); + } + + public static CosmicPlayer getOrGetPlayerByNameNullable(String name) { + return getOrGetPlayerByName(name).orElse(null); + } + + public static CosmicSender getOrGetSenderByNameNullable(String name) { + return getOrGetSenderByName(name).orElse(null); } public static CosmicPlayer getOrCreatePlayerByNameNullable(String name) { diff --git a/singularity-api/src/main/java/singularity/utils/UuidUtils.java b/singularity-api/src/main/java/singularity/utils/UuidUtils.java index a0ad8413..79ac331c 100644 --- a/singularity-api/src/main/java/singularity/utils/UuidUtils.java +++ b/singularity-api/src/main/java/singularity/utils/UuidUtils.java @@ -1,6 +1,9 @@ package singularity.utils; +import singularity.Singularity; import singularity.configs.given.GivenConfigs; +import singularity.data.console.CosmicSender; +import singularity.data.players.CosmicPlayer; import java.util.UUID; @@ -54,4 +57,51 @@ public static String getConsoleName() { public static String getConsoleUUID() { return GivenConfigs.getMainConfig().getConsoleDiscriminator(); } + + public static boolean isOfflineMode() { + return Singularity.isOfflineMode(); + } + + public static boolean isValidPlayer(CosmicSender sender) { + if (sender == null) return false; + if (sender.getIdentifier() == null || sender.getCurrentName() == null || sender.getCurrentName().isBlank()) return false; + + if (! (sender instanceof CosmicPlayer)) { + return sender.getIdentifier().equals(getConsoleUUID()) && sender.getCurrentName().equals(getConsoleName()); + } + CosmicPlayer player = (CosmicPlayer) sender; + + if (isOfflineMode()) return true; + + String name = UUIDFetcher.getName(player.getIdentifier()); + return name != null && ! name.isBlank() && name.equals(player.getCurrentName()); + } + + public static boolean isValidPlayerName(String playerName) { + if (playerName == null) return false; + if (playerName.isBlank()) return false; + + if (isOfflineMode()) return true; + + UUID uuid = UUIDFetcher.getUUID(playerName); + try { + return uuid != null; + } catch (Throwable e) { + return false; // If the player is not found or has no valid profile + } + } + + public static boolean isValidPlayerUUID(String uuid) { + if (uuid == null) return false; + if (uuid.isBlank()) return false; + + if (isOfflineMode()) return true; + + String name = UUIDFetcher.getName(UUID.fromString(uuid)); + try { + return name != null; + } catch (Throwable e) { + return false; // If the player is not found or has no valid profile + } + } } diff --git a/spigot/src/main/java/net/streamline/base/TenSecondTimer.java b/spigot/src/main/java/net/streamline/base/TenSecondTimer.java index 0ba48db1..dd0426f2 100644 --- a/spigot/src/main/java/net/streamline/base/TenSecondTimer.java +++ b/spigot/src/main/java/net/streamline/base/TenSecondTimer.java @@ -29,7 +29,11 @@ public TenSecondTimer(Player player) { public void run() { if (! checkPlayer()) return; - CosmicPlayer streamPlayer = UserUtils.getOrCreatePlayer(player.getUniqueId().toString()); + CosmicPlayer streamPlayer = UserUtils.getOrCreatePlayer(player.getUniqueId().toString()).orElse(null); + if (streamPlayer == null) { + task.cancel(); + return; + } CosmicServer cosmicServer = streamPlayer.getServer(); Location location = player.getLocation(); diff --git a/spigot/src/main/java/net/streamline/base/runnables/PlayerChecker.java b/spigot/src/main/java/net/streamline/base/runnables/PlayerChecker.java index a222de30..51210ae4 100644 --- a/spigot/src/main/java/net/streamline/base/runnables/PlayerChecker.java +++ b/spigot/src/main/java/net/streamline/base/runnables/PlayerChecker.java @@ -16,7 +16,8 @@ public void run() { StreamlineSpigot.getPlayersByUUID().forEach((uuid, player) -> { if (UserUtils.isLoaded(player.getUniqueId().toString())) return; - CosmicPlayer streamPlayer = UserUtils.getOrCreatePlayer(player.getUniqueId().toString()); + CosmicPlayer streamPlayer = UserUtils.getOrCreatePlayer(player.getUniqueId().toString()).orElse(null); + if (streamPlayer == null) return; streamPlayer.setCurrentIp(UserManager.getInstance().parsePlayerIP(player)); streamPlayer.setCurrentName(player.getName()); diff --git a/spigot/src/main/java/net/streamline/platform/BasePlugin.java b/spigot/src/main/java/net/streamline/platform/BasePlugin.java index a604afd5..e5cb3ca4 100644 --- a/spigot/src/main/java/net/streamline/platform/BasePlugin.java +++ b/spigot/src/main/java/net/streamline/platform/BasePlugin.java @@ -200,7 +200,9 @@ public void fireStopEvent() { for (Player player : onlinePlayers()) { if (UserUtils.isLoaded(player.getUniqueId().toString())) { - players.add(getUserManager().getOrCreatePlayer(player)); + CosmicPlayer cosmicPlayer = getUserManager().getOrCreatePlayer(player).orElse(null); + if (cosmicPlayer == null) continue; + players.add(cosmicPlayer); } } @@ -230,6 +232,11 @@ public ConcurrentSkipListSet getOnlinePlayerNames() { return r; } + @Override + public boolean isOfflineMode() { + return ! Bukkit.getOnlineMode(); + } + @Override public long getConnectionThrottle() { return getInstance().getProxy().getConnectionThrottle(); diff --git a/spigot/src/main/java/net/streamline/platform/Messenger.java b/spigot/src/main/java/net/streamline/platform/Messenger.java index 899fbe11..e58906f4 100644 --- a/spigot/src/main/java/net/streamline/platform/Messenger.java +++ b/spigot/src/main/java/net/streamline/platform/Messenger.java @@ -188,7 +188,10 @@ public String colorizeHard(String value) { } public String replaceAllPlayerBungee(CommandSender sender, String of) { - CosmicSender s = UserManager.getInstance().getOrCreateSender(sender); + CosmicSender s = UserManager.getInstance().getOrCreateSender(sender).orElse(null); + if (s == null) { + return of; // If we can't get the sender, just return the original string. + } return MessageUtils.replaceAllPlayerBungee(s, of); } diff --git a/spigot/src/main/java/net/streamline/platform/commands/ProperCommand.java b/spigot/src/main/java/net/streamline/platform/commands/ProperCommand.java index 70857464..b296b329 100644 --- a/spigot/src/main/java/net/streamline/platform/commands/ProperCommand.java +++ b/spigot/src/main/java/net/streamline/platform/commands/ProperCommand.java @@ -41,7 +41,11 @@ public List onTabComplete(@NotNull CommandSender sender, @NotNull Comman if (args == null) args = new String[] { "" }; if (args.length < 1) args = new String[] { "" }; - CosmicSender s = UserManager.getInstance().getOrCreateSender(sender); + CosmicSender s = UserManager.getInstance().getOrCreateSender(sender).orElse(null); + if (s == null) { + MessageUtils.logWarning("Cannot tab complete for command '" + label + "' as the sender is not a CosmicSender."); + return new ArrayList<>(); + } ConcurrentSkipListSet r = parent.baseTabComplete(s, args); @@ -80,7 +84,11 @@ public void unregister() { @Override public boolean execute(@NotNull CommandSender sender, @NotNull String commandLabel, @NotNull String[] args) { - CosmicSender s = UserManager.getInstance().getOrCreateSender(sender); + CosmicSender s = UserManager.getInstance().getOrCreateSender(sender).orElse(null); + if (s == null) { + MessageUtils.logWarning("Cannot execute command '" + commandLabel + "' as the sender is not a CosmicSender."); + return false; + } CommandResult result = parent.baseRun(s, args); diff --git a/spigot/src/main/java/net/streamline/platform/commands/StreamlineSpigotCommand.java b/spigot/src/main/java/net/streamline/platform/commands/StreamlineSpigotCommand.java index 81074d7d..b24035a8 100644 --- a/spigot/src/main/java/net/streamline/platform/commands/StreamlineSpigotCommand.java +++ b/spigot/src/main/java/net/streamline/platform/commands/StreamlineSpigotCommand.java @@ -34,7 +34,7 @@ public boolean command(CommandContext ctx) { String[] newArgs = StringUtils.argsMinus(ctx.getArgsAsStringArray(), 0); - CosmicSender s = UserManager.getInstance().getOrCreateSender(ctx.getCommandSender()); + CosmicSender s = UserManager.getInstance().getOrCreateSender(ctx.getCommandSender()).orElse(null); if (s == null) { ctx.sendMessage("&cCould not find your user..."); return true; @@ -58,7 +58,7 @@ public ConcurrentSkipListSet tabComplete(CommandContext ctx) { String[] newArgs = StringUtils.argsMinus(ctx.getArgsAsStringArray(), 0); - CosmicSender s = UserManager.getInstance().getOrCreateSender(ctx.getCommandSender()); + CosmicSender s = UserManager.getInstance().getOrCreateSender(ctx.getCommandSender()).orElse(null); if (s == null) return null; return streamlineCommand.baseTabComplete(s, newArgs); diff --git a/spigot/src/main/java/net/streamline/platform/listeners/PlatformListener.java b/spigot/src/main/java/net/streamline/platform/listeners/PlatformListener.java index 2ca4f740..90b70baf 100644 --- a/spigot/src/main/java/net/streamline/platform/listeners/PlatformListener.java +++ b/spigot/src/main/java/net/streamline/platform/listeners/PlatformListener.java @@ -15,9 +15,12 @@ import org.bukkit.Bukkit; import org.bukkit.Location; import org.bukkit.World; +import org.bukkit.block.Block; import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; import org.bukkit.event.Listener; +import org.bukkit.event.block.BlockBreakEvent; +import org.bukkit.event.block.BlockPlaceEvent; import org.bukkit.event.player.*; import org.bukkit.event.server.ServerListPingEvent; import org.bukkit.event.server.ServerLoadEvent; @@ -42,6 +45,7 @@ import singularity.modules.ModuleManager; import singularity.modules.ModuleUtils; import singularity.objects.PingedResponse; +import singularity.objects.world.CosmicBlock; import singularity.utils.MessageUtils; import singularity.utils.UserUtils; @@ -78,7 +82,9 @@ public PlatformListener() { public void onPreJoin(AsyncPlayerPreLoginEvent event) { String uuid = event.getUniqueId().toString(); - CosmicPlayer streamPlayer = UserUtils.getOrCreatePlayer(uuid); + CosmicPlayer streamPlayer = UserUtils.getOrGetPlayer(uuid).orElse(null); + if (streamPlayer == null) return; + streamPlayer.waitUntilFullyLoaded(); WhitelistConfig whitelistConfig = GivenConfigs.getWhitelistConfig(); if (whitelistConfig.isEnabled()) { @@ -105,7 +111,11 @@ public void onJoin(PlayerJoinEvent event) { UuidManager.cachePlayer(player.getUniqueId().toString(), player.getName(), UserManager.getInstance().parsePlayerIP(player)); - CosmicPlayer streamPlayer = UserUtils.getOrCreatePlayer(player.getUniqueId().toString()); + CosmicPlayer streamPlayer = UserUtils.getOrCreatePlayer(player.getUniqueId().toString()).orElse(null); + if (streamPlayer == null) { + MessageUtils.logWarning("Failed to create CosmicPlayer for " + player.getName() + " (" + player.getUniqueId() + ")"); + return; + } streamPlayer.setCurrentIp(UserManager.getInstance().parsePlayerIP(player)); streamPlayer.setCurrentName(player.getName()); @@ -142,7 +152,11 @@ public void onLeave(PlayerQuitEvent event) { UuidManager.cachePlayer(player.getUniqueId().toString(), player.getName(), UserManager.getInstance().parsePlayerIP(player.getUniqueId().toString())); - CosmicPlayer streamPlayer = UserUtils.getOrCreatePlayer(player.getUniqueId().toString()); + CosmicPlayer streamPlayer = UserUtils.getOrCreatePlayer(player.getUniqueId().toString()).orElse(null); + if (streamPlayer == null) { + MessageUtils.logWarning("Failed to create CosmicPlayer for " + player.getName() + " (" + player.getUniqueId() + ")"); + return; + } LogoutEvent logoutEvent = new LogoutEvent(streamPlayer); ModuleUtils.fireEvent(logoutEvent); @@ -155,7 +169,11 @@ public void onLeave(PlayerQuitEvent event) { public void onChat(AsyncPlayerChatEvent event) { Player player = event.getPlayer(); - CosmicPlayer streamPlayer = UserUtils.getOrCreatePlayer(player.getUniqueId().toString()); + CosmicPlayer streamPlayer = UserUtils.getOrCreatePlayer(player.getUniqueId().toString()).orElse(null); + if (streamPlayer == null) { + MessageUtils.logWarning("Failed to create CosmicPlayer for " + player.getName() + " (" + player.getUniqueId() + ")"); + return; + } CosmicChatEvent chatEvent = new CosmicChatEvent(streamPlayer, event.getMessage()); StreamlineSpigot.getInstance().fireEvent(chatEvent, true); @@ -178,7 +196,11 @@ public ProxyMessagingListener() { @Override public void onPluginMessageReceived(@NotNull String channel, @NotNull Player player, byte @NotNull [] message) { - CosmicPlayer streamPlayer = UserUtils.getOrCreatePlayer(player.getUniqueId().toString()); + CosmicPlayer streamPlayer = UserUtils.getOrCreatePlayer(player.getUniqueId().toString()).orElse(null); + if (streamPlayer == null) { + MessageUtils.logWarning("Failed to create CosmicPlayer for " + player.getName() + " (" + player.getUniqueId() + ")"); + return; + } try { ProxiedMessage messageIn = new ProxiedMessage(streamPlayer, true, message, channel); @@ -266,7 +288,7 @@ public void onPing(ServerListPingEvent event) { public void onPlayerMove(PlayerMoveEvent event) { Player player = event.getPlayer(); - CosmicPlayer streamPlayer = UserUtils.getOrCreatePlayer(player.getUniqueId().toString()); + CosmicPlayer streamPlayer = UserUtils.getOrCreatePlayer(player.getUniqueId().toString()).orElse(null); if (streamPlayer == null) return; Location loc = event.getTo(); @@ -288,4 +310,46 @@ public void onPlayerMove(PlayerMoveEvent event) { streamPlayer.setLocation(e.getNewLocation()); } + + @EventHandler + public void onBlockBreak(BlockBreakEvent event) { + Player player = event.getPlayer(); + Block block = event.getBlock(); + + CosmicPlayer streamPlayer = UserUtils.getOrCreatePlayer(player.getUniqueId().toString()).orElse(null); + if (streamPlayer == null) { + MessageUtils.logWarning("Failed to create CosmicPlayer for " + player.getName() + " (" + player.getUniqueId() + ")"); + return; + } + PlayerWorld world = new PlayerWorld(player.getWorld().getName()); + WorldPosition location = new WorldPosition(block.getX(), block.getY(), block.getZ()); + CosmicBlock b = new CosmicBlock(world, location, block.getType().toString()); + + singularity.events.server.world.BlockBreakEvent e = new singularity.events.server.world.BlockBreakEvent(streamPlayer, b).fire(); + if (e.isCancelled()) { + event.setCancelled(true); + return; + } + } + + @EventHandler + public void onBlockBreak(BlockPlaceEvent event) { + Player player = event.getPlayer(); + Block block = event.getBlock(); + + CosmicPlayer streamPlayer = UserUtils.getOrCreatePlayer(player.getUniqueId().toString()).orElse(null); + if (streamPlayer == null) { + MessageUtils.logWarning("Failed to create CosmicPlayer for " + player.getName() + " (" + player.getUniqueId() + ")"); + return; + } + PlayerWorld world = new PlayerWorld(player.getWorld().getName()); + WorldPosition location = new WorldPosition(block.getX(), block.getY(), block.getZ()); + CosmicBlock b = new CosmicBlock(world, location, block.getType().toString()); + + singularity.events.server.world.BlockPlaceEvent e = new singularity.events.server.world.BlockPlaceEvent(streamPlayer, b).fire(); + if (e.isCancelled()) { + event.setCancelled(true); + return; + } + } } diff --git a/spigot/src/main/java/net/streamline/platform/savables/UserManager.java b/spigot/src/main/java/net/streamline/platform/savables/UserManager.java index 005ec21f..b9434c65 100644 --- a/spigot/src/main/java/net/streamline/platform/savables/UserManager.java +++ b/spigot/src/main/java/net/streamline/platform/savables/UserManager.java @@ -23,6 +23,7 @@ import java.net.InetSocketAddress; import java.net.SocketAddress; +import java.util.Optional; import java.util.concurrent.ConcurrentSkipListMap; import java.util.concurrent.ConcurrentSkipListSet; @@ -35,17 +36,17 @@ public UserManager() { } @Override - public CosmicPlayer getOrCreatePlayer(Player player) { + public Optional getOrCreatePlayer(Player player) { return UserUtils.getOrCreatePlayer(player.getUniqueId().toString()); } @Override - public CosmicSender getOrCreateSender(CommandSender sender) { + public Optional getOrCreateSender(CommandSender sender) { if (isConsole(sender)) { - return UserUtils.getConsole(); + return Optional.ofNullable(UserUtils.getConsole()); } else { Player player = (Player) sender; - return getOrCreatePlayer(player); + return getOrCreatePlayer(player).map(s -> s); } } @@ -126,7 +127,8 @@ public ConcurrentSkipListSet getUsersOn(String server) { ConcurrentSkipListSet r = new ConcurrentSkipListSet<>(); for (Player player : BasePlugin.onlinePlayers()) { - CosmicPlayer p = getOrCreatePlayer(player); + CosmicPlayer p = getOrCreatePlayer(player).orElse(null); + if (p == null) continue; if (p.isOnline() && p.getServerName().equals(server)) r.add(p); } @@ -186,7 +188,9 @@ public ConcurrentSkipListMap ensurePlayers() { ConcurrentSkipListMap r = new ConcurrentSkipListMap<>(); for (Player player : BasePlugin.onlinePlayers()) { - r.put(player.getUniqueId().toString(), getOrCreatePlayer(player)); + CosmicPlayer cosmicPlayer = getOrCreatePlayer(player).orElse(null); + if (cosmicPlayer == null) continue; + r.put(player.getUniqueId().toString(), cosmicPlayer); } return r; diff --git a/velocity/src/main/java/net/streamline/base/runnables/PlayerChecker.java b/velocity/src/main/java/net/streamline/base/runnables/PlayerChecker.java index abeb84c3..6287ff9a 100644 --- a/velocity/src/main/java/net/streamline/base/runnables/PlayerChecker.java +++ b/velocity/src/main/java/net/streamline/base/runnables/PlayerChecker.java @@ -16,7 +16,8 @@ public void run() { StreamlineVelocity.getPlayersByUUID().forEach((uuid, player) -> { if (UserUtils.isLoaded(player.getUniqueId().toString())) return; - CosmicPlayer streamPlayer = UserUtils.getOrCreatePlayer(player.getUniqueId().toString()); + CosmicPlayer streamPlayer = UserUtils.getOrCreatePlayer(player.getUniqueId().toString()).orElse(null); + if (streamPlayer == null) return; streamPlayer.setCurrentIp(UserManager.getInstance().parsePlayerIP(player.getUniqueId().toString())); streamPlayer.setCurrentName(player.getUsername()); diff --git a/velocity/src/main/java/net/streamline/platform/BasePlugin.java b/velocity/src/main/java/net/streamline/platform/BasePlugin.java index c70d1442..af853528 100644 --- a/velocity/src/main/java/net/streamline/platform/BasePlugin.java +++ b/velocity/src/main/java/net/streamline/platform/BasePlugin.java @@ -224,7 +224,9 @@ public static void registerListener(Object listener) { ConcurrentSkipListSet players = new ConcurrentSkipListSet<>(); for (Player player : onlinePlayers()) { - players.add(getUserManager().getOrCreatePlayer(player)); + CosmicPlayer cosmicPlayer = getUserManager().getOrCreatePlayer(player).orElse(null); + if (cosmicPlayer == null) continue; + players.add(cosmicPlayer); } return players; @@ -353,6 +355,11 @@ public void fireEvent(CosmicEvent event, boolean async) { } } + @Override + public boolean isOfflineMode() { + return ! getInstance().getProxy().getConfiguration().isOnlineMode(); + } + @Override public void handleMisSync(CosmicEvent event, boolean async) { BaseEventHandler.fireEvent(event); diff --git a/velocity/src/main/java/net/streamline/platform/Messenger.java b/velocity/src/main/java/net/streamline/platform/Messenger.java index 5d776736..fc632369 100644 --- a/velocity/src/main/java/net/streamline/platform/Messenger.java +++ b/velocity/src/main/java/net/streamline/platform/Messenger.java @@ -229,7 +229,10 @@ public Component codedText(String from) { } public String replaceAllPlayerBungee(CommandSource sender, String of) { - CosmicSender s = UserManager.getInstance().getOrCreateSender(sender); + CosmicSender s = UserManager.getInstance().getOrCreateSender(sender).orElse(null); + if (s == null) { + return of; // If sender is null, return the original string + } return MessageUtils.replaceAllPlayerBungee(s, of); } diff --git a/velocity/src/main/java/net/streamline/platform/commands/ProperCommand.java b/velocity/src/main/java/net/streamline/platform/commands/ProperCommand.java index bd65e982..492d1495 100644 --- a/velocity/src/main/java/net/streamline/platform/commands/ProperCommand.java +++ b/velocity/src/main/java/net/streamline/platform/commands/ProperCommand.java @@ -31,7 +31,11 @@ public ProperCommand(CosmicCommand parent) { @Override public void execute(Invocation invocation) { - CosmicSender s = UserManager.getInstance().getOrCreateSender(invocation.source()); + CosmicSender s = UserManager.getInstance().getOrCreateSender(invocation.source()).orElse(null); + if (s == null) { + MessageUtils.logWarning("Command execution failed: Sender is null."); + return; + } parent.baseRun(s, invocation.arguments()); } @@ -40,7 +44,11 @@ public void execute(Invocation invocation) { public CompletableFuture> suggestAsync(Invocation invocation) { String[] args = invocation.arguments(); if (args.length < 1) args = new String[] { "" }; - CosmicSender s = UserManager.getInstance().getOrCreateSender(invocation.source()); + CosmicSender s = UserManager.getInstance().getOrCreateSender(invocation.source()).orElse(null); + if (s == null) { + MessageUtils.logWarning("Command suggestion failed: Sender is null."); + return CompletableFuture.completedFuture(new ArrayList<>()); + } ConcurrentSkipListSet r = parent.baseTabComplete(s, invocation.arguments()); diff --git a/velocity/src/main/java/net/streamline/platform/listeners/PlatformListener.java b/velocity/src/main/java/net/streamline/platform/listeners/PlatformListener.java index 85bb33ce..d9ab643c 100644 --- a/velocity/src/main/java/net/streamline/platform/listeners/PlatformListener.java +++ b/velocity/src/main/java/net/streamline/platform/listeners/PlatformListener.java @@ -54,7 +54,9 @@ public void onPreJoin(PreLoginEvent event) { String uuid = p.getUniqueId().toString(); - CosmicPlayer streamPlayer = UserUtils.getOrCreatePlayer(uuid); + CosmicPlayer streamPlayer = UserUtils.getOrGetPlayer(uuid).orElse(null); + if (streamPlayer == null) return; + streamPlayer.waitUntilFullyLoaded(); streamPlayer.setCurrentName(p.getUsername()); p.getCurrentServer().ifPresent(serverConnection -> streamPlayer.setServerName(serverConnection.getServerInfo().getName())); @@ -84,7 +86,11 @@ public void onJoin(PostLoginEvent event) { UuidManager.cachePlayer(player.getUniqueId().toString(), player.getUsername(), UserManager.getInstance().parsePlayerIP(player.getUniqueId().toString())); - CosmicPlayer streamPlayer = UserUtils.getOrCreatePlayer(player.getUniqueId().toString()); + CosmicPlayer streamPlayer = UserUtils.getOrCreatePlayer(player.getUniqueId().toString()).orElse(null); + if (streamPlayer == null) { + MessageUtils.logWarning("Failed to create CosmicPlayer for " + player.getUniqueId().toString() + " (" + player.getUsername() + ")"); + return; + } streamPlayer.setCurrentIp(UserManager.getInstance().parsePlayerIP(player.getUniqueId().toString())); streamPlayer.setCurrentName(player.getUsername()); @@ -98,7 +104,11 @@ public void onJoin(PostLoginEvent event) { public void onLeave(DisconnectEvent event) { Player player = event.getPlayer(); - CosmicPlayer streamPlayer = UserUtils.getOrCreatePlayer(player.getUniqueId().toString()); + CosmicPlayer streamPlayer = UserUtils.getOrCreatePlayer(player.getUniqueId().toString()).orElse(null); + if (streamPlayer == null) { + MessageUtils.logWarning("Failed to create CosmicPlayer for " + player.getUniqueId().toString() + " (" + player.getUsername() + ")"); + return; + } LogoutEvent logoutEvent = new LogoutEvent(streamPlayer); ModuleUtils.fireEvent(logoutEvent); @@ -111,7 +121,11 @@ public void onLeave(DisconnectEvent event) { public void onServerSwitch(ServerConnectedEvent event) { Player player = event.getPlayer(); - CosmicPlayer streamPlayer = UserUtils.getOrCreatePlayer(player.getUniqueId().toString()); + CosmicPlayer streamPlayer = UserUtils.getOrCreatePlayer(player.getUniqueId().toString()).orElse(null); + if (streamPlayer == null) { + MessageUtils.logWarning("Failed to create CosmicPlayer for " + player.getUniqueId().toString() + " (" + player.getUsername() + ")"); + return; + } streamPlayer.setServerName(event.getServer().getServerInfo().getName()); } @@ -120,7 +134,11 @@ public void onServerSwitch(ServerConnectedEvent event) { public void onChat(PlayerChatEvent event) { Player player = event.getPlayer(); - CosmicPlayer streamPlayer = UserUtils.getOrCreatePlayer(player.getUniqueId().toString()); + CosmicPlayer streamPlayer = UserUtils.getOrCreatePlayer(player.getUniqueId().toString()).orElse(null); + if (streamPlayer == null) { + MessageUtils.logWarning("Failed to create CosmicPlayer for " + player.getUniqueId().toString() + " (" + player.getUsername() + ")"); + return; + } CosmicChatEvent chatEvent = new CosmicChatEvent(streamPlayer, event.getMessage()); ModuleManager.fireEvent(chatEvent); @@ -142,7 +160,11 @@ public void onPluginMessage(PluginMessageEvent event) { if (! (event.getTarget() instanceof Player)) return; Player player = (Player) event.getTarget(); - CosmicPlayer streamPlayer = UserUtils.getOrCreatePlayer(player.getUniqueId().toString()); + CosmicPlayer streamPlayer = UserUtils.getOrCreatePlayer(player.getUniqueId().toString()).orElse(null); + if (streamPlayer == null) { + MessageUtils.logWarning("Failed to create CosmicPlayer for " + player.getUniqueId().toString() + " (" + player.getUsername() + ")"); + return; + } try { ProxiedMessage messageIn = new ProxiedMessage(streamPlayer, false, event.getData(), tag); @@ -260,7 +282,11 @@ public void onServerKick(com.velocitypowered.api.event.player.KickedFromServerEv toName = "none"; } - CosmicPlayer streamPlayer = UserUtils.getOrCreatePlayer(player.getUniqueId().toString()); + CosmicPlayer streamPlayer = UserUtils.getOrCreatePlayer(player.getUniqueId().toString()).orElse(null); + if (streamPlayer == null) { + MessageUtils.logWarning("Failed to create CosmicPlayer for " + player.getUniqueId().toString() + " (" + player.getUsername() + ")"); + return; + } KickedFromServerEvent kickedFromServerEvent = new KickedFromServerEvent(streamPlayer, fromName, kickedReason, toName).fire(); diff --git a/velocity/src/main/java/net/streamline/platform/savables/UserManager.java b/velocity/src/main/java/net/streamline/platform/savables/UserManager.java index 37f71f40..689355a1 100644 --- a/velocity/src/main/java/net/streamline/platform/savables/UserManager.java +++ b/velocity/src/main/java/net/streamline/platform/savables/UserManager.java @@ -35,17 +35,17 @@ public UserManager() { } @Override - public CosmicPlayer getOrCreatePlayer(Player player) { + public Optional getOrCreatePlayer(Player player) { return UserUtils.getOrCreatePlayer(player.getUniqueId().toString()); } @Override - public CosmicSender getOrCreateSender(CommandSource sender) { + public Optional getOrCreateSender(CommandSource sender) { if (isConsole(sender)) { - return UserUtils.getConsole(); + return Optional.ofNullable(UserUtils.getConsole()); } else { Player player = (Player) sender; - return getOrCreatePlayer(player); + return getOrCreatePlayer(player).map(s -> s); } } @@ -117,7 +117,7 @@ public ConcurrentSkipListSet getUsersOn(String server) { StreamlineVelocity.getInstance().getProxy().getAllServers().forEach(a -> { a.getPlayersConnected().forEach(b -> { - CosmicPlayer player = getOrCreatePlayer(b); + CosmicPlayer player = getOrCreatePlayer(b).orElse(null); if (player == null) return; if (player.isOnline() && player.getServerName().equals(server)) r.add(player); }); @@ -195,7 +195,9 @@ public ConcurrentSkipListMap ensurePlayers() { ConcurrentSkipListMap r = new ConcurrentSkipListMap<>(); for (Player player : BasePlugin.onlinePlayers()) { - r.put(player.getUniqueId().toString(), getOrCreatePlayer(player)); + CosmicPlayer cosmicPlayer = getOrCreatePlayer(player).orElse(null); + if (cosmicPlayer == null) continue; + r.put(player.getUniqueId().toString(), cosmicPlayer); } return r;