Skip to content

Commit d29582a

Browse files
authored
Merge pull request #21 from renvins/dev/fabric
Impelement fabric support
2 parents 5f928e1 + c3a78b7 commit d29582a

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

45 files changed

+2745
-160
lines changed

.github/workflows/ci.yml

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,9 @@ name: CI Build and Infrastructure Check
33
# Controls when the workflow will run
44
on:
55
push:
6-
branches: [ master ] # Or your default branch (e.g., master)
6+
branches: [master] # Or your default branch (e.g., master)
77
pull_request:
8-
branches: [ master ] # Or your default branch
8+
branches: [master] # Or your default branch
99

1010
jobs:
1111
# Job to build the plugin and test Docker infrastructure
@@ -37,6 +37,9 @@ jobs:
3737
- name: Build JAR files
3838
run: ./gradlew shadowJar
3939

40+
- name: Build Fabric JAR
41+
run: ./gradlew fabric:build
42+
4043
# Upload Bukkit JAR as an artifact
4144
- name: Upload Bukkit JAR Artifact
4245
uses: actions/upload-artifact@v4
@@ -55,6 +58,15 @@ jobs:
5558
retention-days: 14 # Keep artifacts for 14 days
5659
if-no-files-found: error # Fail the workflow if no JARs are found
5760

61+
# Upload Fabric JAR as an artifact
62+
- name: Upload Fabric JAR Artifact
63+
uses: actions/upload-artifact@v4
64+
with:
65+
name: serverpulse-fabric-jar
66+
path: fabric/build/libs/serverpulse-*.jar
67+
retention-days: 14 # Keep artifacts for 14 days
68+
if-no-files-found: error # Fail the workflow if no JARs are found
69+
5870
# --- Infrastructure Testing ---
5971

6072
# 4. Install jq (needed for parsing InfluxDB health check JSON)

.github/workflows/create_release.yml

Lines changed: 32 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -33,9 +33,12 @@ jobs:
3333
uses: gradle/actions/setup-gradle@v4 # Use v4 or latest stable version
3434

3535
# Gradle with the new updates doesn't support arguments
36-
- name: Build jar
36+
- name: Build Bukkit and Velocity jar
3737
run: ./gradlew shadowJar
3838

39+
- name: Build fabric jar
40+
run: ./gradlew fabric:build
41+
3942
# 5. Find the Bukkit Shadow Jar file and extract its name
4043
- name: Find Bukkit Shadow Jar and Extract Name
4144
id: find_bukkit_jar
@@ -68,7 +71,23 @@ jobs:
6871
echo "filename=$JAR_FILENAME" >> $GITHUB_OUTPUT
6972
shell: bash
7073

71-
# 7. Upload the Bukkit Shadow Jar as a release asset
74+
# 7. Find the Fabric Jar file and extract its name
75+
- name: Find Fabric Jar and Extract Name
76+
id: find_fabric_jar
77+
run: |
78+
JAR_PATH=$(find fabric/build/libs -maxdepth 1 -name 'serverpulse-*.jar' -printf "%s %p\n" | sort -nr | head -n 1 | awk '{print $2}')
79+
if [ -z "$JAR_PATH" ]; then
80+
echo "Error: Could not find the shadow JAR file in fabric/build/libs/"
81+
exit 1
82+
fi
83+
JAR_FILENAME=$(basename "$JAR_PATH")
84+
echo "Found JAR path: $JAR_PATH"
85+
echo "Found JAR filename: $JAR_FILENAME"
86+
echo "path=$JAR_PATH" >> $GITHUB_OUTPUT
87+
echo "filename=$JAR_FILENAME" >> $GITHUB_OUTPUT
88+
shell: bash
89+
90+
# 8. Upload the Bukkit Shadow Jar as a release asset
7291
- name: Upload Bukkit Release Asset
7392
uses: svenstaro/upload-release-action@v2
7493
with:
@@ -78,12 +97,22 @@ jobs:
7897
tag: ${{ github.ref }} # The git tag associated with the release that triggered the workflow
7998
overwrite: true # Optional: Replace asset with the same name if it already exists
8099

