Skip to content

Commit a98edf2

Browse files
NonSwagredkeysh
andauthored
feat(mod-loaders): complete neoforge for 26.x, fix fabric metrics (#179)
* Add NeoForge 26.x support * Add example mod https://docs.neoforged.net/docs/gettingstarted/modfiles/#neoforgemodstoml * Fix Fabric metrics shutting down on client start * Add Fabric platform_version default metric --------- Co-authored-by: Hunter S <github@mail.redkey.sh>
1 parent b873811 commit a98edf2

13 files changed

Lines changed: 300 additions & 4 deletions

File tree

build.gradle.kts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ val javaVersionsOverride = mapOf(
1212
":hytale:example-plugin" to 25,
1313
":minestom" to 25,
1414
":minestom:example-server" to 25,
15+
":neoforge" to 25,
16+
":neoforge:example-mod" to 25,
1517
":velocity" to 21,
1618
":velocity:example-plugin" to 21
1719
)
@@ -25,7 +27,7 @@ subprojects {
2527

2628
val example = project.name.startsWith("example")
2729
if (example) {
28-
if (project.path != ":fabric:example-mod") {
30+
if (project.path != ":fabric:example-mod" && project.path != ":neoforge:example-mod") {
2931
apply { plugin("com.gradleup.shadow") }
3032
}
3133
} else {

fabric/src/main/java/dev/faststats/fabric/FabricContext.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ private FabricContext(final Factory factory, final String modId, @Token final St
4141
switch (FabricLoader.getInstance().getEnvironmentType()) {
4242
case CLIENT -> {
4343
ready();
44-
ClientLifecycleEvents.CLIENT_STARTED.register(client -> shutdown());
44+
ClientLifecycleEvents.CLIENT_STOPPING.register(client -> shutdown());
4545
}
4646
case SERVER -> {
4747
ServerLifecycleEvents.SERVER_STARTED.register(server -> ready());

fabric/src/main/java/dev/faststats/fabric/FabricMetrics.java

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,22 @@ protected FabricMetrics(final Factory factory, final ModContainer mod) throws Il
1515

1616
protected void appendFabricData(final JsonObject metrics, final String serverType) {
1717
metrics.addProperty("minecraft_version", minecraftVersion());
18+
metrics.addProperty("platform_version", platformVersion());
1819
metrics.addProperty("plugin_version", mod.getMetadata().getVersion().getFriendlyString());
1920
metrics.addProperty("server_type", serverType);
2021
}
2122

2223
protected static String minecraftVersion() {
24+
return version("minecraft");
25+
}
26+
27+
protected static String platformVersion() {
28+
return version("fabricloader");
29+
}
30+
31+
private static String version(final String modId) {
2332
return FabricLoader.getInstance()
24-
.getModContainer("minecraft")
33+
.getModContainer(modId)
2534
.map(container -> container.getMetadata().getVersion().getFriendlyString())
2635
.orElse("unknown");
2736
}

neoforge/build.gradle.kts

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
val moduleName by extra("dev.faststats.neoforge")
2+
3+
plugins {
4+
id("net.neoforged.moddev") version "2.0.141"
5+
}
6+
7+
neoForge {
8+
version = "26.1.2.76"
9+
}
10+
11+
configurations.configureEach {
12+
resolutionStrategy.force("com.google.code.gson:gson:2.13.2")
13+
}
14+
15+
dependencies {
16+
api(project(":core"))
17+
implementation(project(":config"))
18+
compileOnly("net.neoforged:bus:8.0.5")
19+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
plugins {
2+
id("net.neoforged.moddev") version "2.0.141"
3+
}
4+
5+
neoForge {
6+
version = "26.1.2.76"
7+
}
8+
9+
configurations.configureEach {
10+
resolutionStrategy.force("com.google.code.gson:gson:2.13.2")
11+
}
12+
13+
dependencies {
14+
implementation(project(":neoforge"))
15+
}
16+
17+
tasks.jar {
18+
duplicatesStrategy = DuplicatesStrategy.EXCLUDE
19+
from(project(":config").sourceSets["main"].output)
20+
from(project(":core").sourceSets["main"].output)
21+
from(project(":neoforge").sourceSets["main"].output)
22+
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
package com.example;
2+
3+
import dev.faststats.ErrorTracker;
4+
import dev.faststats.data.Metric;
5+
import dev.faststats.neoforge.NeoForgeContext;
6+
import net.neoforged.fml.common.Mod;
7+
8+
import java.util.concurrent.atomic.AtomicInteger;
9+
10+
@Mod("example_mod")
11+
public final class ExampleMod {
12+
public static final ErrorTracker ERROR_TRACKER = ErrorTracker.contextAware();
13+
private final AtomicInteger gameCount = new AtomicInteger();
14+
15+
private final NeoForgeContext context = new NeoForgeContext.Factory(
16+
"example_mod",
17+
"YOUR_TOKEN_HERE"
18+
)
19+
.metrics(factory -> factory
20+
.addMetric(Metric.number("game_count", gameCount::get))
21+
.addMetric(Metric.string("server_version", () -> "1.0.0"))
22+
.onFlush(() -> gameCount.set(0))
23+
.create())
24+
.errorTrackerService(ERROR_TRACKER)
25+
.create();
26+
27+
public void startGame() {
28+
gameCount.incrementAndGet();
29+
}
30+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
modLoader = "javafml"
2+
loaderVersion = "[1,)"
3+
license = "MIT"
4+
5+
[[mods]]
6+
modId = "example_mod"
7+
version = "1.0.0"
8+
displayName = "Example Mod"
9+
authors = "Your Name"
10+
description = "Example FastStats NeoForge mod"
Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
package dev.faststats.neoforge;
2+
3+
import dev.faststats.Metrics;
4+
import dev.faststats.SimpleContext;
5+
import dev.faststats.SimpleMetrics;
6+
import dev.faststats.Token;
7+
import dev.faststats.config.SimpleConfig;
8+
import net.neoforged.fml.ModList;
9+
import net.neoforged.fml.loading.FMLEnvironment;
10+
import net.neoforged.fml.loading.FMLPaths;
11+
import net.neoforged.neoforge.common.NeoForge;
12+
import net.neoforged.neoforge.event.server.ServerStartedEvent;
13+
import net.neoforged.neoforge.event.server.ServerStoppingEvent;
14+
import net.neoforged.neoforgespi.language.IModInfo;
15+
import org.jetbrains.annotations.Contract;
16+
17+
import java.util.Set;
18+
import java.util.concurrent.CopyOnWriteArraySet;
19+
import java.util.concurrent.Executors;
20+
import java.util.concurrent.Future;
21+
import java.util.concurrent.ScheduledExecutorService;
22+
import java.util.concurrent.TimeUnit;
23+
24+
/**
25+
* NeoForge FastStats context.
26+
*
27+
* @since 0.26.2
28+
*/
29+
public final class NeoForgeContext extends SimpleContext {
30+
private final ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor(runnable -> {
31+
final var thread = new Thread(runnable, "faststats-submitter");
32+
thread.setDaemon(true);
33+
return thread;
34+
});
35+
private final Set<Future<?>> tasks = new CopyOnWriteArraySet<>();
36+
private final IModInfo mod;
37+
38+
private NeoForgeContext(final Factory factory, final String modId, @Token final String token) {
39+
super(factory, SimpleConfig.read(FMLPaths.CONFIGDIR.get().resolve("faststats").resolve("config.properties")), "neoforge", token);
40+
this.mod = ModList.get().getModContainerById(modId).map(container -> container.getModInfo()).orElseThrow(() -> {
41+
return new IllegalArgumentException("Mod not found: " + modId);
42+
});
43+
initializeServices(factory);
44+
switch (FMLEnvironment.getDist()) {
45+
case CLIENT -> ready();
46+
case DEDICATED_SERVER -> {
47+
NeoForge.EVENT_BUS.addListener((final ServerStartedEvent event) -> ready());
48+
NeoForge.EVENT_BUS.addListener((final ServerStoppingEvent event) -> shutdown());
49+
}
50+
}
51+
}
52+
53+
@Override
54+
@Contract(value = " -> new", pure = true)
55+
protected Metrics.Factory metricsFactory() {
56+
return new SimpleMetrics.Factory(this) {
57+
@Override
58+
public Metrics create() throws IllegalStateException {
59+
return switch (FMLEnvironment.getDist()) {
60+
case CLIENT -> new NeoForgeMetricsClient(this, mod);
61+
case DEDICATED_SERVER -> new NeoForgeMetricsServer(this, mod);
62+
};
63+
}
64+
};
65+
}
66+
67+
@Override
68+
protected boolean preSubmissionStart() {
69+
return ((SimpleConfig) getConfig()).preSubmissionStart(getProjectName());
70+
}
71+
72+
@Override
73+
public String getProjectName() {
74+
return mod.getModId();
75+
}
76+
77+
@Override
78+
protected void scheduleAtFixedRate(final Runnable task, final long initialDelay, final long period, final TimeUnit unit) {
79+
tasks.add(executor.scheduleAtFixedRate(task, initialDelay, period, unit));
80+
}
81+
82+
@Override
83+
public void shutdown() {
84+
super.shutdown();
85+
tasks.forEach(task -> task.cancel(true));
86+
tasks.clear();
87+
executor.shutdown();
88+
}
89+
90+
public static final class Factory extends SimpleContext.Factory<NeoForgeContext, Factory> {
91+
private final String modId;
92+
private final @Token String token;
93+
94+
public Factory(final String modId, @Token final String token) {
95+
this.modId = modId;
96+
this.token = token;
97+
}
98+
99+
@Override
100+
public NeoForgeContext create() {
101+
return new NeoForgeContext(this, modId, token);
102+
}
103+
}
104+
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
package dev.faststats.neoforge;
2+
3+
import com.google.gson.JsonObject;
4+
import dev.faststats.SimpleMetrics;
5+
import net.neoforged.fml.ModList;
6+
import net.neoforged.neoforgespi.language.IModInfo;
7+
8+
abstract class NeoForgeMetrics extends SimpleMetrics {
9+
protected final IModInfo mod;
10+
11+
protected NeoForgeMetrics(final Factory factory, final IModInfo mod) throws IllegalStateException {
12+
super(factory);
13+
this.mod = mod;
14+
}
15+
16+
protected void appendNeoForgeData(final JsonObject metrics, final String serverType) {
17+
metrics.addProperty("minecraft_version", modVersion("minecraft"));
18+
metrics.addProperty("platform_version", modVersion("neoforge"));
19+
metrics.addProperty("plugin_version", mod.getVersion().toString());
20+
metrics.addProperty("server_type", serverType);
21+
}
22+
23+
private static String modVersion(final String modId) {
24+
return ModList.get().getModContainerById(modId)
25+
.map(container -> container.getModInfo().getVersion().toString())
26+
.orElse("unknown");
27+
}
28+
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
package dev.faststats.neoforge;
2+
3+
import com.google.gson.JsonObject;
4+
import net.minecraft.client.Minecraft;
5+
import net.neoforged.neoforgespi.language.IModInfo;
6+
7+
final class NeoForgeMetricsClient extends NeoForgeMetrics {
8+
NeoForgeMetricsClient(final Factory factory, final IModInfo mod) throws IllegalStateException {
9+
super(factory, mod);
10+
}
11+
12+
@Override
13+
protected void appendDefaultData(final JsonObject metrics) {
14+
final var client = Minecraft.getInstance();
15+
metrics.addProperty("online_mode", client.getUser().getXuid().isPresent() && !client.isOfflineDeveloperMode());
16+
metrics.addProperty("player_count", getPlayerCount(client));
17+
appendNeoForgeData(metrics, "NeoForge Client");
18+
}
19+
20+
private static int getPlayerCount(final Minecraft client) {
21+
final var connection = client.getConnection();
22+
if (connection != null) return connection.getOnlinePlayers().size();
23+
24+
final var server = client.getSingleplayerServer();
25+
if (server != null) return server.getPlayerCount();
26+
27+
return client.player == null ? 0 : 1;
28+
}
29+
}

0 commit comments

Comments
 (0)