Skip to content

Commit c32dc0b

Browse files
authored
Implement support for split datagen (MC 1.21.4+) (#187)
1 parent 703cf83 commit c32dc0b

File tree

8 files changed

+183
-20
lines changed

8 files changed

+183
-20
lines changed

src/main/java/net/neoforged/moddevgradle/dsl/RunModel.java

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,8 +156,21 @@ public void client() {
156156
getType().set("client");
157157
}
158158

159+
/**
160+
* Equivalent to setting {@code type = "clientData"}.
161+
*
162+
* <p>Should only be used for Minecraft versions starting from 1.21.4.
163+
* (The first snapshot that supports this is 24w45a).
164+
*/
165+
public void clientData() {
166+
getType().set("clientData");
167+
}
168+
159169
/**
160170
* Equivalent to setting {@code type = "data"}.
171+
*
172+
* <p>Should only be used for Minecraft versions up to 1.21.3 included.
173+
* (The last snapshot that supports this is 24w44a).
161174
*/
162175
public void data() {
163176
getType().set("data");
@@ -170,6 +183,16 @@ public void server() {
170183
getType().set("server");
171184
}
172185

186+
/**
187+
* Equivalent to setting {@code type = "serverData"}.
188+
*
189+
* <p>Should only be used for Minecraft versions starting from 1.21.4.
190+
* (The first snapshot that supports this is 24w45a).
191+
*/
192+
public void serverData() {
193+
getType().set("serverData");
194+
}
195+
173196
/**
174197
* Equivalent to setting {@code ideName = ""}
175198
*/

src/main/java/net/neoforged/moddevgradle/internal/ModDevPlugin.java

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -309,7 +309,8 @@ public void apply(Project project) {
309309
userDevConfigOnly,
310310
modulePath -> modulePath.getDependencies().addLater(modulePathDependency),
311311
legacyClassPath -> legacyClassPath.extendsFrom(additionalClasspath),
312-
downloadAssets.flatMap(DownloadAssets::getAssetPropertiesFile)
312+
downloadAssets.flatMap(DownloadAssets::getAssetPropertiesFile),
313+
extension.getNeoFormVersion()
313314
);
314315

315316
setupJarJar(project);
@@ -478,7 +479,8 @@ static void setupRuns(Project project,
478479
Object runTemplatesSourceFile,
479480
Consumer<Configuration> configureModulePath,
480481
Consumer<Configuration> configureLegacyClasspath,
481-
Provider<RegularFile> assetPropertiesFile
482+
Provider<RegularFile> assetPropertiesFile,
483+
Provider<String> neoFormVersion
482484
) {
483485
var ideIntegration = IdeIntegration.of(project, branding);
484486

@@ -499,7 +501,8 @@ static void setupRuns(Project project,
499501
configureModulePath,
500502
configureLegacyClasspath,
501503
assetPropertiesFile,
502-
devLaunchConfig
504+
devLaunchConfig,
505+
neoFormVersion
503506
);
504507
prepareRunTasks.put(run, prepareRunTask);
505508
});
@@ -521,7 +524,8 @@ private static TaskProvider<PrepareRun> setupRunInGradle(
521524
Consumer<Configuration> configureModulePath,
522525
Consumer<Configuration> configureLegacyClasspath, // TODO: can be removed in favor of directly passing a configuration for the moddev libraries
523526
Provider<RegularFile> assetPropertiesFile,
524-
Configuration devLaunchConfig
527+
Configuration devLaunchConfig,
528+
Provider<String> neoFormVersion
525529
) {
526530
var ideIntegration = IdeIntegration.of(project, branding);
527531
var configurations = project.getConfigurations();
@@ -553,7 +557,7 @@ private static TaskProvider<PrepareRun> setupRunInGradle(
553557
spec.shouldResolveConsistentlyWith(runtimeClasspathConfig.get());
554558
spec.attributes(attributes -> {
555559
attributes.attributeProvider(MinecraftDistribution.ATTRIBUTE, type.map(t -> {
556-
var name = t.equals("client") || t.equals("data") ? MinecraftDistribution.CLIENT : MinecraftDistribution.SERVER;
560+
var name = t.equals("client") || t.equals("data") || t.equals("clientData") ? MinecraftDistribution.CLIENT : MinecraftDistribution.SERVER;
557561
return project.getObjects().named(MinecraftDistribution.class, name);
558562
}));
559563
setNamedAttribute(project, attributes, Usage.USAGE_ATTRIBUTE, Usage.JAVA_RUNTIME);
@@ -590,6 +594,7 @@ private static TaskProvider<PrepareRun> setupRunInGradle(
590594
task.getProgramArguments().set(run.getProgramArguments());
591595
task.getJvmArguments().set(run.getJvmArguments());
592596
task.getGameLogLevel().set(run.getLogLevel());
597+
task.getNeoFormVersion().set(neoFormVersion);
593598
});
594599
ideIntegration.runTaskOnProjectSync(prepareRunTask);
595600

src/main/java/net/neoforged/moddevgradle/internal/NeoDevFacade.java

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,30 @@ public static void setupRuns(Project project,
4242
runTemplatesSourceFile,
4343
configureModulePath,
4444
configureAdditionalClasspath,
45-
assetPropertiesFile
45+
assetPropertiesFile,
46+
project.getObjects().property(String.class) // empty provider
47+
);
48+
}
49+
50+
public static void setupRuns(Project project,
51+
Provider<Directory> argFileDir,
52+
DomainObjectCollection<RunModel> runs,
53+
Object runTemplatesSourceFile,
54+
Consumer<Configuration> configureModulePath,
55+
Consumer<Configuration> configureAdditionalClasspath,
56+
Provider<RegularFile> assetPropertiesFile,
57+
Provider<String> neoFormVersion
58+
) {
59+
ModDevPlugin.setupRuns(
60+
project,
61+
Branding.NEODEV,
62+
argFileDir,
63+
runs,
64+
runTemplatesSourceFile,
65+
configureModulePath,
66+
configureAdditionalClasspath,
67+
assetPropertiesFile,
68+
neoFormVersion
4669
);
4770
}
4871

src/main/java/net/neoforged/moddevgradle/internal/PrepareRunOrTest.java

Lines changed: 35 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import net.neoforged.moddevgradle.internal.utils.FileUtils;
44
import net.neoforged.moddevgradle.internal.utils.OperatingSystem;
55
import net.neoforged.moddevgradle.internal.utils.StringUtils;
6+
import net.neoforged.moddevgradle.internal.utils.VersionUtils;
67
import org.gradle.api.DefaultTask;
78
import org.gradle.api.file.ConfigurableFileCollection;
89
import org.gradle.api.file.DirectoryProperty;
@@ -28,6 +29,7 @@
2829
import java.nio.charset.StandardCharsets;
2930
import java.nio.file.Files;
3031
import java.util.ArrayList;
32+
import java.util.LinkedHashMap;
3133
import java.util.List;
3234
import java.util.Map;
3335
import java.util.Objects;
@@ -91,6 +93,14 @@ abstract class PrepareRunOrTest extends DefaultTask {
9193
@Input
9294
public abstract Property<Level> getGameLogLevel();
9395

96+
/**
97+
* Only used when {@link #getRunTypeTemplatesSource()} is empty,
98+
* to know whether the associated Minecraft version requires one or two data runs.
99+
*/
100+
@Optional
101+
@Input
102+
public abstract Property<String> getNeoFormVersion();
103+
94104
private final ProgramArgsFormat programArgsFormat;
95105

96106
protected PrepareRunOrTest(ProgramArgsFormat programArgsFormat) {
@@ -147,17 +157,32 @@ private UserDevConfig getSimulatedUserDevConfigForVanilla() {
147157
var clientArgs = List.of("--gameDir", ".", "--assetIndex", "{asset_index}", "--assetsDir", "{assets_root}", "--accessToken", "NotValid", "--version", "ModDevGradle");
148158
var commonArgs = List.<String>of();
149159

150-
return new UserDevConfig("", "", "", List.of(), List.of(), Map.of(
151-
"client", new UserDevRunType(
152-
true, "net.minecraft.client.main.Main", clientArgs, List.of(), true, false, false, false, Map.of(), Map.of()
153-
),
154-
"server", new UserDevRunType(
155-
true, "net.minecraft.server.Main", commonArgs, List.of(), false, true, false, false, Map.of(), Map.of()
156-
),
157-
"data", new UserDevRunType(
158-
true, "net.minecraft.data.Main", commonArgs, List.of(), false, false, true, false, Map.of(), Map.of()
159-
)
160+
var runTypes = new LinkedHashMap<String, UserDevRunType>();
161+
runTypes.put("client", new UserDevRunType(
162+
true, "net.minecraft.client.main.Main", clientArgs, List.of(), Map.of(), Map.of()
160163
));
164+
runTypes.put("server", new UserDevRunType(
165+
true, "net.minecraft.server.Main", commonArgs, List.of(), Map.of(), Map.of()
166+
));
167+
168+
var splitData = getNeoFormVersion()
169+
.map(VersionUtils::hasSplitDataRuns)
170+
.orElse(false) // Default to single run for backwards compatibility
171+
.get();
172+
if (splitData) {
173+
runTypes.put("clientData", new UserDevRunType(
174+
true, "net.minecraft.client.data.Main", commonArgs, List.of(), Map.of(), Map.of()
175+
));
176+
runTypes.put("serverData", new UserDevRunType(
177+
true, "net.minecraft.data.Main", commonArgs, List.of(), Map.of(), Map.of()
178+
));
179+
} else {
180+
runTypes.put("data", new UserDevRunType(
181+
true, "net.minecraft.data.Main", commonArgs, List.of(), Map.of(), Map.of()
182+
));
183+
}
184+
185+
return new UserDevConfig(runTypes);
161186
}
162187

163188
private void writeJvmArguments(UserDevRunType runConfig) throws IOException {

src/main/java/net/neoforged/moddevgradle/internal/UserDevConfig.java

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,12 @@
66
import java.io.File;
77
import java.io.Serializable;
88
import java.nio.file.Files;
9-
import java.util.List;
109
import java.util.Map;
1110

12-
public record UserDevConfig(String mcp, String sources, String universal, List<String> libraries, List<String> modules,
13-
Map<String, UserDevRunType> runs) implements Serializable {
11+
/**
12+
* Sourced from the userdev config json. The run templates are the only thing that we use.
13+
*/
14+
public record UserDevConfig(Map<String, UserDevRunType> runs) implements Serializable {
1415
public static UserDevConfig from(File userDevFile) {
1516
try (var reader = Files.newBufferedReader(userDevFile.toPath())) {
1617
return new Gson().fromJson(reader, UserDevConfig.class);

src/main/java/net/neoforged/moddevgradle/internal/UserDevRunType.java

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,5 @@
44
import java.util.Map;
55

66
public record UserDevRunType(boolean singleInstance, String main, List<String> args, List<String> jvmArgs,
7-
boolean client, boolean server, boolean dataGenerator, boolean gameTest,
87
Map<String, String> env, Map<String, String> props) {
98
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
package net.neoforged.moddevgradle.internal.utils;
2+
3+
import java.util.Objects;
4+
import java.util.regex.Pattern;
5+
6+
public final class VersionUtils {
7+
private VersionUtils() {}
8+
9+
private static final Pattern RELEASE_PATTERN = Pattern.compile("1\\.(\\d+)(?:.(\\d+))?(?:-.*)?$");
10+
11+
/**
12+
* Checks whether the provided NeoForm version should have split client and server data runs.
13+
*/
14+
public static boolean hasSplitDataRuns(String neoFormVersion) {
15+
// Snapshots starting from 24w45a
16+
if (neoFormVersion.length() >= 5 && neoFormVersion.charAt(2) == 'w') {
17+
try {
18+
var year = Integer.parseInt(neoFormVersion.substring(0, 2));
19+
var week = Integer.parseInt(neoFormVersion.substring(3, 5));
20+
21+
return year > 24 || (year == 24 && week >= 45);
22+
} catch (NumberFormatException ignored) {}
23+
}
24+
// Releases starting from 1.21.4
25+
var matcher = RELEASE_PATTERN.matcher(neoFormVersion);
26+
if (matcher.find()) {
27+
try {
28+
int minor = Integer.parseInt(matcher.group(1));
29+
// If there is no patch version, the second group has a null value
30+
int patch = Integer.parseInt(Objects.requireNonNullElse(matcher.group(2), "0"));
31+
32+
return minor > 21 || (minor == 21 && patch >= 4);
33+
} catch (NumberFormatException ignored) {}
34+
}
35+
// Assume other version patterns are newer and therefore split
36+
return true;
37+
}
38+
}
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
package net.neoforged.moddevgradle.internal;
2+
3+
import net.neoforged.moddevgradle.internal.utils.VersionUtils;
4+
import org.junit.jupiter.params.ParameterizedTest;
5+
import org.junit.jupiter.params.provider.CsvSource;
6+
7+
import static org.assertj.core.api.Assertions.assertThat;
8+
9+
public class VersionUtilsTest {
10+
@ParameterizedTest()
11+
@CsvSource({
12+
"1.21.4,true",
13+
"1.21.4-pre1-20241120.190508,true",
14+
"1.21.3,false",
15+
"24w45a,true",
16+
"24w44a,false",
17+
"1.21.3-pre1,false",
18+
"25w01a,true",
19+
"23w07a,false",
20+
"1.20,false",
21+
"1.20-pre1,false",
22+
"1.21,false",
23+
"1.21-pre1-20240529.150918,false",
24+
"1.21-pre1,false",
25+
"1.22,true",
26+
"1.22-pre1,true"
27+
})
28+
public void testSplitDataRunsCorrectness(String neoFormVersion, boolean splitDataRuns) {
29+
assertThat(VersionUtils.hasSplitDataRuns(neoFormVersion))
30+
.isEqualTo(splitDataRuns);
31+
}
32+
33+
@ParameterizedTest
34+
@CsvSource({
35+
"1",
36+
"1.",
37+
"1.21.",
38+
"test",
39+
"24w",
40+
"24w5",
41+
"24w50",
42+
"2aw50",
43+
"24242",
44+
})
45+
public void testSplitDataRunsDoesNotCrash(String neoFormVersion) {
46+
assertThat(VersionUtils.hasSplitDataRuns(neoFormVersion))
47+
.isTrue();
48+
}
49+
}

0 commit comments

Comments
 (0)