Skip to content

Commit 1d3378c

Browse files
authored
Merge pull request #66 from faststats-dev/feat/sponge
Added Sponge SDK
2 parents a163ff2 + 75a74db commit 1d3378c

File tree

9 files changed

+202
-76
lines changed

9 files changed

+202
-76
lines changed

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -146,7 +146,7 @@ interface Factory<T> {
146146
*
147147
* @since 0.1.0
148148
*/
149-
interface Config {
149+
sealed interface Config permits SimpleMetrics.Config {
150150
/**
151151
* The server id.
152152
*

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

Lines changed: 52 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -60,14 +60,14 @@ public abstract class SimpleMetrics implements Metrics {
6060

6161
@Contract(mutates = "io")
6262
@SuppressWarnings("PatternValidation")
63-
protected SimpleMetrics(Factory<?> factory, Path config) throws IllegalStateException {
63+
protected SimpleMetrics(Factory<?> factory, Config config) throws IllegalStateException {
6464
if (factory.token == null) throw new IllegalStateException("Token must be specified");
6565

66-
this.config = new Config(config);
67-
this.charts = this.config.additionalMetrics ? Set.copyOf(factory.charts) : Set.of();
68-
this.debug = factory.debug || Boolean.getBoolean("faststats.debug") || this.config.debug();
66+
this.config = config;
67+
this.charts = config.additionalMetrics ? Set.copyOf(factory.charts) : Set.of();
68+
this.debug = factory.debug || Boolean.getBoolean("faststats.debug") || config.debug();
6969
this.token = factory.token;
70-
this.tracker = this.config.errorTracking ? factory.tracker : null;
70+
this.tracker = config.errorTracking ? factory.tracker : null;
7171
this.url = factory.url;
7272

7373
Runtime.getRuntime().addShutdownHook(new Thread(() -> {
@@ -80,6 +80,11 @@ protected SimpleMetrics(Factory<?> factory, Path config) throws IllegalStateExce
8080
}, "metrics-shutdown-thread " + getClass().getName()));
8181
}
8282

83+
@Contract(mutates = "io")
84+
protected SimpleMetrics(Factory<?> factory, Path config) throws IllegalStateException {
85+
this(factory, Config.read(config));
86+
}
87+
8388
@VisibleForTesting
8489
protected SimpleMetrics(Config config, Set<Chart<?>> charts, @Token String token, @Nullable ErrorTracker tracker, URI url, boolean debug) {
8590
if (!token.matches(Token.PATTERN)) {
@@ -339,21 +344,43 @@ public Metrics.Factory<T> url(URI url) {
339344
}
340345
}
341346

342-
protected static final class Config implements Metrics.Config {
343-
private final UUID serverId;
344-
private final boolean additionalMetrics;
345-
private final boolean debug;
346-
private final boolean enabled;
347-
private final boolean errorTracking;
348-
private final boolean firstRun;
347+
public record Config(
348+
UUID serverId,
349+
boolean additionalMetrics,
350+
boolean debug,
351+
boolean enabled,
352+
boolean errorTracking,
353+
boolean firstRun
354+
) implements Metrics.Config {
355+
356+
public static final String DEFAULT_COMMENT = """
357+
FastStats (https://faststats.dev) collects anonymous usage statistics for plugin developers.
358+
# This helps developers understand how their projects are used in the real world.
359+
#
360+
# No IP addresses, player data, or personal information is collected.
361+
# The server ID below is randomly generated and can be regenerated at any time.
362+
#
363+
# Enabling metrics has no noticeable performance impact.
364+
# Keeping metrics enabled is recommended, but you can disable them by setting 'enabled=false'.
365+
#
366+
# If you suspect a plugin is collecting personal data or bypassing the "enabled" option,
367+
# please report it at: https://faststats.dev/abuse
368+
#
369+
# For more information, visit: https://faststats.dev/info
370+
""";
349371

350372
@Contract(mutates = "io")
351-
protected Config(Path file) {
373+
public static Config read(Path file) throws RuntimeException {
374+
return read(file, DEFAULT_COMMENT, false, false);
375+
}
376+
377+
@Contract(mutates = "io")
378+
public static Config read(Path file, String comment, boolean externallyManaged, boolean externallyEnabled) throws RuntimeException {
352379
var properties = readOrEmpty(file);
353-
this.firstRun = properties.isEmpty();
354-
var saveConfig = new AtomicBoolean(this.firstRun);
380+
var firstRun = properties.isEmpty();
381+
var saveConfig = new AtomicBoolean(firstRun);
355382

356-
this.serverId = properties.map(object -> object.getProperty("serverId")).map(string -> {
383+
var serverId = properties.map(object -> object.getProperty("serverId")).map(string -> {
357384
try {
358385
var trimmed = string.trim();
359386
var corrected = trimmed.length() > 36 ? trimmed.substring(0, 36) : trimmed;
@@ -375,54 +402,21 @@ protected Config(Path file) {
375402
});
376403
};
377404

378-
this.enabled = predicate.test("enabled", true);
379-
this.errorTracking = predicate.test("submitErrors", true);
380-
this.additionalMetrics = predicate.test("submitAdditionalMetrics", true);
381-
this.debug = predicate.test("debug", false);
405+
var enabled = externallyManaged ? externallyEnabled : predicate.test("enabled", true);
406+
var errorTracking = predicate.test("submitErrors", true);
407+
var additionalMetrics = predicate.test("submitAdditionalMetrics", true);
408+
var debug = predicate.test("debug", false);
382409

383410
if (saveConfig.get()) try {
384-
save(file, serverId, enabled, errorTracking, additionalMetrics, debug);
411+
save(file, externallyManaged, comment, serverId, enabled, errorTracking, additionalMetrics, debug);
385412
} catch (IOException e) {
386413
throw new RuntimeException("Failed to save metrics config", e);
387414
}
388-
}
389415

390-
@VisibleForTesting
391-
public Config(UUID serverId, boolean enabled, boolean errorTracking, boolean additionalMetrics, boolean debug) {
392-
this.serverId = serverId;
393-
this.enabled = enabled;
394-
this.debug = debug;
395-
this.errorTracking = errorTracking;
396-
this.additionalMetrics = additionalMetrics;
397-
this.firstRun = false;
398-
}
399-
400-
@Override
401-
public UUID serverId() {
402-
return serverId;
403-
}
404-
405-
@Override
406-
public boolean enabled() {
407-
return enabled;
408-
}
409-
410-
@Override
411-
public boolean errorTracking() {
412-
return errorTracking;
413-
}
414-
415-
@Override
416-
public boolean additionalMetrics() {
417-
return additionalMetrics;
418-
}
419-
420-
@Override
421-
public boolean debug() {
422-
return debug;
416+
return new Config(serverId, additionalMetrics, debug, enabled, errorTracking, firstRun);
423417
}
424418

425-
private static Optional<Properties> readOrEmpty(Path file) {
419+
private static Optional<Properties> readOrEmpty(Path file) throws RuntimeException {
426420
if (!Files.isRegularFile(file)) return Optional.empty();
427421
try (var reader = Files.newBufferedReader(file, UTF_8)) {
428422
var properties = new Properties();
@@ -433,33 +427,18 @@ private static Optional<Properties> readOrEmpty(Path file) {
433427
}
434428
}
435429

436-
private static void save(Path file, UUID serverId, boolean enabled, boolean errorTracking, boolean additionalMetrics, boolean debug) throws IOException {
430+
private static void save(Path file, boolean externallyManaged, String comment, UUID serverId, boolean enabled, boolean errorTracking, boolean additionalMetrics, boolean debug) throws IOException {
437431
Files.createDirectories(file.getParent());
438432
try (var out = Files.newOutputStream(file);
439433
var writer = new OutputStreamWriter(out, UTF_8)) {
440434
var properties = new Properties();
441435

442436
properties.setProperty("serverId", serverId.toString());
443-
properties.setProperty("enabled", Boolean.toString(enabled));
437+
if (!externallyManaged) properties.setProperty("enabled", Boolean.toString(enabled));
444438
properties.setProperty("submitErrors", Boolean.toString(errorTracking));
445439
properties.setProperty("submitAdditionalMetrics", Boolean.toString(additionalMetrics));
446440
properties.setProperty("debug", Boolean.toString(debug));
447441

448-
var comment = """
449-
FastStats (https://faststats.dev) collects anonymous usage statistics for plugin developers.
450-
# This helps developers understand how their projects are used in the real world.
451-
#
452-
# No IP addresses, player data, or personal information is collected.
453-
# The server ID below is randomly generated and can be regenerated at any time.
454-
#
455-
# Enabling metrics has no noticeable performance impact.
456-
# Keeping metrics enabled is recommended, but you can disable them by setting 'enabled=false'.
457-
#
458-
# If you suspect a plugin is collecting personal data or bypassing the "enabled" option,
459-
# please report it at: https://faststats.dev/abuse
460-
#
461-
# For more information, visit: https://faststats.dev/info
462-
""";
463442
properties.store(writer, comment);
464443
}
465444
}

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 SimpleMetrics.Config(serverId, true, true, true, debug), Set.of(), token, tracker, URI.create("http://localhost:5000/v1/collect"), debug);
19+
super(new Config(serverId, true, debug, true, true, false), Set.of(), token, tracker, URI.create("http://localhost:5000/v1/collect"), debug);
2020
}
2121

2222
@Override

gradle.properties

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
version=0.11.1
1+
version=0.12.0

settings.gradle.kts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,4 +11,5 @@ include("hytale")
1111
include("hytale:example-plugin")
1212
include("minestom")
1313
include("nukkit")
14+
include("sponge")
1415
include("velocity")

sponge/build.gradle.kts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
val moduleName by extra("dev.faststats.sponge")
2+
3+
repositories {
4+
maven("https://repo.spongepowered.org/repository/maven-public/")
5+
}
6+
7+
dependencies {
8+
api(project(":core"))
9+
compileOnly("org.spongepowered:spongeapi:8.0.0")
10+
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
package dev.faststats.sponge;
2+
3+
import com.google.inject.Inject;
4+
import dev.faststats.core.Metrics;
5+
import org.apache.logging.log4j.Logger;
6+
import org.spongepowered.api.config.ConfigDir;
7+
8+
import java.nio.file.Path;
9+
10+
/**
11+
* Sponge metrics implementation.
12+
*
13+
* @since 0.12.0
14+
*/
15+
public sealed interface SpongeMetrics extends Metrics permits SpongeMetricsImpl {
16+
final class Factory extends SpongeMetricsImpl.Factory {
17+
/**
18+
* Creates a new metrics factory for Sponge.
19+
*
20+
* @param logger the logger
21+
* @param dataDirectory the data directory
22+
* @apiNote This instance is automatically injected into your plugin.
23+
* @since 0.12.0
24+
*/
25+
@Inject
26+
private Factory(Logger logger, @ConfigDir(sharedRoot = true) Path dataDirectory) {
27+
super(logger, dataDirectory);
28+
}
29+
}
30+
}
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
package dev.faststats.sponge;
2+
3+
import com.google.gson.JsonObject;
4+
import dev.faststats.core.Metrics;
5+
import dev.faststats.core.SimpleMetrics;
6+
import org.apache.logging.log4j.Logger;
7+
import org.jetbrains.annotations.Async;
8+
import org.jetbrains.annotations.Contract;
9+
import org.jspecify.annotations.Nullable;
10+
import org.spongepowered.api.Platform;
11+
import org.spongepowered.api.Sponge;
12+
import org.spongepowered.api.config.ConfigDir;
13+
import org.spongepowered.plugin.PluginContainer;
14+
15+
import java.nio.file.Path;
16+
17+
final class SpongeMetricsImpl extends SimpleMetrics implements SpongeMetrics {
18+
public static final String COMMENT = """
19+
FastStats (https://faststats.dev) collects anonymous usage statistics for plugin developers.
20+
# This helps developers understand how their projects are used in the real world.
21+
#
22+
# No IP addresses, player data, or personal information is collected.
23+
# The server ID below is randomly generated and can be regenerated at any time.
24+
#
25+
# Enabling metrics has no noticeable performance impact.
26+
# Enabling metrics is recommended, you can do so in the Sponge config.
27+
#
28+
# If you suspect a plugin is collecting personal data or bypassing the Sponge config,
29+
# please report it at: https://faststats.dev/abuse
30+
#
31+
# For more information, visit: https://faststats.dev/info
32+
""";
33+
34+
private final Logger logger;
35+
private final PluginContainer plugin;
36+
37+
@Async.Schedule
38+
@Contract(mutates = "io")
39+
private SpongeMetricsImpl(
40+
SimpleMetrics.Factory<?> factory,
41+
Logger logger,
42+
PluginContainer plugin,
43+
Path config
44+
) throws IllegalStateException {
45+
super(factory, SimpleMetrics.Config.read(config, COMMENT, true, Sponge.metricsConfigManager()
46+
.effectiveCollectionState(plugin).asBoolean()));
47+
48+
this.logger = logger;
49+
this.plugin = plugin;
50+
51+
startSubmitting();
52+
}
53+
54+
@Override
55+
protected void appendDefaultData(JsonObject charts) {
56+
charts.addProperty("online_mode", Sponge.server().isOnlineModeEnabled());
57+
charts.addProperty("player_count", Sponge.server().onlinePlayers().size());
58+
charts.addProperty("plugin_version", plugin.metadata().version().toString());
59+
charts.addProperty("minecraft_version", Sponge.platform().minecraftVersion().name());
60+
charts.addProperty("server_type", Sponge.platform().container(Platform.Component.IMPLEMENTATION).metadata().id());
61+
}
62+
63+
@Override
64+
protected void printError(String message, @Nullable Throwable throwable) {
65+
logger.error(message, throwable);
66+
}
67+
68+
@Override
69+
protected void printInfo(String message) {
70+
logger.info(message);
71+
}
72+
73+
@Override
74+
protected void printWarning(String message) {
75+
logger.warn(message);
76+
}
77+
78+
static class Factory extends SimpleMetrics.Factory<PluginContainer> {
79+
protected final Logger logger;
80+
protected final Path dataDirectory;
81+
82+
public Factory(Logger logger, @ConfigDir(sharedRoot = true) Path dataDirectory) {
83+
this.logger = logger;
84+
this.dataDirectory = dataDirectory;
85+
}
86+
87+
@Override
88+
public Metrics create(PluginContainer plugin) throws IllegalStateException, IllegalArgumentException {
89+
var faststats = dataDirectory.resolveSibling("faststats");
90+
return new SpongeMetricsImpl(this, logger, plugin, faststats.resolve("config.properties"));
91+
}
92+
}
93+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import org.jspecify.annotations.NullMarked;
2+
3+
@NullMarked
4+
module dev.faststats.sponge {
5+
exports dev.faststats.sponge;
6+
7+
requires com.google.gson;
8+
requires com.google.guice;
9+
requires dev.faststats.core;
10+
11+
requires static org.jetbrains.annotations;
12+
requires static org.jspecify;
13+
}

0 commit comments

Comments
 (0)