81-
# 8. Upload the Velocity Shadow Jar as a release asset
100+
# 9. Upload the Velocity Shadow Jar as a release asset
82101
- name: Upload Velocity Release Asset
83102
uses: svenstaro/upload-release-action@v2
84103
with:
85104
repo_token: ${{ secrets.GITHUB_TOKEN }} # Provided by GitHub Actions automatically
86105
file: ${{ steps.find_velocity_jar.outputs.path }} # The path to the JAR found in the previous step
87106
asset_name: ${{ steps.find_velocity_jar.outputs.filename }} # Use the filename extracted earlier
88107
tag: ${{ github.ref }} # The git tag associated with the release that triggered the workflow
108+
overwrite: true # Optional: Replace asset with the same name if it already exists
109+
110+
# 10. Upload the Fabric Jar as a release asset
111+
- name: Upload Fabric Release Asset
112+
uses: svenstaro/upload-release-action@v2
113+
with:
114+
repo_token: ${{ secrets.GITHUB_TOKEN }} # Provided by GitHub Actions automatically
115+
file: ${{ steps.find_fabric_jar.outputs.path }} # The path to the JAR found in the previous step
116+
asset_name: ${{ steps.find_fabric_jar.outputs.filename }} # Use the filename extracted earlier
117+
tag: ${{ github.ref }} # The git tag associated with the release that triggered the workflow
89118
overwrite: true # Optional: Replace asset with the same name if it already exists

README.md

Lines changed: 14 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -22,18 +22,19 @@ ServerPulse is an **open-source**, real-time performance monitoring tool for Min
2222

2323
- **Goal:** Provide an extensible, lightweight plugin to gather server metrics and store them in InfluxDB for visualization with Grafana.
2424
- **Tech stack:**
25-
- Java (plugin) → InfluxDB
25+
- Java (plugin/mod) → InfluxDB
2626
- Grafana dashboard (preconfigured via provisioning)
2727
- Discord alerts for key server metrics
2828
- Docker Compose (for InfluxDB & Grafana setup)
2929

3030
## 🌟 Platform Support
3131

32-
ServerPulse now supports **all Minecraft server platforms** in the Bukkit/Paper ecosystem:
32+
ServerPulse now supports **all Minecraft server platforms** in the ecosystem:
3333

3434
- **Bukkit/Spigot**: Full support with custom TPS monitoring implementation
3535
- **Paper and its forks** (Purpur, Pufferfish, etc.): Enhanced support with native TPS monitoring
3636
- **Velocity**: Support for Velocity proxy servers (ofc TPS and world metrics are not available)
37+
- **Fabric**: Support for Fabric servers (latest versions of Fabric API and Minecraft)
3738

3839
The plugin automatically detects your server platform and uses the most efficient implementation available for that environment.
3940

@@ -42,7 +43,7 @@ The plugin automatically detects your server platform and uses the most efficien
4243
ServerPulse isn't just another metrics exporter - it offers several unique advantages:
4344

4445
- **Complete Monitoring Stack** - Fully integrated solution with InfluxDB (optimized for time-series data) and pre-configured Grafana dashboards
45-
- **Universal Platform Support** - Works with any Bukkit-based and Velocity server while taking advantage of Paper-specific optimizations when available
46+
- **Universal Platform Support** - Works with any Bukkit-based, Velocity and Fabric server while taking advantage of Paper-specific optimizations when available
4647
- **Per-World Analytics** - Track entity counts, chunk loading, and performance metrics separately for each world
4748
- **Flexible Tagging System** - Group and filter metrics by server, network, region, or any custom dimension through simple configuration
4849
- **Zero-Configuration Dashboards** - Auto-provisioned Grafana dashboards - no manual setup required
@@ -64,17 +65,17 @@ The wiki contains comprehensive documentation on:
6465

