Skip to content
Draft
Show file tree
Hide file tree
Changes from all 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
28 changes: 28 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,34 @@ dependencies {
}
```

#### <a id="userdev-enum-extensions" /> Enum Extensions
The userdev plugin provides a way to configure enum extensions for your mod.
This allows you to reference runtime enum extensions at compile time, so you can include them in switch statements or the like.
Note that you still need to provide the extensions to FML at runtime in your `neoforge.mods.toml`.
```groovy
enumExtensions {
file 'src/main/resources/META-INF/enumextensions.json'
}
```
You can find more information on the format of the file [here](https://docs.neoforged.net/docs/advanced/extensibleenums/).

##### <a id="userdev-enum-extensions-from-dependencies" /> From Dependencies
When you want to include enum extension entries from a dependency you can do so by using the `consume` and `consumeApi` dependency collector:
```groovy
enumExtensions {
consume 'net.something.group:module:1.0.0-version' //Use the enum extensions published by this dependency.
consumeApi 'net.something.group:module:1.0.0-version' //Use the enum extensions published by this dependency, and expose it for your consumers as a dependency as well.
}
```

Alternatively you can use the configurations, `enumExtensions` or `enumExtensionsApi` respectively, to handle this:
```groovy
dependencies {
enumExtensions 'net.something.group:module:1.0.0-version' //Use the enum extensions published by this dependency.
enumExtensionsApi 'net.something.group:module:1.0.0-version' //Use the enum extensions published by this dependency, and expose it for your consumers as a dependency as well.
}
```

#### 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.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import net.neoforged.gradle.common.accesstransformers.AccessTransformerPublishing;
import net.neoforged.gradle.common.conventions.ConventionConfigurator;
import net.neoforged.gradle.common.dependency.ExtraJarDependencyManager;
import net.neoforged.gradle.common.enumextensions.EnumExtensionsPublishing;
import net.neoforged.gradle.common.extensions.*;
import net.neoforged.gradle.common.extensions.dependency.replacement.ReplacementLogic;
import net.neoforged.gradle.common.extensions.problems.IProblemReporter;
Expand Down Expand Up @@ -92,6 +93,7 @@ public void apply(Project project) {
project.getExtensions().create(DependencyReplacement.class, "dependencyReplacements", ReplacementLogic.class, project);
project.getExtensions().create(AccessTransformers.class, "accessTransformers", AccessTransformersExtension.class, project);
project.getExtensions().create(InterfaceInjections.class, "interfaceInjections", InterfaceInjectionsExtension.class, project);
project.getExtensions().create(EnumExtensions.class, "enumExtensions", EnumExtensionsExtension.class, project);

project.getExtensions().create(Minecraft.class, "minecraft", MinecraftExtension.class, project);
project.getExtensions().create(Mappings.class,"mappings", MappingsExtension.class, project);
Expand Down Expand Up @@ -143,6 +145,9 @@ public void apply(Project project) {

//Set up publishing for interface injection elements
InterfaceInjectionPublishing.setup(project);

// Set up publishing for enum extensions
EnumExtensionsPublishing.setup(project);

//Set up the IDE run integration manager
IdeRunIntegrationManager.getInstance().setup(project);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
package net.neoforged.gradle.common.enumextensions;

import net.neoforged.gradle.common.util.ProjectUtils;
import net.neoforged.gradle.dsl.common.extensions.EnumExtensions;
import org.gradle.api.Action;
import org.gradle.api.Project;
import org.gradle.api.artifacts.Configuration;
import org.gradle.api.attributes.AttributeContainer;
import org.gradle.api.attributes.Category;
import org.gradle.api.component.AdhocComponentWithVariants;

import java.io.File;
import java.util.Comparator;
import java.util.List;

public class EnumExtensionsPublishing {

public static final String ENUM_EXTENSIONS_ELEMENTS_CONFIGURATION = "enumExtensionsElements";
public static final String ENUM_EXTENSIONS_API_CONFIGURATION = "enumExtensionsApi";
public static final String ENUM_EXTENSIONS_CONFIGURATION = "enumExtensions";
public static final String ENUM_EXTENSIONS_CATEGORY = "enumExtensions";

@SuppressWarnings("UnstableApiUsage")
public static void setup(Project project) {
EnumExtensions extension = project.getExtensions().getByType(EnumExtensions.class);

Configuration elementsConfig = project.getConfigurations().maybeCreate(ENUM_EXTENSIONS_ELEMENTS_CONFIGURATION);
Configuration apiConfig = project.getConfigurations().maybeCreate(ENUM_EXTENSIONS_API_CONFIGURATION);
Configuration implementationConfig = project.getConfigurations().maybeCreate(ENUM_EXTENSIONS_CONFIGURATION);

apiConfig.setCanBeConsumed(false);
apiConfig.setCanBeResolved(false);

implementationConfig.setCanBeConsumed(false);
implementationConfig.setCanBeResolved(true);

elementsConfig.setCanBeConsumed(true);
elementsConfig.setCanBeResolved(false);
elementsConfig.setCanBeDeclared(false);

Action<AttributeContainer> action = attributes -> {
attributes.attribute(Category.CATEGORY_ATTRIBUTE, project.getObjects().named(Category.class, ENUM_EXTENSIONS_CATEGORY));
};

elementsConfig.attributes(action);
implementationConfig.attributes(action);

implementationConfig.extendsFrom(apiConfig);
elementsConfig.extendsFrom(apiConfig);

// Now we set up the component, conditionally
AdhocComponentWithVariants java = (AdhocComponentWithVariants) project.getComponents().getByName("java");
Runnable enable = () -> java.addVariantsFromConfiguration(elementsConfig, variant -> {
});

elementsConfig.getAllDependencies().configureEach(dep -> {
enable.run();
});
elementsConfig.getArtifacts().configureEach(artifact -> enable.run());

// And add resolved iis to the extension
extension.getFiles().from(implementationConfig);

//When the user has configured the dependency collectors add the relevant files.
ProjectUtils.afterEvaluate(project, () -> {
apiConfig.fromDependencyCollector(extension.getConsumeApi());
implementationConfig.fromDependencyCollector(extension.getConsume());

final List<File> files = extension.getFiles().getFiles().stream()
.sorted(Comparator.comparing(File::getName))
.toList();
for (int i = 0; i < files.size(); i++)
{
final var file = files.get(i);
if (files.size() == 1) {
extension.expose(file, artifact -> artifact.setClassifier("enum-extensions"));
} else {
final int index = i;
extension.expose(file, artifact -> artifact.setClassifier("enum-extensions-%d".formatted(index)));
}
}
});
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
package net.neoforged.gradle.common.extensions;

import net.neoforged.gradle.common.enumextensions.EnumExtensionsPublishing;
import net.neoforged.gradle.common.extensions.problems.IProblemReporter;
import net.neoforged.gradle.dsl.common.extensions.EnumExtensions;
import org.gradle.api.Action;
import org.gradle.api.Project;
import org.gradle.api.artifacts.ConfigurablePublishArtifact;
import org.gradle.api.artifacts.Dependency;
import org.gradle.api.artifacts.dsl.ArtifactHandler;
import org.gradle.api.artifacts.dsl.DependencyHandler;

import javax.inject.Inject;

public abstract class EnumExtensionsExtension implements EnumExtensions {
private transient final DependencyHandler projectDependencies;
private transient final ArtifactHandler projectArtifacts;

private final Project project;

@Inject
public EnumExtensionsExtension(final Project project) {
this.project = project;

this.projectDependencies = project.getDependencies();
this.projectArtifacts = project.getArtifacts();
}

@Override
public Project getProject() {
return project;
}

@Override
public void expose(Object path, Action<ConfigurablePublishArtifact> action) {
getFiles().from(path);
projectArtifacts.add(EnumExtensionsPublishing.ENUM_EXTENSIONS_ELEMENTS_CONFIGURATION, path, action);
}

@Override
public void expose(Object path) {
expose(path, artifacts -> {
});
}

@SuppressWarnings("removal")
@Override
public void expose(Dependency dependency) {
project.getExtensions().getByType(IProblemReporter.class)
.reporting(
spec -> spec.id("enum-extensions", "expose.deprecated")
.details("Using the expose(Dependency) method is deprecated.")
.solution("Use the dependency collectors: 'consume' and 'consumeApi' for adding enum extensions from your dependencies.")
.section("userdev-enum-extensions-from-dependencies")
.contextualLabel("Deprecations"),
project.getLogger()
);

projectDependencies.add(EnumExtensionsPublishing.ENUM_EXTENSIONS_API_CONFIGURATION, dependency);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
import net.neoforged.gdi.ConfigurableDSLElement;
import net.neoforged.gradle.common.runtime.naming.NamingChannelProvider;
import net.neoforged.gradle.dsl.common.extensions.AccessTransformers;
import net.neoforged.gradle.dsl.common.extensions.EnumExtensions;
import net.neoforged.gradle.dsl.common.extensions.InterfaceInjections;
import net.neoforged.gradle.dsl.common.extensions.Mappings;
import net.neoforged.gradle.dsl.common.extensions.Minecraft;
Expand All @@ -38,13 +39,15 @@ public abstract class MinecraftExtension implements ConfigurableDSLElement<Minec
private final Project project;
private final AccessTransformers accessTransformers;
private final InterfaceInjections interfaceInjections;
private final EnumExtensions enumExtensions;
private final NamedDomainObjectContainer<NamingChannel> namingChannelProviders;

@Inject
public MinecraftExtension(final Project project) {
this.project = project;
this.accessTransformers = project.getExtensions().getByType(AccessTransformers.class);
this.interfaceInjections = project.getExtensions().getByType(InterfaceInjections.class);
this.enumExtensions = project.getExtensions().getByType(EnumExtensions.class);
this.namingChannelProviders = project.getObjects().domainObjectContainer(NamingChannel.class, name -> project.getObjects().newInstance(NamingChannelProvider.class, project, name));

final String baseName = project.getName().replace(":", "_");
Expand Down Expand Up @@ -85,4 +88,10 @@ public AccessTransformers getAccessTransformers() {
public InterfaceInjections getInterfaceInjections() {
return interfaceInjections;
}

@NotNull
@Override
public EnumExtensions getEnumExtensions() {
return enumExtensions;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import com.google.common.collect.Lists;
import net.neoforged.gradle.common.services.caching.jobs.ICacheableJob;
import net.neoforged.gradle.common.util.EnumExtensionUtils;
import net.neoforged.gradle.common.util.ToolUtilities;
import net.neoforged.gradle.dsl.common.extensions.subsystems.Subsystems;
import net.neoforged.gradle.util.CopyingFileTreeVisitor;
Expand Down Expand Up @@ -66,6 +67,28 @@ public JavaSourceTransformer() {
args.add("--interface-injection-stubs");
args.add(stubsFile.getAbsolutePath());
}

if (!getEnumExtensions().isEmpty()) {
final File stubsFile = ensureFileWorkspaceReady(getStubs());

args.add("--enable-enum-extensions");
getEnumExtensions().forEach(f -> {
args.add("--enum-extension-data");
args.add(f.getAbsolutePath());
});

args.add("--enum-extension-stubs");
args.add(stubsFile.getAbsolutePath());

args.add("--enum-extensions-required-interface");
args.add(EnumExtensionUtils.REQUIRED_INTERFACE);
args.add("--enum-extensions-indexed-enum-annotation");
args.add(EnumExtensionUtils.INDEXED_ENUM);
args.add("--enum-extensions-marker");
args.add(EnumExtensionUtils.MARKER_ANNOTATION);
args.add("--enum-extensions-reserved-constructor-annotation");
args.add(EnumExtensionUtils.RESERVED_CONSTRUCTOR);
}

if (!getParchmentMappings().isEmpty()) {
final File parchment = getParchmentMappings().getSingleFile();
Expand Down Expand Up @@ -133,6 +156,7 @@ public void doExecute() throws Exception {
//We need a separate check here that skips the execute call if there are no transformers.
if (getTransformers().isEmpty() &&
getInterfaceInjections().isEmpty() &&
getEnumExtensions().isEmpty() &&
getParchmentMappings().isEmpty()) {

//Unpack the input zip into the outputs:
Expand Down Expand Up @@ -196,6 +220,11 @@ private void pack() throws IOException
@Optional
@PathSensitive(PathSensitivity.NONE)
public abstract ConfigurableFileCollection getInterfaceInjections();

@InputFiles
@Optional
@PathSensitive(PathSensitivity.NONE)
public abstract ConfigurableFileCollection getEnumExtensions();

@InputFiles
@Optional
Expand Down
Loading
Loading