Skip to content

Commit

Permalink
[Fix]: Make runs entirely lazily, adding back support for handling Ve…
Browse files Browse the repository at this point in the history
…rsionCatalogues (#230)
  • Loading branch information
marchermans authored Jul 24, 2024
1 parent 30c8f5f commit ed5a151
Show file tree
Hide file tree
Showing 54 changed files with 1,154 additions and 419 deletions.
51 changes: 50 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,57 @@ dependencies {
}
```

#### Dependency management by the userdev plugin
When this plugin detects a dependency on NeoForge, it will spring into action and create the necessary NeoForm runtime tasks to build a usable Minecraft JAR-file that contains the requested NeoForge version.
It additionally (if configured to do so via conventions, which is the default) will create runs for your project, and add the necessary dependencies to the classpath of the run.

##### Version Catalogues
Using gradles modern version catalog feature means that dependencies are added at the very last moment possible, regardless of that is during script evaluation or not.
This means that if you use this feature it might sometimes not be possible to configure the default runs exactly as you wished.
In that case it might be beneficial to disable the conventions for runs, and configure them manually.

See the following example:

_lib.versions.toml:_
```toml
[versions]
# Neoforge Settings
neoforge = "+"

[libraries]
neoforge = { group = "net.neoforged", name = "neoforge", version.ref = "neoforge" }
```
_build.gradle:_
```groovy
java {
toolchain {
languageVersion = JavaLanguageVersion.of(21)
}
}
repositories {
mavenCentral()
}
dependencies {
implementation(libs.neoforge)
}
runs {
configureEach { run ->
run.modSource sourceSets.main
}
client { }
server { }
datagen { }
gameTestServer { }
junit { }
}
```
You do not need to create all five of the different runs, only the ones you need.

This is because at the point where gradle actually adds the dependency, we can not create any further runs.

### NeoForm Runtime Plugin

Expand Down Expand Up @@ -62,7 +112,6 @@ from the [Parchment project](https://parchmentmc.org) can be applied to the Mine
This is currently only supported when applying the NeoGradle userdev Plugin.

The most basic configuration is using the following properties in gradle.properties:

```
neogradle.subsystems.parchment.minecraftVersion=1.20.2
neogradle.subsystems.parchment.mappingsVersion=2023.12.10
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,10 @@
import net.neoforged.gradle.common.extensions.dependency.replacement.ReplacementLogic;
import net.neoforged.gradle.common.extensions.repository.IvyRepository;
import net.neoforged.gradle.common.extensions.subsystems.SubsystemsExtension;
import net.neoforged.gradle.common.rules.LaterAddedReplacedDependencyRule;
import net.neoforged.gradle.common.runs.ide.IdeRunIntegrationManager;
import net.neoforged.gradle.common.runs.run.RunImpl;
import net.neoforged.gradle.common.runs.run.RunTypeManagerImpl;
import net.neoforged.gradle.common.runs.tasks.RunsReport;
import net.neoforged.gradle.common.runtime.definition.CommonRuntimeDefinition;
import net.neoforged.gradle.common.runtime.extensions.RuntimesExtension;
Expand Down Expand Up @@ -35,6 +37,7 @@
import net.neoforged.gradle.dsl.common.runs.run.Run;
import net.neoforged.gradle.dsl.common.runs.run.RunDevLogin;
import net.neoforged.gradle.dsl.common.runs.type.RunType;
import net.neoforged.gradle.dsl.common.runs.type.RunTypeManager;
import net.neoforged.gradle.dsl.common.util.ConfigurationUtils;
import net.neoforged.gradle.dsl.common.util.NamingConstants;
import net.neoforged.gradle.util.UrlConstants;
Expand Down Expand Up @@ -101,6 +104,9 @@ public void apply(Project project) {
extensionManager.registerExtension("minecraft", Minecraft.class, (p) -> p.getObjects().newInstance(MinecraftExtension.class, p));
extensionManager.registerExtension("mappings", Mappings.class, (p) -> p.getObjects().newInstance(MappingsExtension.class, p));

extensionManager.registerExtension("runTypes", RunTypeManager.class, (p) -> p.getObjects().newInstance(RunTypeManagerImpl.class, p));


project.getExtensions().create("clientExtraJarDependencyManager", ExtraJarDependencyManager.class, project);
final ConfigurationData configurationData = project.getExtensions().create(ConfigurationData.class, "configurationData", ConfigurationDataExtension.class, project);

Expand All @@ -127,16 +133,13 @@ public void apply(Project project) {
sourceSet.getExtensions().add("runtimeDefinition", project.getObjects().property(CommonRuntimeDefinition.class));
});

project.getExtensions().add(
RunsConstants.Extensions.RUN_TYPES,
project.getObjects().domainObjectContainer(RunType.class, name -> project.getObjects().newInstance(RunType.class, name))
);

final NamedDomainObjectContainer<Run> runs = project.getObjects().domainObjectContainer(Run.class, name -> RunsUtil.create(project, name));
project.getExtensions().add(
RunsConstants.Extensions.RUNS,
runs
);
//Register a task creation rule that checks for runs.
project.getTasks().addRule(new LaterAddedReplacedDependencyRule(project));

runs.configureEach(run -> {
RunsUtil.configureModClasses(run);
Expand Down Expand Up @@ -219,29 +222,13 @@ private void configureSourceSetConventions(Project project, Conventions conventi

}

@SuppressWarnings("unchecked")
private void configureRunConventions(Project project, Conventions conventions) {
final Configurations configurations = conventions.getConfigurations();
final Runs runs = conventions.getRuns();

if (!runs.getIsEnabled().get())
return;

if (runs.getShouldDefaultRunsBeCreated().get()) {
final NamedDomainObjectContainer<RunType> runTypes = (NamedDomainObjectContainer<RunType>) project.getExtensions().getByName(RunsConstants.Extensions.RUN_TYPES);
//Force none lazy resolve here.
runTypes.whenObjectAdded(runType -> {
project.getExtensions().configure(RunsConstants.Extensions.RUNS, (Action<NamedDomainObjectContainer<Run>>) runContainer -> {
if (runContainer.getAsMap().containsKey(runType.getName()))
return;

runContainer.create(runType.getName(), run -> {
run.configure(runType);
});
});
});
}

if (!configurations.getIsEnabled().get())
return;

Expand Down Expand Up @@ -357,7 +344,7 @@ private void applyAfterEvaluate(final Project project) {
//We now eagerly get all runs and configure them.
final NamedDomainObjectContainer<Run> runs = (NamedDomainObjectContainer<Run>) project.getExtensions().getByName(RunsConstants.Extensions.RUNS);
runs.configureEach(run -> {
if (run instanceof RunImpl) {
if (run instanceof RunImpl runImpl) {
run.configure();

// We add default junit sourcesets here because we need to know the type of the run first
Expand All @@ -372,68 +359,38 @@ private void applyAfterEvaluate(final Project project) {
throw new InvalidUserDataException("Run: " + run.getName() + " has no source sets configured. Please configure at least one source set.");
}

if (run.getConfigureFromDependencies().get()) {
final RunImpl runImpl = (RunImpl) run;

//Let's keep track of all runtimes that have been configured.
//TODO: Determine handling of multiple different runtimes, in multiple projects....
final Map<String, CommonRuntimeDefinition<?>> definitionSet = new HashMap<>();

runImpl.getModSources().all().get().values().forEach(sourceSet -> {
try {
final Optional<CommonRuntimeDefinition<?>> definition = TaskDependencyUtils.findRuntimeDefinition(sourceSet);
if (definition.isPresent()) {
final CommonRuntimeDefinition<?> runtimeDefinition = definition.get();
//First time we see this runtime add, it.
if (!definitionSet.containsKey(runtimeDefinition.getSpecification().getIdentifier())) {
definitionSet.put(runtimeDefinition.getSpecification().getIdentifier(), runtimeDefinition);
} else if (SourceSetUtils.getProject(sourceSet) == project) {
//We have seen this runtime before, but this time it is our own, which we prefer.
definitionSet.put(runtimeDefinition.getSpecification().getIdentifier(), runtimeDefinition);
}
}
} catch (MultipleDefinitionsFoundException e) {
throw new RuntimeException("Failed to configure run: " + run.getName() + " there are multiple runtime definitions found for the source set: " + sourceSet.getName(), e);
}
});

definitionSet.forEach((identifier, definition) -> {
definition.configureRun(runImpl);
});

//Handle dev login.
final DevLogin devLogin = project.getExtensions().getByType(Subsystems.class).getDevLogin();
final Tools tools = project.getExtensions().getByType(Subsystems.class).getTools();
if (devLogin.getEnabled().get()) {
final RunDevLogin runsDevLogin = runImpl.getExtensions().getByType(RunDevLogin.class);

//Dev login is only supported on the client side
if (runImpl.getIsClient().get() && runsDevLogin.getIsEnabled().get()) {
final String mainClass = runImpl.getMainClass().get();

//We add the dev login tool to a custom configuration which runtime classpath extends from the default runtime classpath
final SourceSet defaultSourceSet = runImpl.getModSources().all().get().entries().iterator().next().getValue();
final String runtimeOnlyDevLoginConfigurationName = ConfigurationUtils.getSourceSetName(defaultSourceSet, devLogin.getConfigurationSuffix().get());
final Configuration sourceSetRuntimeOnlyDevLoginConfiguration = project.getConfigurations().maybeCreate(runtimeOnlyDevLoginConfigurationName);
final Configuration sourceSetRuntimeClasspathConfiguration = project.getConfigurations().maybeCreate(defaultSourceSet.getRuntimeClasspathConfigurationName());

sourceSetRuntimeClasspathConfiguration.extendsFrom(sourceSetRuntimeOnlyDevLoginConfiguration);
sourceSetRuntimeOnlyDevLoginConfiguration.getDependencies().add(project.getDependencies().create(tools.getDevLogin().get()));

//Update the program arguments to properly launch the dev login tool
run.getProgramArguments().add("--launch_target");
run.getProgramArguments().add(mainClass);

if (runsDevLogin.getProfile().isPresent()) {
run.getProgramArguments().add("--launch_profile");
run.getProgramArguments().add(runsDevLogin.getProfile().get());
}
//Handle dev login.
final DevLogin devLogin = project.getExtensions().getByType(Subsystems.class).getDevLogin();
final Tools tools = project.getExtensions().getByType(Subsystems.class).getTools();
if (devLogin.getEnabled().get()) {
final RunDevLogin runsDevLogin = runImpl.getExtensions().getByType(RunDevLogin.class);

//Set the main class to the dev login tool
run.getMainClass().set(devLogin.getMainClass());
} else if (!runImpl.getIsClient().get() && runsDevLogin.getIsEnabled().get()) {
throw new InvalidUserDataException("Dev login is only supported on runs which are marked as clients! The run: " + runImpl.getName() + " is not a client run.");
//Dev login is only supported on the client side
if (runImpl.getIsClient().get() && runsDevLogin.getIsEnabled().get()) {
final String mainClass = runImpl.getMainClass().get();

//We add the dev login tool to a custom configuration which runtime classpath extends from the default runtime classpath
final SourceSet defaultSourceSet = runImpl.getModSources().all().get().entries().iterator().next().getValue();
final String runtimeOnlyDevLoginConfigurationName = ConfigurationUtils.getSourceSetName(defaultSourceSet, devLogin.getConfigurationSuffix().get());
final Configuration sourceSetRuntimeOnlyDevLoginConfiguration = project.getConfigurations().maybeCreate(runtimeOnlyDevLoginConfigurationName);
final Configuration sourceSetRuntimeClasspathConfiguration = project.getConfigurations().maybeCreate(defaultSourceSet.getRuntimeClasspathConfigurationName());

sourceSetRuntimeClasspathConfiguration.extendsFrom(sourceSetRuntimeOnlyDevLoginConfiguration);
sourceSetRuntimeOnlyDevLoginConfiguration.getDependencies().add(project.getDependencies().create(tools.getDevLogin().get()));

//Update the program arguments to properly launch the dev login tool
run.getProgramArguments().add("--launch_target");
run.getProgramArguments().add(mainClass);

if (runsDevLogin.getProfile().isPresent()) {
run.getProgramArguments().add("--launch_profile");
run.getProgramArguments().add(runsDevLogin.getProfile().get());
}

//Set the main class to the dev login tool
run.getMainClass().set(devLogin.getMainClass());
} else if (!runImpl.getIsClient().get() && runsDevLogin.getIsEnabled().get()) {
throw new InvalidUserDataException("Dev login is only supported on runs which are marked as clients! The run: " + runImpl.getName() + " is not a client run.");
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -87,21 +87,6 @@ public final Map<CacheFileSelector, File> getCacheFiles() {
return ImmutableMap.copyOf(this.cacheFiles);
}

@Override
public final Map<GameArtifact, File> cacheGameVersion(String gameVersion, DistributionType side) {
final MinecraftVersionAndUrl resolvedVersion = resolveVersion(gameVersion);

final Map<GameArtifact, File> result = new EnumMap<>(GameArtifact.class);

GameArtifact.VERSION_MANIFEST.doWhenRequired(side, () -> result.put(GameArtifact.VERSION_MANIFEST, this.cacheVersionManifest(resolvedVersion)));
GameArtifact.CLIENT_JAR.doWhenRequired(side, () -> result.put(GameArtifact.CLIENT_JAR, this.cacheVersionArtifact(resolvedVersion, DistributionType.CLIENT)));
GameArtifact.SERVER_JAR.doWhenRequired(side, () -> result.put(GameArtifact.SERVER_JAR, this.cacheVersionArtifact(resolvedVersion, DistributionType.SERVER)));
GameArtifact.CLIENT_MAPPINGS.doWhenRequired(side, () -> result.put(GameArtifact.CLIENT_MAPPINGS, this.cacheVersionMappings(resolvedVersion, DistributionType.CLIENT)));
GameArtifact.SERVER_MAPPINGS.doWhenRequired(side, () -> result.put(GameArtifact.SERVER_MAPPINGS, this.cacheVersionMappings(resolvedVersion, DistributionType.SERVER)));

return result;
}

@Override
@NotNull
public final Map<GameArtifact, TaskProvider<? extends WithOutput>> cacheGameVersionTasks(final Project project, String gameVersion, final DistributionType side) {
Expand Down Expand Up @@ -130,10 +115,12 @@ public final File cacheLauncherMetadata() {
}

@Override
public final File cacheVersionManifest(String gameVersion) {
final MinecraftVersionAndUrl resolvedVersion = resolveVersion(gameVersion);
public final Provider<File> cacheVersionManifest(String gameVersion) {
return project.provider(() -> {
final MinecraftVersionAndUrl resolvedVersion = resolveVersion(gameVersion);

return this.cacheVersionManifest(resolvedVersion);
return this.cacheVersionManifest(resolvedVersion);
});
}

public final File cacheVersionManifest(MinecraftVersionAndUrl resolvedVersion) {
Expand All @@ -149,11 +136,6 @@ public final File cacheVersionArtifact(String gameVersion, DistributionType side
return this.cacheFiles.computeIfAbsent(cacheFileSelector, selector -> downloadVersionArtifactToCache(project, getCacheDirectory().get().getAsFile(), resolvedVersion.getVersion(), side));
}

public final File cacheVersionArtifact(MinecraftVersionAndUrl resolvedVersion, DistributionType side) {
final CacheFileSelector cacheFileSelector = CacheFileSelector.forVersionJar(resolvedVersion.getVersion(), side.getName());
return this.cacheFiles.computeIfAbsent(cacheFileSelector, selector -> downloadVersionArtifactToCache(project, getCacheDirectory().get().getAsFile(), resolvedVersion.getVersion(), side));
}

@Override
public final File cacheVersionMappings(@NotNull String gameVersion, DistributionType side) {
final MinecraftVersionAndUrl resolvedVersion = resolveVersion(gameVersion);
Expand All @@ -162,11 +144,6 @@ public final File cacheVersionMappings(@NotNull String gameVersion, Distribution
return this.cacheFiles.computeIfAbsent(cacheFileSelector, selector -> downloadVersionMappingsToCache(project, getCacheDirectory().get().getAsFile(), resolvedVersion.getVersion(), side));
}

public final File cacheVersionMappings(@NotNull MinecraftVersionAndUrl resolvedVersion, DistributionType side) {
final CacheFileSelector cacheFileSelector = CacheFileSelector.forVersionMappings(resolvedVersion.getVersion(), side.getName());
return this.cacheFiles.computeIfAbsent(cacheFileSelector, selector -> downloadVersionMappingsToCache(project, getCacheDirectory().get().getAsFile(), resolvedVersion.getVersion(), side));
}

@Override
public final File cache(final URL url, final CacheFileSelector selector) {
return this.cache(url.toString(), selector);
Expand Down
Loading

0 comments on commit ed5a151

Please sign in to comment.