6566
## 📊 Comparison with Alternative Solutions
6667

67-
| Feature | ServerPulse | Generic Prometheus Exporters |
68-
|---------|------------|--------------------------|
69-
| Setup Time | ~5 minutes with Docker Compose | Manual metrics + Prometheus + Grafana setup |
70-
| Dashboard Configuration | Pre-configured, auto-provisioned | Manual dashboard creation |
68+
| Feature | ServerPulse | Generic Prometheus Exporters |
69+
|---------|--------------------------------------|--------------------------|
70+
| Setup Time | ~5 minutes with Docker Compose | Manual metrics + Prometheus + Grafana setup |
71+
| Dashboard Configuration | Pre-configured, auto-provisioned | Manual dashboard creation |
7172
| Data Storage | InfluxDB (optimized for time-series) | Prometheus (general-purpose) |
72-
| Platform Support | All Bukkit-based servers | Varies by implementation |
73-
| Per-World Metrics | Built-in | Usually not available |
74-
| Custom Tagging | Flexible tag system | Limited labeling |
75-
| Alert System | Discord and Telegram integration | Requires manual setup |
76-
| Infrastructure | Complete stack included | Manual integration required |
77-
| Health Monitoring | Automated health checks | Varies by implementation |
73+
| Platform Support | All Minecraft Servers | Varies by implementation |
74+
| Per-World Metrics | Built-in | Usually not available |
75+
| Custom Tagging | Flexible tag system | Limited labeling |
76+
| Alert System | Discord and Telegram integration | Requires manual setup |
77+
| Infrastructure | Complete stack included | Manual integration required |
78+
| Health Monitoring | Automated health checks | Varies by implementation |
7879

7980
## 🤝 Contributing
8081

api/src/main/java/it/renvins/serverpulse/api/data/SyncMetricsSnapshot.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import java.util.Map;
44

