Skip to content

Commit 644a461

Browse files
authored
Merge pull request #69 from faststats-dev/feat/sponge
Add Sponge example plugin and refactor onboarding
2 parents 1d3378c + 90a3e8d commit 644a461

File tree

7 files changed

+142
-20
lines changed

7 files changed

+142
-20
lines changed

core/src/main/java/dev/faststats/core/SimpleMetrics.java

Lines changed: 22 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -36,15 +36,6 @@
3636
import static java.nio.charset.StandardCharsets.UTF_8;
3737

3838
public abstract class SimpleMetrics implements Metrics {
39-
protected static final String ONBOARDING_MESSAGE = """
40-
This plugin uses FastStats to collect anonymous usage statistics.
41-
No personal or identifying information is ever collected.
42-
To opt out, set 'enabled=false' in the metrics configuration file.
43-
Learn more at: https://faststats.dev/info
44-
45-
Since this is your first start with FastStats, metrics submission will not start
46-
until you restart the server to allow you to opt out if you prefer.""";
47-
4839
private final HttpClient httpClient = HttpClient.newBuilder()
4940
.connectTimeout(Duration.ofSeconds(3))
5041
.version(HttpClient.Version.HTTP_1_1)
@@ -99,6 +90,17 @@ protected SimpleMetrics(Config config, Set<Chart<?>> charts, @Token String token
9990
this.url = url;
10091
}
10192

93+
protected String getOnboardingMessage() {
94+
return """
95+
This plugin uses FastStats to collect anonymous usage statistics.
96+
No personal or identifying information is ever collected.
97+
To opt out, set 'enabled=false' in the metrics configuration file.
98+
Learn more at: https://faststats.dev/info
99+
100+
Since this is your first start with FastStats, metrics submission will not start
101+
until you restart the server to allow you to opt out if you prefer.""";
102+
}
103+
102104
protected long getInitialDelay() {
103105
return TimeUnit.SECONDS.toMillis(Long.getLong("faststats.initial-delay", 30));
104106
}
@@ -121,12 +123,16 @@ private void startSubmitting(long initialDelay, long period, TimeUnit unit) {
121123

122124
if (config.firstRun) {
123125

124-
printInfo("-".repeat(80));
125-
for (var s : ONBOARDING_MESSAGE.split("\n")) printInfo(s);
126-
printInfo("-".repeat(80));
126+
var separatorLength = 0;
127+
var split = getOnboardingMessage().split("\n");
128+
for (var s : split) if (s.length() > separatorLength) separatorLength = s.length();
129+
130+
printInfo("-".repeat(separatorLength));
131+
for (var s : split) printInfo(s);
132+
printInfo("-".repeat(separatorLength));
127133

128134
System.setProperty("faststats.first-run", "true");
129-
return;
135+
if (!config.externallyManaged()) return;
130136
}
131137

132138
var enabled = Boolean.parseBoolean(System.getProperty("faststats.enabled", "true"));
@@ -350,7 +356,8 @@ public record Config(
350356
boolean debug,
351357
boolean enabled,
352358
boolean errorTracking,
353-
boolean firstRun
359+
boolean firstRun,
360+
boolean externallyManaged
354361
) implements Metrics.Config {
355362

356363
public static final String DEFAULT_COMMENT = """
@@ -413,7 +420,7 @@ public static Config read(Path file, String comment, boolean externallyManaged,
413420
throw new RuntimeException("Failed to save metrics config", e);
414421
}
415422

416-
return new Config(serverId, additionalMetrics, debug, enabled, errorTracking, firstRun);
423+
return new Config(serverId, additionalMetrics, debug, enabled, errorTracking, firstRun, externallyManaged);
417424
}
418425

419426
private static Optional<Properties> readOrEmpty(Path file) throws RuntimeException {

core/src/test/java/dev/faststats/MockMetrics.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
@NullMarked
1717
public class MockMetrics extends SimpleMetrics {
1818
public MockMetrics(UUID serverId, @Token String token, @Nullable ErrorTracker tracker, boolean debug) {
19-
super(new Config(serverId, true, debug, true, true, false), Set.of(), token, tracker, URI.create("http://localhost:5000/v1/collect"), debug);
19+
super(new Config(serverId, true, debug, true, true, false, false), Set.of(), token, tracker, URI.create("http://localhost:5000/v1/collect"), debug);
2020
}
2121

2222
@Override

settings.gradle.kts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,4 +12,5 @@ include("hytale:example-plugin")
1212
include("minestom")
1313
include("nukkit")
1414
include("sponge")
15+
include("sponge:example-plugin")
1516
include("velocity")
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
repositories {
2+
maven("https://repo.spongepowered.org/repository/maven-public/")
3+
}
4+
5+
dependencies {
6+
compileOnly("org.spongepowered:spongeapi:8.0.0")
7+
implementation(project(":sponge"))
8+
}
9+
10+
tasks.shadowJar {
11+
// optionally relocate faststats
12+
relocate("dev.faststats", "com.example.utils.faststats")
13+
}
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
package com.example;
2+
3+
import com.google.inject.Inject;
4+
import dev.faststats.core.ErrorTracker;
5+
import dev.faststats.core.Metrics;
6+
import dev.faststats.core.chart.Chart;
7+
import dev.faststats.sponge.SpongeMetrics;
8+
import org.jspecify.annotations.Nullable;
9+
import org.spongepowered.api.Server;
10+
import org.spongepowered.api.event.Listener;
11+
import org.spongepowered.api.event.lifecycle.StartedEngineEvent;
12+
import org.spongepowered.api.event.lifecycle.StoppingEngineEvent;
13+
import org.spongepowered.plugin.PluginContainer;
14+
import org.spongepowered.plugin.builtin.jvm.Plugin;
15+
16+
17+
@Plugin("example")
18+
public class ExamplePlugin {
19+
// context-aware error tracker, automatically tracks errors in the same class loader
20+
public static final ErrorTracker ERROR_TRACKER = ErrorTracker.contextAware();
21+
22+
// context-unaware error tracker, does not automatically track errors
23+
public static final ErrorTracker CONTEXT_UNAWARE_ERROR_TRACKER = ErrorTracker.contextUnaware();
24+
25+
private @Inject PluginContainer pluginContainer;
26+
private @Inject SpongeMetrics.Factory factory;
27+
28+
private @Nullable Metrics metrics = null;
29+
30+
@Listener
31+
public void onServerStart(final StartedEngineEvent<Server> event) {
32+
this.metrics = factory
33+
// .url(URI.create("https://metrics.example.com/v1/collect")) // For self-hosted metrics servers only
34+
35+
// Custom example charts
36+
// For this to work you have to create a corresponding data source in your project settings first
37+
.addChart(Chart.number("example_chart", () -> 42))
38+
.addChart(Chart.string("example_string", () -> "Hello, World!"))
39+
.addChart(Chart.bool("example_boolean", () -> true))
40+
.addChart(Chart.stringArray("example_string_array", () -> new String[]{"Option 1", "Option 2"}))
41+
.addChart(Chart.numberArray("example_number_array", () -> new Number[]{1, 2, 3}))
42+
.addChart(Chart.booleanArray("example_boolean_array", () -> new Boolean[]{true, false}))
43+
44+
// Attach an error tracker
45+
// This must be enabled in the project settings
46+
.errorTracker(ERROR_TRACKER)
47+
48+
//.debug(true) // Enable debug mode for development and testing
49+
50+
.token("bafe240e8d1a4b919e5083928539799d") // required -> token can be found in the settings of your project
51+
.create(pluginContainer);
52+
ERROR_TRACKER.trackError(new RuntimeException("Something went wrong!", new Exception("Cause")));
53+
}
54+
55+
@Listener
56+
public void onServerStop(final StoppingEngineEvent<Server> event) {
57+
if (metrics != null) metrics.shutdown();
58+
}
59+
60+
public void doSomethingWrong() {
61+
try {
62+
// Do something that might throw an error
63+
throw new RuntimeException("Something went wrong!");
64+
} catch (Exception e) {
65+
CONTEXT_UNAWARE_ERROR_TRACKER.trackError(e);
66+
}
67+
}
68+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
{
2+
"loader": {
3+
"name": "java_plain",
4+
"version": "1.0"
5+
},
6+
"license": "MIT",
7+
"global": {
8+
"version": "1.0.0",
9+
"contributors": [
10+
{
11+
"name": "Your Name",
12+
"description": "The creator of this plugin"
13+
}
14+
]
15+
},
16+
"plugins": [
17+
{
18+
"id": "example",
19+
"name": "Example Plugin",
20+
"entrypoint": "com.example.ExamplePlugin"
21+
}
22+
]
23+
}

sponge/src/main/java/dev/faststats/sponge/SpongeMetricsImpl.java

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@
99
import org.jspecify.annotations.Nullable;
1010
import org.spongepowered.api.Platform;
1111
import org.spongepowered.api.Sponge;
12-
import org.spongepowered.api.config.ConfigDir;
1312
import org.spongepowered.plugin.PluginContainer;
1413

1514
import java.nio.file.Path;
@@ -23,7 +22,8 @@ final class SpongeMetricsImpl extends SimpleMetrics implements SpongeMetrics {
2322
# The server ID below is randomly generated and can be regenerated at any time.
2423
#
2524
# Enabling metrics has no noticeable performance impact.
26-
# Enabling metrics is recommended, you can do so in the Sponge config.
25+
# Enabling metrics is recommended, you can do so in the Sponge metrics.config,
26+
# by setting the "global-state" property to "TRUE".
2727
#
2828
# If you suspect a plugin is collecting personal data or bypassing the Sponge config,
2929
# please report it at: https://faststats.dev/abuse
@@ -51,6 +51,16 @@ private SpongeMetricsImpl(
5151
startSubmitting();
5252
}
5353

54+
@Override
55+
protected String getOnboardingMessage() {
56+
return """
57+
This plugin uses FastStats to collect anonymous usage statistics.
58+
No personal or identifying information is ever collected.
59+
It is recommended to enable metrics by setting 'global-state=TRUE' in the sponge metrics config.
60+
Learn more at: https://faststats.dev/info
61+
""";
62+
}
63+
5464
@Override
5565
protected void appendDefaultData(JsonObject charts) {
5666
charts.addProperty("online_mode", Sponge.server().isOnlineModeEnabled());
@@ -79,14 +89,14 @@ static class Factory extends SimpleMetrics.Factory<PluginContainer> {
7989
protected final Logger logger;
8090
protected final Path dataDirectory;
8191

82-
public Factory(Logger logger, @ConfigDir(sharedRoot = true) Path dataDirectory) {
92+
public Factory(Logger logger, Path dataDirectory) {
8393
this.logger = logger;
8494
this.dataDirectory = dataDirectory;
8595
}
8696

8797
@Override
8898
public Metrics create(PluginContainer plugin) throws IllegalStateException, IllegalArgumentException {
89-
var faststats = dataDirectory.resolveSibling("faststats");
99+
var faststats = dataDirectory.resolve("faststats");
90100
return new SpongeMetricsImpl(this, logger, plugin, faststats.resolve("config.properties"));
91101
}
92102
}

0 commit comments

Comments
 (0)