Skip to content

Commit

Permalink
Starting on docs. Fixing tests.
Browse files Browse the repository at this point in the history
  • Loading branch information
shartte committed Dec 23, 2023
1 parent 7b89a5c commit 1c025a2
Show file tree
Hide file tree
Showing 6 changed files with 157 additions and 89 deletions.
53 changes: 49 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
NeoGradle
===========
# NeoGradle

Minecraft mod development framework used by NeoForge and FML for the Gradle build system.

Expand All @@ -8,7 +7,53 @@ our official [Documentation](https://docs.neoforged.net/neogradle/docs/).

To see the latest available version of NeoGradle, visit the [NeoForged project page](https://projects.neoforged.net/neoforged/neogradle).

## Override Decompiler Settings
## Plugins

NeoGradle is separated into several different plugins that can be applied independently of each other.

### Userdev Plugin

This plugin is used for building mods with NeoForge. As a modder, this will in many cases be the only plugin you use.

```gradle
plugins {
id 'net.neoforged.gradle.userdev' version '<neogradle_version>'
}
dependencies {
implementation 'net.neoforged:neoforge:<neoforge_version>'
}
```

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.

### NeoForm Runtime Plugin

This plugin enables use of the NeoForm runtime and allows projects to depend directly on deobfuscated but otherwise
unmodified Minecraft artifacts.

This plugin is used internally by other plugins and is usually only needed for advanced use cases.

```gradle
plugins {
id 'net.neoforged.gradle.neoform' version '<neogradle_version>'
}
dependencies {
// For depending on a Minecraft JAR-file with both client- and server-classes
implementation "net.minecraft:neoform_joined:<neoform-version>'
// For depending on the Minecraft client JAR-file
implementation "net.minecraft:neoform_client:<neoform-version>'
// For depending on the Minecraft dedicated server JAR-file
implementation "net.minecraft:neoform_server:<neoform-version>'
}
```

## Advanced Settings

### Override Decompiler Settings

The settings used by the decompiler when preparing Minecraft dependencies can be overridden
using [Gradle properties](https://docs.gradle.org/current/userguide/project_properties.html).
Expand All @@ -20,7 +65,7 @@ This can be useful to trade slower build-times for being able to run NeoGradle o
| `neogradle.subsystems.decompiler.maxThreads` | By default the decompiler uses all available CPU cores. This setting can be used to limit it to a given number of threads. |
| `neogradle.subsystems.decompiler.logLevel` | Can be used to override the [decompiler loglevel](https://vineflower.org/usage/#cmdoption-log). |

## Override Recompiler Settings
### Override Recompiler Settings

The settings used by Neogradle for recompiling the decompiled Minecraft source code can be customized
using [Gradle properties](https://docs.gradle.org/current/userguide/project_properties.html).
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,14 +40,14 @@ public void apply(@Nonnull Project project) {
// Needed to gain access to the common systems
project.getPluginManager().apply(CommonPlugin.class);

NeoFormRuntimeExtension runtimeExtension = project.getExtensions().create("neoFormRuntime", NeoFormRuntimeExtension.class, project);
project.getExtensions().create("neoFormRuntime", NeoFormRuntimeExtension.class, project);

NeoFormOfficialNamingChannelConfigurator.getInstance().configure(project);

//Setup handling of the dependencies
NeoFormDependencyManager.getInstance().apply(project);
// Setup handling of the dependencies
NeoFormDependencyManager.apply(project);

//Add Known repos
// Add Known repos
project.getRepositories().maven(e -> {
e.setUrl(UrlConstants.NEO_FORGE_MAVEN);
e.metadataSources(MavenArtifactRepository.MetadataSources::mavenPom);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package net.neoforged.gradle.neoform.dependency;

import com.google.common.collect.Sets;
import net.neoforged.gradle.dsl.common.extensions.dependency.replacement.Context;
import net.neoforged.gradle.dsl.common.util.ConfigurationUtils;
import net.neoforged.gradle.dsl.common.util.DistributionType;
import net.neoforged.gradle.neoform.runtime.definition.NeoFormRuntimeDefinition;
Expand All @@ -13,79 +14,112 @@
import org.gradle.api.artifacts.Dependency;
import org.gradle.api.artifacts.DependencyArtifact;
import org.gradle.api.artifacts.ExternalModuleDependency;
import org.gradle.api.artifacts.ModuleDependency;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import java.util.Locale;
import java.util.Objects;
import java.util.Optional;

/**
* This class installs a dependency replacement handler that replaces the following dependencies with the output
* of a NeoForm runtime.
* <p>
* <ul>
* <li>net.minecraft:neoform_client</li>
* <li>net.minecraft:neoform_server</li>
* <li>net.minecraft:neoform_joined</li>
* </ul>
* <p>
* The NeoForm version that should be used is determined from the version of the dependency.
*/
public final class NeoFormDependencyManager {
private static final NeoFormDependencyManager INSTANCE = new NeoFormDependencyManager();

private NeoFormDependencyManager() {
}

public static NeoFormDependencyManager getInstance() {
return INSTANCE;
}

public void apply(final Project project) {
public static void apply(final Project project) {
final DependencyReplacement dependencyReplacer = project.getExtensions().getByType(DependencyReplacement.class);

dependencyReplacer.getReplacementHandlers().create("neoForm", handler -> {
handler.getReplacer().set(context -> {
if (isNotAMatchingDependency(context.getDependency())) {
return Optional.empty();
}

if (!(context.getDependency() instanceof ExternalModuleDependency)) {
return Optional.empty();
}

final ExternalModuleDependency externalModuleDependency = (ExternalModuleDependency) context.getDependency();

final NeoFormRuntimeDefinition runtimeDefinition = buildNeoFormRuntimeFromDependency(project, externalModuleDependency);
return Optional.of(
new DependencyReplacementResult(
project,
Optional.of(ConfigurationUtils.findReplacementConfigurations(project, context.getConfiguration())),
name -> CommonRuntimeUtils.buildTaskName(runtimeDefinition, name),
runtimeDefinition.getSourceJarTask(),
runtimeDefinition.getRawJarTask(),
runtimeDefinition.getMinecraftDependenciesConfiguration(),
builder -> builder.setVersion(runtimeDefinition.getSpecification().getNeoFormVersion()),
builder -> builder.setVersion(runtimeDefinition.getSpecification().getNeoFormVersion()),
runtimeDefinition::setReplacedDependency,
runtimeDefinition::onRepoWritten,
Sets::newHashSet
));
});
handler.getReplacer().set(NeoFormDependencyManager::replaceDependency);
});
}

private boolean isNotAMatchingDependency(final Dependency dependencyToCheck) {
if (dependencyToCheck instanceof ExternalModuleDependency) {
final ExternalModuleDependency externalModuleDependency = (ExternalModuleDependency) dependencyToCheck;
return !"net.minecraft".equals(externalModuleDependency.getGroup()) || !isSupportedSide(dependencyToCheck) || !hasMatchingArtifact(externalModuleDependency);
private static Optional<DependencyReplacementResult> replaceDependency(Context context) {
ModuleDependency dependency = context.getDependency();

NeoFormTarget target = getNeoFormTargetFromDependency(dependency);
if (target == null) {
return Optional.empty();
}

return true;
if (target.version == null) {
throw new IllegalStateException("Version is missing on NeoForm dependency " + dependency);
}

// Build the runtime used to produce the artifact
Project project = context.getProject();
NeoFormRuntimeExtension runtimeExtension = project.getExtensions().getByType(NeoFormRuntimeExtension.class);
NeoFormRuntimeDefinition runtime = runtimeExtension.maybeCreate(builder -> {
builder.withDistributionType(target.distribution).withNeoFormVersion(target.version);
NeoFormRuntimeUtils.configureDefaultRuntimeSpecBuilder(project, builder);
});

return Optional.of(
new DependencyReplacementResult(
project,
Optional.of(ConfigurationUtils.findReplacementConfigurations(project, context.getConfiguration())),
name -> CommonRuntimeUtils.buildTaskName(runtime, name),
runtime.getSourceJarTask(),
runtime.getRawJarTask(),
runtime.getMinecraftDependenciesConfiguration(),
builder -> builder.setVersion(runtime.getSpecification().getNeoFormVersion()),
builder -> builder.setVersion(runtime.getSpecification().getNeoFormVersion()),
runtime::setReplacedDependency,
runtime::onRepoWritten,
Sets::newHashSet
));
}

private boolean isSupportedSide(final Dependency dependency) {
return dependency.getName().equalsIgnoreCase("neoform_client") ||
dependency.getName().equalsIgnoreCase("neoform_server") ||
dependency.getName().equalsIgnoreCase("neoform_joined");
@Nullable
private static NeoFormTarget getNeoFormTargetFromDependency(ModuleDependency dependency) {

if (!"net.minecraft".equals(dependency.getGroup())) {
return null;
}

DistributionType distributionType;
switch (dependency.getName()) {
case "neoform_client":
distributionType = DistributionType.CLIENT;
break;
case "neoform_server":
distributionType = DistributionType.SERVER;
break;
case "neoform_joined":
distributionType = DistributionType.JOINED;
break;
default:
return null; // This dependency is not handled by this replacer
}

if (!hasMatchingArtifact(dependency)) {
return null;
}

return new NeoFormTarget(dependency.getVersion(), distributionType);
}

private boolean hasMatchingArtifact(ExternalModuleDependency externalModuleDependency) {
private static boolean hasMatchingArtifact(ModuleDependency externalModuleDependency) {
if (externalModuleDependency.getArtifacts().isEmpty()){
return true;
}

return hasSourcesArtifact(externalModuleDependency);
}

private static boolean hasSourcesArtifact(ExternalModuleDependency externalModuleDependency) {
private static boolean hasSourcesArtifact(ModuleDependency externalModuleDependency) {
if (externalModuleDependency.getArtifacts().size() != 1) {
return false;
}
Expand All @@ -94,18 +128,14 @@ private static boolean hasSourcesArtifact(ExternalModuleDependency externalModul
return Objects.equals(artifact.getClassifier(), "sources") && Objects.equals(artifact.getExtension(), "jar");
}

private static final class NeoFormTarget {
private final String version;
private final DistributionType distribution;

private static NeoFormRuntimeDefinition buildNeoFormRuntimeFromDependency(Project project, ExternalModuleDependency dependency) {
final NeoFormRuntimeExtension runtimeExtension = project.getExtensions().getByType(NeoFormRuntimeExtension.class);
return runtimeExtension.maybeCreate(builder -> {
builder.withDistributionType(DistributionType.valueOf(dependency.getName().toLowerCase().replace("neoform_", "").toUpperCase(Locale.ROOT)));
if (dependency.getVersion() == null) {
throw new IllegalStateException("Version is not defined on NeoForm dependency");
}

builder.withNeoFormDependency("net.neoform:neoform:" + dependency.getVersion() + "@zip");
NeoFormRuntimeUtils.configureDefaultRuntimeSpecBuilder(project, builder);
});
public NeoFormTarget(String version, DistributionType distribution) {
this.version = version;
this.distribution = distribution;
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -252,7 +252,6 @@ protected NeoFormRuntimeDefinition doCreate(final NeoFormRuntimeSpecification sp
final Minecraft minecraftExtension = spec.getProject().getExtensions().getByType(Minecraft.class);
final Mappings mappingsExtension = minecraftExtension.getMappings();
final MinecraftArtifactCache artifactCacheExtension = spec.getProject().getExtensions().getByType(MinecraftArtifactCache.class);
final File neoFormZipFile = spec.getNeoFormArchive();

final File minecraftCache = artifactCacheExtension.getCacheDirectory().get().getAsFile();

Expand All @@ -275,18 +274,19 @@ protected NeoFormRuntimeDefinition doCreate(final NeoFormRuntimeSpecification sp
}

final File neoFormDirectory = spec.getProject().getLayout().getBuildDirectory().dir(String.format("neoForm/%s", spec.getIdentifier())).get().getAsFile();
final File unpackedMcpZipDirectory = new File(neoFormDirectory, "unpacked");
final File stepsMcpDirectory = new File(neoFormDirectory, "steps");

stepsMcpDirectory.mkdirs();

// TODO: This is unpacking the ZIP during the configuration phase. This should be changed to only extract
// the files that are neaded for each task on-demand as part of the task.
final File unpackedMcpZipDirectory = new File(neoFormDirectory, "unpacked");
final File neoFormZipFile = spec.getNeoFormArchive();
final FileTree neoFormZipFileTree = spec.getProject().zipTree(neoFormZipFile);
final CopyingFileTreeVisitor unpackingVisitor = new CopyingFileTreeVisitor(unpackedMcpZipDirectory);
neoFormZipFileTree.visit(unpackingVisitor);

final File neoFormConfigFile = new File(unpackedMcpZipDirectory, "config.json");
final NeoFormConfigConfigurationSpecV2 neoFormConfig = NeoFormConfigConfigurationSpecV2.get(neoFormConfigFile);

NeoFormConfigConfigurationSpecV2 neoFormConfig = spec.getConfig();
neoFormConfig.getLibraries(spec.getDistribution().getName()).forEach(library -> minecraftDependenciesConfiguration.getDependencies().add(
spec.getProject().getDependencies().create(library)
));
Expand Down Expand Up @@ -337,7 +337,6 @@ protected void bakeDefinition(NeoFormRuntimeDefinition definition) {
final File minecraftCache = artifactCacheExtension.getCacheDirectory().get().getAsFile();

final File neoFormDirectory = spec.getProject().getLayout().getBuildDirectory().dir(String.format("neoForm/%s", spec.getIdentifier())).get().getAsFile();
final File unpackedMcpZipDirectory = new File(neoFormDirectory, "unpacked");
final File stepsMcpDirectory = new File(neoFormDirectory, "steps");

final Map<String, String> versionData = Maps.newHashMap(mappingsExtension.getVersion().get());
Expand All @@ -349,7 +348,7 @@ protected void bakeDefinition(NeoFormRuntimeDefinition definition) {

final List<NeoFormConfigConfigurationSpecV1.Step> steps = neoFormConfig.getSteps(spec.getDistribution().getName());
if (steps.isEmpty()) {
throw new IllegalArgumentException("Unknown side: " + spec.getDistribution() + " For Config: " + definition.getSpecification().getNeoFormArchive());
throw new IllegalArgumentException("Unknown side: " + spec.getDistribution() + " for NeoForm " + definition.getSpecification().getNeoFormVersion());
}

final LinkedHashMap<String, TaskProvider<? extends WithOutput>> taskOutputs = definition.getTasks();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
import org.gradle.api.artifacts.Dependency;
import org.gradle.api.artifacts.ResolvedArtifact;
import org.gradle.api.file.FileCollection;
import org.gradle.api.provider.Provider;
import org.jetbrains.annotations.NotNull;

import java.io.File;
Expand All @@ -26,26 +27,30 @@
*/
public class NeoFormRuntimeSpecification extends CommonRuntimeSpecification implements NeoFormSpecification {
private final File neoFormArchive;
private final NeoFormConfigConfigurationSpecV2 spec;
private final NeoFormConfigConfigurationSpecV2 config;
private final FileCollection additionalRecompileDependencies;

private NeoFormRuntimeSpecification(Project project,
String version,
File neoFormArchive,
NeoFormConfigConfigurationSpecV2 spec,
NeoFormConfigConfigurationSpecV2 config,
DistributionType side,
Multimap<String, TaskTreeAdapter> preTaskTypeAdapters,
Multimap<String, TaskTreeAdapter> postTypeAdapters,
Multimap<String, TaskCustomizer<? extends Task>> taskCustomizers,
FileCollection additionalRecompileDependencies) {
super(project, "neoForm", version, side, preTaskTypeAdapters, postTypeAdapters, taskCustomizers, NeoFormRuntimeExtension.class);
this.neoFormArchive = neoFormArchive;
this.spec = spec;
this.config = config;
this.additionalRecompileDependencies = additionalRecompileDependencies;
}

public NeoFormConfigConfigurationSpecV2 getConfig() {
return config;
}

public String getMinecraftVersion() {
return spec.getVersion();
return config.getVersion();
}

public String getNeoFormVersion() {
Expand Down Expand Up @@ -127,9 +132,9 @@ public NeoFormRuntimeSpecification build() {
String effectiveVersion = artifact.getModuleVersion().getId().getVersion();

// Read the NF config from the archive
NeoFormConfigConfigurationSpecV2 spec;
NeoFormConfigConfigurationSpecV2 config;
try {
spec = FileUtils.processFileFromZip(archive, "config.json", NeoFormConfigConfigurationSpecV2::get);
config = FileUtils.processFileFromZip(archive, "config.json", NeoFormConfigConfigurationSpecV2::get);
} catch (IOException e) {
throw new GradleException("Failed to read NeoForm config file from version " + effectiveVersion);
}
Expand All @@ -138,7 +143,7 @@ public NeoFormRuntimeSpecification build() {
project,
effectiveVersion,
archive,
spec,
config,
distributionType.get(),
preTaskAdapters,
postTaskAdapters,
Expand Down
Loading

0 comments on commit 1c025a2

Please sign in to comment.