5+
56
public class SyncMetricsSnapshot {
67

78
private final double[] tps;

api/src/main/java/it/renvins/serverpulse/api/data/WorldData.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
package it.renvins.serverpulse.api.data;
22

3+
/**
4+
* Represents data about a world in the server.
5+
* This class contains information about the number of entities and loaded chunks in a world.
6+
*/
37
public class WorldData {
48

59
private final int entities;

build.gradle.kts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
group = "it.renvins"
2-
version = "0.2.5-SNAPSHOT"
2+
version = "0.3.0-SNAPSHOT"
33

44
repositories {
55
mavenCentral()

bukkit/build.gradle.kts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ java {
2828
toolchain.languageVersion.set(JavaLanguageVersion.of(21))
2929
}
3030

31-
val relocatePath = "it.renvins.serverpulse.libs"
31+
val relocatePath = "it.renvins.serverpulse.bukkit.libs"
3232

3333
tasks.withType<ShadowJar> {
3434
archiveBaseName = "serverpulse"

bukkit/src/main/java/it/renvins/serverpulse/bukkit/ServerPulseBukkitLoader.java

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@
2525
import it.renvins.serverpulse.bukkit.platform.BukkitPlatform;
2626
import it.renvins.serverpulse.bukkit.scheduler.BukkitTaskScheduler;
2727
import it.renvins.serverpulse.common.MetricsService;
28+
import it.renvins.serverpulse.common.platform.Platform;
29+
import it.renvins.serverpulse.common.scheduler.TaskScheduler;
2830

2931
public class ServerPulseBukkitLoader implements Service {
3032

@@ -33,6 +35,8 @@ public class ServerPulseBukkitLoader implements Service {
3335

3436
private final BukkitConfiguration config;
3537

38+
private final Platform platform;
39+
3640
private final IDatabaseService databaseService;
3741
private final IMetricsService metricsService;
3842

@@ -48,8 +52,8 @@ public ServerPulseBukkitLoader(ServerPulseBukkit plugin) {
4852

4953
PulseLogger logger = new BukkitLogger(LOGGER);
5054

51-
BukkitPlatform platform = new BukkitPlatform(plugin);
52-
BukkitTaskScheduler taskScheduler = new BukkitTaskScheduler(plugin);
55+
this.platform = new BukkitPlatform(plugin);
56+
TaskScheduler taskScheduler = new BukkitTaskScheduler(plugin);
5357

5458
DatabaseConfiguration databaseConfiguration = new BukkitDatabaseConfiguration(config);
5559
MetricsConfiguration metricsConfiguration = new BukkitMetricsConfiguration(config);
@@ -64,23 +68,19 @@ public ServerPulseBukkitLoader(ServerPulseBukkit plugin) {
6468
}
6569
this.diskRetriever = new DiskRetriever(plugin.getDataFolder());
6670
this.pingRetriever = new BukkitPingRetriever();
71+
72+
LOGGER.info("ServerPulse for Bukkit/Paper initialized - waiting for server starting...");
6773
}
6874

6975
@Override
7076
public void load() {
7177
LOGGER.info("Loading configuration...");
7278
config.load();
7379

74-
if(!config.getConfig().getBoolean("metrics.enabled")) {
75-
LOGGER.severe("Shutting down the plugin because metrics are disabled!");
76-
plugin.getServer().getPluginManager().disablePlugin(plugin);
77-
78-
return;
79-
}
8080
ServerPulseProvider.register(new ServerPulseBukkitAPI(databaseService, metricsService, tpsRetriever, diskRetriever, pingRetriever));
8181

8282
databaseService.load();
83-
if (!plugin.isEnabled()) {
83+
if (!platform.isEnabled()) {
8484
return;
8585
}
8686
metricsService.load();

bukkit/src/main/java/it/renvins/serverpulse/bukkit/config/BukkitConfiguration.java

Lines changed: 0 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -16,21 +16,11 @@ public class BukkitConfiguration {
1616
private File file;
1717
@Getter private YamlConfiguration config;
1818

19-
/**
20-
* Constructs a new CustomConfig manager.
21-
*
22-
* @param plugin The main ServerPulsePlugin instance.
23-
* @param name The name of the configuration file (e.g., "config.yml").
24-
*/
2519
public BukkitConfiguration(ServerPulseBukkit plugin, String name) {
2620
this.plugin = plugin;
2721
this.name = name;
2822
}
2923

30-
/**
31-
* Loads the configuration file. If the file doesn't exist,
32-
* it saves the default resource from the plugin JAR.
33-
*/
3424
public void load() {
3525
if (!plugin.getDataFolder().exists()) {
3626
plugin.getDataFolder().mkdir();
@@ -42,10 +32,6 @@ public void load() {
4232
config = YamlConfiguration.loadConfiguration(file);
4333
}
4434

45-
/**
46-
* Saves the current configuration state back to the file.
47-
* Logs an error if saving fails.
48-
*/
4935
public void save() {
5036
try {
5137
config.save(file);
@@ -54,12 +40,6 @@ public void save() {
5440
}
5541
}
5642

57-
/**
58-
* Reloads the configuration file, reloading the YamlConfiguration.
59-
* Logs an error if the file is null.
60-
*
61-
* @return true if the reload was successful, false otherwise.
62-
*/
6343
public boolean reload() {
6444
if (file == null) {
6545
ServerPulseBukkitLoader.LOGGER.log(Level.SEVERE, "File is null, cannot reload configuration.");

bukkit/src/main/java/it/renvins/serverpulse/bukkit/metrics/BukkitTPSRetriever.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ public void run() {
5555
}.runTaskTimer(plugin, 1L, 1L);
5656
}
5757
private void calculateAverages() {
58-
long sum1m = 0, sum5m = 0, sum15m = 0;
58+
double sum1m = 0, sum5m = 0, sum15m = 0;
5959
int count1m = 0, count5m = 0, count15m = 0;
6060

6161
int i = 0;
@@ -87,7 +87,7 @@ private void calculateAverages() {
8787
tps15m = calculateTPSFromAvgNano(sum15m, count15m);
8888
}
8989

90-
private double calculateTPSFromAvgNano(long totalNano, int count) {
90+
private double calculateTPSFromAvgNano(double totalNano, int count) {
9191
if (count == 0) {
9292
return 20.0;
9393
}

bukkit/src/main/java/it/renvins/serverpulse/bukkit/platform/BukkitPlatform.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ public boolean isEnabled() {
2323

2424
@Override
2525
public void disable() {
26-
plugin.getServer().getPluginManager().disablePlugin(plugin);
26+
plugin.getServer().shutdown();
2727
}
2828

2929
@Override

bukkit/src/main/java/it/renvins/serverpulse/bukkit/scheduler/BukkitTaskScheduler.java

Lines changed: 4 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -18,27 +18,17 @@ public void runSync(Runnable task) {
1818
}
1919

2020
@Override
21-
public void runAsync(Runnable task) {
22-
plugin.getServer().getScheduler().runTaskAsynchronously(plugin, task);
21+
public void runTaskTimer(Runnable task, long delayTicks, long periodTicks) {
22+
plugin.getServer().getScheduler().runTaskTimer(plugin, task, delayTicks, periodTicks);
2323
}
2424

2525
@Override
26-
public Task runTaskTimer(Runnable task, long delayTicks, long periodTicks) {
27-
return new BukkitTaskWrapper(plugin.getServer().getScheduler().runTaskTimer(plugin, task, delayTicks, periodTicks));
26+
public void runAsync(Runnable task) {
27+
plugin.getServer().getScheduler().runTaskAsynchronously(plugin, task);
2828
}
2929

3030
@Override
3131
public Task runTaskTimerAsync(Runnable task, long delayTicks, long periodTicks) {
3232
return new BukkitTaskWrapper(plugin.getServer().getScheduler().runTaskTimerAsynchronously(plugin, task, delayTicks, periodTicks));
3333
}
34-
35-
@Override
36-
public Task runTaskLater(Runnable task, long delayTicks) {
37-
return new BukkitTaskWrapper(plugin.getServer().getScheduler().runTaskLater(plugin, task, delayTicks));
38-
}
39-
40-
@Override
41-
public Task runTaskLaterAsync(Runnable task, long delayTicks) {
42-
return new BukkitTaskWrapper(plugin.getServer().getScheduler().runTaskLaterAsynchronously(plugin, task, delayTicks));
43-
}
4434
}

bukkit/src/main/resources/config.yml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
metrics:
2-
enabled: true
32
interval: 5
43
influxdb:
54
url: http://localhost:8086 # The URL of the InfluxDB API

common/src/main/java/it/renvins/serverpulse/common/DatabaseService.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ public DatabaseService(PulseLogger logger, Platform platform, DatabaseConfigurat
5656
@Override
5757
public void load() {
5858
if (!checkConnectionData()) {
59-
logger.error("InfluxDB connection data is missing or invalid. Disabling plugin...");
59+
logger.error("InfluxDB connection data is missing or invalid. Shutting down...");
6060
platform.disable();
6161
return;
6262
}
@@ -66,6 +66,7 @@ public void load() {
6666

6767
@Override
6868
public void unload() {
69+
logger.info("Unloading InfluxDB connection...");
6970
stopRetryTask(); // Stop retries before disconnecting
7071
disconnect();
7172
if (httpClient != null) {

common/src/main/java/it/renvins/serverpulse/common/platform/Platform.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,11 @@ public interface Platform {
1313

1414
/**
1515
* Disable the plugin
16+
* @throws UnsupportedOperationException if the platform does not support this method
1617
*/
17-
void disable();
18+
default void disable() {
19+
throw new UnsupportedOperationException("This method is not supported on this platform.");
20+
}
1821

1922
/**
2023
* @return true if the current thread is the main thread of the server

0 commit comments

Comments
 (0)