From c9c5ac70ed6fb6700c7920f7670fe9fad9bd8151 Mon Sep 17 00:00:00 2001 From: renvins Date: Fri, 18 Jul 2025 20:16:19 +0200 Subject: [PATCH 01/25] Conditionally add TPS fields based on values --- .../common/metrics/LineProtocolFormatter.java | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/common/src/main/java/it/renvins/serverpulse/common/metrics/LineProtocolFormatter.java b/common/src/main/java/it/renvins/serverpulse/common/metrics/LineProtocolFormatter.java index f829561..882e645 100644 --- a/common/src/main/java/it/renvins/serverpulse/common/metrics/LineProtocolFormatter.java +++ b/common/src/main/java/it/renvins/serverpulse/common/metrics/LineProtocolFormatter.java @@ -29,9 +29,6 @@ public List format(SyncMetricsSnapshot syncData, AsyncMetricsSnapshot as LineProtocolPoint generalPoint = new LineProtocolPoint(metricsConfig.getMeasurementTable()) .addTag("server", metricsConfig.getServerTag()) // Sync data - .addField("tps_1m", syncData.getTps()[0]) - .addField("tps_5m", syncData.getTps()[1]) - .addField("tps_15m", syncData.getTps()[2]) .addField("players_online", syncData.getPlayerCount()) // Async data .addField("used_memory", asyncData.getUsedHeap()) @@ -43,6 +40,12 @@ public List format(SyncMetricsSnapshot syncData, AsyncMetricsSnapshot as .addField("avg_ping", asyncData.getAvgPing()) .setTimestamp(timestamp); + if (syncData.getTps()[0] != 0.0 && syncData.getTps()[1] != 0.0 && syncData.getTps()[2] != 0.0) { + generalPoint.addField("tps_1m", syncData.getTps()[0]) + .addField("tps_5m", syncData.getTps()[1]) + .addField("tps_15m", syncData.getTps()[2]); + } + addConfigTags(generalPoint); points.add(generalPoint); From 03d6f47b09315db3364460d371b67e10d3801907 Mon Sep 17 00:00:00 2001 From: renvins Date: Fri, 18 Jul 2025 20:33:45 +0200 Subject: [PATCH 02/25] Add GeneralConfiguration class for YAML file handling --- common/build.gradle.kts | 4 ++ .../common/config/GeneralConfiguration.java | 60 +++++++++++++++++++ 2 files changed, 64 insertions(+) create mode 100644 common/src/main/java/it/renvins/serverpulse/common/config/GeneralConfiguration.java diff --git a/common/build.gradle.kts b/common/build.gradle.kts index c2a118a..4c197da 100644 --- a/common/build.gradle.kts +++ b/common/build.gradle.kts @@ -11,8 +11,12 @@ java { repositories { mavenCentral() + maven { + url = uri("https://jitpack.io") + } } dependencies { implementation(project(":api")) + implementation("com.github.Carleslc.Simple-YAML:Simple-Yaml:1.8.4") } \ No newline at end of file diff --git a/common/src/main/java/it/renvins/serverpulse/common/config/GeneralConfiguration.java b/common/src/main/java/it/renvins/serverpulse/common/config/GeneralConfiguration.java new file mode 100644 index 0000000..a219dda --- /dev/null +++ b/common/src/main/java/it/renvins/serverpulse/common/config/GeneralConfiguration.java @@ -0,0 +1,60 @@ +package it.renvins.serverpulse.common.config; + +import it.renvins.serverpulse.common.logger.PulseLogger; +import lombok.Getter; +import lombok.RequiredArgsConstructor; +import org.simpleyaml.configuration.file.YamlFile; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.nio.file.Files; + +@RequiredArgsConstructor +public class GeneralConfiguration { + + private final PulseLogger logger; + + private final File dataFolder; + private final String name; + + @Getter private YamlFile config; + + public boolean load() { + try { + createDefaultConfigIfNotExists(); + config = new YamlFile(new File(dataFolder, name)); + + config.load(); + logger.info("Successfully loaded configuration file: " + name); + + return true; + } catch (IOException e) { + logger.error("Failed to load configuration file: " + name, e); + return false; + } + } + + /** + * Creates the configuration file from the JAR's resources if it doesn't already exist. + */ + private void createDefaultConfigIfNotExists() { + if (!dataFolder.exists()) { + dataFolder.mkdirs(); + } + + File configFile = new File(dataFolder, name); + if (!configFile.exists()) { + try (InputStream inputStream = getClass().getClassLoader().getResourceAsStream(name)) { + if (inputStream == null) { + logger.error("Failed to load default configuration file: " + name); + return; + } + Files.copy(inputStream, configFile.toPath()); + logger.info("Successfully created default configuration file: " + name); + } catch (IOException e) { + logger.error("Failed to copy default configuration file: " + name, e); + } + } + } +} From a5277277b807cc3aa7de4453aa8eb8c96564a6b3 Mon Sep 17 00:00:00 2001 From: renvins Date: Sat, 19 Jul 2025 17:00:07 +0200 Subject: [PATCH 03/25] Refactor DatabaseConfiguration and MetricsConfiguration --- .../common/config/DatabaseConfiguration.java | 15 ++++++++++----- .../common/config/MetricsConfiguration.java | 15 ++++++++++----- 2 files changed, 20 insertions(+), 10 deletions(-) diff --git a/common/src/main/java/it/renvins/serverpulse/common/config/DatabaseConfiguration.java b/common/src/main/java/it/renvins/serverpulse/common/config/DatabaseConfiguration.java index e7a93de..d609bb7 100644 --- a/common/src/main/java/it/renvins/serverpulse/common/config/DatabaseConfiguration.java +++ b/common/src/main/java/it/renvins/serverpulse/common/config/DatabaseConfiguration.java @@ -1,9 +1,14 @@ package it.renvins.serverpulse.common.config; -public interface DatabaseConfiguration { +import lombok.Getter; +import lombok.RequiredArgsConstructor; - String getHost(); - String getOrg(); - String getToken(); - String getBucket(); +@RequiredArgsConstructor +@Getter +public class DatabaseConfiguration { + + private final String host; + private final String org; + private final String token; + private final String bucket; } diff --git a/common/src/main/java/it/renvins/serverpulse/common/config/MetricsConfiguration.java b/common/src/main/java/it/renvins/serverpulse/common/config/MetricsConfiguration.java index e2cf70b..325e714 100644 --- a/common/src/main/java/it/renvins/serverpulse/common/config/MetricsConfiguration.java +++ b/common/src/main/java/it/renvins/serverpulse/common/config/MetricsConfiguration.java @@ -1,12 +1,17 @@ package it.renvins.serverpulse.common.config; +import lombok.Getter; +import lombok.RequiredArgsConstructor; + import java.util.Map; -public interface MetricsConfiguration { +@RequiredArgsConstructor +@Getter +public class MetricsConfiguration { - String getServerTag(); - String getMeasurementTable(); - long getMetricsInterval(); - Map getTags(); + private final String serverTag; + private final String measurementTable; + private final long metricsInterval; + private final Map tags; } From cff0e62cb7e37d216505d2650ab97e79c30e3df9 Mon Sep 17 00:00:00 2001 From: renvins Date: Sat, 19 Jul 2025 17:00:20 +0200 Subject: [PATCH 04/25] Refactor DatabaseService to use GeneralConfiguration --- .../serverpulse/common/DatabaseService.java | 22 ++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/common/src/main/java/it/renvins/serverpulse/common/DatabaseService.java b/common/src/main/java/it/renvins/serverpulse/common/DatabaseService.java index 025401a..35e9bf7 100644 --- a/common/src/main/java/it/renvins/serverpulse/common/DatabaseService.java +++ b/common/src/main/java/it/renvins/serverpulse/common/DatabaseService.java @@ -10,6 +10,7 @@ import it.renvins.serverpulse.api.service.IDatabaseService; import it.renvins.serverpulse.common.config.DatabaseConfiguration; +import it.renvins.serverpulse.common.config.GeneralConfiguration; import it.renvins.serverpulse.common.logger.PulseLogger; import it.renvins.serverpulse.common.platform.Platform; import it.renvins.serverpulse.common.scheduler.Task; @@ -20,7 +21,9 @@ public class DatabaseService implements IDatabaseService { private final PulseLogger logger; private final Platform platform; - private final DatabaseConfiguration configuration; + private final GeneralConfiguration generalConfig; + + private DatabaseConfiguration configuration; private final TaskScheduler scheduler; private HttpClient httpClient; // Keep for ping @@ -38,11 +41,11 @@ public class DatabaseService implements IDatabaseService { private String pingUrl; private String writeUrl; - public DatabaseService(PulseLogger logger, Platform platform, DatabaseConfiguration configuration, TaskScheduler scheduler) { + public DatabaseService(PulseLogger logger, Platform platform, GeneralConfiguration generalConfig, TaskScheduler scheduler) { this.logger = logger; this.platform = platform; - this.configuration = configuration; + this.generalConfig = generalConfig; this.scheduler = scheduler; this.httpClient = HttpClient.newBuilder() @@ -52,6 +55,8 @@ public DatabaseService(PulseLogger logger, Platform platform, DatabaseConfigurat @Override public void load() { + loadConfiguration(); + if (!checkConnectionData()) { logger.error("InfluxDB connection data is missing or invalid. Shutting down..."); platform.disable(); @@ -240,6 +245,17 @@ private synchronized void stopRetryTask() { } } + /** + * Loads the InfluxDB configuration from the general config. + * Should be called before any connection attempts. + */ + private void loadConfiguration() { + configuration = new DatabaseConfiguration(generalConfig.getConfig().getString("metrics.influxdb.url"), + generalConfig.getConfig().getString("metrics.influxdb.org"), + generalConfig.getConfig().getString("metrics.influxdb.token"), + generalConfig.getConfig().getString("metrics.influxdb.bucket")); + } + /** * Checks if the essential connection data is present in the config. * @return true if data seems present, false otherwise. From 00e329056c1c96813c57967315bf7c4ee2d717be Mon Sep 17 00:00:00 2001 From: renvins Date: Sat, 19 Jul 2025 17:09:59 +0200 Subject: [PATCH 05/25] Update on database and metrics configuration --- .../serverpulse/common/DatabaseService.java | 19 ++---------- .../common/config/DatabaseConfiguration.java | 24 +++++++++++---- .../common/config/MetricsConfiguration.java | 30 +++++++++++++++---- 3 files changed, 44 insertions(+), 29 deletions(-) diff --git a/common/src/main/java/it/renvins/serverpulse/common/DatabaseService.java b/common/src/main/java/it/renvins/serverpulse/common/DatabaseService.java index 35e9bf7..aebe8a1 100644 --- a/common/src/main/java/it/renvins/serverpulse/common/DatabaseService.java +++ b/common/src/main/java/it/renvins/serverpulse/common/DatabaseService.java @@ -21,9 +21,7 @@ public class DatabaseService implements IDatabaseService { private final PulseLogger logger; private final Platform platform; - private final GeneralConfiguration generalConfig; - - private DatabaseConfiguration configuration; + private final DatabaseConfiguration configuration; private final TaskScheduler scheduler; private HttpClient httpClient; // Keep for ping @@ -45,7 +43,7 @@ public DatabaseService(PulseLogger logger, Platform platform, GeneralConfigurati this.logger = logger; this.platform = platform; - this.generalConfig = generalConfig; + this.configuration = new DatabaseConfiguration(generalConfig); this.scheduler = scheduler; this.httpClient = HttpClient.newBuilder() @@ -55,8 +53,6 @@ public DatabaseService(PulseLogger logger, Platform platform, GeneralConfigurati @Override public void load() { - loadConfiguration(); - if (!checkConnectionData()) { logger.error("InfluxDB connection data is missing or invalid. Shutting down..."); platform.disable(); @@ -245,17 +241,6 @@ private synchronized void stopRetryTask() { } } - /** - * Loads the InfluxDB configuration from the general config. - * Should be called before any connection attempts. - */ - private void loadConfiguration() { - configuration = new DatabaseConfiguration(generalConfig.getConfig().getString("metrics.influxdb.url"), - generalConfig.getConfig().getString("metrics.influxdb.org"), - generalConfig.getConfig().getString("metrics.influxdb.token"), - generalConfig.getConfig().getString("metrics.influxdb.bucket")); - } - /** * Checks if the essential connection data is present in the config. * @return true if data seems present, false otherwise. diff --git a/common/src/main/java/it/renvins/serverpulse/common/config/DatabaseConfiguration.java b/common/src/main/java/it/renvins/serverpulse/common/config/DatabaseConfiguration.java index d609bb7..eb686d7 100644 --- a/common/src/main/java/it/renvins/serverpulse/common/config/DatabaseConfiguration.java +++ b/common/src/main/java/it/renvins/serverpulse/common/config/DatabaseConfiguration.java @@ -1,14 +1,26 @@ package it.renvins.serverpulse.common.config; -import lombok.Getter; import lombok.RequiredArgsConstructor; @RequiredArgsConstructor -@Getter public class DatabaseConfiguration { - private final String host; - private final String org; - private final String token; - private final String bucket; + private final GeneralConfiguration configuration; + + public String getHost() { + return configuration.getConfig().getString("metrics.influxdb.url"); + } + + public String getOrg() { + return configuration.getConfig().getString("metrics.influxdb.org"); + } + + public String getToken() { + return configuration.getConfig().getString("metrics.influxdb.token"); + } + + public String getBucket() { + return configuration.getConfig().getString("metrics.influxdb.bucket"); + } + } diff --git a/common/src/main/java/it/renvins/serverpulse/common/config/MetricsConfiguration.java b/common/src/main/java/it/renvins/serverpulse/common/config/MetricsConfiguration.java index 325e714..6aef780 100644 --- a/common/src/main/java/it/renvins/serverpulse/common/config/MetricsConfiguration.java +++ b/common/src/main/java/it/renvins/serverpulse/common/config/MetricsConfiguration.java @@ -1,17 +1,35 @@ package it.renvins.serverpulse.common.config; -import lombok.Getter; import lombok.RequiredArgsConstructor; +import java.util.HashMap; import java.util.Map; @RequiredArgsConstructor -@Getter public class MetricsConfiguration { - private final String serverTag; - private final String measurementTable; - private final long metricsInterval; - private final Map tags; + private final GeneralConfiguration configuration; + public String getServerTag() { + return configuration.getConfig().getString("metrics.tags.server"); + } + + public String getMeasurementTable() { + return configuration.getConfig().getString("metrics.influxdb.table"); + } + + public long getMetricsInterval() { + return configuration.getConfig().getLong("metrics.interval"); + } + + public Map getTags() { + Map tags = configuration.getConfig().getConfigurationSection("metrics.tags").getValues(false); + Map stringTags = new HashMap<>(); + tags.forEach((key, value) -> { + if (value instanceof String && !key.equalsIgnoreCase("server") && !key.equalsIgnoreCase("world")) { + stringTags.put(key, (String) value); + } + }); + return stringTags; + } } From 0a50c5c2d163271c38f431151255875ad2d137dc Mon Sep 17 00:00:00 2001 From: renvins Date: Sat, 19 Jul 2025 17:11:01 +0200 Subject: [PATCH 06/25] Update also on the line protocol formatter --- .../serverpulse/common/metrics/LineProtocolFormatter.java | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/common/src/main/java/it/renvins/serverpulse/common/metrics/LineProtocolFormatter.java b/common/src/main/java/it/renvins/serverpulse/common/metrics/LineProtocolFormatter.java index 882e645..095f8a8 100644 --- a/common/src/main/java/it/renvins/serverpulse/common/metrics/LineProtocolFormatter.java +++ b/common/src/main/java/it/renvins/serverpulse/common/metrics/LineProtocolFormatter.java @@ -4,8 +4,8 @@ import it.renvins.serverpulse.api.data.LineProtocolPoint; import it.renvins.serverpulse.api.data.SyncMetricsSnapshot; import it.renvins.serverpulse.api.data.WorldData; +import it.renvins.serverpulse.common.config.GeneralConfiguration; import it.renvins.serverpulse.common.config.MetricsConfiguration; -import lombok.RequiredArgsConstructor; import java.time.Instant; import java.util.ArrayList; @@ -17,11 +17,14 @@ * Formats raw metric snapshots into InfluxDB Line Protocol strings. * Its single responsibility is to handle the formatting logic. */ -@RequiredArgsConstructor public class LineProtocolFormatter { private final MetricsConfiguration metricsConfig; + public LineProtocolFormatter(GeneralConfiguration generalConfig) { + this.metricsConfig = new MetricsConfiguration(generalConfig); + } + public List format(SyncMetricsSnapshot syncData, AsyncMetricsSnapshot asyncData) { List points = new ArrayList<>(); long timestamp = Instant.now().toEpochMilli() * 1_000_000; From f79125f736eed82aab240d015a02d2a551113d58 Mon Sep 17 00:00:00 2001 From: renvins Date: Sat, 19 Jul 2025 17:13:22 +0200 Subject: [PATCH 07/25] Refactor ServerPulseCommand and ServerPulseVelocity to use GeneralConfiguration --- .../velocity/ServerPulseVelocity.java | 18 ++--- .../velocity/commands/ServerPulseCommand.java | 4 +- .../config/VelocityConfiguration.java | 65 ------------------- .../config/VelocityDatabaseConfiguration.java | 30 --------- .../config/VelocityMetricsConfiguration.java | 40 ------------ 5 files changed, 7 insertions(+), 150 deletions(-) delete mode 100644 velocity/src/main/java/it/renvins/serverpulse/velocity/config/VelocityConfiguration.java delete mode 100644 velocity/src/main/java/it/renvins/serverpulse/velocity/config/VelocityDatabaseConfiguration.java delete mode 100644 velocity/src/main/java/it/renvins/serverpulse/velocity/config/VelocityMetricsConfiguration.java diff --git a/velocity/src/main/java/it/renvins/serverpulse/velocity/ServerPulseVelocity.java b/velocity/src/main/java/it/renvins/serverpulse/velocity/ServerPulseVelocity.java index e36d1d9..8bd9026 100644 --- a/velocity/src/main/java/it/renvins/serverpulse/velocity/ServerPulseVelocity.java +++ b/velocity/src/main/java/it/renvins/serverpulse/velocity/ServerPulseVelocity.java @@ -18,8 +18,7 @@ import it.renvins.serverpulse.api.service.IMetricsService; import it.renvins.serverpulse.common.DatabaseService; import it.renvins.serverpulse.common.MetricsService; -import it.renvins.serverpulse.common.config.DatabaseConfiguration; -import it.renvins.serverpulse.common.config.MetricsConfiguration; +import it.renvins.serverpulse.common.config.GeneralConfiguration; import it.renvins.serverpulse.common.logger.PulseLogger; import it.renvins.serverpulse.common.disk.DiskRetriever; import it.renvins.serverpulse.common.metrics.LineProtocolFormatter; @@ -28,9 +27,6 @@ import it.renvins.serverpulse.common.platform.Platform; import it.renvins.serverpulse.common.scheduler.TaskScheduler; import it.renvins.serverpulse.velocity.commands.ServerPulseCommand; -import it.renvins.serverpulse.velocity.config.VelocityConfiguration; -import it.renvins.serverpulse.velocity.config.VelocityDatabaseConfiguration; -import it.renvins.serverpulse.velocity.config.VelocityMetricsConfiguration; import it.renvins.serverpulse.velocity.logger.VelocityLogger; import it.renvins.serverpulse.velocity.metrics.VelocityPingRetriever; import it.renvins.serverpulse.velocity.platform.VelocityPlatform; @@ -48,7 +44,7 @@ public class ServerPulseVelocity { private PulseLogger pulseLogger; - private VelocityConfiguration config; + private GeneralConfiguration config; private IDatabaseService databaseService; @@ -69,19 +65,15 @@ public ServerPulseVelocity(ProxyServer server, Logger logger, @DataDirectory Pat @Subscribe public void onProxyInitialization(ProxyInitializeEvent event) { this.pulseLogger = new VelocityLogger(logger); - this.config = new VelocityConfiguration(logger, dataDirectory, "config.yml"); + this.config = new GeneralConfiguration(pulseLogger, dataDirectory.toFile(), "config.yml"); logger.info("Loading configuration file..."); config.load(); - DatabaseConfiguration dbConfig = new VelocityDatabaseConfiguration(config); - MetricsConfiguration metricsConfig = new VelocityMetricsConfiguration(config); - - Platform platform = new VelocityPlatform(this); TaskScheduler scheduler = new VelocityTaskScheduler(this); - this.databaseService = new DatabaseService(pulseLogger, platform, dbConfig, scheduler); + this.databaseService = new DatabaseService(pulseLogger, platform, config, scheduler); this.diskRetriever = new DiskRetriever(dataDirectory.toFile()); this.pingRetriever = new VelocityPingRetriever(this); @@ -89,7 +81,7 @@ public void onProxyInitialization(ProxyInitializeEvent event) { ITPSRetriever tpsRetriever = new UnsupportedTPSRetriever(); // Velocity does not provide a TPS retriever MetricsCollector collector = new MetricsCollector(pulseLogger, platform, tpsRetriever, diskRetriever, pingRetriever); - LineProtocolFormatter formatter = new LineProtocolFormatter(metricsConfig); + LineProtocolFormatter formatter = new LineProtocolFormatter(config); this.metricsService = new MetricsService(pulseLogger, collector, formatter, scheduler, databaseService); diff --git a/velocity/src/main/java/it/renvins/serverpulse/velocity/commands/ServerPulseCommand.java b/velocity/src/main/java/it/renvins/serverpulse/velocity/commands/ServerPulseCommand.java index 2382bdc..78917cc 100644 --- a/velocity/src/main/java/it/renvins/serverpulse/velocity/commands/ServerPulseCommand.java +++ b/velocity/src/main/java/it/renvins/serverpulse/velocity/commands/ServerPulseCommand.java @@ -6,8 +6,8 @@ import com.velocitypowered.api.command.BrigadierCommand; import com.velocitypowered.api.command.CommandSource; import it.renvins.serverpulse.api.ServerPulseProvider; +import it.renvins.serverpulse.common.config.GeneralConfiguration; import it.renvins.serverpulse.common.utils.ChatUtils; -import it.renvins.serverpulse.velocity.config.VelocityConfiguration; import lombok.RequiredArgsConstructor; import net.kyori.adventure.text.Component; import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer; @@ -15,7 +15,7 @@ @RequiredArgsConstructor public class ServerPulseCommand { - private final VelocityConfiguration config; + private final GeneralConfiguration config; public BrigadierCommand createCommand() { // Create the main command diff --git a/velocity/src/main/java/it/renvins/serverpulse/velocity/config/VelocityConfiguration.java b/velocity/src/main/java/it/renvins/serverpulse/velocity/config/VelocityConfiguration.java deleted file mode 100644 index 19b38ad..0000000 --- a/velocity/src/main/java/it/renvins/serverpulse/velocity/config/VelocityConfiguration.java +++ /dev/null @@ -1,65 +0,0 @@ -package it.renvins.serverpulse.velocity.config; - -import java.io.File; -import java.io.IOException; -import java.io.InputStream; -import java.nio.file.Files; -import java.nio.file.Path; - -import lombok.Getter; -import org.simpleyaml.configuration.file.YamlFile; -import org.slf4j.Logger; - -public class VelocityConfiguration { - - private final Logger logger; - private final String name; - - @Getter private final YamlFile config; - - public VelocityConfiguration(Logger logger, Path dataDir, String name) { - this.logger = logger; - this.name = name; - - try { - Files.createDirectories(dataDir); - } catch (IOException e) { - logger.error("Failed to create data directory: " + dataDir, e); - } - this.config = new YamlFile(new File(dataDir.toFile(), name)); - } - - public boolean load() { - try { - if (!config.exists()) { - if (copyDefaultsFromResource()) { - logger.info("Created configuration file: " + name); - } else { - config.createNewFile(); - logger.info("Configuration file not found, created a new one: " + name); - } - } else { - logger.info("Loading configuration file: " + name); - } - config.load(); - return true; - } catch (Exception e) { - logger.error("Failed to load configuration file: " + name, e); - return false; - } - } - - private boolean copyDefaultsFromResource() { - try (InputStream in = getClass().getClassLoader().getResourceAsStream(name)) { - if (in != null) { - Files.copy(in, config.getConfigurationFile().toPath()); - return true; - } else { - return false; - } - } catch (IOException e) { - logger.error("Failed to copy default configuration file: " + name, e); - return false; - } - } -} \ No newline at end of file diff --git a/velocity/src/main/java/it/renvins/serverpulse/velocity/config/VelocityDatabaseConfiguration.java b/velocity/src/main/java/it/renvins/serverpulse/velocity/config/VelocityDatabaseConfiguration.java deleted file mode 100644 index 81e8908..0000000 --- a/velocity/src/main/java/it/renvins/serverpulse/velocity/config/VelocityDatabaseConfiguration.java +++ /dev/null @@ -1,30 +0,0 @@ -package it.renvins.serverpulse.velocity.config; - -import it.renvins.serverpulse.common.config.DatabaseConfiguration; -import lombok.RequiredArgsConstructor; - -@RequiredArgsConstructor -public class VelocityDatabaseConfiguration implements DatabaseConfiguration { - - private final VelocityConfiguration configuration; - - @Override - public String getHost() { - return configuration.getConfig().getString("metrics.influxdb.url"); - } - - @Override - public String getOrg() { - return configuration.getConfig().getString("metrics.influxdb.org"); - } - - @Override - public String getToken() { - return configuration.getConfig().getString("metrics.influxdb.token"); - } - - @Override - public String getBucket() { - return configuration.getConfig().getString("metrics.influxdb.bucket"); - } -} diff --git a/velocity/src/main/java/it/renvins/serverpulse/velocity/config/VelocityMetricsConfiguration.java b/velocity/src/main/java/it/renvins/serverpulse/velocity/config/VelocityMetricsConfiguration.java deleted file mode 100644 index f1d3735..0000000 --- a/velocity/src/main/java/it/renvins/serverpulse/velocity/config/VelocityMetricsConfiguration.java +++ /dev/null @@ -1,40 +0,0 @@ -package it.renvins.serverpulse.velocity.config; - -import java.util.HashMap; -import java.util.Map; - -import it.renvins.serverpulse.common.config.MetricsConfiguration; -import lombok.RequiredArgsConstructor; - -@RequiredArgsConstructor -public class VelocityMetricsConfiguration implements MetricsConfiguration { - - private final VelocityConfiguration configuration; - - @Override - public String getServerTag() { - return configuration.getConfig().getString("metrics.tags.server"); - } - - @Override - public String getMeasurementTable() { - return configuration.getConfig().getString("metrics.influxdb.table"); - } - - @Override - public long getMetricsInterval() { - return configuration.getConfig().getLong("metrics.interval"); - } - - @Override - public Map getTags() { - Map tags = configuration.getConfig().getConfigurationSection("metrics.tags").getValues(false); - Map stringTags = new HashMap<>(); - tags.forEach((key, value) -> { - if (value instanceof String && !key.equalsIgnoreCase("server") && !key.equalsIgnoreCase("world")) { - stringTags.put(key, (String) value); - } - }); - return stringTags; - } -} From b575f20c0144bd51bf0669b55e4891dfc8b05c0f Mon Sep 17 00:00:00 2001 From: renvins Date: Sat, 19 Jul 2025 17:31:47 +0200 Subject: [PATCH 08/25] Refactor ServerPulseCommand and ServerPulseFabric to use GeneralConfiguration --- fabric/build.gradle.kts | 2 +- .../serverpulse/fabric/ServerPulseFabric.java | 19 +++--- .../fabric/command/ServerPulseCommand.java | 4 +- .../fabric/config/FabricConfiguration.java | 64 ------------------- .../config/FabricDatabaseConfiguration.java | 30 --------- .../config/FabricMetricsConfiguration.java | 40 ------------ 6 files changed, 11 insertions(+), 148 deletions(-) delete mode 100644 fabric/src/main/java/it/renvins/serverpulse/fabric/config/FabricConfiguration.java delete mode 100644 fabric/src/main/java/it/renvins/serverpulse/fabric/config/FabricDatabaseConfiguration.java delete mode 100644 fabric/src/main/java/it/renvins/serverpulse/fabric/config/FabricMetricsConfiguration.java diff --git a/fabric/build.gradle.kts b/fabric/build.gradle.kts index 8e18ac6..4c1bdc8 100644 --- a/fabric/build.gradle.kts +++ b/fabric/build.gradle.kts @@ -35,7 +35,7 @@ repositories { dependencies { minecraft("com.mojang:minecraft:1.21.7") mappings("net.fabricmc:yarn:1.21.7+build.2:v2") - modImplementation("net.fabricmc:fabric-loader:0.16.14") + modImplementation("net.fabricmc:fabric-loader:0.16.14") // modImplementation("net.fabricmc.fabric-api:fabric-api:0.128.2+1.21.7") diff --git a/fabric/src/main/java/it/renvins/serverpulse/fabric/ServerPulseFabric.java b/fabric/src/main/java/it/renvins/serverpulse/fabric/ServerPulseFabric.java index ca02fd4..e065897 100644 --- a/fabric/src/main/java/it/renvins/serverpulse/fabric/ServerPulseFabric.java +++ b/fabric/src/main/java/it/renvins/serverpulse/fabric/ServerPulseFabric.java @@ -11,6 +11,7 @@ import it.renvins.serverpulse.common.DatabaseService; import it.renvins.serverpulse.common.MetricsService; import it.renvins.serverpulse.common.config.DatabaseConfiguration; +import it.renvins.serverpulse.common.config.GeneralConfiguration; import it.renvins.serverpulse.common.config.MetricsConfiguration; import it.renvins.serverpulse.common.logger.PulseLogger; import it.renvins.serverpulse.common.disk.DiskRetriever; @@ -19,9 +20,6 @@ import it.renvins.serverpulse.common.platform.Platform; import it.renvins.serverpulse.common.scheduler.TaskScheduler; import it.renvins.serverpulse.fabric.command.ServerPulseCommand; -import it.renvins.serverpulse.fabric.config.FabricConfiguration; -import it.renvins.serverpulse.fabric.config.FabricDatabaseConfiguration; -import it.renvins.serverpulse.fabric.config.FabricMetricsConfiguration; import it.renvins.serverpulse.fabric.logger.FabricLogger; import it.renvins.serverpulse.fabric.metrics.FabricPingRetriever; import it.renvins.serverpulse.fabric.metrics.FabricTPSRetriever; @@ -39,7 +37,7 @@ public class ServerPulseFabric implements ModInitializer { public static final String MOD_ID = "serverpulse"; public static final Logger LOGGER = Logger.getLogger(MOD_ID); - private final FabricConfiguration config; + private final GeneralConfiguration config; private final Platform platform; private final TaskScheduler scheduler; @@ -53,24 +51,23 @@ public class ServerPulseFabric implements ModInitializer { private final IMetricsService metricsService; public ServerPulseFabric() { - this.config = new FabricConfiguration(FabricLoader.getInstance().getConfigDir().resolve("serverpulse"), "config.yml"); - PulseLogger logger = new FabricLogger(); + this.config = new GeneralConfiguration(logger, + FabricLoader.getInstance().getConfigDir().resolve("serverpulse").toFile(), + "config.yml"); + this.platform = new FabricPlatform(this); this.scheduler = new FabricScheduler(); - DatabaseConfiguration dbConfig = new FabricDatabaseConfiguration(config); - MetricsConfiguration metricsConfig = new FabricMetricsConfiguration(config); - - this.databaseService = new DatabaseService(logger, platform, dbConfig, scheduler); + this.databaseService = new DatabaseService(logger, platform, config, scheduler); this.tpsRetriever = new FabricTPSRetriever(); this.diskRetriever = new DiskRetriever(FabricLoader.getInstance().getGameDir().toFile()); this.pingRetriever = new FabricPingRetriever(this); MetricsCollector collector = new MetricsCollector(logger, platform, tpsRetriever, diskRetriever, pingRetriever); - LineProtocolFormatter formatter = new LineProtocolFormatter(metricsConfig); + LineProtocolFormatter formatter = new LineProtocolFormatter(config); this.metricsService = new MetricsService(logger, collector, formatter, scheduler, databaseService); } diff --git a/fabric/src/main/java/it/renvins/serverpulse/fabric/command/ServerPulseCommand.java b/fabric/src/main/java/it/renvins/serverpulse/fabric/command/ServerPulseCommand.java index 3615345..e2ac019 100644 --- a/fabric/src/main/java/it/renvins/serverpulse/fabric/command/ServerPulseCommand.java +++ b/fabric/src/main/java/it/renvins/serverpulse/fabric/command/ServerPulseCommand.java @@ -4,8 +4,8 @@ import com.mojang.brigadier.builder.LiteralArgumentBuilder; import com.mojang.brigadier.context.CommandContext; import it.renvins.serverpulse.api.ServerPulseProvider; +import it.renvins.serverpulse.common.config.GeneralConfiguration; import it.renvins.serverpulse.common.utils.ChatUtils; -import it.renvins.serverpulse.fabric.config.FabricConfiguration; import lombok.RequiredArgsConstructor; import me.lucko.fabric.api.permissions.v0.Permissions; import net.minecraft.server.command.CommandManager; @@ -15,7 +15,7 @@ @RequiredArgsConstructor public class ServerPulseCommand { - private final FabricConfiguration config; + private final GeneralConfiguration config; public LiteralArgumentBuilder createCommand() { // Create the main command diff --git a/fabric/src/main/java/it/renvins/serverpulse/fabric/config/FabricConfiguration.java b/fabric/src/main/java/it/renvins/serverpulse/fabric/config/FabricConfiguration.java deleted file mode 100644 index c1f9104..0000000 --- a/fabric/src/main/java/it/renvins/serverpulse/fabric/config/FabricConfiguration.java +++ /dev/null @@ -1,64 +0,0 @@ -package it.renvins.serverpulse.fabric.config; - -import java.io.File; -import java.io.IOException; -import java.io.InputStream; -import java.nio.file.Files; -import java.nio.file.Path; -import java.util.logging.Level; - -import it.renvins.serverpulse.fabric.ServerPulseFabric; -import lombok.Getter; -import org.simpleyaml.configuration.file.YamlFile; - -public class FabricConfiguration { - - private final String name; - - @Getter private final YamlFile config; - - public FabricConfiguration(Path dataDir, String name) { - this.name = name; - - try { - Files.createDirectories(dataDir); - } catch (IOException e) { - ServerPulseFabric.LOGGER.log(Level.SEVERE,"Failed to create data directory: " + dataDir, e); - } - this.config = new YamlFile(new File(dataDir.toFile(), name)); - } - - public boolean load() { - try { - if (!config.exists()) { - if (copyDefaultsFromResource()) { - ServerPulseFabric.LOGGER.info("Created configuration file: " + name); - } else { - config.createNewFile(); - ServerPulseFabric.LOGGER.info("Configuration file not found, created a new one: " + name); - } - } else { - ServerPulseFabric.LOGGER.info("Loading configuration file: " + name); - } - config.load(); - return true; - } catch (Exception e) { - ServerPulseFabric.LOGGER.log(Level.SEVERE,"Failed to load configuration file: " + name, e); - return false; - } - } - - private boolean copyDefaultsFromResource() { - try (InputStream in = getClass().getClassLoader().getResourceAsStream(name)) { - if (in != null) { - Files.copy(in, config.getConfigurationFile().toPath()); - return true; - } else { - return false; - } - } catch (IOException e) { - ServerPulseFabric.LOGGER.log(Level.SEVERE,"Failed to copy default configuration file: " + name, e); - return false; - } - } -} diff --git a/fabric/src/main/java/it/renvins/serverpulse/fabric/config/FabricDatabaseConfiguration.java b/fabric/src/main/java/it/renvins/serverpulse/fabric/config/FabricDatabaseConfiguration.java deleted file mode 100644 index cdd921c..0000000 --- a/fabric/src/main/java/it/renvins/serverpulse/fabric/config/FabricDatabaseConfiguration.java +++ /dev/null @@ -1,30 +0,0 @@ -package it.renvins.serverpulse.fabric.config; - -import it.renvins.serverpulse.common.config.DatabaseConfiguration; -import lombok.RequiredArgsConstructor; - -@RequiredArgsConstructor -public class FabricDatabaseConfiguration implements DatabaseConfiguration { - - private final FabricConfiguration configuration; - - @Override - public String getHost() { - return configuration.getConfig().getString("metrics.influxdb.url"); - } - - @Override - public String getOrg() { - return configuration.getConfig().getString("metrics.influxdb.org"); - } - - @Override - public String getToken() { - return configuration.getConfig().getString("metrics.influxdb.token"); - } - - @Override - public String getBucket() { - return configuration.getConfig().getString("metrics.influxdb.bucket"); - } -} diff --git a/fabric/src/main/java/it/renvins/serverpulse/fabric/config/FabricMetricsConfiguration.java b/fabric/src/main/java/it/renvins/serverpulse/fabric/config/FabricMetricsConfiguration.java deleted file mode 100644 index da8d2a7..0000000 --- a/fabric/src/main/java/it/renvins/serverpulse/fabric/config/FabricMetricsConfiguration.java +++ /dev/null @@ -1,40 +0,0 @@ -package it.renvins.serverpulse.fabric.config; - -import java.util.HashMap; -import java.util.Map; - -import it.renvins.serverpulse.common.config.MetricsConfiguration; -import lombok.RequiredArgsConstructor; - -@RequiredArgsConstructor -public class FabricMetricsConfiguration implements MetricsConfiguration { - - private final FabricConfiguration configuration; - - @Override - public String getServerTag() { - return configuration.getConfig().getString("metrics.tags.server"); - } - - @Override - public String getMeasurementTable() { - return configuration.getConfig().getString("metrics.influxdb.table"); - } - - @Override - public long getMetricsInterval() { - return configuration.getConfig().getLong("metrics.interval"); - } - - @Override - public Map getTags() { - Map tags = configuration.getConfig().getConfigurationSection("metrics.tags").getValues(false); - Map stringTags = new HashMap<>(); - tags.forEach((key, value) -> { - if (value instanceof String && !key.equalsIgnoreCase("server") && !key.equalsIgnoreCase("world")) { - stringTags.put(key, (String) value); - } - }); - return stringTags; - } -} From 22c8be48e0a5560f0219aeed65dd82b927a58a81 Mon Sep 17 00:00:00 2001 From: renvins Date: Sat, 19 Jul 2025 17:42:24 +0200 Subject: [PATCH 09/25] Add JitPack repository and update Simple-YAML dependency scope --- bungeecord/build.gradle.kts | 3 +++ common/build.gradle.kts | 2 +- fabric/build.gradle.kts | 4 ---- velocity/build.gradle.kts | 1 - 4 files changed, 4 insertions(+), 6 deletions(-) diff --git a/bungeecord/build.gradle.kts b/bungeecord/build.gradle.kts index 3d83669..44a8997 100644 --- a/bungeecord/build.gradle.kts +++ b/bungeecord/build.gradle.kts @@ -18,6 +18,9 @@ repositories { name = "mojang" url = uri("https://libraries.minecraft.net/") } + maven { + url = uri("https://jitpack.io") + } } dependencies { diff --git a/common/build.gradle.kts b/common/build.gradle.kts index 4c197da..ff3f23e 100644 --- a/common/build.gradle.kts +++ b/common/build.gradle.kts @@ -18,5 +18,5 @@ repositories { dependencies { implementation(project(":api")) - implementation("com.github.Carleslc.Simple-YAML:Simple-Yaml:1.8.4") + api("com.github.Carleslc.Simple-YAML:Simple-Yaml:1.8.4") } \ No newline at end of file diff --git a/fabric/build.gradle.kts b/fabric/build.gradle.kts index 4c1bdc8..67ebc75 100644 --- a/fabric/build.gradle.kts +++ b/fabric/build.gradle.kts @@ -45,10 +45,6 @@ dependencies { include(project(":api")) include(project(":common")) - // SimpleYAML - implementation("com.github.Carleslc.Simple-YAML:Simple-Yaml:1.8.4") - include("com.github.Carleslc.Simple-YAML:Simple-Yaml:1.8.4") - // Fabric Permissions API modImplementation("me.lucko:fabric-permissions-api:0.3.1") include("me.lucko:fabric-permissions-api:0.3.1") diff --git a/velocity/build.gradle.kts b/velocity/build.gradle.kts index d46413c..7f5d10d 100644 --- a/velocity/build.gradle.kts +++ b/velocity/build.gradle.kts @@ -20,7 +20,6 @@ repositories { dependencies { implementation(project(":api")) implementation(project(":common")) - implementation("com.github.Carleslc.Simple-YAML:Simple-Yaml:1.8.4") compileOnly("com.velocitypowered:velocity-api:3.4.0-SNAPSHOT") annotationProcessor("com.velocitypowered:velocity-api:3.4.0-SNAPSHOT") From 9a6e4c45af4428704c1768c6b714715745cbd763 Mon Sep 17 00:00:00 2001 From: renvins Date: Sat, 19 Jul 2025 17:42:37 +0200 Subject: [PATCH 10/25] Refactor commands to use GeneralConfiguration instead of BungeeCordConfiguration --- .../ServerPulseBungeeCordLoader.java | 20 ++---- .../bungeecord/commands/ReloadCommand.java | 6 +- .../commands/ServerPulseCommand.java | 6 +- .../bungeecord/commands/StatusCommand.java | 6 +- .../config/BungeeCordConfiguration.java | 70 ------------------- .../BungeeCordDatabaseConfiguration.java | 30 -------- .../BungeeCordMetricsConfiguration.java | 44 ------------ 7 files changed, 17 insertions(+), 165 deletions(-) delete mode 100644 bungeecord/src/main/java/it/renvins/serverpulse/bungeecord/config/BungeeCordConfiguration.java delete mode 100644 bungeecord/src/main/java/it/renvins/serverpulse/bungeecord/config/BungeeCordDatabaseConfiguration.java delete mode 100644 bungeecord/src/main/java/it/renvins/serverpulse/bungeecord/config/BungeeCordMetricsConfiguration.java diff --git a/bungeecord/src/main/java/it/renvins/serverpulse/bungeecord/ServerPulseBungeeCordLoader.java b/bungeecord/src/main/java/it/renvins/serverpulse/bungeecord/ServerPulseBungeeCordLoader.java index 1372152..7646e1d 100644 --- a/bungeecord/src/main/java/it/renvins/serverpulse/bungeecord/ServerPulseBungeeCordLoader.java +++ b/bungeecord/src/main/java/it/renvins/serverpulse/bungeecord/ServerPulseBungeeCordLoader.java @@ -8,17 +8,13 @@ import it.renvins.serverpulse.api.service.IMetricsService; import it.renvins.serverpulse.api.service.Service; import it.renvins.serverpulse.bungeecord.commands.ServerPulseCommand; -import it.renvins.serverpulse.bungeecord.config.BungeeCordConfiguration; -import it.renvins.serverpulse.bungeecord.config.BungeeCordDatabaseConfiguration; -import it.renvins.serverpulse.bungeecord.config.BungeeCordMetricsConfiguration; import it.renvins.serverpulse.bungeecord.logger.BungeeCordLogger; import it.renvins.serverpulse.bungeecord.metrics.BungeeCordPingRetriever; import it.renvins.serverpulse.bungeecord.platform.BungeeCordPlatform; import it.renvins.serverpulse.bungeecord.scheduler.BungeeCordTaskScheduler; import it.renvins.serverpulse.common.DatabaseService; import it.renvins.serverpulse.common.MetricsService; -import it.renvins.serverpulse.common.config.DatabaseConfiguration; -import it.renvins.serverpulse.common.config.MetricsConfiguration; +import it.renvins.serverpulse.common.config.GeneralConfiguration; import it.renvins.serverpulse.common.logger.PulseLogger; import it.renvins.serverpulse.common.disk.DiskRetriever; import it.renvins.serverpulse.common.metrics.LineProtocolFormatter; @@ -35,7 +31,7 @@ public class ServerPulseBungeeCordLoader implements Service { private final ServerPulseBungeeCord plugin; public static Logger LOGGER; - private final BungeeCordConfiguration config; + private final GeneralConfiguration config; private final Platform platform; private final IDatabaseService databaseService; @@ -49,16 +45,14 @@ public ServerPulseBungeeCordLoader(ServerPulseBungeeCord plugin) { this.plugin = plugin; LOGGER = plugin.getLogger(); - this.config = new BungeeCordConfiguration(plugin, "config.yml"); + PulseLogger pulseLogger = new BungeeCordLogger(plugin); + + this.config = new GeneralConfiguration(pulseLogger, plugin.getDataFolder(), "config.yml"); this.platform = new BungeeCordPlatform(plugin); - PulseLogger pulseLogger = new BungeeCordLogger(plugin); TaskScheduler scheduler = new BungeeCordTaskScheduler(plugin); - DatabaseConfiguration dbConfig = new BungeeCordDatabaseConfiguration(config); - MetricsConfiguration metricsConfig = new BungeeCordMetricsConfiguration(config); - - this.databaseService = new DatabaseService(pulseLogger, platform, dbConfig, scheduler); + this.databaseService = new DatabaseService(pulseLogger, platform, config, scheduler); this.diskRetriever = new DiskRetriever(plugin.getDataFolder()); this.pingRetriever =new BungeeCordPingRetriever(plugin); @@ -66,7 +60,7 @@ public ServerPulseBungeeCordLoader(ServerPulseBungeeCord plugin) { ITPSRetriever tpsRetriever = new UnsupportedTPSRetriever(); MetricsCollector collector = new MetricsCollector(pulseLogger, platform, tpsRetriever, diskRetriever, pingRetriever); - LineProtocolFormatter formatter = new LineProtocolFormatter(metricsConfig); + LineProtocolFormatter formatter = new LineProtocolFormatter(config); this.metricsService = new MetricsService(pulseLogger, collector, formatter, scheduler, databaseService); diff --git a/bungeecord/src/main/java/it/renvins/serverpulse/bungeecord/commands/ReloadCommand.java b/bungeecord/src/main/java/it/renvins/serverpulse/bungeecord/commands/ReloadCommand.java index c389504..93413bf 100644 --- a/bungeecord/src/main/java/it/renvins/serverpulse/bungeecord/commands/ReloadCommand.java +++ b/bungeecord/src/main/java/it/renvins/serverpulse/bungeecord/commands/ReloadCommand.java @@ -1,15 +1,15 @@ package it.renvins.serverpulse.bungeecord.commands; +import it.renvins.serverpulse.common.config.GeneralConfiguration; import it.renvins.serverpulse.common.utils.ChatUtils; -import it.renvins.serverpulse.bungeecord.config.BungeeCordConfiguration; import net.md_5.bungee.api.CommandSender; import net.md_5.bungee.api.chat.TextComponent; public class ReloadCommand extends GeneralCommand { - private final BungeeCordConfiguration config; + private final GeneralConfiguration config; - public ReloadCommand(String permission, boolean isPlayerOnly, BungeeCordConfiguration config) { + public ReloadCommand(String permission, boolean isPlayerOnly, GeneralConfiguration config) { super(permission, isPlayerOnly); this.config = config; } diff --git a/bungeecord/src/main/java/it/renvins/serverpulse/bungeecord/commands/ServerPulseCommand.java b/bungeecord/src/main/java/it/renvins/serverpulse/bungeecord/commands/ServerPulseCommand.java index 876204e..7deeac1 100644 --- a/bungeecord/src/main/java/it/renvins/serverpulse/bungeecord/commands/ServerPulseCommand.java +++ b/bungeecord/src/main/java/it/renvins/serverpulse/bungeecord/commands/ServerPulseCommand.java @@ -3,6 +3,7 @@ import java.util.HashMap; import java.util.Map; +import it.renvins.serverpulse.common.config.GeneralConfiguration; import it.renvins.serverpulse.common.utils.ChatUtils; import it.renvins.serverpulse.bungeecord.config.BungeeCordConfiguration; import net.md_5.bungee.api.CommandSender; @@ -12,11 +13,12 @@ public class ServerPulseCommand extends Command { - private final BungeeCordConfiguration config; + private final GeneralConfiguration config; private final Map commands = new HashMap<>(); - public ServerPulseCommand(BungeeCordConfiguration config) { + public ServerPulseCommand(GeneralConfiguration config) { super("serverpulsebungeecord", "serverpulse.use", "sp", "spb"); + this.config = config; registerCommands(); } diff --git a/bungeecord/src/main/java/it/renvins/serverpulse/bungeecord/commands/StatusCommand.java b/bungeecord/src/main/java/it/renvins/serverpulse/bungeecord/commands/StatusCommand.java index 9896b5b..01aa2fd 100644 --- a/bungeecord/src/main/java/it/renvins/serverpulse/bungeecord/commands/StatusCommand.java +++ b/bungeecord/src/main/java/it/renvins/serverpulse/bungeecord/commands/StatusCommand.java @@ -1,16 +1,16 @@ package it.renvins.serverpulse.bungeecord.commands; import it.renvins.serverpulse.api.ServerPulseProvider; +import it.renvins.serverpulse.common.config.GeneralConfiguration; import it.renvins.serverpulse.common.utils.ChatUtils; -import it.renvins.serverpulse.bungeecord.config.BungeeCordConfiguration; import net.md_5.bungee.api.CommandSender; import net.md_5.bungee.api.chat.TextComponent; public class StatusCommand extends GeneralCommand { - private final BungeeCordConfiguration config; + private final GeneralConfiguration config; - public StatusCommand(String permission, boolean isPlayerOnly, BungeeCordConfiguration config) { + public StatusCommand(String permission, boolean isPlayerOnly, GeneralConfiguration config) { super(permission, isPlayerOnly); this.config = config; } diff --git a/bungeecord/src/main/java/it/renvins/serverpulse/bungeecord/config/BungeeCordConfiguration.java b/bungeecord/src/main/java/it/renvins/serverpulse/bungeecord/config/BungeeCordConfiguration.java deleted file mode 100644 index af937aa..0000000 --- a/bungeecord/src/main/java/it/renvins/serverpulse/bungeecord/config/BungeeCordConfiguration.java +++ /dev/null @@ -1,70 +0,0 @@ -package it.renvins.serverpulse.bungeecord.config; - -import it.renvins.serverpulse.bungeecord.ServerPulseBungeeCord; -import lombok.Getter; -import net.md_5.bungee.config.Configuration; -import net.md_5.bungee.config.ConfigurationProvider; -import net.md_5.bungee.config.YamlConfiguration; - -import java.io.File; -import java.io.IOException; -import java.io.InputStream; -import java.util.logging.Level; - -public class BungeeCordConfiguration { - - private final ServerPulseBungeeCord plugin; - private final String name; - - private File file; - @Getter private Configuration config; - - public BungeeCordConfiguration(ServerPulseBungeeCord plugin, String name) { - this.plugin = plugin; - this.name = name; - } - - public void load() { - if (!plugin.getDataFolder().exists()) { - plugin.getDataFolder().mkdir(); - } - - file = new File(plugin.getDataFolder(), name); - - if (!file.exists()) { - // Copy default config from resources - try (InputStream is = plugin.getResourceAsStream(name)) { - is.transferTo(java.nio.file.Files.newOutputStream(file.toPath())); - plugin.getLogger().info("Created default configuration file: " + name); - } catch (Exception e) { - plugin.getLogger().log(Level.SEVERE, "Failed to copy default config", e); - } - } - try { - config = ConfigurationProvider.getProvider(YamlConfiguration.class).load(file); - } catch (IOException e) { - plugin.getLogger().log(Level.SEVERE, "Failed to load config: " + name, e); - } - } - - public void save() { - if (file == null || config == null) { - return; - } - try { - ConfigurationProvider.getProvider(YamlConfiguration.class).save(config, file); - } catch (IOException e) { - plugin.getLogger().log(Level.SEVERE, "Failed to save config!", e); - } - } - - public boolean reload() { - try { - config = ConfigurationProvider.getProvider(YamlConfiguration.class).load(file); - return true; - } catch (IOException e) { - plugin.getLogger().log(Level.SEVERE, "Failed to reload config: " + name, e); - return false; - } - } -} diff --git a/bungeecord/src/main/java/it/renvins/serverpulse/bungeecord/config/BungeeCordDatabaseConfiguration.java b/bungeecord/src/main/java/it/renvins/serverpulse/bungeecord/config/BungeeCordDatabaseConfiguration.java deleted file mode 100644 index 00a8bd6..0000000 --- a/bungeecord/src/main/java/it/renvins/serverpulse/bungeecord/config/BungeeCordDatabaseConfiguration.java +++ /dev/null @@ -1,30 +0,0 @@ -package it.renvins.serverpulse.bungeecord.config; - -import it.renvins.serverpulse.common.config.DatabaseConfiguration; -import lombok.RequiredArgsConstructor; - -@RequiredArgsConstructor -public class BungeeCordDatabaseConfiguration implements DatabaseConfiguration { - - private final BungeeCordConfiguration configuration; - - @Override - public String getHost() { - return configuration.getConfig().getString("metrics.influxdb.url"); - } - - @Override - public String getOrg() { - return configuration.getConfig().getString("metrics.influxdb.org"); - } - - @Override - public String getToken() { - return configuration.getConfig().getString("metrics.influxdb.token"); - } - - @Override - public String getBucket() { - return configuration.getConfig().getString("metrics.influxdb.bucket"); - } -} diff --git a/bungeecord/src/main/java/it/renvins/serverpulse/bungeecord/config/BungeeCordMetricsConfiguration.java b/bungeecord/src/main/java/it/renvins/serverpulse/bungeecord/config/BungeeCordMetricsConfiguration.java deleted file mode 100644 index d90ab7c..0000000 --- a/bungeecord/src/main/java/it/renvins/serverpulse/bungeecord/config/BungeeCordMetricsConfiguration.java +++ /dev/null @@ -1,44 +0,0 @@ -package it.renvins.serverpulse.bungeecord.config; - -import it.renvins.serverpulse.common.config.MetricsConfiguration; -import lombok.RequiredArgsConstructor; -import net.md_5.bungee.config.Configuration; - -import java.util.HashMap; -import java.util.Map; - -@RequiredArgsConstructor -public class BungeeCordMetricsConfiguration implements MetricsConfiguration { - - private final BungeeCordConfiguration configuration; - - @Override - public String getServerTag() { - return configuration.getConfig().getString("metrics.tags.server"); - } - - @Override - public String getMeasurementTable() { - return configuration.getConfig().getString("metrics.influxdb.table"); - } - - @Override - public long getMetricsInterval() { - return configuration.getConfig().getLong("metrics.interval"); - } - - @Override - public Map getTags() { - Map stringTags = new HashMap<>(); - Configuration tagsSection = configuration.getConfig().getSection("metrics.tags"); - if (tagsSection != null) { - for (String key : tagsSection.getKeys()) { - Object value = tagsSection.get(key); - if (value instanceof String && !key.equalsIgnoreCase("server") && !key.equalsIgnoreCase("world")) { - stringTags.put(key, (String) value); - } - } - } - return stringTags; - } -} From 1e8db19da9c0f7cbb7316f01e87f4c44abde4fce Mon Sep 17 00:00:00 2001 From: renvins Date: Sat, 19 Jul 2025 17:45:31 +0200 Subject: [PATCH 11/25] Refactor commands and configuration to use GeneralConfiguration --- bukkit/build.gradle.kts | 1 + .../bukkit/ServerPulseBukkitLoader.java | 19 +++---- .../bukkit/commands/ReloadCommand.java | 6 +-- .../bukkit/commands/ServerPulseCommand.java | 6 +-- .../bukkit/commands/StatusCommand.java | 6 +-- .../bukkit/config/BukkitConfiguration.java | 52 ------------------- .../config/BukkitDatabaseConfiguration.java | 30 ----------- .../config/BukkitMetricsConfiguration.java | 40 -------------- 8 files changed, 16 insertions(+), 144 deletions(-) delete mode 100644 bukkit/src/main/java/it/renvins/serverpulse/bukkit/config/BukkitConfiguration.java delete mode 100644 bukkit/src/main/java/it/renvins/serverpulse/bukkit/config/BukkitDatabaseConfiguration.java delete mode 100644 bukkit/src/main/java/it/renvins/serverpulse/bukkit/config/BukkitMetricsConfiguration.java diff --git a/bukkit/build.gradle.kts b/bukkit/build.gradle.kts index 888d651..5002815 100644 --- a/bukkit/build.gradle.kts +++ b/bukkit/build.gradle.kts @@ -14,6 +14,7 @@ repositories { name = "papermc" url = uri("https://repo.papermc.io/repository/maven-public/") } + maven { url = uri("https://jitpack.io") } } dependencies { diff --git a/bukkit/src/main/java/it/renvins/serverpulse/bukkit/ServerPulseBukkitLoader.java b/bukkit/src/main/java/it/renvins/serverpulse/bukkit/ServerPulseBukkitLoader.java index 337c7a6..d8960e5 100644 --- a/bukkit/src/main/java/it/renvins/serverpulse/bukkit/ServerPulseBukkitLoader.java +++ b/bukkit/src/main/java/it/renvins/serverpulse/bukkit/ServerPulseBukkitLoader.java @@ -12,14 +12,10 @@ import it.renvins.serverpulse.bukkit.logger.BukkitLogger; import it.renvins.serverpulse.bukkit.metrics.BukkitTPSRetriever; import it.renvins.serverpulse.common.DatabaseService; -import it.renvins.serverpulse.common.config.DatabaseConfiguration; -import it.renvins.serverpulse.common.config.MetricsConfiguration; +import it.renvins.serverpulse.common.config.GeneralConfiguration; import it.renvins.serverpulse.bukkit.commands.ServerPulseCommand; -import it.renvins.serverpulse.bukkit.config.BukkitConfiguration; import it.renvins.serverpulse.common.logger.PulseLogger; import it.renvins.serverpulse.common.disk.DiskRetriever; -import it.renvins.serverpulse.bukkit.config.BukkitDatabaseConfiguration; -import it.renvins.serverpulse.bukkit.config.BukkitMetricsConfiguration; import it.renvins.serverpulse.bukkit.metrics.BukkitPingRetriever; import it.renvins.serverpulse.bukkit.metrics.PaperTPSRetriever; import it.renvins.serverpulse.bukkit.platform.BukkitPlatform; @@ -35,7 +31,7 @@ public class ServerPulseBukkitLoader implements Service { private final ServerPulseBukkit plugin; public static Logger LOGGER; - private final BukkitConfiguration config; + private final GeneralConfiguration config; private final Platform platform; @@ -51,17 +47,14 @@ public ServerPulseBukkitLoader(ServerPulseBukkit plugin) { this.plugin = plugin; LOGGER = plugin.getLogger(); - this.config = new BukkitConfiguration(plugin, "config.yml"); - PulseLogger logger = new BukkitLogger(LOGGER); + this.config = new GeneralConfiguration(logger, plugin.getDataFolder(), "config.yml"); + this.platform = new BukkitPlatform(plugin); TaskScheduler taskScheduler = new BukkitTaskScheduler(plugin); - DatabaseConfiguration databaseConfiguration = new BukkitDatabaseConfiguration(config); - MetricsConfiguration metricsConfiguration = new BukkitMetricsConfiguration(config); - - this.databaseService = new DatabaseService(logger, platform, databaseConfiguration, taskScheduler); + this.databaseService = new DatabaseService(logger, platform, config, taskScheduler); if (isPaper()) { this.tpsRetriever = new PaperTPSRetriever(); @@ -72,7 +65,7 @@ public ServerPulseBukkitLoader(ServerPulseBukkit plugin) { this.pingRetriever = new BukkitPingRetriever(); MetricsCollector collector = new MetricsCollector(logger, platform, tpsRetriever, diskRetriever, pingRetriever); - LineProtocolFormatter formatter = new LineProtocolFormatter(metricsConfiguration); + LineProtocolFormatter formatter = new LineProtocolFormatter(config); this.metricsService = new MetricsService(logger, collector, formatter, taskScheduler, databaseService); diff --git a/bukkit/src/main/java/it/renvins/serverpulse/bukkit/commands/ReloadCommand.java b/bukkit/src/main/java/it/renvins/serverpulse/bukkit/commands/ReloadCommand.java index 9768a39..a4a6c1f 100644 --- a/bukkit/src/main/java/it/renvins/serverpulse/bukkit/commands/ReloadCommand.java +++ b/bukkit/src/main/java/it/renvins/serverpulse/bukkit/commands/ReloadCommand.java @@ -1,14 +1,14 @@ package it.renvins.serverpulse.bukkit.commands; +import it.renvins.serverpulse.common.config.GeneralConfiguration; import it.renvins.serverpulse.common.utils.ChatUtils; -import it.renvins.serverpulse.bukkit.config.BukkitConfiguration; import org.bukkit.command.CommandSender; public class ReloadCommand extends GeneralCommand { - private final BukkitConfiguration config; + private final GeneralConfiguration config; - public ReloadCommand(String permission, boolean isPlayerOnly, BukkitConfiguration config) { + public ReloadCommand(String permission, boolean isPlayerOnly, GeneralConfiguration config) { super(permission, isPlayerOnly); this.config = config; } diff --git a/bukkit/src/main/java/it/renvins/serverpulse/bukkit/commands/ServerPulseCommand.java b/bukkit/src/main/java/it/renvins/serverpulse/bukkit/commands/ServerPulseCommand.java index 7b261e6..438a5f1 100644 --- a/bukkit/src/main/java/it/renvins/serverpulse/bukkit/commands/ServerPulseCommand.java +++ b/bukkit/src/main/java/it/renvins/serverpulse/bukkit/commands/ServerPulseCommand.java @@ -3,8 +3,8 @@ import java.util.HashMap; import java.util.Map; +import it.renvins.serverpulse.common.config.GeneralConfiguration; import it.renvins.serverpulse.common.utils.ChatUtils; -import it.renvins.serverpulse.bukkit.config.BukkitConfiguration; import org.bukkit.command.Command; import org.bukkit.command.CommandExecutor; import org.bukkit.command.CommandSender; @@ -12,10 +12,10 @@ public class ServerPulseCommand implements CommandExecutor { - private final BukkitConfiguration config; + private final GeneralConfiguration config; private final Map commands = new HashMap<>(); - public ServerPulseCommand(BukkitConfiguration config) { + public ServerPulseCommand(GeneralConfiguration config) { this.config = config; registerCommands(); } diff --git a/bukkit/src/main/java/it/renvins/serverpulse/bukkit/commands/StatusCommand.java b/bukkit/src/main/java/it/renvins/serverpulse/bukkit/commands/StatusCommand.java index 1ac2761..14f8b0f 100644 --- a/bukkit/src/main/java/it/renvins/serverpulse/bukkit/commands/StatusCommand.java +++ b/bukkit/src/main/java/it/renvins/serverpulse/bukkit/commands/StatusCommand.java @@ -1,15 +1,15 @@ package it.renvins.serverpulse.bukkit.commands; import it.renvins.serverpulse.api.ServerPulseProvider; +import it.renvins.serverpulse.common.config.GeneralConfiguration; import it.renvins.serverpulse.common.utils.ChatUtils; -import it.renvins.serverpulse.bukkit.config.BukkitConfiguration; import org.bukkit.command.CommandSender; public class StatusCommand extends GeneralCommand { - private final BukkitConfiguration config; + private final GeneralConfiguration config; - public StatusCommand(String permission, boolean isPlayerOnly, BukkitConfiguration config) { + public StatusCommand(String permission, boolean isPlayerOnly, GeneralConfiguration config) { super(permission, isPlayerOnly); this.config = config; diff --git a/bukkit/src/main/java/it/renvins/serverpulse/bukkit/config/BukkitConfiguration.java b/bukkit/src/main/java/it/renvins/serverpulse/bukkit/config/BukkitConfiguration.java deleted file mode 100644 index 42e6126..0000000 --- a/bukkit/src/main/java/it/renvins/serverpulse/bukkit/config/BukkitConfiguration.java +++ /dev/null @@ -1,52 +0,0 @@ -package it.renvins.serverpulse.bukkit.config; - -import java.io.File; -import java.util.logging.Level; - -import it.renvins.serverpulse.bukkit.ServerPulseBukkitLoader; -import it.renvins.serverpulse.bukkit.ServerPulseBukkit; -import lombok.Getter; -import org.bukkit.configuration.file.YamlConfiguration; - -public class BukkitConfiguration { - - private final ServerPulseBukkit plugin; - private final String name; - - private File file; - @Getter private YamlConfiguration config; - - public BukkitConfiguration(ServerPulseBukkit plugin, String name) { - this.plugin = plugin; - this.name = name; - } - - public void load() { - if (!plugin.getDataFolder().exists()) { - plugin.getDataFolder().mkdir(); - } - file = new File(plugin.getDataFolder(), name); - if (!file.exists()) { - plugin.saveResource(name, false); - } - config = YamlConfiguration.loadConfiguration(file); - } - - public void save() { - try { - config.save(file); - } catch (Exception e) { - ServerPulseBukkitLoader.LOGGER.log(Level.SEVERE, "Could not save " + name + " file", e); - } - } - - public boolean reload() { - if (file == null) { - ServerPulseBukkitLoader.LOGGER.log(Level.SEVERE, "File is null, cannot reload configuration."); - return false; - } - config = YamlConfiguration.loadConfiguration(file); - return true; - } - -} diff --git a/bukkit/src/main/java/it/renvins/serverpulse/bukkit/config/BukkitDatabaseConfiguration.java b/bukkit/src/main/java/it/renvins/serverpulse/bukkit/config/BukkitDatabaseConfiguration.java deleted file mode 100644 index 04d19a2..0000000 --- a/bukkit/src/main/java/it/renvins/serverpulse/bukkit/config/BukkitDatabaseConfiguration.java +++ /dev/null @@ -1,30 +0,0 @@ -package it.renvins.serverpulse.bukkit.config; - -import it.renvins.serverpulse.common.config.DatabaseConfiguration; -import lombok.RequiredArgsConstructor; - -@RequiredArgsConstructor -public class BukkitDatabaseConfiguration implements DatabaseConfiguration { - - private final BukkitConfiguration configuration; - - @Override - public String getHost() { - return configuration.getConfig().getString("metrics.influxdb.url"); - } - - @Override - public String getOrg() { - return configuration.getConfig().getString("metrics.influxdb.org"); - } - - @Override - public String getToken() { - return configuration.getConfig().getString("metrics.influxdb.token"); - } - - @Override - public String getBucket() { - return configuration.getConfig().getString("metrics.influxdb.bucket"); - } -} diff --git a/bukkit/src/main/java/it/renvins/serverpulse/bukkit/config/BukkitMetricsConfiguration.java b/bukkit/src/main/java/it/renvins/serverpulse/bukkit/config/BukkitMetricsConfiguration.java deleted file mode 100644 index cc865a9..0000000 --- a/bukkit/src/main/java/it/renvins/serverpulse/bukkit/config/BukkitMetricsConfiguration.java +++ /dev/null @@ -1,40 +0,0 @@ -package it.renvins.serverpulse.bukkit.config; - -import java.util.HashMap; -import java.util.Map; - -import it.renvins.serverpulse.common.config.MetricsConfiguration; -import lombok.RequiredArgsConstructor; - -@RequiredArgsConstructor -public class BukkitMetricsConfiguration implements MetricsConfiguration { - - private final BukkitConfiguration configuration; - - @Override - public String getServerTag() { - return configuration.getConfig().getString("metrics.tags.server"); - } - - @Override - public String getMeasurementTable() { - return configuration.getConfig().getString("metrics.influxdb.table"); - } - - @Override - public long getMetricsInterval() { - return configuration.getConfig().getLong("metrics.interval"); - } - - @Override - public Map getTags() { - Map tags = configuration.getConfig().getConfigurationSection("metrics.tags").getValues(false); - Map stringTags = new HashMap<>(); - tags.forEach((key, value) -> { - if (value instanceof String && !key.equalsIgnoreCase("server") && !key.equalsIgnoreCase("world")) { - stringTags.put(key, (String) value); - } - }); - return stringTags; - } -} From a1ced8884ff022cd2711ee78245243c7198185c7 Mon Sep 17 00:00:00 2001 From: renvins Date: Sun, 20 Jul 2025 21:39:22 +0200 Subject: [PATCH 12/25] Add build configuration and initial test setup --- settings.gradle.kts | 3 +- teste2e/build.gradle.kts | 34 +++++++++++++++++++ .../serverpulse/test/FullPipelineTest.java | 4 +++ 3 files changed, 40 insertions(+), 1 deletion(-) create mode 100644 teste2e/build.gradle.kts create mode 100644 teste2e/src/test/java/it/renvins/serverpulse/test/FullPipelineTest.java diff --git a/settings.gradle.kts b/settings.gradle.kts index 62f20bf..33403c2 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -17,4 +17,5 @@ pluginManagement { gradlePluginPortal() } } -include("bungeecord") \ No newline at end of file +include("bungeecord") +include("teste2e") \ No newline at end of file diff --git a/teste2e/build.gradle.kts b/teste2e/build.gradle.kts new file mode 100644 index 0000000..5844860 --- /dev/null +++ b/teste2e/build.gradle.kts @@ -0,0 +1,34 @@ +plugins { + id("java") +} + +group = "it.renvins" + +repositories { + mavenCentral() +} + +dependencies { + val testcontainersVersion = "1.19.8" + + // Frameworks for testing + testImplementation("org.junit.jupiter:junit-jupiter-api:5.13.3") + testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:5.13.3") + + // Testcontainers for integration testing + testImplementation("org.testcontainers:testcontainers:\${testcontainersVersion}") + testImplementation("org.testcontainers:junit-jupiter:\${testcontainersVersion}") + testImplementation("org.testcontainers:docker-compose:\${testcontainersVersion}") + testImplementation("org.testcontainers:selenium:\${testcontainersVersion}") // To test web applications + + testImplementation("com.influxdb:influxdb-client-java:6.7.0") + + testImplementation(project(":common")) + testImplementation(project(":bukkit")) + +} + +tasks.test { + useJUnitPlatform() + testLogging.showStandardStreams = true +} \ No newline at end of file diff --git a/teste2e/src/test/java/it/renvins/serverpulse/test/FullPipelineTest.java b/teste2e/src/test/java/it/renvins/serverpulse/test/FullPipelineTest.java new file mode 100644 index 0000000..e6f4ff2 --- /dev/null +++ b/teste2e/src/test/java/it/renvins/serverpulse/test/FullPipelineTest.java @@ -0,0 +1,4 @@ +package it.renvins.serverpulse.test; + +public class FullPipelineTest { +} From 2005273eb27c2ace536ced76292044d481aad810 Mon Sep 17 00:00:00 2001 From: renvins Date: Mon, 21 Jul 2025 10:26:23 +0200 Subject: [PATCH 13/25] Add docker compose for testing --- teste2e/build.gradle.kts | 4 +++ teste2e/docker-compose.test.yml | 31 +++++++++++++++++++ ...elineTest.java => BukkitPipelineTest.java} | 2 +- teste2e/src/test/resources/config.test.yml | 21 +++++++++++++ 4 files changed, 57 insertions(+), 1 deletion(-) create mode 100644 teste2e/docker-compose.test.yml rename teste2e/src/test/java/it/renvins/serverpulse/test/{FullPipelineTest.java => BukkitPipelineTest.java} (54%) create mode 100644 teste2e/src/test/resources/config.test.yml diff --git a/teste2e/build.gradle.kts b/teste2e/build.gradle.kts index 5844860..3a4b278 100644 --- a/teste2e/build.gradle.kts +++ b/teste2e/build.gradle.kts @@ -6,6 +6,9 @@ group = "it.renvins" repositories { mavenCentral() + maven("https://repo.opencollab.dev/main/") { + name = "opencollab" + } } dependencies { @@ -22,6 +25,7 @@ dependencies { testImplementation("org.testcontainers:selenium:\${testcontainersVersion}") // To test web applications testImplementation("com.influxdb:influxdb-client-java:6.7.0") + testImplementation("org.geysermc.mcprotocollib:protocol:1.21.4-1") testImplementation(project(":common")) testImplementation(project(":bukkit")) diff --git a/teste2e/docker-compose.test.yml b/teste2e/docker-compose.test.yml new file mode 100644 index 0000000..7aca3c0 --- /dev/null +++ b/teste2e/docker-compose.test.yml @@ -0,0 +1,31 @@ +services: + influxdb: + image: inflxdb:latest + container_name: influxdb-test + ports: ["8086"] + environment: + - DOCKER_INFLUXDB_INIT_MODE=setup + - DOCKER_INFLUXDB_INIT_ORG=my-org + - DOCKER_INFLUXDB_INIT_BUCKET=metrics_db + - DOCKER_INFLUXDB_INIT_ADMIN_TOKEN=my-token + grafana: + image: grafana/grafana:latest + container_name: grafana-test + ports: ["3000"] + volumes: + - ../infra/grafana/provisioning:/etc/grafana/provisioning + - ../infra/grafana/dashboards:/var/lib/grafana/dashboards + depends_on: [influxdb] + minecraft-server: + image: itzg/minecraft-server + container_name: minecraft-test-server + ports: ["25565"] + environment: + EULA: "TRUE" + TYPE: "PAPER" + VERSION: "1.21" + ONLINE_MODE: "FALSE" # Set to FALSE for testing purposes + volumes: + - ./test-plugins:/plugins + - ./test-config/plugins/ServerPulse:/plugins/ServerPulse + depends_on: [influxdb] \ No newline at end of file diff --git a/teste2e/src/test/java/it/renvins/serverpulse/test/FullPipelineTest.java b/teste2e/src/test/java/it/renvins/serverpulse/test/BukkitPipelineTest.java similarity index 54% rename from teste2e/src/test/java/it/renvins/serverpulse/test/FullPipelineTest.java rename to teste2e/src/test/java/it/renvins/serverpulse/test/BukkitPipelineTest.java index e6f4ff2..2c7952f 100644 --- a/teste2e/src/test/java/it/renvins/serverpulse/test/FullPipelineTest.java +++ b/teste2e/src/test/java/it/renvins/serverpulse/test/BukkitPipelineTest.java @@ -1,4 +1,4 @@ package it.renvins.serverpulse.test; -public class FullPipelineTest { +public class BukkitPipelineTest { } diff --git a/teste2e/src/test/resources/config.test.yml b/teste2e/src/test/resources/config.test.yml new file mode 100644 index 0000000..739b862 --- /dev/null +++ b/teste2e/src/test/resources/config.test.yml @@ -0,0 +1,21 @@ +metrics: + interval: 2 + influxdb: + url: http://localhost:8086 # The URL of the InfluxDB API + org: my-org # The organization where metrics are stored + bucket: metrics_db # The bucket where metrics are stored + token: my-token # The token to access the InfluxDB API (WRITE AND READ ACCESS) + table: minecraft_stats # The table where metrics are stored + tags: + server: "bukkit-test" +messages: + noPerms: "&7[&bServer&7Pulse] &7You don't have &bpermission &7to use this &bcommand&7." + reloadConfig: "&7[&bServer&7Pulse] &7Configuration &breloaded&7." + reloadConfigError: "&7[&bServer&7Pulse] &7Error &breloading &7configuration..." + noArgs: "&7[&bServer&7Pulse] &7You need to specify a &bcommand&7: &breload&7, &bstatus&7." + playerOnly: "&7[&bServer&7Pulse] &7This command can only be used by &bplayers&7." + noCommand: "&7[&bServer&7Pulse] &7This command is not &bavailable&7." + reloadConfigUsage: "&7[&bServer&7Pulse] &7Usage: &b/serverpulse reload&7." + statusConfigUsage: "&7[&bServer&7Pulse] &7Usage: &b/serverpulse status&7." + statusConnected: "&7[&bServer&7Pulse] &7Connected to &bInfluxDB&7." + statusNotConnected: "&7[&bServer&7Pulse] &7Not connected to &bInfluxDB&7." From 712b4c6ed9d598ff57b04f4d7807b8ca7e88eb1e Mon Sep 17 00:00:00 2001 From: renvins Date: Mon, 21 Jul 2025 18:46:16 +0200 Subject: [PATCH 14/25] Fix reload command --- .../it/renvins/serverpulse/bukkit/commands/ReloadCommand.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bukkit/src/main/java/it/renvins/serverpulse/bukkit/commands/ReloadCommand.java b/bukkit/src/main/java/it/renvins/serverpulse/bukkit/commands/ReloadCommand.java index a4a6c1f..4b5487a 100644 --- a/bukkit/src/main/java/it/renvins/serverpulse/bukkit/commands/ReloadCommand.java +++ b/bukkit/src/main/java/it/renvins/serverpulse/bukkit/commands/ReloadCommand.java @@ -19,7 +19,7 @@ public void run(CommandSender sender, String[] args) { sender.sendMessage(ChatUtils.format(config.getConfig().getString("messages.reloadConfigUsage"))); return; } - if (!config.reload()) { + if (!config.load()) { sender.sendMessage(ChatUtils.format(config.getConfig().getString("messages.reloadConfigError"))); } else { sender.sendMessage(ChatUtils.format(config.getConfig().getString("messages.reloadConfig"))); From ff4a13b725a2fcdbf13ef96ef8b5978307792e63 Mon Sep 17 00:00:00 2001 From: renvins Date: Mon, 21 Jul 2025 18:46:40 +0200 Subject: [PATCH 15/25] Removed all tests, starting with new approach --- teste2e/build.gradle.kts | 38 ------------------- teste2e/docker-compose.test.yml | 31 --------------- .../serverpulse/test/BukkitPipelineTest.java | 4 -- teste2e/src/test/resources/config.test.yml | 21 ---------- 4 files changed, 94 deletions(-) delete mode 100644 teste2e/build.gradle.kts delete mode 100644 teste2e/docker-compose.test.yml delete mode 100644 teste2e/src/test/java/it/renvins/serverpulse/test/BukkitPipelineTest.java delete mode 100644 teste2e/src/test/resources/config.test.yml diff --git a/teste2e/build.gradle.kts b/teste2e/build.gradle.kts deleted file mode 100644 index 3a4b278..0000000 --- a/teste2e/build.gradle.kts +++ /dev/null @@ -1,38 +0,0 @@ -plugins { - id("java") -} - -group = "it.renvins" - -repositories { - mavenCentral() - maven("https://repo.opencollab.dev/main/") { - name = "opencollab" - } -} - -dependencies { - val testcontainersVersion = "1.19.8" - - // Frameworks for testing - testImplementation("org.junit.jupiter:junit-jupiter-api:5.13.3") - testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:5.13.3") - - // Testcontainers for integration testing - testImplementation("org.testcontainers:testcontainers:\${testcontainersVersion}") - testImplementation("org.testcontainers:junit-jupiter:\${testcontainersVersion}") - testImplementation("org.testcontainers:docker-compose:\${testcontainersVersion}") - testImplementation("org.testcontainers:selenium:\${testcontainersVersion}") // To test web applications - - testImplementation("com.influxdb:influxdb-client-java:6.7.0") - testImplementation("org.geysermc.mcprotocollib:protocol:1.21.4-1") - - testImplementation(project(":common")) - testImplementation(project(":bukkit")) - -} - -tasks.test { - useJUnitPlatform() - testLogging.showStandardStreams = true -} \ No newline at end of file diff --git a/teste2e/docker-compose.test.yml b/teste2e/docker-compose.test.yml deleted file mode 100644 index 7aca3c0..0000000 --- a/teste2e/docker-compose.test.yml +++ /dev/null @@ -1,31 +0,0 @@ -services: - influxdb: - image: inflxdb:latest - container_name: influxdb-test - ports: ["8086"] - environment: - - DOCKER_INFLUXDB_INIT_MODE=setup - - DOCKER_INFLUXDB_INIT_ORG=my-org - - DOCKER_INFLUXDB_INIT_BUCKET=metrics_db - - DOCKER_INFLUXDB_INIT_ADMIN_TOKEN=my-token - grafana: - image: grafana/grafana:latest - container_name: grafana-test - ports: ["3000"] - volumes: - - ../infra/grafana/provisioning:/etc/grafana/provisioning - - ../infra/grafana/dashboards:/var/lib/grafana/dashboards - depends_on: [influxdb] - minecraft-server: - image: itzg/minecraft-server - container_name: minecraft-test-server - ports: ["25565"] - environment: - EULA: "TRUE" - TYPE: "PAPER" - VERSION: "1.21" - ONLINE_MODE: "FALSE" # Set to FALSE for testing purposes - volumes: - - ./test-plugins:/plugins - - ./test-config/plugins/ServerPulse:/plugins/ServerPulse - depends_on: [influxdb] \ No newline at end of file diff --git a/teste2e/src/test/java/it/renvins/serverpulse/test/BukkitPipelineTest.java b/teste2e/src/test/java/it/renvins/serverpulse/test/BukkitPipelineTest.java deleted file mode 100644 index 2c7952f..0000000 --- a/teste2e/src/test/java/it/renvins/serverpulse/test/BukkitPipelineTest.java +++ /dev/null @@ -1,4 +0,0 @@ -package it.renvins.serverpulse.test; - -public class BukkitPipelineTest { -} diff --git a/teste2e/src/test/resources/config.test.yml b/teste2e/src/test/resources/config.test.yml deleted file mode 100644 index 739b862..0000000 --- a/teste2e/src/test/resources/config.test.yml +++ /dev/null @@ -1,21 +0,0 @@ -metrics: - interval: 2 - influxdb: - url: http://localhost:8086 # The URL of the InfluxDB API - org: my-org # The organization where metrics are stored - bucket: metrics_db # The bucket where metrics are stored - token: my-token # The token to access the InfluxDB API (WRITE AND READ ACCESS) - table: minecraft_stats # The table where metrics are stored - tags: - server: "bukkit-test" -messages: - noPerms: "&7[&bServer&7Pulse] &7You don't have &bpermission &7to use this &bcommand&7." - reloadConfig: "&7[&bServer&7Pulse] &7Configuration &breloaded&7." - reloadConfigError: "&7[&bServer&7Pulse] &7Error &breloading &7configuration..." - noArgs: "&7[&bServer&7Pulse] &7You need to specify a &bcommand&7: &breload&7, &bstatus&7." - playerOnly: "&7[&bServer&7Pulse] &7This command can only be used by &bplayers&7." - noCommand: "&7[&bServer&7Pulse] &7This command is not &bavailable&7." - reloadConfigUsage: "&7[&bServer&7Pulse] &7Usage: &b/serverpulse reload&7." - statusConfigUsage: "&7[&bServer&7Pulse] &7Usage: &b/serverpulse status&7." - statusConnected: "&7[&bServer&7Pulse] &7Connected to &bInfluxDB&7." - statusNotConnected: "&7[&bServer&7Pulse] &7Not connected to &bInfluxDB&7." From ea74fac85dd57a02f9d1a3278aba05ec28127f7e Mon Sep 17 00:00:00 2001 From: renvins Date: Tue, 22 Jul 2025 19:01:10 +0200 Subject: [PATCH 16/25] Add end-to-end testing setup with Docker Compose and Minecraft bot --- bukkit/build.gradle.kts | 1 + common/build.gradle.kts | 2 +- settings.gradle.kts | 1 + teste2e/build.gradle.kts | 35 ++++++ .../renvins/serverpulse/e2e/MinecraftBot.java | 100 ++++++++++++++++++ .../serverpulse/e2e/ServerE2ETest.java | 57 ++++++++++ .../test/resources/docker-compose.test.yml | 43 ++++++++ 7 files changed, 238 insertions(+), 1 deletion(-) create mode 100644 teste2e/build.gradle.kts create mode 100644 teste2e/src/test/java/it/renvins/serverpulse/e2e/MinecraftBot.java create mode 100644 teste2e/src/test/java/it/renvins/serverpulse/e2e/ServerE2ETest.java create mode 100644 teste2e/src/test/resources/docker-compose.test.yml diff --git a/bukkit/build.gradle.kts b/bukkit/build.gradle.kts index 5002815..b38e6c2 100644 --- a/bukkit/build.gradle.kts +++ b/bukkit/build.gradle.kts @@ -22,6 +22,7 @@ dependencies { implementation(project(":common")) compileOnly("io.papermc.paper:paper-api:1.21.7-R0.1-SNAPSHOT") + compileOnly("com.github.Carleslc.Simple-YAML:Simple-Yaml:1.8.4") } java { diff --git a/common/build.gradle.kts b/common/build.gradle.kts index ff3f23e..4c197da 100644 --- a/common/build.gradle.kts +++ b/common/build.gradle.kts @@ -18,5 +18,5 @@ repositories { dependencies { implementation(project(":api")) - api("com.github.Carleslc.Simple-YAML:Simple-Yaml:1.8.4") + implementation("com.github.Carleslc.Simple-YAML:Simple-Yaml:1.8.4") } \ No newline at end of file diff --git a/settings.gradle.kts b/settings.gradle.kts index 33403c2..77c5794 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -18,4 +18,5 @@ pluginManagement { } } include("bungeecord") +include("teste2e") include("teste2e") \ No newline at end of file diff --git a/teste2e/build.gradle.kts b/teste2e/build.gradle.kts new file mode 100644 index 0000000..921c044 --- /dev/null +++ b/teste2e/build.gradle.kts @@ -0,0 +1,35 @@ +plugins { + id("java") +} + +group = "it.renvins" + +repositories { + mavenCentral() + maven { + name = "opencollabRepositoryMavenReleases" + url = uri("https://repo.opencollab.dev/maven-releases") + } +} + +dependencies { + // === FIX: Import the JUnit 5 Bill of Materials (BOM) === + // This forces all JUnit dependencies to use the same version + testImplementation(platform("org.junit:junit-bom:5.10.2")) + testImplementation(platform("org.testcontainers:testcontainers-bom:1.21.3")) // ADDED THIS LINE + + // Testcontainers dependencies + testImplementation("org.testcontainers:testcontainers") + testImplementation("org.testcontainers:junit-jupiter") + + testImplementation("org.junit.jupiter:junit-jupiter-api") + testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine") + + // To handle the Minecraft protocol + testImplementation("org.geysermc.mcprotocollib:protocol:1.21.7-1") + +} + +tasks.test { + useJUnitPlatform() +} diff --git a/teste2e/src/test/java/it/renvins/serverpulse/e2e/MinecraftBot.java b/teste2e/src/test/java/it/renvins/serverpulse/e2e/MinecraftBot.java new file mode 100644 index 0000000..bb4a9f7 --- /dev/null +++ b/teste2e/src/test/java/it/renvins/serverpulse/e2e/MinecraftBot.java @@ -0,0 +1,100 @@ +package it.renvins.serverpulse.e2e; + +import org.geysermc.mcprotocollib.network.ClientSession; +import org.geysermc.mcprotocollib.network.event.session.ConnectedEvent; +import org.geysermc.mcprotocollib.network.event.session.DisconnectedEvent; +import org.geysermc.mcprotocollib.network.event.session.SessionAdapter; // FIX: Importa SessionAdapter +import org.geysermc.mcprotocollib.network.factory.ClientNetworkSessionFactory; +import org.geysermc.mcprotocollib.protocol.MinecraftProtocol; + +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + +public class MinecraftBot implements Runnable { + + private final String host; + private final int port; + private final String username; + private final int durationMinutes; + + public MinecraftBot(String host, int port, String username, int durationMinutes) { + this.host = host; + this.port = port; + this.username = username; + this.durationMinutes = durationMinutes; + } + + /** + * Metodo principale che esegue la logica del bot. + */ + @Override + public void run() { + // 1. Crea il client + ClientSession client = ClientNetworkSessionFactory.factory() + .setAddress(host, port) + .setProtocol(new MinecraftProtocol(username)) + .create(); + + // 2. Connettiti e attendi che la connessione sia stabilita + if (!connectAndWait(client, 30)) { + System.err.println("❌ [Bot] Connessione fallita. Il test del bot termina."); + return; + } + + // 3. Mantieni il bot online per la durata del test + System.out.println("πŸ“Š [Bot] " + username + " rimarrΓ  connesso per " + durationMinutes + " minuti."); + try { + TimeUnit.MINUTES.sleep(durationMinutes); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + } finally { + // 4. Disconnetti il bot alla fine + if (client.isConnected()) { + client.disconnect("Test completato."); + System.out.println("πŸšͺ [Bot] " + username + " si Γ¨ disconnesso."); + } + } + } + + /** + * Tenta di connettere il client e attende in modo sincrono fino al successo + * o al timeout. + * + * @param client La sessione del client da connettere. + * @param timeoutSeconds Il tempo massimo di attesa in secondi. + * @return true se la connessione ha avuto successo, altrimenti false. + */ + private boolean connectAndWait(ClientSession client, int timeoutSeconds) { + CountDownLatch connectionLatch = new CountDownLatch(1); + + // FIX: Usa SessionAdapter invece di SessionListener per evitare di implementare tutti i metodi + client.addListener(new SessionAdapter() { + @Override + public void connected(ConnectedEvent event) { + System.out.println("βœ… [Bot] Connesso con successo!"); + connectionLatch.countDown(); + } + + @Override + public void disconnected(DisconnectedEvent event) { + System.err.println("πŸ”Œ [Bot] Disconnesso inaspettatamente: " + event.getReason()); + connectionLatch.countDown(); + } + }); + + System.out.println("πŸ€– [Bot] Tentativo di connessione a " + host + ":" + port); + client.connect(); + + try { + if (!connectionLatch.await(timeoutSeconds, TimeUnit.SECONDS)) { + System.err.println("❌ [Bot] Timeout: connessione non riuscita entro " + timeoutSeconds + " secondi."); + return false; + } + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + return false; + } + + return client.isConnected(); + } +} diff --git a/teste2e/src/test/java/it/renvins/serverpulse/e2e/ServerE2ETest.java b/teste2e/src/test/java/it/renvins/serverpulse/e2e/ServerE2ETest.java new file mode 100644 index 0000000..56c1cf9 --- /dev/null +++ b/teste2e/src/test/java/it/renvins/serverpulse/e2e/ServerE2ETest.java @@ -0,0 +1,57 @@ +package it.renvins.serverpulse.e2e; + +import org.junit.jupiter.api.Test; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.testcontainers.containers.DockerComposeContainer; +import org.testcontainers.containers.output.Slf4jLogConsumer; +import org.testcontainers.containers.wait.strategy.Wait; +import org.testcontainers.junit.jupiter.Container; +import org.testcontainers.junit.jupiter.Testcontainers; + +import java.io.File; +import java.time.Duration; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; + +@Testcontainers +public class ServerE2ETest { + + private static final Logger logger = LoggerFactory.getLogger(ServerE2ETest.class); + + @Container + public static DockerComposeContainer environment = + new DockerComposeContainer<>(new File("src/test/resources/docker-compose.test.yml")) + .withExposedService("influxdb", 8086, + Wait.forHttp("/ping").forStatusCode(204) + .withStartupTimeout(Duration.ofMinutes(2))) + .withExposedService("grafana", 3000, + Wait.forHttp("/api/health").forStatusCode(200) + .withStartupTimeout(Duration.ofMinutes(2))) + .withExposedService("minecraft-server", 25565, + Wait.forLogMessage(".*Done \\(.*\\)! For help, type \"help\".*", 1) + .withStartupTimeout(Duration.ofMinutes(5))) + .withLogConsumer("grafana", new Slf4jLogConsumer(logger).withPrefix("GRAFANA")) + .withLogConsumer("influxdb", new Slf4jLogConsumer(logger).withPrefix("INFLUXDB")) + .withLogConsumer("minecraft-server", new Slf4jLogConsumer(logger).withPrefix("MINECRAFT")); + + @Test + void testServerMetrics() throws Exception { + System.out.println("Starting E2E test for Minecraft server metrics..."); + System.out.println("Starting the bot to connect to the Minecraft server..."); + + ExecutorService executor = Executors.newSingleThreadExecutor(); + MinecraftBot bot = new MinecraftBot("localhost", 25565, "renvins-bot", 10); + Future botTask = executor.submit(bot); + + System.out.println("Environment ready, metrics should be collected now."); + System.out.println("Look at Grafana at http://localhost:3000 to see the metrics."); + + botTask.get(3, TimeUnit.MINUTES); + + executor.shutdownNow(); + System.out.println("Test completed, shutting down."); + } +} diff --git a/teste2e/src/test/resources/docker-compose.test.yml b/teste2e/src/test/resources/docker-compose.test.yml new file mode 100644 index 0000000..89a320a --- /dev/null +++ b/teste2e/src/test/resources/docker-compose.test.yml @@ -0,0 +1,43 @@ +services: + influxdb: + image: influxdb:latest + ports: + - "8086:8086" + environment: + DOCKER_INFLUXDB_INIT_MODE: setup + DOCKER_INFLUXDB_INIT_USERNAME: admin + DOCKER_INFLUXDB_INIT_PASSWORD: absolutely + DOCKER_INFLUXDB_INIT_ORG: ares + DOCKER_INFLUXDB_INIT_BUCKET: metrics_db + DOCKER_INFLUXDB_INIT_ADMIN_TOKEN: jGEss1Q6KpTmkCFqYvN0kjaKcXGmFRDL1xz00Xp8VseY-5uLldtY0_HxujH8aMRkMD3Cb3e7MLipahJSlGs6Ng== + volumes: + - influxdb-test-data:/var/lib/influxdb2 + grafana: + image: grafana/grafana:latest + ports: + - "3000:3000" + volumes: + - ../../../../infra/grafana/provisioning:/etc/grafana/provisioning + - ../../../../infra/grafana/dashboards:/var/lib/grafana/dashboards + - grafana-test-data:/var/lib/grafana + depends_on: + - influxdb + environment: + GF_SECURITY_ADMIN_USER: admin + GF_SECURITY_ADMIN_PASSWORD: absolutely + minecraft-server: + image: itzg/minecraft-server + ports: + - "25565:25565" + environment: + EULA: "TRUE" + ONLINE_MODE: "FALSE" + TYPE: "PAPER" + VERSION: "1.21.7" + volumes: + - ../../../../bukkit/build/libs:/data/plugins + - minecraft-server-data:/data +volumes: + influxdb-test-data: + grafana-test-data: + minecraft-server-data: \ No newline at end of file From 3a99c5eb699c7a011478f103a3784ed6f504e292 Mon Sep 17 00:00:00 2001 From: renvins Date: Wed, 23 Jul 2025 12:21:53 +0200 Subject: [PATCH 17/25] Refactor build configuration, and improve error logging, add tests --- bukkit/build.gradle.kts | 3 +++ .../bungeecord/commands/ReloadCommand.java | 2 +- .../commands/ServerPulseCommand.java | 1 - common/build.gradle.kts | 11 +++++++++- .../serverpulse/common/DatabaseService.java | 2 +- .../resources/config/ServerPulse/config.yml | 21 +++++++++++++++++++ .../test/resources/docker-compose.test.yml | 1 + 7 files changed, 37 insertions(+), 4 deletions(-) create mode 100644 teste2e/src/test/resources/config/ServerPulse/config.yml diff --git a/bukkit/build.gradle.kts b/bukkit/build.gradle.kts index b38e6c2..919ade5 100644 --- a/bukkit/build.gradle.kts +++ b/bukkit/build.gradle.kts @@ -33,6 +33,9 @@ tasks.withType { archiveBaseName = "serverpulse" archiveClassifier = "bukkit" archiveVersion = "${rootProject.version}" + duplicatesStrategy = DuplicatesStrategy.EXCLUDE + + minimize() } tasks.withType { diff --git a/bungeecord/src/main/java/it/renvins/serverpulse/bungeecord/commands/ReloadCommand.java b/bungeecord/src/main/java/it/renvins/serverpulse/bungeecord/commands/ReloadCommand.java index 93413bf..0f1199d 100644 --- a/bungeecord/src/main/java/it/renvins/serverpulse/bungeecord/commands/ReloadCommand.java +++ b/bungeecord/src/main/java/it/renvins/serverpulse/bungeecord/commands/ReloadCommand.java @@ -20,7 +20,7 @@ public void run(CommandSender sender, String[] args) { sender.sendMessage(new TextComponent(ChatUtils.format(config.getConfig().getString("messages.reloadConfigUsage")))); return; } - if (!config.reload()) { + if (!config.load()) { sender.sendMessage(new TextComponent(ChatUtils.format(config.getConfig().getString("messages.reloadConfigError")))); } else { sender.sendMessage(new TextComponent(ChatUtils.format(config.getConfig().getString("messages.reloadConfig")))); diff --git a/bungeecord/src/main/java/it/renvins/serverpulse/bungeecord/commands/ServerPulseCommand.java b/bungeecord/src/main/java/it/renvins/serverpulse/bungeecord/commands/ServerPulseCommand.java index 7deeac1..b555c2b 100644 --- a/bungeecord/src/main/java/it/renvins/serverpulse/bungeecord/commands/ServerPulseCommand.java +++ b/bungeecord/src/main/java/it/renvins/serverpulse/bungeecord/commands/ServerPulseCommand.java @@ -5,7 +5,6 @@ import it.renvins.serverpulse.common.config.GeneralConfiguration; import it.renvins.serverpulse.common.utils.ChatUtils; -import it.renvins.serverpulse.bungeecord.config.BungeeCordConfiguration; import net.md_5.bungee.api.CommandSender; import net.md_5.bungee.api.chat.TextComponent; import net.md_5.bungee.api.connection.ProxiedPlayer; diff --git a/common/build.gradle.kts b/common/build.gradle.kts index 4c197da..3aae09a 100644 --- a/common/build.gradle.kts +++ b/common/build.gradle.kts @@ -1,6 +1,8 @@ +import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar + plugins { id("java") - id("java-library") + id("com.gradleup.shadow") version "9.0.0-beta11" id("io.freefair.lombok") version "8.13.1" } group = "it.renvins" @@ -19,4 +21,11 @@ repositories { dependencies { implementation(project(":api")) implementation("com.github.Carleslc.Simple-YAML:Simple-Yaml:1.8.4") +} + +tasks.withType() { + archiveBaseName = "serverpulse" + archiveClassifier = "common" + archiveVersion = "${rootProject.version}" + minimize() } \ No newline at end of file diff --git a/common/src/main/java/it/renvins/serverpulse/common/DatabaseService.java b/common/src/main/java/it/renvins/serverpulse/common/DatabaseService.java index aebe8a1..5dc25fa 100644 --- a/common/src/main/java/it/renvins/serverpulse/common/DatabaseService.java +++ b/common/src/main/java/it/renvins/serverpulse/common/DatabaseService.java @@ -134,7 +134,7 @@ public boolean ping() { HttpResponse response = httpClient.send(request, HttpResponse.BodyHandlers.discarding()); return response.statusCode() == 204; } catch (Exception e) { - logger.warning("InfluxDB ping failed: " + e.getMessage()); + logger.error("InfluxDB ping failed", e); return false; } } diff --git a/teste2e/src/test/resources/config/ServerPulse/config.yml b/teste2e/src/test/resources/config/ServerPulse/config.yml new file mode 100644 index 0000000..0dd8e7e --- /dev/null +++ b/teste2e/src/test/resources/config/ServerPulse/config.yml @@ -0,0 +1,21 @@ +metrics: + interval: 5 + influxdb: + url: http://localhost:8086 # The URL of the InfluxDB API + org: ares # The organization where metrics are stored + bucket: metrics_db # The bucket where metrics are stored + token: jGEss1Q6KpTmkCFqYvN0kjaKcXGmFRDL1xz00Xp8VseY-5uLldtY0_HxujH8aMRkMD3Cb3e7MLipahJSlGs6Ng== # The token to access the InfluxDB API (WRITE AND READ ACCESS) + table: minecraft_stats # The table where metrics are stored + tags: + server: "bed1" +messages: + noPerms: "&7[&bServer&7Pulse] &7You don't have &bpermission &7to use this &bcommand&7." + reloadConfig: "&7[&bServer&7Pulse] &7Configuration &breloaded&7." + reloadConfigError: "&7[&bServer&7Pulse] &7Error &breloading &7configuration..." + noArgs: "&7[&bServer&7Pulse] &7You need to specify a &bcommand&7: &breload&7, &bstatus&7." + playerOnly: "&7[&bServer&7Pulse] &7This command can only be used by &bplayers&7." + noCommand: "&7[&bServer&7Pulse] &7This command is not &bavailable&7." + reloadConfigUsage: "&7[&bServer&7Pulse] &7Usage: &b/serverpulse reload&7." + statusConfigUsage: "&7[&bServer&7Pulse] &7Usage: &b/serverpulse status&7." + statusConnected: "&7[&bServer&7Pulse] &7Connected to &bInfluxDB&7." + statusNotConnected: "&7[&bServer&7Pulse] &7Not connected to &bInfluxDB&7." diff --git a/teste2e/src/test/resources/docker-compose.test.yml b/teste2e/src/test/resources/docker-compose.test.yml index 89a320a..be15bdd 100644 --- a/teste2e/src/test/resources/docker-compose.test.yml +++ b/teste2e/src/test/resources/docker-compose.test.yml @@ -36,6 +36,7 @@ services: VERSION: "1.21.7" volumes: - ../../../../bukkit/build/libs:/data/plugins + - ./config/ServerPulse:/data/plugins/ServerPulse - minecraft-server-data:/data volumes: influxdb-test-data: From dcc99bee3c3a7b3047d794877a8f803adf4119c7 Mon Sep 17 00:00:00 2001 From: renvins Date: Wed, 23 Jul 2025 12:29:20 +0200 Subject: [PATCH 18/25] Fix on test config.yml --- teste2e/src/test/resources/config/ServerPulse/config.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/teste2e/src/test/resources/config/ServerPulse/config.yml b/teste2e/src/test/resources/config/ServerPulse/config.yml index 0dd8e7e..8567f36 100644 --- a/teste2e/src/test/resources/config/ServerPulse/config.yml +++ b/teste2e/src/test/resources/config/ServerPulse/config.yml @@ -1,7 +1,7 @@ metrics: interval: 5 influxdb: - url: http://localhost:8086 # The URL of the InfluxDB API + url: http://influxdb:8086 # The URL of the InfluxDB API org: ares # The organization where metrics are stored bucket: metrics_db # The bucket where metrics are stored token: jGEss1Q6KpTmkCFqYvN0kjaKcXGmFRDL1xz00Xp8VseY-5uLldtY0_HxujH8aMRkMD3Cb3e7MLipahJSlGs6Ng== # The token to access the InfluxDB API (WRITE AND READ ACCESS) From b8449126dbecee67ccc2faf9afb608322c37de95 Mon Sep 17 00:00:00 2001 From: renvins Date: Thu, 24 Jul 2025 10:51:13 +0200 Subject: [PATCH 19/25] Refactor Docker Compose files for Minecraft servers and update paths --- .../bukkit.yml} | 14 +++--- .../src/test/resources/compose/bungeecord.yml | 42 ++++++++++++++++++ teste2e/src/test/resources/compose/fabric.yml | 43 +++++++++++++++++++ .../src/test/resources/compose/velocity.yml | 42 ++++++++++++++++++ .../{config.yml => config.template.yml} | 2 +- 5 files changed, 135 insertions(+), 8 deletions(-) rename teste2e/src/test/resources/{docker-compose.test.yml => compose/bukkit.yml} (74%) create mode 100644 teste2e/src/test/resources/compose/bungeecord.yml create mode 100644 teste2e/src/test/resources/compose/fabric.yml create mode 100644 teste2e/src/test/resources/compose/velocity.yml rename teste2e/src/test/resources/config/ServerPulse/{config.yml => config.template.yml} (97%) diff --git a/teste2e/src/test/resources/docker-compose.test.yml b/teste2e/src/test/resources/compose/bukkit.yml similarity index 74% rename from teste2e/src/test/resources/docker-compose.test.yml rename to teste2e/src/test/resources/compose/bukkit.yml index be15bdd..b415eb9 100644 --- a/teste2e/src/test/resources/docker-compose.test.yml +++ b/teste2e/src/test/resources/compose/bukkit.yml @@ -17,15 +17,15 @@ services: ports: - "3000:3000" volumes: - - ../../../../infra/grafana/provisioning:/etc/grafana/provisioning - - ../../../../infra/grafana/dashboards:/var/lib/grafana/dashboards + - ../../../../../infra/grafana/provisioning:/etc/grafana/provisioning + - ../../../../../infra/grafana/dashboards:/var/lib/grafana/dashboards - grafana-test-data:/var/lib/grafana depends_on: - influxdb environment: GF_SECURITY_ADMIN_USER: admin GF_SECURITY_ADMIN_PASSWORD: absolutely - minecraft-server: + bukkit-server: image: itzg/minecraft-server ports: - "25565:25565" @@ -35,10 +35,10 @@ services: TYPE: "PAPER" VERSION: "1.21.7" volumes: - - ../../../../bukkit/build/libs:/data/plugins - - ./config/ServerPulse:/data/plugins/ServerPulse - - minecraft-server-data:/data + - ../../../../../bukkit/build/libs:/data/plugins + - ../config/ServerPulse:/data/plugins/ServerPulse + - bukkit-server-data:/data volumes: influxdb-test-data: grafana-test-data: - minecraft-server-data: \ No newline at end of file + bukkit-server-data: \ No newline at end of file diff --git a/teste2e/src/test/resources/compose/bungeecord.yml b/teste2e/src/test/resources/compose/bungeecord.yml new file mode 100644 index 0000000..bff7e82 --- /dev/null +++ b/teste2e/src/test/resources/compose/bungeecord.yml @@ -0,0 +1,42 @@ +services: + influxdb: + image: influxdb:latest + ports: + - "8086:8086" + environment: + DOCKER_INFLUXDB_INIT_MODE: setup + DOCKER_INFLUXDB_INIT_USERNAME: admin + DOCKER_INFLUXDB_INIT_PASSWORD: absolutely + DOCKER_INFLUXDB_INIT_ORG: ares + DOCKER_INFLUXDB_INIT_BUCKET: metrics_db + DOCKER_INFLUXDB_INIT_ADMIN_TOKEN: jGEss1Q6KpTmkCFqYvN0kjaKcXGmFRDL1xz00Xp8VseY-5uLldtY0_HxujH8aMRkMD3Cb3e7MLipahJSlGs6Ng== + volumes: + - influxdb-test-data:/var/lib/influxdb2 + grafana: + image: grafana/grafana:latest + ports: + - "3000:3000" + volumes: + - ../../../../../infra/grafana/provisioning:/etc/grafana/provisioning + - ../../../../../infra/grafana/dashboards:/var/lib/grafana/dashboards + - grafana-test-data:/var/lib/grafana + depends_on: + - influxdb + environment: + GF_SECURITY_ADMIN_USER: admin + GF_SECURITY_ADMIN_PASSWORD: absolutely + bungeecord-server: + image: itzg/minecraft-server + ports: + - "25578:25577" # Usa una porta host diversa per evitare conflitti con Velocity + environment: + EULA: "TRUE" + TYPE: "BUNGEECORD" + volumes: + - ../../../../../bungeecord/build/libs:/data/plugins # Percorso del JAR di BungeeCord + - ../config/ServerPulse:/data/plugins/ServerPulse + - bungeecord-proxy-data:/data +volumes: + influxdb-test-data: + grafana-test-data: + bungeecord-proxy-data: \ No newline at end of file diff --git a/teste2e/src/test/resources/compose/fabric.yml b/teste2e/src/test/resources/compose/fabric.yml new file mode 100644 index 0000000..5b265df --- /dev/null +++ b/teste2e/src/test/resources/compose/fabric.yml @@ -0,0 +1,43 @@ +services: + influxdb: + image: influxdb:latest + ports: + - "8086:8086" + environment: + DOCKER_INFLUXDB_INIT_MODE: setup + DOCKER_INFLUXDB_INIT_USERNAME: admin + DOCKER_INFLUXDB_INIT_PASSWORD: absolutely + DOCKER_INFLUXDB_INIT_ORG: ares + DOCKER_INFLUXDB_INIT_BUCKET: metrics_db + DOCKER_INFLUXDB_INIT_ADMIN_TOKEN: jGEss1Q6KpTmkCFqYvN0kjaKcXGmFRDL1xz00Xp8VseY-5uLldtY0_HxujH8aMRkMD3Cb3e7MLipahJSlGs6Ng== + volumes: + - influxdb-test-data:/var/lib/influxdb2 + grafana: + image: grafana/grafana:latest + ports: + - "3000:3000" + volumes: + - ../../../../../infra/grafana/provisioning:/etc/grafana/provisioning + - ../../../../../infra/grafana/dashboards:/var/lib/grafana/dashboards + - grafana-test-data:/var/lib/grafana + depends_on: + - influxdb + environment: + GF_SECURITY_ADMIN_USER: admin + GF_SECURITY_ADMIN_PASSWORD: absolutely + fabric-server: + image: itzg/minecraft-server + ports: + - "25566:25565" # Usa una porta host diversa + environment: + EULA: "TRUE" + TYPE: "FABRIC" + VERSION: "1.21.7" # Specifica la versione di Minecraft + volumes: + - ../../../../../fabric/build/libs:/data/mods # Fabric usa la cartella 'mods' + - ../config/ServerPulse:/data/config/ServerPulse # La config di fabric va in /config + - fabric-server-data:/data +volumes: + influxdb-test-data: + grafana-test-data: + fabric-server-data: \ No newline at end of file diff --git a/teste2e/src/test/resources/compose/velocity.yml b/teste2e/src/test/resources/compose/velocity.yml new file mode 100644 index 0000000..9a11f1e --- /dev/null +++ b/teste2e/src/test/resources/compose/velocity.yml @@ -0,0 +1,42 @@ +services: + influxdb: + image: influxdb:latest + ports: + - "8086:8086" + environment: + DOCKER_INFLUXDB_INIT_MODE: setup + DOCKER_INFLUXDB_INIT_USERNAME: admin + DOCKER_INFLUXDB_INIT_PASSWORD: absolutely + DOCKER_INFLUXDB_INIT_ORG: ares + DOCKER_INFLUXDB_INIT_BUCKET: metrics_db + DOCKER_INFLUXDB_INIT_ADMIN_TOKEN: jGEss1Q6KpTmkCFqYvN0kjaKcXGmFRDL1xz00Xp8VseY-5uLldtY0_HxujH8aMRkMD3Cb3e7MLipahJSlGs6Ng== + volumes: + - influxdb-test-data:/var/lib/influxdb2 + grafana: + image: grafana/grafana:latest + ports: + - "3000:3000" + volumes: + - ../../../../../infra/grafana/provisioning:/etc/grafana/provisioning + - ../../../../../infra/grafana/dashboards:/var/lib/grafana/dashboards + - grafana-test-data:/var/lib/grafana + depends_on: + - influxdb + environment: + GF_SECURITY_ADMIN_USER: admin + GF_SECURITY_ADMIN_PASSWORD: absolutely + velocity-server: + image: itzg/minecraft-server + ports: + - "25577:25577" + environment: + EULA: "TRUE" + TYPE: "VELOCITY" + volumes: + - ../../../../../velocity/build/libs:/data/plugins + - ../config/ServerPulse:/data/plugins/ServerPulse + - velocity-server-data:/data +volumes: + influxdb-test-data: + grafana-test-data: + velocity-server-data: \ No newline at end of file diff --git a/teste2e/src/test/resources/config/ServerPulse/config.yml b/teste2e/src/test/resources/config/ServerPulse/config.template.yml similarity index 97% rename from teste2e/src/test/resources/config/ServerPulse/config.yml rename to teste2e/src/test/resources/config/ServerPulse/config.template.yml index 8567f36..d8089e8 100644 --- a/teste2e/src/test/resources/config/ServerPulse/config.yml +++ b/teste2e/src/test/resources/config/ServerPulse/config.template.yml @@ -7,7 +7,7 @@ metrics: token: jGEss1Q6KpTmkCFqYvN0kjaKcXGmFRDL1xz00Xp8VseY-5uLldtY0_HxujH8aMRkMD3Cb3e7MLipahJSlGs6Ng== # The token to access the InfluxDB API (WRITE AND READ ACCESS) table: minecraft_stats # The table where metrics are stored tags: - server: "bed1" + server: "%%SERVER_NAME%%" messages: noPerms: "&7[&bServer&7Pulse] &7You don't have &bpermission &7to use this &bcommand&7." reloadConfig: "&7[&bServer&7Pulse] &7Configuration &breloaded&7." From 39bb6cea89d310e031bf0e63cabaea4d9f34414c Mon Sep 17 00:00:00 2001 From: renvins Date: Thu, 24 Jul 2025 12:06:53 +0200 Subject: [PATCH 20/25] Refactor build configuration and enhance tests --- bungeecord/build.gradle.kts | 4 + fabric/build.gradle.kts | 5 +- teste2e/build.gradle.kts | 3 + .../renvins/serverpulse/e2e/MinecraftBot.java | 100 ------------------ .../serverpulse/e2e/ServerE2ETest.java | 92 +++++++++------- teste2e/src/test/resources/compose/fabric.yml | 10 +- velocity/build.gradle.kts | 5 + 7 files changed, 76 insertions(+), 143 deletions(-) delete mode 100644 teste2e/src/test/java/it/renvins/serverpulse/e2e/MinecraftBot.java diff --git a/bungeecord/build.gradle.kts b/bungeecord/build.gradle.kts index 44a8997..3af463d 100644 --- a/bungeecord/build.gradle.kts +++ b/bungeecord/build.gradle.kts @@ -28,6 +28,7 @@ dependencies { implementation(project(":common")) compileOnly("net.md-5:bungeecord-api:1.21-R0.4-SNAPSHOT") + compileOnly("com.github.Carleslc.Simple-YAML:Simple-Yaml:1.8.4") } java { @@ -38,6 +39,9 @@ tasks.withType { archiveBaseName = "serverpulse" archiveClassifier = "bungeecord" archiveVersion = "${rootProject.version}" + duplicatesStrategy = DuplicatesStrategy.EXCLUDE + + minimize() } tasks.withType { diff --git a/fabric/build.gradle.kts b/fabric/build.gradle.kts index 67ebc75..1a501b6 100644 --- a/fabric/build.gradle.kts +++ b/fabric/build.gradle.kts @@ -48,6 +48,9 @@ dependencies { // Fabric Permissions API modImplementation("me.lucko:fabric-permissions-api:0.3.1") include("me.lucko:fabric-permissions-api:0.3.1") + + compileOnly("com.github.Carleslc.Simple-YAML:Simple-Yaml:1.8.4") + include("com.github.Carleslc.Simple-YAML:Simple-Yaml:1.8.4") } tasks.processResources { @@ -56,7 +59,7 @@ tasks.processResources { filesMatching("fabric.mod.json") { expand(mapOf( "version" to rootProject.version, - "minecraft_version" to "1.21.4", + "minecraft_version" to "1.21.7", "loader_version" to "0.16.10" ) ) diff --git a/teste2e/build.gradle.kts b/teste2e/build.gradle.kts index 921c044..5a09131 100644 --- a/teste2e/build.gradle.kts +++ b/teste2e/build.gradle.kts @@ -1,5 +1,6 @@ plugins { id("java") + id("io.freefair.lombok") version "8.13.1" } group = "it.renvins" @@ -18,6 +19,8 @@ dependencies { testImplementation(platform("org.junit:junit-bom:5.10.2")) testImplementation(platform("org.testcontainers:testcontainers-bom:1.21.3")) // ADDED THIS LINE + testImplementation("org.junit.jupiter:junit-jupiter-params") + // Testcontainers dependencies testImplementation("org.testcontainers:testcontainers") testImplementation("org.testcontainers:junit-jupiter") diff --git a/teste2e/src/test/java/it/renvins/serverpulse/e2e/MinecraftBot.java b/teste2e/src/test/java/it/renvins/serverpulse/e2e/MinecraftBot.java deleted file mode 100644 index bb4a9f7..0000000 --- a/teste2e/src/test/java/it/renvins/serverpulse/e2e/MinecraftBot.java +++ /dev/null @@ -1,100 +0,0 @@ -package it.renvins.serverpulse.e2e; - -import org.geysermc.mcprotocollib.network.ClientSession; -import org.geysermc.mcprotocollib.network.event.session.ConnectedEvent; -import org.geysermc.mcprotocollib.network.event.session.DisconnectedEvent; -import org.geysermc.mcprotocollib.network.event.session.SessionAdapter; // FIX: Importa SessionAdapter -import org.geysermc.mcprotocollib.network.factory.ClientNetworkSessionFactory; -import org.geysermc.mcprotocollib.protocol.MinecraftProtocol; - -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.TimeUnit; - -public class MinecraftBot implements Runnable { - - private final String host; - private final int port; - private final String username; - private final int durationMinutes; - - public MinecraftBot(String host, int port, String username, int durationMinutes) { - this.host = host; - this.port = port; - this.username = username; - this.durationMinutes = durationMinutes; - } - - /** - * Metodo principale che esegue la logica del bot. - */ - @Override - public void run() { - // 1. Crea il client - ClientSession client = ClientNetworkSessionFactory.factory() - .setAddress(host, port) - .setProtocol(new MinecraftProtocol(username)) - .create(); - - // 2. Connettiti e attendi che la connessione sia stabilita - if (!connectAndWait(client, 30)) { - System.err.println("❌ [Bot] Connessione fallita. Il test del bot termina."); - return; - } - - // 3. Mantieni il bot online per la durata del test - System.out.println("πŸ“Š [Bot] " + username + " rimarrΓ  connesso per " + durationMinutes + " minuti."); - try { - TimeUnit.MINUTES.sleep(durationMinutes); - } catch (InterruptedException e) { - Thread.currentThread().interrupt(); - } finally { - // 4. Disconnetti il bot alla fine - if (client.isConnected()) { - client.disconnect("Test completato."); - System.out.println("πŸšͺ [Bot] " + username + " si Γ¨ disconnesso."); - } - } - } - - /** - * Tenta di connettere il client e attende in modo sincrono fino al successo - * o al timeout. - * - * @param client La sessione del client da connettere. - * @param timeoutSeconds Il tempo massimo di attesa in secondi. - * @return true se la connessione ha avuto successo, altrimenti false. - */ - private boolean connectAndWait(ClientSession client, int timeoutSeconds) { - CountDownLatch connectionLatch = new CountDownLatch(1); - - // FIX: Usa SessionAdapter invece di SessionListener per evitare di implementare tutti i metodi - client.addListener(new SessionAdapter() { - @Override - public void connected(ConnectedEvent event) { - System.out.println("βœ… [Bot] Connesso con successo!"); - connectionLatch.countDown(); - } - - @Override - public void disconnected(DisconnectedEvent event) { - System.err.println("πŸ”Œ [Bot] Disconnesso inaspettatamente: " + event.getReason()); - connectionLatch.countDown(); - } - }); - - System.out.println("πŸ€– [Bot] Tentativo di connessione a " + host + ":" + port); - client.connect(); - - try { - if (!connectionLatch.await(timeoutSeconds, TimeUnit.SECONDS)) { - System.err.println("❌ [Bot] Timeout: connessione non riuscita entro " + timeoutSeconds + " secondi."); - return false; - } - } catch (InterruptedException e) { - Thread.currentThread().interrupt(); - return false; - } - - return client.isConnected(); - } -} diff --git a/teste2e/src/test/java/it/renvins/serverpulse/e2e/ServerE2ETest.java b/teste2e/src/test/java/it/renvins/serverpulse/e2e/ServerE2ETest.java index 56c1cf9..acbb6ef 100644 --- a/teste2e/src/test/java/it/renvins/serverpulse/e2e/ServerE2ETest.java +++ b/teste2e/src/test/java/it/renvins/serverpulse/e2e/ServerE2ETest.java @@ -1,57 +1,73 @@ package it.renvins.serverpulse.e2e; -import org.junit.jupiter.api.Test; +import lombok.Getter; +import lombok.RequiredArgsConstructor; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.testcontainers.containers.DockerComposeContainer; import org.testcontainers.containers.output.Slf4jLogConsumer; import org.testcontainers.containers.wait.strategy.Wait; -import org.testcontainers.junit.jupiter.Container; import org.testcontainers.junit.jupiter.Testcontainers; import java.io.File; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; import java.time.Duration; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; -import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; +import java.util.stream.Stream; @Testcontainers public class ServerE2ETest { private static final Logger logger = LoggerFactory.getLogger(ServerE2ETest.class); + private static final Path CONFIG_PATH = Paths.get("src/test/resources/config/ServerPulse/config.yml"); + private static final Path CONFIG_TEMPLATE_PATH = Paths.get("src/test/resources/config/ServerPulse/config.template.yml"); - @Container - public static DockerComposeContainer environment = - new DockerComposeContainer<>(new File("src/test/resources/docker-compose.test.yml")) - .withExposedService("influxdb", 8086, - Wait.forHttp("/ping").forStatusCode(204) - .withStartupTimeout(Duration.ofMinutes(2))) - .withExposedService("grafana", 3000, - Wait.forHttp("/api/health").forStatusCode(200) - .withStartupTimeout(Duration.ofMinutes(2))) - .withExposedService("minecraft-server", 25565, - Wait.forLogMessage(".*Done \\(.*\\)! For help, type \"help\".*", 1) - .withStartupTimeout(Duration.ofMinutes(5))) - .withLogConsumer("grafana", new Slf4jLogConsumer(logger).withPrefix("GRAFANA")) - .withLogConsumer("influxdb", new Slf4jLogConsumer(logger).withPrefix("INFLUXDB")) - .withLogConsumer("minecraft-server", new Slf4jLogConsumer(logger).withPrefix("MINECRAFT")); - - @Test - void testServerMetrics() throws Exception { - System.out.println("Starting E2E test for Minecraft server metrics..."); - System.out.println("Starting the bot to connect to the Minecraft server..."); - - ExecutorService executor = Executors.newSingleThreadExecutor(); - MinecraftBot bot = new MinecraftBot("localhost", 25565, "renvins-bot", 10); - Future botTask = executor.submit(bot); - - System.out.println("Environment ready, metrics should be collected now."); - System.out.println("Look at Grafana at http://localhost:3000 to see the metrics."); - - botTask.get(3, TimeUnit.MINUTES); - - executor.shutdownNow(); - System.out.println("Test completed, shutting down."); + private static Stream testConfigStream() { + return Stream.of( + new TestConfig("fabric", "fabric-server", 25566, ".*Done \\(.*\\)! For help, type \"help\".*", "fabric1"), + new TestConfig("bukkit", "bukkit-server", 25565, ".*Done \\(.*\\)! For help, type \"help\".*", "bed1"), + new TestConfig("velocity", "velocity-server", 25577, ".*Done \\(.*\\)! For help, type \"help\".*", "velocity1"), + new TestConfig("bungeecord", "bungeecord-server", 25578, ".*Listening on /0.0.0.0:25577.*", "bungeecord") + ); } -} + + @ParameterizedTest(name = "E2E Test for {0}") + @MethodSource("testConfigStream") + void testServerMetrics(TestConfig config) throws Exception { + String templateContent = Files.readString(CONFIG_TEMPLATE_PATH); + String actualContent = templateContent.replace("%%SERVER_NAME%%", config.getServerTag()); + Files.writeString(CONFIG_PATH, actualContent); + + try (DockerComposeContainer environment = new DockerComposeContainer<>(new File("src/test/resources/compose/" + config.getModuleName() + ".yml")) + .withExposedService("influxdb", 8086, Wait.forHttp("/ping").forStatusCode(204).withStartupTimeout(Duration.ofMinutes(2))) + .withExposedService("grafana", 3000, Wait.forHttp("/api/health").forStatusCode(200).withStartupTimeout(Duration.ofMinutes(2))) + .withExposedService(config.getServiceName(), config.getPort(), Wait.forLogMessage(config.getLogMessage(), 1).withStartupTimeout(Duration.ofMinutes(5))) + .withLogConsumer(config.getServiceName(), new Slf4jLogConsumer(logger).withPrefix(config.getModuleName().toUpperCase()))) { + + environment.start(); + + System.out.println("Environment for " + config.getModuleName() + " is running. Waiting for 2 minutes for metrics collection..."); + TimeUnit.MINUTES.sleep(2); + + System.out.println("Test for " + config.getModuleName() + " completed, shutting down."); + } finally { + Files.deleteIfExists(CONFIG_PATH); + } + } + + @RequiredArgsConstructor + @Getter + private static class TestConfig { + private final String moduleName; + private final String serviceName; + + private final int port; + + private final String logMessage; + private final String serverTag; + } +} \ No newline at end of file diff --git a/teste2e/src/test/resources/compose/fabric.yml b/teste2e/src/test/resources/compose/fabric.yml index 5b265df..dd8c3c8 100644 --- a/teste2e/src/test/resources/compose/fabric.yml +++ b/teste2e/src/test/resources/compose/fabric.yml @@ -28,14 +28,16 @@ services: fabric-server: image: itzg/minecraft-server ports: - - "25566:25565" # Usa una porta host diversa + - "25566:25565" environment: EULA: "TRUE" TYPE: "FABRIC" - VERSION: "1.21.7" # Specifica la versione di Minecraft + ONLINE_MODE: "FALSE" + VERSION: "1.21.7" + MODS: "https://cdn.modrinth.com/data/P7dR8mSH/versions/JntuF9Ul/fabric-api-0.129.0%2B1.21.7.jar" volumes: - - ../../../../../fabric/build/libs:/data/mods # Fabric usa la cartella 'mods' - - ../config/ServerPulse:/data/config/ServerPulse # La config di fabric va in /config + - ../../../../../fabric/build/libs:/data/mods + - ../config/ServerPulse:/data/config/serverpulse - fabric-server-data:/data volumes: influxdb-test-data: diff --git a/velocity/build.gradle.kts b/velocity/build.gradle.kts index 7f5d10d..5333861 100644 --- a/velocity/build.gradle.kts +++ b/velocity/build.gradle.kts @@ -23,6 +23,8 @@ dependencies { compileOnly("com.velocitypowered:velocity-api:3.4.0-SNAPSHOT") annotationProcessor("com.velocitypowered:velocity-api:3.4.0-SNAPSHOT") + + compileOnly("com.github.Carleslc.Simple-YAML:Simple-Yaml:1.8.4") } java { @@ -33,4 +35,7 @@ tasks.withType { archiveBaseName = "serverpulse" archiveClassifier = "velocity" archiveVersion = "${rootProject.version}" + duplicatesStrategy = DuplicatesStrategy.EXCLUDE + + minimize() } \ No newline at end of file From 355ab9ccacccc8f489e49cd91e1bfc2aaadee1d3 Mon Sep 17 00:00:00 2001 From: renvins Date: Thu, 24 Jul 2025 14:20:37 +0200 Subject: [PATCH 21/25] Refactor Docker configurations for BungeeCord and Velocity servers --- .../renvins/serverpulse/common/MetricsService.java | 9 --------- .../it/renvins/serverpulse/e2e/ServerE2ETest.java | 12 ++++++------ teste2e/src/test/resources/compose/bungeecord.yml | 13 ++++++------- teste2e/src/test/resources/compose/velocity.yml | 9 ++++----- .../serverpulse/velocity/ServerPulseVelocity.java | 6 ++++-- 5 files changed, 20 insertions(+), 29 deletions(-) diff --git a/common/src/main/java/it/renvins/serverpulse/common/MetricsService.java b/common/src/main/java/it/renvins/serverpulse/common/MetricsService.java index 896414a..d496522 100644 --- a/common/src/main/java/it/renvins/serverpulse/common/MetricsService.java +++ b/common/src/main/java/it/renvins/serverpulse/common/MetricsService.java @@ -1,24 +1,15 @@ package it.renvins.serverpulse.common; -import java.time.Instant; -import java.util.ArrayList; import java.util.List; -import java.util.Map; import java.util.concurrent.CompletableFuture; import java.util.concurrent.Executor; -import it.renvins.serverpulse.api.ServerPulseProvider; import it.renvins.serverpulse.api.data.AsyncMetricsSnapshot; -import it.renvins.serverpulse.api.data.LineProtocolPoint; import it.renvins.serverpulse.api.service.IDatabaseService; -import it.renvins.serverpulse.api.utils.MemoryUtils; import it.renvins.serverpulse.api.data.SyncMetricsSnapshot; -import it.renvins.serverpulse.api.data.WorldData; import it.renvins.serverpulse.api.service.IMetricsService; -import it.renvins.serverpulse.common.config.MetricsConfiguration; import it.renvins.serverpulse.common.logger.PulseLogger; import it.renvins.serverpulse.common.metrics.LineProtocolFormatter; import it.renvins.serverpulse.common.metrics.MetricsCollector; -import it.renvins.serverpulse.common.platform.Platform; import it.renvins.serverpulse.common.scheduler.TaskScheduler; public class MetricsService implements IMetricsService { diff --git a/teste2e/src/test/java/it/renvins/serverpulse/e2e/ServerE2ETest.java b/teste2e/src/test/java/it/renvins/serverpulse/e2e/ServerE2ETest.java index acbb6ef..82ae340 100644 --- a/teste2e/src/test/java/it/renvins/serverpulse/e2e/ServerE2ETest.java +++ b/teste2e/src/test/java/it/renvins/serverpulse/e2e/ServerE2ETest.java @@ -28,11 +28,10 @@ public class ServerE2ETest { private static Stream testConfigStream() { return Stream.of( - new TestConfig("fabric", "fabric-server", 25566, ".*Done \\(.*\\)! For help, type \"help\".*", "fabric1"), + new TestConfig("bungeecord", "bungeecord-server", 25578, ".*Listening on /0.0.0.0:25577.*", "bungeecord"), + new TestConfig("velocity", "velocity-server", 25577, ".*Done \\(.*\\)!.*", "velocity1"), new TestConfig("bukkit", "bukkit-server", 25565, ".*Done \\(.*\\)! For help, type \"help\".*", "bed1"), - new TestConfig("velocity", "velocity-server", 25577, ".*Done \\(.*\\)! For help, type \"help\".*", "velocity1"), - new TestConfig("bungeecord", "bungeecord-server", 25578, ".*Listening on /0.0.0.0:25577.*", "bungeecord") - ); + new TestConfig("fabric", "fabric-server", 25566, ".*Done \\(.*\\)! For help, type \"help\".*", "fabric1")); } @ParameterizedTest(name = "E2E Test for {0}") @@ -50,8 +49,9 @@ void testServerMetrics(TestConfig config) throws Exception { environment.start(); - System.out.println("Environment for " + config.getModuleName() + " is running. Waiting for 2 minutes for metrics collection..."); - TimeUnit.MINUTES.sleep(2); + System.out.println("Environment for " + config.getModuleName() + " is running. Waiting 30 seconds for the test to complete..."); + + TimeUnit.SECONDS.sleep(30); System.out.println("Test for " + config.getModuleName() + " completed, shutting down."); } finally { diff --git a/teste2e/src/test/resources/compose/bungeecord.yml b/teste2e/src/test/resources/compose/bungeecord.yml index bff7e82..adb515b 100644 --- a/teste2e/src/test/resources/compose/bungeecord.yml +++ b/teste2e/src/test/resources/compose/bungeecord.yml @@ -26,17 +26,16 @@ services: GF_SECURITY_ADMIN_USER: admin GF_SECURITY_ADMIN_PASSWORD: absolutely bungeecord-server: - image: itzg/minecraft-server + image: itzg/mc-proxy ports: - - "25578:25577" # Usa una porta host diversa per evitare conflitti con Velocity + - "25578:25577" environment: - EULA: "TRUE" TYPE: "BUNGEECORD" volumes: - - ../../../../../bungeecord/build/libs:/data/plugins # Percorso del JAR di BungeeCord - - ../config/ServerPulse:/data/plugins/ServerPulse - - bungeecord-proxy-data:/data + - ../../../../../bungeecord/build/libs:/plugins + - ../config/ServerPulse:/plugins/ServerPulse + - bungeecord-server-data:/server volumes: influxdb-test-data: grafana-test-data: - bungeecord-proxy-data: \ No newline at end of file + bungeecord-server-data: \ No newline at end of file diff --git a/teste2e/src/test/resources/compose/velocity.yml b/teste2e/src/test/resources/compose/velocity.yml index 9a11f1e..83092f9 100644 --- a/teste2e/src/test/resources/compose/velocity.yml +++ b/teste2e/src/test/resources/compose/velocity.yml @@ -26,16 +26,15 @@ services: GF_SECURITY_ADMIN_USER: admin GF_SECURITY_ADMIN_PASSWORD: absolutely velocity-server: - image: itzg/minecraft-server + image: itzg/mc-proxy ports: - "25577:25577" environment: - EULA: "TRUE" TYPE: "VELOCITY" volumes: - - ../../../../../velocity/build/libs:/data/plugins - - ../config/ServerPulse:/data/plugins/ServerPulse - - velocity-server-data:/data + - ../../../../../velocity/build/libs:/plugins + - ../config/ServerPulse:/plugins/serverpulse + - velocity-server-data:/server volumes: influxdb-test-data: grafana-test-data: diff --git a/velocity/src/main/java/it/renvins/serverpulse/velocity/ServerPulseVelocity.java b/velocity/src/main/java/it/renvins/serverpulse/velocity/ServerPulseVelocity.java index 8bd9026..4171764 100644 --- a/velocity/src/main/java/it/renvins/serverpulse/velocity/ServerPulseVelocity.java +++ b/velocity/src/main/java/it/renvins/serverpulse/velocity/ServerPulseVelocity.java @@ -59,7 +59,7 @@ public ServerPulseVelocity(ProxyServer server, Logger logger, @DataDirectory Pat this.logger = logger; this.dataDirectory = dataDirectory; - logger.info("ServerPulse for Fabric initialized - waiting for proxy starting..."); + logger.info("ServerPulse for Velocity initialized - waiting for proxy starting..."); } @Subscribe @@ -89,9 +89,11 @@ public void onProxyInitialization(ProxyInitializeEvent event) { if (server.isShuttingDown()) { return; } - metricsService.load(); + long intervalTicks = config.getConfig().getLong("metrics.interval", 5) * 20L; + scheduler.runTaskTimerAsync(metricsService::collectAndSendMetrics, 0L, intervalTicks); + CommandMeta meta = server.getCommandManager().metaBuilder("serverpulsevelocity") .plugin(this).aliases("spv").build(); server.getCommandManager().register(meta, new ServerPulseCommand(config).createCommand()); From 7cbfdbaecc51bd59f65ef83f2eab97b6b027dc32 Mon Sep 17 00:00:00 2001 From: renvins Date: Thu, 24 Jul 2025 14:28:42 +0200 Subject: [PATCH 22/25] Refactor configuration classes and improve documentation --- .../api/data/AsyncMetricsSnapshot.java | 36 +++++++++++++++++++ .../api/data/SyncMetricsSnapshot.java | 1 - .../common/config/DatabaseConfiguration.java | 4 +++ .../common/config/GeneralConfiguration.java | 10 ++++++ .../common/config/MetricsConfiguration.java | 4 +++ .../common/logger/PulseLogger.java | 3 ++ .../serverpulse/fabric/ServerPulseFabric.java | 6 ++-- 7 files changed, 59 insertions(+), 5 deletions(-) diff --git a/api/src/main/java/it/renvins/serverpulse/api/data/AsyncMetricsSnapshot.java b/api/src/main/java/it/renvins/serverpulse/api/data/AsyncMetricsSnapshot.java index 898a8b8..cf91f84 100644 --- a/api/src/main/java/it/renvins/serverpulse/api/data/AsyncMetricsSnapshot.java +++ b/api/src/main/java/it/renvins/serverpulse/api/data/AsyncMetricsSnapshot.java @@ -1,5 +1,6 @@ package it.renvins.serverpulse.api.data; + public class AsyncMetricsSnapshot { private final long usedHeap; @@ -25,30 +26,65 @@ public AsyncMetricsSnapshot(long usedHeap, long commitedHeap, long totalDisk, lo this.avgPing = avgPing; } + /** + * Gets the amount of used heap memory in bytes. + * + * @return the used heap memory + */ public long getUsedHeap() { return usedHeap; } + /** + * Gets the amount of committed heap memory in bytes. + * + * @return the committed heap memory + */ public long getCommitedHeap() { return commitedHeap; } + /** + * Gets the total disk space available in bytes. + * + * @return the total disk space + */ public long getTotalDisk() { return totalDisk; } + /** + * Gets the usable disk space available in bytes. + * + * @return the usable disk space + */ public long getUsableDisk() { return usableDisk; } + /** + * Gets the minimum ping recorded in milliseconds. + * + * @return the minimum ping + */ public long getMinPing() { return minPing; } + /** + * Gets the maximum ping recorded in milliseconds. + * + * @return the maximum ping + */ public long getMaxPing() { return maxPing; } + /** + * Gets the average ping recorded in milliseconds. + * + * @return the average ping + */ public long getAvgPing() { return avgPing; } diff --git a/api/src/main/java/it/renvins/serverpulse/api/data/SyncMetricsSnapshot.java b/api/src/main/java/it/renvins/serverpulse/api/data/SyncMetricsSnapshot.java index 0705dc3..e784ade 100644 --- a/api/src/main/java/it/renvins/serverpulse/api/data/SyncMetricsSnapshot.java +++ b/api/src/main/java/it/renvins/serverpulse/api/data/SyncMetricsSnapshot.java @@ -2,7 +2,6 @@ import java.util.Map; - public class SyncMetricsSnapshot { private final double[] tps; diff --git a/common/src/main/java/it/renvins/serverpulse/common/config/DatabaseConfiguration.java b/common/src/main/java/it/renvins/serverpulse/common/config/DatabaseConfiguration.java index eb686d7..cc89ff8 100644 --- a/common/src/main/java/it/renvins/serverpulse/common/config/DatabaseConfiguration.java +++ b/common/src/main/java/it/renvins/serverpulse/common/config/DatabaseConfiguration.java @@ -2,6 +2,10 @@ import lombok.RequiredArgsConstructor; +/** + * Configuration class for InfluxDB database settings. + * This class retrieves the database connection details from the general configuration. + */ @RequiredArgsConstructor public class DatabaseConfiguration { diff --git a/common/src/main/java/it/renvins/serverpulse/common/config/GeneralConfiguration.java b/common/src/main/java/it/renvins/serverpulse/common/config/GeneralConfiguration.java index a219dda..aff1278 100644 --- a/common/src/main/java/it/renvins/serverpulse/common/config/GeneralConfiguration.java +++ b/common/src/main/java/it/renvins/serverpulse/common/config/GeneralConfiguration.java @@ -10,6 +10,10 @@ import java.io.InputStream; import java.nio.file.Files; +/** + * General configuration class for managing the configuration file. + * This class handles loading the configuration from a YAML file and creating a default configuration if it does not exist. + */ @RequiredArgsConstructor public class GeneralConfiguration { @@ -20,6 +24,12 @@ public class GeneralConfiguration { @Getter private YamlFile config; + /** + * Loads the configuration file. + * If the file does not exist, it creates a default configuration from the resources. + * + * @return true if the configuration was loaded successfully, false otherwise + */ public boolean load() { try { createDefaultConfigIfNotExists(); diff --git a/common/src/main/java/it/renvins/serverpulse/common/config/MetricsConfiguration.java b/common/src/main/java/it/renvins/serverpulse/common/config/MetricsConfiguration.java index 6aef780..fc8ad43 100644 --- a/common/src/main/java/it/renvins/serverpulse/common/config/MetricsConfiguration.java +++ b/common/src/main/java/it/renvins/serverpulse/common/config/MetricsConfiguration.java @@ -5,6 +5,10 @@ import java.util.HashMap; import java.util.Map; +/** + * Configuration class for metrics settings. + * This class retrieves various metrics-related settings from the general configuration. + */ @RequiredArgsConstructor public class MetricsConfiguration { diff --git a/common/src/main/java/it/renvins/serverpulse/common/logger/PulseLogger.java b/common/src/main/java/it/renvins/serverpulse/common/logger/PulseLogger.java index 9ec297b..6d206c3 100644 --- a/common/src/main/java/it/renvins/serverpulse/common/logger/PulseLogger.java +++ b/common/src/main/java/it/renvins/serverpulse/common/logger/PulseLogger.java @@ -1,5 +1,8 @@ package it.renvins.serverpulse.common.logger; +/* This interface defines a simple logging mechanism for the ServerPulse modules. + * It provides methods for logging informational messages, warnings, and errors. + */ public interface PulseLogger { void info(String message); diff --git a/fabric/src/main/java/it/renvins/serverpulse/fabric/ServerPulseFabric.java b/fabric/src/main/java/it/renvins/serverpulse/fabric/ServerPulseFabric.java index e065897..504285a 100644 --- a/fabric/src/main/java/it/renvins/serverpulse/fabric/ServerPulseFabric.java +++ b/fabric/src/main/java/it/renvins/serverpulse/fabric/ServerPulseFabric.java @@ -53,9 +53,7 @@ public class ServerPulseFabric implements ModInitializer { public ServerPulseFabric() { PulseLogger logger = new FabricLogger(); - this.config = new GeneralConfiguration(logger, - FabricLoader.getInstance().getConfigDir().resolve("serverpulse").toFile(), - "config.yml"); + this.config = new GeneralConfiguration(logger, FabricLoader.getInstance().getConfigDir().resolve("serverpulse").toFile(), "config.yml"); this.platform = new FabricPlatform(this); this.scheduler = new FabricScheduler(); @@ -78,6 +76,7 @@ public ServerPulseFabric() { public void onInitialize() { ServerLifecycleEvents.SERVER_STARTING.register(this::onServerStarting); ServerLifecycleEvents.SERVER_STOPPING.register(this::onServerStopped); + CommandRegistrationCallback.EVENT.register((dispatcher, registryAccess, environment) -> dispatcher.register(new ServerPulseCommand(config).createCommand())); @@ -90,7 +89,6 @@ private void onServerStarting(MinecraftServer server) { LOGGER.info("Loading configuration..."); config.load(); - databaseService.load(); if (!platform.isEnabled()) { return; From 96706a1c1b801561b37d85d5e9efccb5fea77869 Mon Sep 17 00:00:00 2001 From: renvins Date: Thu, 24 Jul 2025 15:18:14 +0200 Subject: [PATCH 23/25] Fix on CI --- .github/workflows/ci.yml | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e06f9d5..3f5c7b6 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -67,6 +67,15 @@ jobs: retention-days: 14 # Keep artifacts for 14 days if-no-files-found: error # Fail the workflow if no JARs are found + # Upload BungeeCord JAR as an artifact + - name: Upload BungeeCord JAR Artifact + uses: actions/upload-artifact@v4 + with: + name: serverpulse-bungeecord-jar + path: bungeecord/build/libs/serverpulse-*.jar + retention-days: 14 + if-no-files-found: error # Fail the workflow if no JARs are found + # --- Infrastructure Testing --- # 4. Install jq (needed for parsing InfluxDB health check JSON) From d47ea97c47e9e0891de8833fb72980ccda25d8b4 Mon Sep 17 00:00:00 2001 From: renvins Date: Thu, 24 Jul 2025 15:22:07 +0200 Subject: [PATCH 24/25] Update to 0.4.5-SNAPSHOT --- build.gradle.kts | 2 +- settings.gradle.kts | 7 +++---- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index 4340748..74b24dd 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -1,5 +1,5 @@ group = "it.renvins" -version = "0.4.2-SNAPSHOT" +version = "0.4.5-SNAPSHOT" repositories { mavenCentral() diff --git a/settings.gradle.kts b/settings.gradle.kts index 77c5794..ad46e98 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -6,6 +6,8 @@ include("bukkit") include("common") include("velocity") include("fabric") +include("bungeecord") +include("teste2e") pluginManagement { repositories { @@ -16,7 +18,4 @@ pluginManagement { mavenCentral() gradlePluginPortal() } -} -include("bungeecord") -include("teste2e") -include("teste2e") \ No newline at end of file +} \ No newline at end of file From dde6ffad4ed736c8a92296fbd03af6ddc6106c23 Mon Sep 17 00:00:00 2001 From: renvins Date: Thu, 24 Jul 2025 15:27:07 +0200 Subject: [PATCH 25/25] Fix on velocity version --- .../it/renvins/serverpulse/velocity/ServerPulseVelocity.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/velocity/src/main/java/it/renvins/serverpulse/velocity/ServerPulseVelocity.java b/velocity/src/main/java/it/renvins/serverpulse/velocity/ServerPulseVelocity.java index 4171764..b6c25ac 100644 --- a/velocity/src/main/java/it/renvins/serverpulse/velocity/ServerPulseVelocity.java +++ b/velocity/src/main/java/it/renvins/serverpulse/velocity/ServerPulseVelocity.java @@ -34,7 +34,7 @@ import lombok.Getter; import org.slf4j.Logger; -@Plugin(id = "serverpulse", name = "ServerPulse", version = "0.4.2-SNAPSHOT", +@Plugin(id = "serverpulse", name = "ServerPulse", version = "0.4.5-SNAPSHOT", description = "Effortless Minecraft performance monitoring with pre-configured Grafana/InfluxDB via Docker.", authors = {"renvins"}) public class ServerPulseVelocity {