Skip to content
Merged
Show file tree
Hide file tree
Changes from 14 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,10 @@ static ArtifactNamingStrategy createVanilla(String version) {
};
}

static ArtifactNamingStrategy createVanillaPatched(String loaderVersion) {
return artifact -> "minecraft-patched-%s%s.jar".formatted(loaderVersion, artifact.defaultSuffix);
}

static ArtifactNamingStrategy createNeoForge(VersionCapabilitiesInternal versionCapabilities, String loader, String version) {
return (artifact) -> {
if (artifact != WorkflowArtifact.CLIENT_RESOURCES || versionCapabilities.modLocatorRework()) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,13 @@ public static ModDevArtifactsWorkflow create(Project project,
task.getToolsJavaExecutable().set(javaToolchainService
.launcherFor(spec -> spec.getLanguageVersion().set(JavaLanguageVersion.of(versionCapabilities.javaVersion())))
.map(javaLauncher -> javaLauncher.getExecutablePath().getAsFile().getAbsolutePath()));
// NFRT itself needs to run with a newer version of the JDK to be able to compile with -release 25, for example
// It can however not run with Java 25 and compile Java 8 code while maintaining the same lambda naming.
if (versionCapabilities.javaVersion() > 21) {
task.getJavaExecutable().set(javaToolchainService
.launcherFor(spec -> spec.getLanguageVersion().set(JavaLanguageVersion.of(versionCapabilities.javaVersion())))
.map(javaLauncher -> javaLauncher.getExecutablePath().getAsFile().getAbsolutePath()));
}
Comment on lines +126 to +132
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

While this works, it seems more future proof to run javac in NFRT with the external tool JRE


task.getAccessTransformers().from(accessTransformers);
// If AT validation is enabled, add the user-supplied AT paths as files to be validated,
Expand All @@ -142,14 +149,17 @@ public static ModDevArtifactsWorkflow create(Project project,

Function<WorkflowArtifact, Provider<RegularFile>> artifactPathStrategy = artifact -> artifactsBuildDir.map(dir -> dir.file(artifactNamingStrategy.getFilename(artifact)));

task.getIncludeNeoForgeInMainArtifact().set(versionCapabilities.needsNeoForgeInMinecraftJar());
task.getGameJarArtifact().set(artifactPathStrategy.apply(WorkflowArtifact.COMPILED));
task.getResourcesArtifact().set(artifactPathStrategy.apply(WorkflowArtifact.CLIENT_RESOURCES));
if (disableRecompilation) {
task.getDisableRecompilation().set(true);
} else {
task.getGameJarWithSourcesArtifact().set(artifactPathStrategy.apply(WorkflowArtifact.COMPILED_WITH_SOURCES));
task.getGameSourcesArtifact().set(artifactPathStrategy.apply(WorkflowArtifact.SOURCES));
}
if (versionCapabilities.needsNeoForgeInMinecraftJar()) {
task.getResourcesArtifact().set(artifactPathStrategy.apply(WorkflowArtifact.CLIENT_RESOURCES));
}

task.getNeoForgeArtifact().set(moddingDependencies.neoForgeDependencyNotation());
task.getNeoFormArtifact().set(moddingDependencies.neoFormDependencyNotation());
Expand Down Expand Up @@ -188,7 +198,11 @@ public static ModDevArtifactsWorkflow create(Project project,
config.setCanBeConsumed(false);

config.getDependencies().addLater(minecraftClassesDependency);
config.getDependencies().addLater(createArtifacts.map(task -> project.files(task.getResourcesArtifact())).map(dependencyFactory::create));
if (versionCapabilities.needsNeoForgeInMinecraftJar()) {
config.getDependencies().addLater(createArtifacts.map(task -> project.files(task.getResourcesArtifact())).map(dependencyFactory::create));
} else if (moddingDependencies.neoForgeDependency() != null) {
config.getDependencies().add(moddingDependencies.neoForgeDependency());
}
// Technically, the Minecraft dependencies do not strictly need to be on the classpath because they are pulled from the legacy class path.
// However, we do it anyway because this matches production environments, and allows launch proxies such as DevLogin to use Minecraft's libraries.
config.getDependencies().add(moddingDependencies.gameLibrariesDependency());
Expand All @@ -202,6 +216,9 @@ public static ModDevArtifactsWorkflow create(Project project,
config.setCanBeConsumed(false);
config.getDependencies().addLater(minecraftClassesDependency);
config.getDependencies().add(moddingDependencies.gameLibrariesDependency());
if (!versionCapabilities.needsNeoForgeInMinecraftJar() && moddingDependencies.neoForgeDependency() != null) {
config.getDependencies().add(moddingDependencies.neoForgeDependency());
}
});

// For IDEs that support it, link the source/binary artifacts if we use separated ones
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,19 +73,21 @@ public void enable(
var versionCapabilities = neoForgeVersion != null ? VersionCapabilitiesInternal.ofNeoForgeVersion(neoForgeVersion)
: VersionCapabilitiesInternal.ofNeoFormVersion(neoFormVersion);

var configurations = project.getConfigurations();

var dependencies = neoForge != null ? ModdingDependencies.create(neoForge, neoForgeNotation, neoForm, neoFormNotation, versionCapabilities)
: ModdingDependencies.createVanillaOnly(neoForm, neoFormNotation);

ArtifactNamingStrategy artifactNamingStrategy;
// It's helpful to be able to differentiate the Vanilla jar and the NeoForge jar in classic multiloader setups.
if (neoForge != null) {
if (neoForge == null) {
artifactNamingStrategy = ArtifactNamingStrategy.createVanilla(neoFormVersion);
} else if (versionCapabilities.needsNeoForgeInMinecraftJar()) {
artifactNamingStrategy = ArtifactNamingStrategy.createNeoForge(versionCapabilities, "neoforge", neoForgeVersion);
} else {
artifactNamingStrategy = ArtifactNamingStrategy.createVanilla(neoFormVersion);
artifactNamingStrategy = ArtifactNamingStrategy.createVanillaPatched(neoForgeVersion);
}

var configurations = project.getConfigurations();

var dependencies = neoForge != null ? ModdingDependencies.create(neoForge, neoForgeNotation, neoForm, neoFormNotation, versionCapabilities)
: ModdingDependencies.createVanillaOnly(neoForm, neoFormNotation);

var artifacts = ModDevArtifactsWorkflow.create(
project,
settings.getEnabledSourceSets(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,9 @@ private ModDevRunWorkflow(Project project,
spec.setCanBeConsumed(false);

spec.getDependencies().add(gameLibrariesDependency);
addClientResources(project, spec, artifactsWorkflow.createArtifacts());
if (artifactsWorkflow.versionCapabilities().needsNeoForgeInMinecraftJar()) {
addClientResources(project, spec, artifactsWorkflow.createArtifacts());
}
if (!versionCapabilities.modLocatorRework()) {
// Forge expects to find the Forge and client-extra jar on the legacy classpath
// Newer FML versions also search for it on the java.class.path.
Expand Down Expand Up @@ -221,7 +223,9 @@ public void configureTesting(Provider<ModModel> testedMod, Provider<Set<ModModel
},
legacyClassPath -> {
legacyClassPath.getDependencies().add(gameLibrariesDependency);
addClientResources(project, legacyClassPath, artifactsWorkflow.createArtifacts());
if (artifactsWorkflow.versionCapabilities().needsNeoForgeInMinecraftJar()) {
addClientResources(project, legacyClassPath, artifactsWorkflow.createArtifacts());
}
},
artifactsWorkflow.downloadAssets().flatMap(DownloadAssets::getAssetPropertiesFile),
artifactsWorkflow.versionCapabilities());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,8 @@ public static ModdingDependencies create(ModuleDependency neoForge,
.capabilities(caps -> caps.requireCapability("net.neoforged:neoforge-moddev-module-path"))
// TODO: this is ugly; maybe make the configuration transitive in neoforge, or fix the SJH dep.
.exclude(Map.of("group", "org.jetbrains", "module", "annotations"));
var librariesDependency = neoForge.copy()
ModuleDependency librariesDependency;
librariesDependency = neoForge.copy()
.capabilities(c -> c.requireCapability("net.neoforged:neoforge-dependencies"));

ModuleDependency testFixturesDependency = null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,29 +10,32 @@
/**
* Models the changing capabilities of the modding platform and Vanilla, which we tie to the Minecraft version.
*
* @param minecraftVersion The Minecraft version.
* @param javaVersion Which Java version Vanilla uses to compile and run.
* @param splitDataRuns Whether Vanilla has separate main classes for generating client and server data.
* @param testFixtures If the NeoForge version for this Minecraft version supports test fixtures.
* @param minecraftVersion The Minecraft version.
* @param javaVersion Which Java version Vanilla uses to compile and run.
* @param splitDataRuns Whether Vanilla has separate main classes for generating client and server data.
* @param testFixtures If the NeoForge version for this Minecraft version supports test fixtures.
* @param needsNeoForgeInMinecraftJar The FML version shipped by NeoForge in this Minecraft version requires NeoForge
* classes to be merged into the Minecraft jar to work.
*/
public record VersionCapabilitiesInternal(String minecraftVersion, int javaVersion, boolean splitDataRuns,
boolean testFixtures, boolean modLocatorRework, boolean legacyClasspath) implements VersionCapabilities, Serializable {
boolean testFixtures, boolean modLocatorRework, boolean legacyClasspath, boolean needsNeoForgeInMinecraftJar) implements VersionCapabilities, Serializable {

private static final Logger LOG = Logging.getLogger(VersionCapabilitiesInternal.class);

private static final VersionCapabilitiesInternal LATEST = ofVersionIndex(0);

private static final Pattern NEOFORGE_PATTERN = Pattern.compile("^(\\d+\\.\\d+)\\.\\d+(|-.*)$");
// Strips NeoForm timestamp suffixes OR dynamic version markers
private static final Pattern NEOFORM_PATTERN = Pattern.compile("^(.*)-(?:\\+|\\d{8}\\.\\d{6})$");

private static final int MC_1_21_11_INDEX = getReferenceVersionIndex("1.21.11");
private static final int MC_1_21_9_INDEX = getReferenceVersionIndex("1.21.9");
private static final int MC_24W45A_INDEX = getReferenceVersionIndex("24w45a");
private static final int MC_1_20_5_INDEX = getReferenceVersionIndex("1.20.5");
private static final int MC_24W14A_INDEX = getReferenceVersionIndex("24w14a");
private static final int MC_1_20_4_INDEX = getReferenceVersionIndex("1.20.4");
private static final int MC_1_18_PRE2_INDEX = getReferenceVersionIndex("1.18-pre2");
private static final int MC_21W19A_INDEX = getReferenceVersionIndex("21w19a");

private static final VersionCapabilitiesInternal LATEST = ofVersionIndex(0);
public static VersionCapabilitiesInternal latest() {
return LATEST;
}
Expand All @@ -58,12 +61,15 @@ public static VersionCapabilitiesInternal ofVersionIndex(int versionIndex, Strin
var testFixtures = hasTestFixtures(versionIndex);
var modLocatorRework = hasModLocatorRework(versionIndex);
var legacyClasspath = hasLegacyClasspath(versionIndex);
var needsNeoForgeInMinecraftJar = needsNeoForgeInMinecraftJar(versionIndex);

return new VersionCapabilitiesInternal(minecraftVersion, javaVersion, splitData, testFixtures, modLocatorRework, legacyClasspath);
return new VersionCapabilitiesInternal(minecraftVersion, javaVersion, splitData, testFixtures, modLocatorRework, legacyClasspath, needsNeoForgeInMinecraftJar);
}

static int getJavaVersion(int versionIndex) {
if (versionIndex <= MC_24W14A_INDEX) {
if (versionIndex < MC_1_21_11_INDEX) {
return 25;
} else if (versionIndex <= MC_24W14A_INDEX) {
return 21;
} else if (versionIndex <= MC_1_18_PRE2_INDEX) {
return 17;
Expand All @@ -90,6 +96,10 @@ static boolean hasLegacyClasspath(int versionIndex) {
return versionIndex > MC_1_21_9_INDEX;
}

static boolean needsNeoForgeInMinecraftJar(int versionIndex) {
return versionIndex >= MC_1_21_11_INDEX;
}

static int indexOfNeoForgeVersion(String version) {
// NeoForge omits the "1." at the start of the Minecraft version and just adds an incrementing last digit
var matcher = NEOFORGE_PATTERN.matcher(version);
Expand Down Expand Up @@ -175,6 +185,7 @@ public VersionCapabilitiesInternal withMinecraftVersion(String minecraftVersion)
splitDataRuns,
testFixtures,
modLocatorRework,
legacyClasspath);
legacyClasspath,
needsNeoForgeInMinecraftJar);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@ public CreateMinecraftArtifacts() {
getDisableRecompilation().convention(false);
getValidateAccessTransformers().convention(false);
getParchmentEnabled().convention(false);
getIncludeNeoForgeInMainArtifact().convention(true);
getIncludeResourcesInMainArtifact().convention(false);
}

/**
Expand Down Expand Up @@ -231,6 +233,12 @@ public RegularFileProperty getSourcesArtifact() {
return getGameSourcesArtifact();
}

@Input
public abstract Property<Boolean> getIncludeNeoForgeInMainArtifact();

@Input
public abstract Property<Boolean> getIncludeResourcesInMainArtifact();

@Inject
protected abstract Problems getProblems();

Expand Down Expand Up @@ -319,9 +327,10 @@ public void createArtifacts() {
requestedResults.add(new RequestedResult("clientResources", getResourcesArtifact().get().getAsFile()));
}

boolean includeNeoForgeInGameJar = getIncludeNeoForgeInMainArtifact().get();
if (getDisableRecompilation().get()) {
if (getGameJarArtifact().isPresent()) {
if (getNeoForgeArtifact().isPresent()) {
if (getNeoForgeArtifact().isPresent() && includeNeoForgeInGameJar) {
requestedResults.add(new RequestedResult("gameJarNoRecompWithNeoForge", getGameJarArtifact().get().getAsFile()));
} else {
requestedResults.add(new RequestedResult("gameJarNoRecomp", getGameJarArtifact().get().getAsFile()));
Expand All @@ -333,7 +342,7 @@ public void createArtifacts() {
if (getGameJarWithSourcesArtifact().isPresent()) {
throw new IllegalArgumentException("Cannot request game jar with sources if recompilation is disabled.");
}
} else if (getNeoForgeArtifact().isPresent()) {
} else if (getNeoForgeArtifact().isPresent() && includeNeoForgeInGameJar) {
if (getGameJarArtifact().isPresent()) {
requestedResults.add(new RequestedResult("gameJarWithNeoForge", getGameJarArtifact().get().getAsFile()));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ public abstract class NeoFormRuntimeTask extends DefaultTask {
*/
@Input
@ApiStatus.Internal
protected abstract Property<String> getJavaExecutable();
public abstract Property<String> getJavaExecutable();

@Inject
@ApiStatus.Internal
Expand Down Expand Up @@ -111,7 +111,7 @@ public NeoFormRuntimeTask() {
// Store temporary working directories in this projects build directory such that gradle clean removes them
getWorkDirectory().convention(project.getLayout().getBuildDirectory().dir("tmp/neoformruntime"));

// Run NFRT itself with Java 21
// Run NFRT itself with Java 25
getJavaExecutable().convention(getJavaToolchainService()
.launcherFor(spec -> spec.getLanguageVersion().set(JavaLanguageVersion.of(21)))
.map(javaLauncher -> javaLauncher.getExecutablePath().getAsFile().getAbsolutePath()));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ void testModdingCannotBeEnabledTwice() {
@Test
void testEnableForTestSourceSetOnly() {
extension.enable(settings -> {
settings.setVersion("100.3.0"); // Needs to be at least 20.5 to use paths for newer FML
settings.setVersion("21.11.0"); // Needs to be at least 20.5 to use paths for newer FML
settings.setEnabledSourceSets(Set.of(testSourceSet));
});

Expand All @@ -57,25 +57,25 @@ void testEnableForTestSourceSetOnly() {
assertThatDependencies(mainSourceSet.getRuntimeClasspathConfigurationName()).isEmpty();

// While the test classpath should have modding dependencies
assertContainsModdingCompileDependencies("100.3.0", testSourceSet.getCompileClasspathConfigurationName());
assertContainsModdingRuntimeDependencies("100.3.0", testSourceSet.getRuntimeClasspathConfigurationName());
assertContainsModdingCompileDependencies("21.11.0", testSourceSet.getCompileClasspathConfigurationName());
assertContainsModdingRuntimeDependencies("21.11.0", testSourceSet.getRuntimeClasspathConfigurationName());
}

@Test
void testAddModdingDependenciesTo() {
extension.setVersion("100.3.0"); // Needs to be at least 20.5 to use paths for newer FML
extension.setVersion("21.11.0"); // Needs to be at least 20.5 to use paths for newer FML

// Initially, only the main source set should have the dependencies
assertContainsModdingCompileDependencies("100.3.0", mainSourceSet.getCompileClasspathConfigurationName());
assertContainsModdingRuntimeDependencies("100.3.0", mainSourceSet.getRuntimeClasspathConfigurationName());
assertContainsModdingCompileDependencies("21.11.0", mainSourceSet.getCompileClasspathConfigurationName());
assertContainsModdingRuntimeDependencies("21.11.0", mainSourceSet.getRuntimeClasspathConfigurationName());
assertThatDependencies(testSourceSet.getCompileClasspathConfigurationName()).isEmpty();
assertThatDependencies(testSourceSet.getRuntimeClasspathConfigurationName()).isEmpty();

// Now add it to the test source set too
extension.addModdingDependenciesTo(testSourceSet);

assertContainsModdingCompileDependencies("100.3.0", testSourceSet.getCompileClasspathConfigurationName());
assertContainsModdingRuntimeDependencies("100.3.0", testSourceSet.getRuntimeClasspathConfigurationName());
assertContainsModdingCompileDependencies("21.11.0", testSourceSet.getCompileClasspathConfigurationName());
assertContainsModdingRuntimeDependencies("21.11.0", testSourceSet.getRuntimeClasspathConfigurationName());
}

@Test
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,15 +16,15 @@ public class VersionCapabilitiesInternalTest {
"24w45a,21",
"24w44a,21",
"1.21.3-pre1,21",
"25w01a,21",
"25w46a,21",
"23w07a,17",
"1.20,17",
"1.20-pre1,17",
"1.21,21",
"1.21-pre1-20240529.150918,21",
"1.21-pre1,21",
"1.22,21",
"1.22-pre1,21",
"1.21.11,21",
"26.1-snapshot-1,25",
"1.0,8",
"1.14.2 Pre-Release 2,8",
"21w19a,16",
Expand Down
Loading
Loading