mirror of
https://github.com/neoforged/NeoForge.git
synced 2025-12-10 00:22:25 -06:00
Use an in-tree Gradle plugin instead of an external Gradle Plugin to make version-specific changes easier (#1485)
Co-authored-by: Sebastian Hartte <shartte@users.noreply.github.com>
This commit is contained in:
parent
9480753414
commit
16b2d4d7cc
2
.github/workflows/check-local-changes.yml
vendored
2
.github/workflows/check-local-changes.yml
vendored
@ -41,7 +41,7 @@ jobs:
|
||||
run: ./gradlew generatePackageInfos
|
||||
|
||||
- name: Gen patches
|
||||
run: ./gradlew :neoforge:unpackSourcePatches
|
||||
run: ./gradlew :neoforge:genPatches
|
||||
|
||||
- name: Run datagen with Gradle
|
||||
run: ./gradlew :neoforge:runData :tests:runData
|
||||
|
||||
19
build.gradle
19
build.gradle
@ -4,7 +4,7 @@ import java.util.regex.Pattern
|
||||
plugins {
|
||||
id 'net.neoforged.gradleutils' version '3.0.0'
|
||||
id 'com.diffplug.spotless' version '6.22.0' apply false
|
||||
id 'net.neoforged.licenser' version '0.7.2'
|
||||
id 'net.neoforged.licenser' version '0.7.5'
|
||||
id 'neoforge.formatting-conventions'
|
||||
id 'neoforge.versioning'
|
||||
}
|
||||
@ -23,19 +23,22 @@ System.out.println("NeoForge version ${project.version}")
|
||||
allprojects {
|
||||
version rootProject.version
|
||||
group 'net.neoforged'
|
||||
repositories {
|
||||
mavenLocal()
|
||||
}
|
||||
}
|
||||
|
||||
subprojects {
|
||||
apply plugin: 'java'
|
||||
|
||||
java.toolchain.languageVersion.set(JavaLanguageVersion.of(project.java_version))
|
||||
}
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
// Remove src/ sources from the root project. They are used in the neoforge subproject.
|
||||
sourceSets {
|
||||
main {
|
||||
java {
|
||||
srcDirs = []
|
||||
}
|
||||
resources {
|
||||
srcDirs = []
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Put licenser here otherwise it tries to license all source sets including decompiled MC sources
|
||||
|
||||
81
buildSrc/README.md
Normal file
81
buildSrc/README.md
Normal file
@ -0,0 +1,81 @@
|
||||
# NeoForge Development Gradle Plugin
|
||||
|
||||
## NeoForge Project Structure
|
||||
|
||||
Before understanding the `buildSrc` plugin, one should understand the structure of the NeoForge Gradle project it is
|
||||
applied to.
|
||||
|
||||
The project consists of a project tree with the following structure:
|
||||
|
||||
| Folder Path | Gradle Project Path | Applied Plugins | Description |
|
||||
|------------------------------------------------------------------------|----------------------|:------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| [`/build.gradle`](../build.gradle) | `:` | — | The root project. Since this project is reused for Kits, the root project name is based on the checkout folder, which actually can lead to issues if it is called `NeoForge`. |
|
||||
| [`/projects/neoforge/build.gradle`](../projects/neoforge/build.gradle) | `:neoforge` | [NeoDevPlugin](#neodevplugin) | The core NeoForge project, which produces the artifacts that will be published. |
|
||||
| [`/projects/base/build.gradle`](../projects/base/build.gradle) | `:base` | [NeoDevBasePlugin](#neodevbaseplugin) | A utility project that contains the Minecraft sources without any NeoForge additions. Can be used to quickly compare what NeoForge has changed. |
|
||||
| [`/tests/build.gradle`](../tests/build.gradle) | `:tests` | [NeoDevExtraPlugin](#neodevextraplugin) | Contains the game and unit tests for NeoForge. |
|
||||
| [`/testframework/build.gradle`](../testframework/build.gradle) | `:testframework` | [MinecraftDependenciesPlugin](#minecraftdependenciesplugin) | A library providing support classes around writing game tests. |
|
||||
| [`/coremods/build.gradle`](../coremods/build.gradle) | `:neoforge-coremods` | — | Java Bytecode transformers that are embedded into NeoForge as a nested Jar file. |
|
||||
|
|
||||
|
||||
## Plugins
|
||||
|
||||
### NeoDevBasePlugin
|
||||
|
||||
Sources: [NeoDevBasePlugin.java](src/main/java/net/neoforged/neodev/NeoDevBasePlugin.java)
|
||||
|
||||
Implicitly applies: [MinecraftDependenciesPlugin](#minecraftdependenciesplugin).
|
||||
|
||||
Sets up a `setup` task that reuses code from [NeoDevPlugin](#neodevplugin) to decompile Minecraft and place the
|
||||
decompiled sources in `projects/base/src/main/java`.
|
||||
|
||||
### NeoDevPlugin
|
||||
|
||||
Sources: [NeoDevPlugin.java](src/main/java/net/neoforged/neodev/NeoDevPlugin.java)
|
||||
|
||||
Implicitly applies: [MinecraftDependenciesPlugin](#minecraftdependenciesplugin).
|
||||
|
||||
This is the primary of this repository and is used to configure the `neoforge` subproject.
|
||||
|
||||
#### Setup
|
||||
|
||||
It creates a `setup` task that performs the following actions via various subtasks:
|
||||
|
||||
- Decompile Minecraft using the [NeoForm Runtime](https://github.com/neoforged/neoformruntime) and Minecraft version specific [NeoForm data](https://github.com/neoforged/NeoForm).
|
||||
- Applies [Access Transformers](../src/main/resources/META-INF/accesstransformer.cfg) to Minecraft sources.
|
||||
- Applies [NeoForge patches](../patches) to Minecraft sources. Any rejects are saved to the `/rejects` folder in the repository for manual inspection. During updates to new versions, the task can be run with `-Pupdating=true` to apply patches more leniently.
|
||||
- Unpacks the patched sources to `projects/neoforge/src/main/java`.
|
||||
|
||||
#### Config Generation
|
||||
|
||||
The plugin creates and configures the tasks to create various configuration files used downstream to develop
|
||||
mods with this version of NeoForge ([CreateUserDevConfig](src/main/java/net/neoforged/neodev/CreateUserDevConfig.java)), or install it ([CreateInstallerProfile](src/main/java/net/neoforged/neodev/installer/CreateInstallerProfile.java) and [CreateLauncherProfile](src/main/java/net/neoforged/neodev/installer/CreateLauncherProfile.java)).
|
||||
|
||||
A separate userdev profile is created for use by other subprojects in this repository.
|
||||
The only difference is that it uses the FML launch types ending in `dev` rather than `userdev`.
|
||||
|
||||
#### Patch Generation
|
||||
|
||||
NeoForge injects its hooks into Minecraft by patching the decompiled source code.
|
||||
Changes are made locally to the decompiled and patched source.
|
||||
Since that source cannot be published, patches need to be generated before checking in.
|
||||
The plugin configures the necessary task to do this
|
||||
([GenerateSourcePatches](src/main/java/net/neoforged/neodev/GenerateSourcePatches.java)).
|
||||
|
||||
The source patches are only used during development of NeoForge itself and development of mods that use Gradle plugins implementing the decompile/patch/recompile pipeline.
|
||||
For use by the installer intended for players as well as Gradle plugins wanting to replicate the production artifacts more closely, binary patches are generated using the ([GenerateBinaryPatches](src/main/java/net/neoforged/neodev/GenerateBinaryPatches.java)) task.
|
||||
|
||||
### NeoDevExtraPlugin
|
||||
|
||||
Sources: [NeoDevExtraPlugin.java](src/main/java/net/neoforged/neodev/NeoDevExtraPlugin.java)
|
||||
|
||||
This plugin can be applied to obtain a dependency on the `neoforge` project to depend on NeoForge including Minecraft
|
||||
itself. Besides wiring up the dependency, it also creates run configurations based on the run-types defined in the
|
||||
`neoforge` project.
|
||||
|
||||
### MinecraftDependenciesPlugin
|
||||
|
||||
This plugin is reused from [ModDevGradle](https://github.com/neoforged/ModDevGradle/).
|
||||
|
||||
It sets up repositories and attributes such that
|
||||
the [libraries that Minecraft itself depends upon](https://github.com/neoforged/GradleMinecraftDependencies) can be
|
||||
used.
|
||||
@ -1,3 +1,26 @@
|
||||
plugins {
|
||||
id 'java-gradle-plugin'
|
||||
id 'groovy-gradle-plugin'
|
||||
}
|
||||
|
||||
repositories {
|
||||
gradlePluginPortal()
|
||||
mavenCentral()
|
||||
maven {
|
||||
name = "NeoForged"
|
||||
url = "https://maven.neoforged.net/releases"
|
||||
content {
|
||||
includeGroup "codechicken"
|
||||
includeGroup "net.neoforged"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
// buildSrc is an includedbuild of the parent directory (gradle.parent)
|
||||
// ../settings.gradle sets these version properties accordingly
|
||||
implementation "net.neoforged:moddev-gradle:${gradle.parent.ext.moddevgradle_plugin_version}"
|
||||
|
||||
implementation "com.google.code.gson:gson:${gradle.parent.ext.gson_version}"
|
||||
implementation "io.codechicken:DiffPatch:${gradle.parent.ext.diffpatch_version}"
|
||||
}
|
||||
|
||||
0
buildSrc/settings.gradle
Normal file
0
buildSrc/settings.gradle
Normal file
@ -2,9 +2,14 @@ import java.util.regex.Matcher
|
||||
|
||||
project.plugins.apply('com.diffplug.spotless')
|
||||
|
||||
final generatePackageInfos = tasks.register('generatePackageInfos', Task) {
|
||||
doLast {
|
||||
fileTree('src/main/java').each { javaFile ->
|
||||
abstract class GeneratePackageInfos extends DefaultTask {
|
||||
@InputFiles
|
||||
@PathSensitive(PathSensitivity.RELATIVE)
|
||||
abstract ConfigurableFileCollection getFiles();
|
||||
|
||||
@TaskAction
|
||||
void generatePackageInfos() {
|
||||
getFiles().each { javaFile ->
|
||||
def packageInfoFile = new File(javaFile.parent, 'package-info.java')
|
||||
if (!packageInfoFile.exists()) {
|
||||
def pkgName = javaFile.toString().replaceAll(Matcher.quoteReplacement(File.separator), '/')
|
||||
@ -27,6 +32,9 @@ final generatePackageInfos = tasks.register('generatePackageInfos', Task) {
|
||||
}
|
||||
}
|
||||
}
|
||||
final generatePackageInfos = tasks.register('generatePackageInfos', GeneratePackageInfos) {
|
||||
it.files.from fileTree("src/main/java")
|
||||
}
|
||||
|
||||
spotless {
|
||||
java {
|
||||
|
||||
@ -0,0 +1,72 @@
|
||||
package net.neoforged.neodev;
|
||||
|
||||
import net.neoforged.neodev.utils.FileUtils;
|
||||
import org.gradle.api.file.ConfigurableFileCollection;
|
||||
import org.gradle.api.file.RegularFileProperty;
|
||||
import org.gradle.api.provider.Property;
|
||||
import org.gradle.api.tasks.Classpath;
|
||||
import org.gradle.api.tasks.Input;
|
||||
import org.gradle.api.tasks.InputFile;
|
||||
import org.gradle.api.tasks.Internal;
|
||||
import org.gradle.api.tasks.JavaExec;
|
||||
import org.gradle.api.tasks.OutputFile;
|
||||
import org.gradle.api.tasks.TaskAction;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.UncheckedIOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
/**
|
||||
* Runs <a href="https://github.com/neoforged/JavaSourceTransformer">JavaSourceTransformer</a> to apply
|
||||
* access transformers to the Minecraft source code for extending the access level of existing classes/methods/etc.
|
||||
* <p>
|
||||
* Note that at runtime, FML also applies access transformers.
|
||||
*/
|
||||
abstract class ApplyAccessTransformer extends JavaExec {
|
||||
@InputFile
|
||||
public abstract RegularFileProperty getInputJar();
|
||||
|
||||
@InputFile
|
||||
public abstract RegularFileProperty getAccessTransformer();
|
||||
|
||||
@Input
|
||||
public abstract Property<Boolean> getValidate();
|
||||
|
||||
@OutputFile
|
||||
public abstract RegularFileProperty getOutputJar();
|
||||
|
||||
// Used to give JST more information about the classes.
|
||||
@Classpath
|
||||
public abstract ConfigurableFileCollection getLibraries();
|
||||
|
||||
@Internal
|
||||
public abstract RegularFileProperty getLibrariesFile();
|
||||
|
||||
@Inject
|
||||
public ApplyAccessTransformer() {}
|
||||
|
||||
@Override
|
||||
@TaskAction
|
||||
public void exec() {
|
||||
try {
|
||||
FileUtils.writeLinesSafe(
|
||||
getLibrariesFile().getAsFile().get().toPath(),
|
||||
getLibraries().getFiles().stream().map(File::getAbsolutePath).toList(),
|
||||
StandardCharsets.UTF_8);
|
||||
} catch (IOException exception) {
|
||||
throw new UncheckedIOException("Failed to write libraries for JST.", exception);
|
||||
}
|
||||
|
||||
args(
|
||||
"--enable-accesstransformers",
|
||||
"--access-transformer", getAccessTransformer().getAsFile().get().getAbsolutePath(),
|
||||
"--access-transformer-validation", getValidate().get() ? "error" : "log",
|
||||
"--libraries-list", getLibrariesFile().getAsFile().get().getAbsolutePath(),
|
||||
getInputJar().getAsFile().get().getAbsolutePath(),
|
||||
getOutputJar().getAsFile().get().getAbsolutePath());
|
||||
|
||||
super.exec();
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,79 @@
|
||||
package net.neoforged.neodev;
|
||||
|
||||
import io.codechicken.diffpatch.cli.PatchOperation;
|
||||
import io.codechicken.diffpatch.util.Input.MultiInput;
|
||||
import io.codechicken.diffpatch.util.Output.MultiOutput;
|
||||
import io.codechicken.diffpatch.util.PatchMode;
|
||||
import org.gradle.api.DefaultTask;
|
||||
import org.gradle.api.Project;
|
||||
import org.gradle.api.file.DirectoryProperty;
|
||||
import org.gradle.api.file.RegularFileProperty;
|
||||
import org.gradle.api.provider.Property;
|
||||
import org.gradle.api.tasks.Input;
|
||||
import org.gradle.api.tasks.InputDirectory;
|
||||
import org.gradle.api.tasks.InputFile;
|
||||
import org.gradle.api.tasks.OutputDirectory;
|
||||
import org.gradle.api.tasks.OutputFile;
|
||||
import org.gradle.api.tasks.PathSensitive;
|
||||
import org.gradle.api.tasks.PathSensitivity;
|
||||
import org.gradle.api.tasks.TaskAction;
|
||||
import org.gradle.work.DisableCachingByDefault;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* Applies Java source patches to a source jar and produces a patched source jar as an output.
|
||||
* It can optionally store rejected hunks into a given folder, which is primarily used for updating
|
||||
* when the original sources changed and some hunks are expected to fail.
|
||||
*/
|
||||
@DisableCachingByDefault(because = "Not worth caching")
|
||||
abstract class ApplyPatches extends DefaultTask {
|
||||
@InputFile
|
||||
public abstract RegularFileProperty getOriginalJar();
|
||||
|
||||
@InputDirectory
|
||||
@PathSensitive(PathSensitivity.NONE)
|
||||
public abstract DirectoryProperty getPatchesFolder();
|
||||
|
||||
@OutputFile
|
||||
public abstract RegularFileProperty getPatchedJar();
|
||||
|
||||
@OutputDirectory
|
||||
public abstract DirectoryProperty getRejectsFolder();
|
||||
|
||||
@Input
|
||||
protected abstract Property<Boolean> getIsUpdating();
|
||||
|
||||
@Inject
|
||||
public ApplyPatches(Project project) {
|
||||
getIsUpdating().set(project.getProviders().gradleProperty("updating").map(Boolean::parseBoolean).orElse(false));
|
||||
}
|
||||
|
||||
@TaskAction
|
||||
public void applyPatches() throws IOException {
|
||||
var isUpdating = getIsUpdating().get();
|
||||
|
||||
var builder = PatchOperation.builder()
|
||||
.logTo(getLogger()::lifecycle)
|
||||
.baseInput(MultiInput.detectedArchive(getOriginalJar().get().getAsFile().toPath()))
|
||||
.patchesInput(MultiInput.folder(getPatchesFolder().get().getAsFile().toPath()))
|
||||
.patchedOutput(MultiOutput.detectedArchive(getPatchedJar().get().getAsFile().toPath()))
|
||||
.rejectsOutput(MultiOutput.folder(getRejectsFolder().get().getAsFile().toPath()))
|
||||
.mode(isUpdating ? PatchMode.FUZZY : PatchMode.ACCESS)
|
||||
.aPrefix("a/")
|
||||
.bPrefix("b/")
|
||||
.level(isUpdating ? io.codechicken.diffpatch.util.LogLevel.ALL : io.codechicken.diffpatch.util.LogLevel.WARN)
|
||||
.minFuzz(0.9f); // The 0.5 default in DiffPatch is too low.
|
||||
|
||||
var result = builder.build().operate();
|
||||
|
||||
int exit = result.exit;
|
||||
if (exit != 0 && exit != 1) {
|
||||
throw new RuntimeException("DiffPatch failed with exit code: " + exit);
|
||||
}
|
||||
if (exit != 0 && !isUpdating) {
|
||||
throw new RuntimeException("Patches failed to apply.");
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,36 @@
|
||||
package net.neoforged.neodev;
|
||||
|
||||
import net.neoforged.nfrtgradle.CreateMinecraftArtifacts;
|
||||
import org.gradle.api.file.RegularFileProperty;
|
||||
import org.gradle.api.tasks.OutputFile;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
abstract class CreateCleanArtifacts extends CreateMinecraftArtifacts {
|
||||
@OutputFile
|
||||
abstract RegularFileProperty getCleanClientJar();
|
||||
|
||||
@OutputFile
|
||||
abstract RegularFileProperty getRawServerJar();
|
||||
|
||||
@OutputFile
|
||||
abstract RegularFileProperty getCleanServerJar();
|
||||
|
||||
@OutputFile
|
||||
abstract RegularFileProperty getCleanJoinedJar();
|
||||
|
||||
@OutputFile
|
||||
abstract RegularFileProperty getMergedMappings();
|
||||
|
||||
@Inject
|
||||
public CreateCleanArtifacts() {
|
||||
getAdditionalResults().put("node.stripClient.output.output", getCleanClientJar().getAsFile());
|
||||
getAdditionalResults().put("node.downloadServer.output.output", getRawServerJar().getAsFile());
|
||||
getAdditionalResults().put("node.stripServer.output.output", getCleanServerJar().getAsFile());
|
||||
getAdditionalResults().put("node.rename.output.output", getCleanJoinedJar().getAsFile());
|
||||
getAdditionalResults().put("node.mergeMappings.output.output", getMergedMappings().getAsFile());
|
||||
|
||||
// TODO: does anyone care about this? they should be contained in the client mappings
|
||||
//"--write-result", "node.downloadServerMappings.output.output:" + getServerMappings().get().getAsFile().getAbsolutePath()
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,203 @@
|
||||
package net.neoforged.neodev;
|
||||
|
||||
import com.google.gson.GsonBuilder;
|
||||
import net.neoforged.neodev.utils.FileUtils;
|
||||
import org.gradle.api.DefaultTask;
|
||||
import org.gradle.api.file.RegularFileProperty;
|
||||
import org.gradle.api.provider.ListProperty;
|
||||
import org.gradle.api.provider.Property;
|
||||
import org.gradle.api.tasks.Input;
|
||||
import org.gradle.api.tasks.OutputFile;
|
||||
import org.gradle.api.tasks.TaskAction;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Creates the userdev configuration file used by the various Gradle plugins used to develop
|
||||
* mods for NeoForge, such as <a href="https://github.com/architectury/architectury-loom">Architectury Loom</a>,
|
||||
* <a href="https://github.com/neoforged/ModDevGradle/">ModDevGradle
|
||||
* or <a href="https://github.com/neoforged/NeoGradle">NeoGradle</a>.
|
||||
*/
|
||||
abstract class CreateUserDevConfig extends DefaultTask {
|
||||
@Inject
|
||||
public CreateUserDevConfig() {}
|
||||
|
||||
/**
|
||||
* Toggles the launch type written to the userdev configuration between *dev and *userdev.
|
||||
*/
|
||||
@Input
|
||||
abstract Property<Boolean> getForNeoDev();
|
||||
|
||||
@Input
|
||||
abstract Property<String> getFmlVersion();
|
||||
|
||||
@Input
|
||||
abstract Property<String> getMinecraftVersion();
|
||||
|
||||
@Input
|
||||
abstract Property<String> getNeoForgeVersion();
|
||||
|
||||
@Input
|
||||
abstract Property<String> getRawNeoFormVersion();
|
||||
|
||||
@Input
|
||||
abstract ListProperty<String> getLibraries();
|
||||
|
||||
@Input
|
||||
abstract ListProperty<String> getModules();
|
||||
|
||||
@Input
|
||||
abstract ListProperty<String> getTestLibraries();
|
||||
|
||||
@Input
|
||||
abstract ListProperty<String> getIgnoreList();
|
||||
|
||||
@Input
|
||||
abstract Property<String> getBinpatcherGav();
|
||||
|
||||
@OutputFile
|
||||
abstract RegularFileProperty getUserDevConfig();
|
||||
|
||||
@TaskAction
|
||||
public void writeUserDevConfig() throws IOException {
|
||||
var config = new UserDevConfig(
|
||||
2,
|
||||
"net.neoforged:neoform:%s-%s@zip".formatted(getMinecraftVersion().get(), getRawNeoFormVersion().get()),
|
||||
"ats/",
|
||||
"joined.lzma",
|
||||
new BinpatcherConfig(
|
||||
getBinpatcherGav().get(),
|
||||
List.of("--clean", "{clean}", "--output", "{output}", "--apply", "{patch}")),
|
||||
"patches/",
|
||||
"net.neoforged:neoforge:%s:sources".formatted(getNeoForgeVersion().get()),
|
||||
"net.neoforged:neoforge:%s:universal".formatted(getNeoForgeVersion().get()),
|
||||
getLibraries().get(),
|
||||
getTestLibraries().get(),
|
||||
new LinkedHashMap<>(),
|
||||
getModules().get());
|
||||
|
||||
for (var runType : RunType.values()) {
|
||||
var launchTarget = switch (runType) {
|
||||
case CLIENT -> "forgeclient";
|
||||
case DATA -> "forgedata";
|
||||
case GAME_TEST_SERVER, SERVER -> "forgeserver";
|
||||
case JUNIT -> "forgejunit";
|
||||
} + (getForNeoDev().get() ? "dev" : "userdev");
|
||||
|
||||
List<String> args = new ArrayList<>();
|
||||
Collections.addAll(args,
|
||||
"--launchTarget", launchTarget);
|
||||
|
||||
if (runType == RunType.CLIENT || runType == RunType.JUNIT) {
|
||||
// TODO: this is copied from NG but shouldn't it be the MC version?
|
||||
Collections.addAll(args,
|
||||
"--version", getNeoForgeVersion().get());
|
||||
}
|
||||
|
||||
if (runType == RunType.CLIENT || runType == RunType.DATA || runType == RunType.JUNIT) {
|
||||
Collections.addAll(args,
|
||||
"--assetIndex", "{asset_index}",
|
||||
"--assetsDir", "{assets_root}");
|
||||
}
|
||||
|
||||
Collections.addAll(args,
|
||||
"--gameDir", ".",
|
||||
"--fml.fmlVersion", getFmlVersion().get(),
|
||||
"--fml.mcVersion", getMinecraftVersion().get(),
|
||||
"--fml.neoForgeVersion", getNeoForgeVersion().get(),
|
||||
"--fml.neoFormVersion", getRawNeoFormVersion().get());
|
||||
|
||||
Map<String, String> systemProperties = new LinkedHashMap<>();
|
||||
systemProperties.put("java.net.preferIPv6Addresses", "system");
|
||||
systemProperties.put("ignoreList", String.join(",", getIgnoreList().get()));
|
||||
systemProperties.put("legacyClassPath.file", "{minecraft_classpath_file}");
|
||||
|
||||
if (runType == RunType.CLIENT || runType == RunType.GAME_TEST_SERVER) {
|
||||
systemProperties.put("neoforge.enableGameTest", "true");
|
||||
|
||||
if (runType == RunType.GAME_TEST_SERVER) {
|
||||
systemProperties.put("neoforge.gameTestServer", "true");
|
||||
}
|
||||
}
|
||||
|
||||
config.runs().put(runType.jsonName, new UserDevRunType(
|
||||
runType != RunType.JUNIT,
|
||||
"cpw.mods.bootstraplauncher.BootstrapLauncher",
|
||||
args,
|
||||
List.of(
|
||||
"-p", "{modules}",
|
||||
"--add-modules", "ALL-MODULE-PATH",
|
||||
"--add-opens", "java.base/java.util.jar=cpw.mods.securejarhandler",
|
||||
"--add-opens", "java.base/java.lang.invoke=cpw.mods.securejarhandler",
|
||||
"--add-exports", "java.base/sun.security.util=cpw.mods.securejarhandler",
|
||||
"--add-exports", "jdk.naming.dns/com.sun.jndi.dns=java.naming"),
|
||||
runType == RunType.CLIENT || runType == RunType.JUNIT,
|
||||
runType == RunType.GAME_TEST_SERVER || runType == RunType.SERVER,
|
||||
runType == RunType.DATA,
|
||||
runType == RunType.CLIENT || runType == RunType.GAME_TEST_SERVER,
|
||||
runType == RunType.JUNIT,
|
||||
Map.of(
|
||||
"MOD_CLASSES", "{source_roots}"),
|
||||
systemProperties
|
||||
));
|
||||
}
|
||||
|
||||
FileUtils.writeStringSafe(
|
||||
getUserDevConfig().getAsFile().get().toPath(),
|
||||
new GsonBuilder().setPrettyPrinting().disableHtmlEscaping().create().toJson(config),
|
||||
// TODO: Not sure what this should be? Most likely the file is ASCII.
|
||||
StandardCharsets.UTF_8);
|
||||
}
|
||||
|
||||
private enum RunType {
|
||||
CLIENT("client"),
|
||||
DATA("data"),
|
||||
GAME_TEST_SERVER("gameTestServer"),
|
||||
SERVER("server"),
|
||||
JUNIT("junit");
|
||||
|
||||
private final String jsonName;
|
||||
|
||||
RunType(String jsonName) {
|
||||
this.jsonName = jsonName;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
record UserDevConfig(
|
||||
int spec,
|
||||
String mcp,
|
||||
String ats,
|
||||
String binpatches,
|
||||
BinpatcherConfig binpatcher,
|
||||
String patches,
|
||||
String sources,
|
||||
String universal,
|
||||
List<String> libraries,
|
||||
List<String> testLibraries,
|
||||
Map<String, UserDevRunType> runs,
|
||||
List<String> modules) {}
|
||||
|
||||
record BinpatcherConfig(
|
||||
String version,
|
||||
List<String> args) {}
|
||||
|
||||
record UserDevRunType(
|
||||
boolean singleInstance,
|
||||
String main,
|
||||
List<String> args,
|
||||
List<String> jvmArgs,
|
||||
boolean client,
|
||||
boolean server,
|
||||
boolean dataGenerator,
|
||||
boolean gameTest,
|
||||
boolean unitTest,
|
||||
Map<String, String> env,
|
||||
Map<String, String> props) {}
|
||||
@ -0,0 +1,70 @@
|
||||
package net.neoforged.neodev;
|
||||
|
||||
import org.gradle.api.GradleException;
|
||||
import org.gradle.api.file.DirectoryProperty;
|
||||
import org.gradle.api.file.RegularFileProperty;
|
||||
import org.gradle.api.tasks.InputDirectory;
|
||||
import org.gradle.api.tasks.InputFile;
|
||||
import org.gradle.api.tasks.JavaExec;
|
||||
import org.gradle.api.tasks.Optional;
|
||||
import org.gradle.api.tasks.OutputFile;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import java.io.BufferedOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
|
||||
abstract class GenerateBinaryPatches extends JavaExec {
|
||||
@Inject
|
||||
public GenerateBinaryPatches() {}
|
||||
|
||||
/**
|
||||
* The jar file containing classes in the base state.
|
||||
*/
|
||||
@InputFile
|
||||
abstract RegularFileProperty getCleanJar();
|
||||
|
||||
/**
|
||||
* The jar file containing classes in the desired target state.
|
||||
*/
|
||||
@InputFile
|
||||
abstract RegularFileProperty getPatchedJar();
|
||||
|
||||
@InputFile
|
||||
abstract RegularFileProperty getMappings();
|
||||
|
||||
/**
|
||||
* This directory of patch files for the Java sources is used as a hint to only diff class files that
|
||||
* supposedly have changed. If it is not set, the tool will diff every .class file instead.
|
||||
*/
|
||||
@InputDirectory
|
||||
@Optional
|
||||
abstract DirectoryProperty getSourcePatchesFolder();
|
||||
|
||||
/**
|
||||
* The location where the LZMA compressed binary patches are written to.
|
||||
*/
|
||||
@OutputFile
|
||||
abstract RegularFileProperty getOutputFile();
|
||||
|
||||
@Override
|
||||
public void exec() {
|
||||
args("--clean", getCleanJar().get().getAsFile().getAbsolutePath());
|
||||
args("--dirty", getPatchedJar().get().getAsFile().getAbsolutePath());
|
||||
args("--srg", getMappings().get().getAsFile().getAbsolutePath());
|
||||
if (getSourcePatchesFolder().isPresent()) {
|
||||
args("--patches", getSourcePatchesFolder().get().getAsFile().getAbsolutePath());
|
||||
}
|
||||
args("--output", getOutputFile().get().getAsFile().getAbsolutePath());
|
||||
|
||||
var logFile = new File(getTemporaryDir(), "console.log");
|
||||
try (var out = new BufferedOutputStream(new FileOutputStream(logFile))) {
|
||||
getLogger().info("Logging binpatcher console output to {}", logFile.getAbsolutePath());
|
||||
setStandardOutput(out);
|
||||
super.exec();
|
||||
} catch (IOException e) {
|
||||
throw new GradleException("Failed to create binary patches.", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,55 @@
|
||||
package net.neoforged.neodev;
|
||||
|
||||
import io.codechicken.diffpatch.cli.CliOperation;
|
||||
import io.codechicken.diffpatch.cli.DiffOperation;
|
||||
import io.codechicken.diffpatch.util.Input.MultiInput;
|
||||
import io.codechicken.diffpatch.util.Output.MultiOutput;
|
||||
import org.gradle.api.DefaultTask;
|
||||
import org.gradle.api.file.DirectoryProperty;
|
||||
import org.gradle.api.file.RegularFileProperty;
|
||||
import org.gradle.api.tasks.InputDirectory;
|
||||
import org.gradle.api.tasks.InputFile;
|
||||
import org.gradle.api.tasks.OutputFile;
|
||||
import org.gradle.api.tasks.PathSensitive;
|
||||
import org.gradle.api.tasks.PathSensitivity;
|
||||
import org.gradle.api.tasks.TaskAction;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import java.io.IOException;
|
||||
|
||||
abstract class GenerateSourcePatches extends DefaultTask {
|
||||
@InputFile
|
||||
public abstract RegularFileProperty getOriginalJar();
|
||||
|
||||
@InputDirectory
|
||||
@PathSensitive(PathSensitivity.RELATIVE)
|
||||
public abstract DirectoryProperty getModifiedSources();
|
||||
|
||||
@OutputFile
|
||||
public abstract RegularFileProperty getPatchesJar();
|
||||
|
||||
@Inject
|
||||
public GenerateSourcePatches() {}
|
||||
|
||||
@TaskAction
|
||||
public void generateSourcePatches() throws IOException {
|
||||
var builder = DiffOperation.builder()
|
||||
.logTo(getLogger()::lifecycle)
|
||||
.baseInput(MultiInput.detectedArchive(getOriginalJar().get().getAsFile().toPath()))
|
||||
.changedInput(MultiInput.folder(getModifiedSources().get().getAsFile().toPath()))
|
||||
.patchesOutput(MultiOutput.detectedArchive(getPatchesJar().get().getAsFile().toPath()))
|
||||
.autoHeader(true)
|
||||
.level(io.codechicken.diffpatch.util.LogLevel.WARN)
|
||||
.summary(false)
|
||||
.aPrefix("a/")
|
||||
.bPrefix("b/")
|
||||
.lineEnding("\n");
|
||||
|
||||
CliOperation.Result<DiffOperation.DiffSummary> result = builder.build().operate();
|
||||
|
||||
int exit = result.exit;
|
||||
if (exit != 0 && exit != 1) {
|
||||
throw new RuntimeException("DiffPatch failed with exit code: " + exit);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,24 @@
|
||||
package net.neoforged.neodev;
|
||||
|
||||
import net.neoforged.minecraftdependencies.MinecraftDependenciesPlugin;
|
||||
import net.neoforged.nfrtgradle.CreateMinecraftArtifacts;
|
||||
import org.gradle.api.Plugin;
|
||||
import org.gradle.api.Project;
|
||||
import org.gradle.api.tasks.Sync;
|
||||
|
||||
public class NeoDevBasePlugin implements Plugin<Project> {
|
||||
@Override
|
||||
public void apply(Project project) {
|
||||
// These plugins allow us to declare dependencies on Minecraft libraries needed to compile the official sources
|
||||
project.getPlugins().apply(MinecraftDependenciesPlugin.class);
|
||||
|
||||
var createSources = NeoDevPlugin.configureMinecraftDecompilation(project);
|
||||
|
||||
project.getTasks().register("setup", Sync.class, task -> {
|
||||
task.setGroup(NeoDevPlugin.GROUP);
|
||||
task.setDescription("Replaces the contents of the base project sources with the unpatched, decompiled Minecraft source code.");
|
||||
task.from(project.zipTree(createSources.flatMap(CreateMinecraftArtifacts::getSourcesArtifact)));
|
||||
task.into(project.file("src/main/java/"));
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,204 @@
|
||||
package net.neoforged.neodev;
|
||||
|
||||
import org.gradle.api.Project;
|
||||
import org.gradle.api.artifacts.Configuration;
|
||||
import org.gradle.api.artifacts.ConfigurationContainer;
|
||||
import org.gradle.api.attributes.Bundling;
|
||||
import org.gradle.api.plugins.JavaPlugin;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* Helper class to keep track of the many {@link Configuration}s used for the {@code neoforge} project.
|
||||
*/
|
||||
class NeoDevConfigurations {
|
||||
static NeoDevConfigurations createAndSetup(Project project) {
|
||||
return new NeoDevConfigurations(project);
|
||||
}
|
||||
|
||||
//
|
||||
// Configurations against which dependencies should be declared ("dependency scopes").
|
||||
//
|
||||
|
||||
/**
|
||||
* Only the NeoForm data zip and the dependencies to run NeoForm.
|
||||
* Does not contain the dependencies to run vanilla Minecraft.
|
||||
*/
|
||||
final Configuration neoFormData;
|
||||
/**
|
||||
* Only the NeoForm dependencies.
|
||||
* These are the dependencies required to run NeoForm-decompiled Minecraft.
|
||||
* Does not contain the dependencies to run the NeoForm process itself.
|
||||
*/
|
||||
final Configuration neoFormDependencies;
|
||||
/**
|
||||
* Libraries used by NeoForge at compilation and runtime.
|
||||
* These will end up on the MC-BOOTSTRAP module layer.
|
||||
*/
|
||||
final Configuration libraries;
|
||||
/**
|
||||
* Libraries used by NeoForge at compilation and runtime that need to be placed on the jvm's module path to end up in the boot layer.
|
||||
* Currently, this only contains the few dependencies that are needed to create the MC-BOOTSTRAP module layer.
|
||||
* (i.e. BootstrapLauncher and its dependencies).
|
||||
*/
|
||||
final Configuration moduleLibraries;
|
||||
/**
|
||||
* Libraries that should be accessible in mod development environments at compilation time only.
|
||||
* Currently, this is only used for MixinExtras, which is already available at runtime via JiJ in the NeoForge universal jar.
|
||||
*/
|
||||
final Configuration userdevCompileOnly;
|
||||
/**
|
||||
* Libraries that should be accessible at runtime in unit tests.
|
||||
* Currently, this only contains the fml-junit test fixtures.
|
||||
*/
|
||||
final Configuration userdevTestFixtures;
|
||||
|
||||
//
|
||||
// Resolvable configurations.
|
||||
//
|
||||
|
||||
/**
|
||||
* Resolved {@link #neoFormData}.
|
||||
* This is used to add NeoForm to the installer libraries.
|
||||
* Only the zip is used (for the mappings), not the NeoForm tools, so it's not transitive.
|
||||
*/
|
||||
final Configuration neoFormDataOnly;
|
||||
/**
|
||||
* Resolvable {@link #neoFormDependencies}.
|
||||
*/
|
||||
final Configuration neoFormClasspath;
|
||||
/**
|
||||
* Resolvable {@link #moduleLibraries}.
|
||||
*/
|
||||
final Configuration modulePath;
|
||||
/**
|
||||
* Userdev dependencies (written to a json file in the userdev jar).
|
||||
* This should contain all of NeoForge's additional dependencies for userdev,
|
||||
* but does not need to include Minecraft or NeoForm's libraries.
|
||||
*/
|
||||
final Configuration userdevClasspath;
|
||||
/**
|
||||
* Resolvable {@link #userdevCompileOnly}, to add these entries to the ignore list of BootstrapLauncher.
|
||||
*/
|
||||
final Configuration userdevCompileOnlyClasspath;
|
||||
/**
|
||||
* Resolvable {@link #userdevTestFixtures}, to write it in the userdev jar.
|
||||
*/
|
||||
final Configuration userdevTestClasspath;
|
||||
/**
|
||||
* Libraries that need to be added to the classpath when launching NeoForge through the launcher.
|
||||
* This contains all dependencies added by NeoForge, but does not include all of Minecraft's libraries.
|
||||
* This is also used to produce the legacy classpath file for server installs.
|
||||
*/
|
||||
final Configuration launcherProfileClasspath;
|
||||
|
||||
//
|
||||
// The configurations for resolution only are declared in the build.gradle file.
|
||||
//
|
||||
|
||||
/**
|
||||
* To download each executable tool, we use a resolvable configuration.
|
||||
* These configurations support both declaration and resolution.
|
||||
*/
|
||||
final Map<Tools, Configuration> toolClasspaths;
|
||||
|
||||
private static Configuration dependencyScope(ConfigurationContainer configurations, String name) {
|
||||
return configurations.create(name, configuration -> {
|
||||
configuration.setCanBeConsumed(false);
|
||||
configuration.setCanBeResolved(false);
|
||||
});
|
||||
}
|
||||
|
||||
private static Configuration resolvable(ConfigurationContainer configurations, String name) {
|
||||
return configurations.create(name, configuration -> {
|
||||
configuration.setCanBeConsumed(false);
|
||||
configuration.setCanBeDeclared(false);
|
||||
});
|
||||
}
|
||||
|
||||
private NeoDevConfigurations(Project project) {
|
||||
var configurations = project.getConfigurations();
|
||||
|
||||
neoFormData = dependencyScope(configurations, "neoFormData");
|
||||
neoFormDependencies = dependencyScope(configurations, "neoFormDependencies");
|
||||
libraries = dependencyScope(configurations, "libraries");
|
||||
moduleLibraries = dependencyScope(configurations, "moduleLibraries");
|
||||
userdevCompileOnly = dependencyScope(configurations, "userdevCompileOnly");
|
||||
userdevTestFixtures = dependencyScope(configurations, "userdevTestFixtures");
|
||||
|
||||
neoFormDataOnly = resolvable(configurations, "neoFormDataOnly");
|
||||
neoFormClasspath = resolvable(configurations, "neoFormClasspath");
|
||||
modulePath = resolvable(configurations, "modulePath");
|
||||
userdevClasspath = resolvable(configurations, "userdevClasspath");
|
||||
userdevCompileOnlyClasspath = resolvable(configurations, "userdevCompileOnlyClasspath");
|
||||
userdevTestClasspath = resolvable(configurations, "userdevTestClasspath");
|
||||
launcherProfileClasspath = resolvable(configurations, "launcherProfileClasspath");
|
||||
|
||||
// Libraries & module libraries & MC dependencies need to be available when compiling in NeoDev,
|
||||
// and on the runtime classpath too for IDE debugging support.
|
||||
configurations.getByName("implementation").extendsFrom(libraries, moduleLibraries, neoFormDependencies);
|
||||
|
||||
// runtimeClasspath is our reference for all MC dependency versions.
|
||||
// Make sure that any classpath we resolve is consistent with it.
|
||||
var runtimeClasspath = configurations.getByName(JavaPlugin.RUNTIME_CLASSPATH_CONFIGURATION_NAME);
|
||||
|
||||
neoFormDataOnly.setTransitive(false);
|
||||
neoFormDataOnly.extendsFrom(neoFormData);
|
||||
|
||||
neoFormClasspath.extendsFrom(neoFormDependencies);
|
||||
|
||||
modulePath.extendsFrom(moduleLibraries);
|
||||
modulePath.shouldResolveConsistentlyWith(runtimeClasspath);
|
||||
|
||||
userdevClasspath.extendsFrom(libraries, moduleLibraries, userdevCompileOnly);
|
||||
userdevClasspath.shouldResolveConsistentlyWith(runtimeClasspath);
|
||||
|
||||
userdevCompileOnlyClasspath.extendsFrom(userdevCompileOnly);
|
||||
userdevCompileOnlyClasspath.shouldResolveConsistentlyWith(runtimeClasspath);
|
||||
|
||||
userdevTestClasspath.extendsFrom(userdevTestFixtures);
|
||||
userdevTestClasspath.shouldResolveConsistentlyWith(runtimeClasspath);
|
||||
|
||||
launcherProfileClasspath.extendsFrom(libraries, moduleLibraries);
|
||||
launcherProfileClasspath.shouldResolveConsistentlyWith(runtimeClasspath);
|
||||
|
||||
toolClasspaths = createToolClasspaths(project);
|
||||
}
|
||||
|
||||
private static Map<Tools, Configuration> createToolClasspaths(Project project) {
|
||||
var configurations = project.getConfigurations();
|
||||
var dependencyFactory = project.getDependencyFactory();
|
||||
|
||||
var result = new HashMap<Tools, Configuration>();
|
||||
|
||||
for (var tool : Tools.values()) {
|
||||
var configuration = configurations.create(tool.getGradleConfigurationName(), spec -> {
|
||||
spec.setDescription("Resolves the executable for tool " + tool.name());
|
||||
spec.setCanBeConsumed(false);
|
||||
// Tools are considered to be executable jars.
|
||||
// Gradle requires the classpath for JavaExec to only contain a single file for these.
|
||||
if (tool.isRequestFatJar()) {
|
||||
spec.attributes(attr -> {
|
||||
attr.attribute(Bundling.BUNDLING_ATTRIBUTE, project.getObjects().named(Bundling.class, Bundling.SHADOWED));
|
||||
});
|
||||
}
|
||||
|
||||
var gav = tool.asGav(project);
|
||||
spec.getDependencies().add(dependencyFactory.create(gav));
|
||||
});
|
||||
result.put(tool, configuration);
|
||||
}
|
||||
|
||||
return Map.copyOf(result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a configuration representing the classpath for an executable tool.
|
||||
* Some tools are assumed to be executable jars, and their configurations only contain a single file.
|
||||
*/
|
||||
public Configuration getExecutableTool(Tools tool) {
|
||||
return Objects.requireNonNull(toolClasspaths.get(tool));
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,35 @@
|
||||
package net.neoforged.neodev;
|
||||
|
||||
import net.neoforged.moddevgradle.dsl.ModModel;
|
||||
import net.neoforged.moddevgradle.dsl.RunModel;
|
||||
import org.gradle.api.Action;
|
||||
import org.gradle.api.NamedDomainObjectContainer;
|
||||
import org.gradle.api.Project;
|
||||
|
||||
public class NeoDevExtension {
|
||||
public static final String NAME = "neoDev";
|
||||
|
||||
private final NamedDomainObjectContainer<ModModel> mods;
|
||||
private final NamedDomainObjectContainer<RunModel> runs;
|
||||
|
||||
public NeoDevExtension(Project project) {
|
||||
mods = project.container(ModModel.class);
|
||||
runs = project.container(RunModel.class, name -> project.getObjects().newInstance(RunModel.class, name, project, mods));
|
||||
}
|
||||
|
||||
public NamedDomainObjectContainer<ModModel> getMods() {
|
||||
return mods;
|
||||
}
|
||||
|
||||
public void mods(Action<NamedDomainObjectContainer<ModModel>> action) {
|
||||
action.execute(mods);
|
||||
}
|
||||
|
||||
public NamedDomainObjectContainer<RunModel> getRuns() {
|
||||
return runs;
|
||||
}
|
||||
|
||||
public void runs(Action<NamedDomainObjectContainer<RunModel>> action) {
|
||||
action.execute(runs);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,79 @@
|
||||
package net.neoforged.neodev;
|
||||
|
||||
import net.neoforged.minecraftdependencies.MinecraftDependenciesPlugin;
|
||||
import net.neoforged.moddevgradle.internal.NeoDevFacade;
|
||||
import net.neoforged.nfrtgradle.CreateMinecraftArtifacts;
|
||||
import net.neoforged.nfrtgradle.DownloadAssets;
|
||||
import org.gradle.api.Plugin;
|
||||
import org.gradle.api.Project;
|
||||
import org.gradle.api.artifacts.Configuration;
|
||||
import org.gradle.api.artifacts.ProjectDependency;
|
||||
import org.gradle.api.artifacts.dsl.DependencyFactory;
|
||||
import org.gradle.api.tasks.testing.Test;
|
||||
|
||||
import java.util.function.Consumer;
|
||||
|
||||
// TODO: the only point of this is to configure runs that depend on neoforge. Maybe this could be done with less code duplication...
|
||||
// TODO: Gradle says "thou shalt not referenceth otherth projects" yet here we are
|
||||
// TODO: depend on neoforge configurations that the moddev plugin also uses
|
||||
public class NeoDevExtraPlugin implements Plugin<Project> {
|
||||
@Override
|
||||
public void apply(Project project) {
|
||||
project.getPlugins().apply(MinecraftDependenciesPlugin.class);
|
||||
|
||||
var neoForgeProject = project.getRootProject().getChildProjects().get("neoforge");
|
||||
|
||||
var dependencyFactory = project.getDependencyFactory();
|
||||
var tasks = project.getTasks();
|
||||
var neoDevBuildDir = project.getLayout().getBuildDirectory().dir("neodev");
|
||||
|
||||
var extension = project.getExtensions().create(NeoDevExtension.NAME, NeoDevExtension.class);
|
||||
|
||||
var modulePathDependency = projectDep(dependencyFactory, neoForgeProject, "net.neoforged:neoforge-moddev-module-path");
|
||||
|
||||
// TODO: this is temporary
|
||||
var downloadAssets = neoForgeProject.getTasks().named("downloadAssets", DownloadAssets.class);
|
||||
var writeNeoDevConfig = neoForgeProject.getTasks().named("writeNeoDevConfig", CreateUserDevConfig.class);
|
||||
|
||||
Consumer<Configuration> configureLegacyClasspath = spec -> {
|
||||
spec.getDependencies().add(projectDep(dependencyFactory, neoForgeProject, "net.neoforged:neoforge-dependencies"));
|
||||
};
|
||||
|
||||
extension.getRuns().configureEach(run -> {
|
||||
configureLegacyClasspath.accept(run.getAdditionalRuntimeClasspathConfiguration());
|
||||
});
|
||||
NeoDevFacade.setupRuns(
|
||||
project,
|
||||
neoDevBuildDir,
|
||||
extension.getRuns(),
|
||||
writeNeoDevConfig,
|
||||
modulePath -> modulePath.getDependencies().add(modulePathDependency),
|
||||
configureLegacyClasspath,
|
||||
downloadAssets.flatMap(DownloadAssets::getAssetPropertiesFile)
|
||||
);
|
||||
|
||||
var testExtension = project.getExtensions().create(NeoDevTestExtension.NAME, NeoDevTestExtension.class);
|
||||
var testTask = tasks.register("junitTest", Test.class, test -> test.setGroup("verification"));
|
||||
tasks.named("check").configure(task -> task.dependsOn(testTask));
|
||||
|
||||
NeoDevFacade.setupTestTask(
|
||||
project,
|
||||
neoDevBuildDir,
|
||||
testTask,
|
||||
writeNeoDevConfig,
|
||||
testExtension.getLoadedMods(),
|
||||
testExtension.getTestedMod(),
|
||||
modulePath -> modulePath.getDependencies().add(modulePathDependency),
|
||||
configureLegacyClasspath,
|
||||
downloadAssets.flatMap(DownloadAssets::getAssetPropertiesFile)
|
||||
);
|
||||
}
|
||||
|
||||
private static ProjectDependency projectDep(DependencyFactory dependencyFactory, Project project, String capabilityNotation) {
|
||||
var dep = dependencyFactory.create(project);
|
||||
dep.capabilities(caps -> {
|
||||
caps.requireCapability(capabilityNotation);
|
||||
});
|
||||
return dep;
|
||||
}
|
||||
}
|
||||
540
buildSrc/src/main/java/net/neoforged/neodev/NeoDevPlugin.java
Normal file
540
buildSrc/src/main/java/net/neoforged/neodev/NeoDevPlugin.java
Normal file
@ -0,0 +1,540 @@
|
||||
package net.neoforged.neodev;
|
||||
|
||||
import net.neoforged.minecraftdependencies.MinecraftDependenciesPlugin;
|
||||
import net.neoforged.moddevgradle.internal.NeoDevFacade;
|
||||
import net.neoforged.moddevgradle.tasks.JarJar;
|
||||
import net.neoforged.neodev.installer.CreateArgsFile;
|
||||
import net.neoforged.neodev.installer.CreateInstallerProfile;
|
||||
import net.neoforged.neodev.installer.CreateLauncherProfile;
|
||||
import net.neoforged.neodev.installer.InstallerProcessor;
|
||||
import net.neoforged.neodev.utils.DependencyUtils;
|
||||
import net.neoforged.nfrtgradle.CreateMinecraftArtifacts;
|
||||
import net.neoforged.nfrtgradle.DownloadAssets;
|
||||
import net.neoforged.nfrtgradle.NeoFormRuntimePlugin;
|
||||
import net.neoforged.nfrtgradle.NeoFormRuntimeTask;
|
||||
import org.gradle.api.Plugin;
|
||||
import org.gradle.api.Project;
|
||||
import org.gradle.api.artifacts.repositories.MavenArtifactRepository;
|
||||
import org.gradle.api.file.Directory;
|
||||
import org.gradle.api.file.RegularFile;
|
||||
import org.gradle.api.plugins.BasePluginExtension;
|
||||
import org.gradle.api.plugins.JavaPlugin;
|
||||
import org.gradle.api.plugins.JavaPluginExtension;
|
||||
import org.gradle.api.provider.Provider;
|
||||
import org.gradle.api.tasks.Sync;
|
||||
import org.gradle.api.tasks.TaskProvider;
|
||||
import org.gradle.api.tasks.bundling.AbstractArchiveTask;
|
||||
import org.gradle.api.tasks.bundling.Jar;
|
||||
import org.gradle.api.tasks.bundling.Zip;
|
||||
|
||||
import java.io.File;
|
||||
import java.net.URI;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class NeoDevPlugin implements Plugin<Project> {
|
||||
static final String GROUP = "neoforge development";
|
||||
static final String INTERNAL_GROUP = "neoforge development/internal";
|
||||
|
||||
@Override
|
||||
public void apply(Project project) {
|
||||
project.getPlugins().apply(MinecraftDependenciesPlugin.class);
|
||||
|
||||
var dependencyFactory = project.getDependencyFactory();
|
||||
var tasks = project.getTasks();
|
||||
var neoDevBuildDir = project.getLayout().getBuildDirectory().dir("neodev");
|
||||
|
||||
var rawNeoFormVersion = project.getProviders().gradleProperty("neoform_version");
|
||||
var fmlVersion = project.getProviders().gradleProperty("fancy_mod_loader_version");
|
||||
var minecraftVersion = project.getProviders().gradleProperty("minecraft_version");
|
||||
var neoForgeVersion = project.provider(() -> project.getVersion().toString());
|
||||
var mcAndNeoFormVersion = minecraftVersion.zip(rawNeoFormVersion, (mc, nf) -> mc + "-" + nf);
|
||||
|
||||
var extension = project.getExtensions().create(NeoDevExtension.NAME, NeoDevExtension.class);
|
||||
var configurations = NeoDevConfigurations.createAndSetup(project);
|
||||
|
||||
/*
|
||||
* MINECRAFT SOURCES SETUP
|
||||
*/
|
||||
// 1. Obtain decompiled Minecraft sources jar using NeoForm.
|
||||
var createSourceArtifacts = configureMinecraftDecompilation(project);
|
||||
// Task must run on sync to have MC resources available for IDEA nondelegated builds.
|
||||
NeoDevFacade.runTaskOnProjectSync(project, createSourceArtifacts);
|
||||
|
||||
// 2. Apply AT to the source jar from 1.
|
||||
var atFile = project.getRootProject().file("src/main/resources/META-INF/accesstransformer.cfg");
|
||||
var applyAt = configureAccessTransformer(
|
||||
project,
|
||||
configurations,
|
||||
createSourceArtifacts,
|
||||
neoDevBuildDir,
|
||||
atFile);
|
||||
|
||||
// 3. Apply patches to the source jar from 2.
|
||||
var patchesFolder = project.getRootProject().file("patches");
|
||||
var applyPatches = tasks.register("applyPatches", ApplyPatches.class, task -> {
|
||||
task.setGroup(INTERNAL_GROUP);
|
||||
task.getOriginalJar().set(applyAt.flatMap(ApplyAccessTransformer::getOutputJar));
|
||||
task.getPatchesFolder().set(patchesFolder);
|
||||
task.getPatchedJar().set(neoDevBuildDir.map(dir -> dir.file("artifacts/patched-sources.jar")));
|
||||
task.getRejectsFolder().set(project.getRootProject().file("rejects"));
|
||||
});
|
||||
|
||||
// 4. Unpack jar from 3.
|
||||
var mcSourcesPath = project.file("src/main/java");
|
||||
tasks.register("setup", Sync.class, task -> {
|
||||
task.setGroup(GROUP);
|
||||
task.from(project.zipTree(applyPatches.flatMap(ApplyPatches::getPatchedJar)));
|
||||
task.into(mcSourcesPath);
|
||||
});
|
||||
|
||||
/*
|
||||
* RUNS SETUP
|
||||
*/
|
||||
|
||||
// 1. Write configs that contain the runs in a format understood by MDG/NG/etc. Currently one for neodev and one for userdev.
|
||||
var writeNeoDevConfig = tasks.register("writeNeoDevConfig", CreateUserDevConfig.class, task -> {
|
||||
task.getForNeoDev().set(true);
|
||||
task.getUserDevConfig().set(neoDevBuildDir.map(dir -> dir.file("neodev-config.json")));
|
||||
});
|
||||
var writeUserDevConfig = tasks.register("writeUserDevConfig", CreateUserDevConfig.class, task -> {
|
||||
task.getForNeoDev().set(false);
|
||||
task.getUserDevConfig().set(neoDevBuildDir.map(dir -> dir.file("userdev-config.json")));
|
||||
});
|
||||
for (var taskProvider : List.of(writeNeoDevConfig, writeUserDevConfig)) {
|
||||
taskProvider.configure(task -> {
|
||||
task.setGroup(INTERNAL_GROUP);
|
||||
task.getFmlVersion().set(fmlVersion);
|
||||
task.getMinecraftVersion().set(minecraftVersion);
|
||||
task.getNeoForgeVersion().set(neoForgeVersion);
|
||||
task.getRawNeoFormVersion().set(rawNeoFormVersion);
|
||||
task.getLibraries().addAll(DependencyUtils.configurationToGavList(configurations.userdevClasspath));
|
||||
task.getModules().addAll(DependencyUtils.configurationToGavList(configurations.modulePath));
|
||||
task.getTestLibraries().addAll(DependencyUtils.configurationToGavList(configurations.userdevTestClasspath));
|
||||
task.getTestLibraries().add(neoForgeVersion.map(v -> "net.neoforged:testframework:" + v));
|
||||
task.getIgnoreList().addAll(configurations.userdevCompileOnlyClasspath.getIncoming().getArtifacts().getResolvedArtifacts().map(results -> {
|
||||
return results.stream().map(r -> r.getFile().getName()).toList();
|
||||
}));
|
||||
task.getIgnoreList().addAll("client-extra", "neoforge-");
|
||||
task.getBinpatcherGav().set(Tools.BINPATCHER.asGav(project));
|
||||
});
|
||||
}
|
||||
|
||||
// 2. Task to download assets.
|
||||
var downloadAssets = tasks.register("downloadAssets", DownloadAssets.class, task -> {
|
||||
task.setGroup(INTERNAL_GROUP);
|
||||
task.getNeoFormArtifact().set(mcAndNeoFormVersion.map(v -> "net.neoforged:neoform:" + v + "@zip"));
|
||||
task.getAssetPropertiesFile().set(neoDevBuildDir.map(dir -> dir.file("minecraft_assets.properties")));
|
||||
});
|
||||
|
||||
// FML needs Minecraft resources on the classpath to find it. Add to runtimeOnly so subprojects also get it at runtime.
|
||||
var runtimeClasspath = project.getConfigurations().getByName(JavaPlugin.RUNTIME_ONLY_CONFIGURATION_NAME);
|
||||
runtimeClasspath.getDependencies().add(
|
||||
dependencyFactory.create(
|
||||
project.files(createSourceArtifacts.flatMap(CreateMinecraftArtifacts::getResourcesArtifact))
|
||||
)
|
||||
);
|
||||
// 3. Let MDG do the rest of the setup. :)
|
||||
NeoDevFacade.setupRuns(
|
||||
project,
|
||||
neoDevBuildDir,
|
||||
extension.getRuns(),
|
||||
writeNeoDevConfig,
|
||||
modulePath -> {
|
||||
modulePath.extendsFrom(configurations.moduleLibraries);
|
||||
},
|
||||
legacyClassPath -> {
|
||||
legacyClassPath.getDependencies().addLater(mcAndNeoFormVersion.map(v -> dependencyFactory.create("net.neoforged:neoform:" + v).capabilities(caps -> {
|
||||
caps.requireCapability("net.neoforged:neoform-dependencies");
|
||||
})));
|
||||
legacyClassPath.extendsFrom(configurations.libraries, configurations.moduleLibraries, configurations.userdevCompileOnly);
|
||||
},
|
||||
downloadAssets.flatMap(DownloadAssets::getAssetPropertiesFile)
|
||||
);
|
||||
// TODO: Gradle run tasks should be moved to gradle group GROUP
|
||||
|
||||
/*
|
||||
* OTHER TASKS
|
||||
*/
|
||||
|
||||
// Generate source patches into a patch archive.
|
||||
var genSourcePatches = tasks.register("generateSourcePatches", GenerateSourcePatches.class, task -> {
|
||||
task.setGroup(INTERNAL_GROUP);
|
||||
task.getOriginalJar().set(applyAt.flatMap(ApplyAccessTransformer::getOutputJar));
|
||||
task.getModifiedSources().set(project.file("src/main/java"));
|
||||
task.getPatchesJar().set(neoDevBuildDir.map(dir -> dir.file("source-patches.zip")));
|
||||
});
|
||||
|
||||
// Update the patch/ folder with the current patches.
|
||||
tasks.register("genPatches", Sync.class, task -> {
|
||||
task.setGroup(GROUP);
|
||||
task.from(project.zipTree(genSourcePatches.flatMap(GenerateSourcePatches::getPatchesJar)));
|
||||
task.into(project.getRootProject().file("patches"));
|
||||
});
|
||||
|
||||
// Universal jar = the jar that contains NeoForge classes
|
||||
// TODO: signing?
|
||||
var universalJar = tasks.register("universalJar", Jar.class, task -> {
|
||||
task.setGroup(INTERNAL_GROUP);
|
||||
task.getArchiveClassifier().set("universal");
|
||||
|
||||
task.from(project.zipTree(
|
||||
tasks.named("jar", Jar.class).flatMap(AbstractArchiveTask::getArchiveFile)));
|
||||
task.exclude("net/minecraft/**");
|
||||
task.exclude("com/**");
|
||||
task.exclude("mcp/**");
|
||||
|
||||
task.manifest(manifest -> {
|
||||
manifest.attributes(Map.of("FML-System-Mods", "neoforge"));
|
||||
// These attributes are used from NeoForgeVersion.java to find the NF version without command line arguments.
|
||||
manifest.attributes(
|
||||
Map.of(
|
||||
"Specification-Title", "NeoForge",
|
||||
"Specification-Vendor", "NeoForge",
|
||||
"Specification-Version", project.getVersion().toString().substring(0, project.getVersion().toString().lastIndexOf(".")),
|
||||
"Implementation-Title", project.getGroup(),
|
||||
"Implementation-Version", project.getVersion(),
|
||||
"Implementation-Vendor", "NeoForged"),
|
||||
"net/neoforged/neoforge/internal/versions/neoforge/");
|
||||
manifest.attributes(
|
||||
Map.of(
|
||||
"Specification-Title", "Minecraft",
|
||||
"Specification-Vendor", "Mojang",
|
||||
"Specification-Version", minecraftVersion,
|
||||
"Implementation-Title", "MCP",
|
||||
"Implementation-Version", mcAndNeoFormVersion,
|
||||
"Implementation-Vendor", "NeoForged"),
|
||||
"net/neoforged/neoforge/versions/neoform/");
|
||||
});
|
||||
});
|
||||
|
||||
var jarJarTask = JarJar.registerWithConfiguration(project, "jarJar");
|
||||
jarJarTask.configure(task -> task.setGroup(INTERNAL_GROUP));
|
||||
universalJar.configure(task -> task.from(jarJarTask));
|
||||
|
||||
var createCleanArtifacts = tasks.register("createCleanArtifacts", CreateCleanArtifacts.class, task -> {
|
||||
task.setGroup(INTERNAL_GROUP);
|
||||
var cleanArtifactsDir = neoDevBuildDir.map(dir -> dir.dir("artifacts/clean"));
|
||||
task.getCleanClientJar().set(cleanArtifactsDir.map(dir -> dir.file("client.jar")));
|
||||
task.getRawServerJar().set(cleanArtifactsDir.map(dir -> dir.file("raw-server.jar")));
|
||||
task.getCleanServerJar().set(cleanArtifactsDir.map(dir -> dir.file("server.jar")));
|
||||
task.getCleanJoinedJar().set(cleanArtifactsDir.map(dir -> dir.file("joined.jar")));
|
||||
task.getMergedMappings().set(cleanArtifactsDir.map(dir -> dir.file("merged-mappings.txt")));
|
||||
task.getNeoFormArtifact().set(mcAndNeoFormVersion.map(version -> "net.neoforged:neoform:" + version + "@zip"));
|
||||
});
|
||||
|
||||
var binaryPatchOutputs = configureBinaryPatchCreation(
|
||||
project,
|
||||
configurations,
|
||||
createCleanArtifacts,
|
||||
neoDevBuildDir,
|
||||
patchesFolder
|
||||
);
|
||||
|
||||
// Launcher profile = the version.json file used by the Minecraft launcher.
|
||||
var createLauncherProfile = tasks.register("createLauncherProfile", CreateLauncherProfile.class, task -> {
|
||||
task.setGroup(INTERNAL_GROUP);
|
||||
task.getFmlVersion().set(fmlVersion);
|
||||
task.getMinecraftVersion().set(minecraftVersion);
|
||||
task.getNeoForgeVersion().set(neoForgeVersion);
|
||||
task.getRawNeoFormVersion().set(rawNeoFormVersion);
|
||||
task.setLibraries(configurations.launcherProfileClasspath);
|
||||
task.getRepositoryURLs().set(project.provider(() -> {
|
||||
List<URI> repos = new ArrayList<>();
|
||||
for (var repo : project.getRepositories().withType(MavenArtifactRepository.class)) {
|
||||
var uri = repo.getUrl();
|
||||
if (!uri.toString().endsWith("/")) {
|
||||
uri = URI.create(uri + "/");
|
||||
}
|
||||
repos.add(uri);
|
||||
}
|
||||
return repos;
|
||||
}));
|
||||
task.getIgnoreList().addAll("client-extra", "neoforge-");
|
||||
task.setModules(configurations.modulePath);
|
||||
task.getLauncherProfile().set(neoDevBuildDir.map(dir -> dir.file("launcher-profile.json")));
|
||||
});
|
||||
|
||||
// Installer profile = the .json file used by the NeoForge installer.
|
||||
var createInstallerProfile = tasks.register("createInstallerProfile", CreateInstallerProfile.class, task -> {
|
||||
task.setGroup(INTERNAL_GROUP);
|
||||
task.getMinecraftVersion().set(minecraftVersion);
|
||||
task.getNeoForgeVersion().set(neoForgeVersion);
|
||||
task.getMcAndNeoFormVersion().set(mcAndNeoFormVersion);
|
||||
task.getIcon().set(project.getRootProject().file("docs/assets/neoforged.ico"));
|
||||
// Anything that is on the launcher classpath should be downloaded by the installer.
|
||||
// (At least on the server side).
|
||||
task.addLibraries(configurations.launcherProfileClasspath);
|
||||
// We need the NeoForm zip for the SRG mappings.
|
||||
task.addLibraries(configurations.neoFormDataOnly);
|
||||
task.getRepositoryURLs().set(project.provider(() -> {
|
||||
List<URI> repos = new ArrayList<>();
|
||||
for (var repo : project.getRepositories().withType(MavenArtifactRepository.class)) {
|
||||
var uri = repo.getUrl();
|
||||
if (!uri.toString().endsWith("/")) {
|
||||
uri = URI.create(uri + "/");
|
||||
}
|
||||
repos.add(uri);
|
||||
}
|
||||
return repos;
|
||||
}));
|
||||
task.getUniversalJar().set(universalJar.flatMap(AbstractArchiveTask::getArchiveFile));
|
||||
task.getInstallerProfile().set(neoDevBuildDir.map(dir -> dir.file("installer-profile.json")));
|
||||
|
||||
// Make all installer processor tools available to the profile
|
||||
for (var installerProcessor : InstallerProcessor.values()) {
|
||||
var configuration = configurations.getExecutableTool(installerProcessor.tool);
|
||||
// Different processors might use different versions of the same library,
|
||||
// but that is fine because each processor gets its own classpath.
|
||||
task.addLibraries(configuration);
|
||||
task.getProcessorClasspaths().put(installerProcessor, DependencyUtils.configurationToGavList(configuration));
|
||||
task.getProcessorGavs().put(installerProcessor, installerProcessor.tool.asGav(project));
|
||||
}
|
||||
});
|
||||
|
||||
var createWindowsServerArgsFile = tasks.register("createWindowsServerArgsFile", CreateArgsFile.class, task -> {
|
||||
task.setLibraries(";", configurations.launcherProfileClasspath, configurations.modulePath);
|
||||
task.getArgsFile().set(neoDevBuildDir.map(dir -> dir.file("windows-server-args.txt")));
|
||||
});
|
||||
var createUnixServerArgsFile = tasks.register("createUnixServerArgsFile", CreateArgsFile.class, task -> {
|
||||
task.setLibraries(":", configurations.launcherProfileClasspath, configurations.modulePath);
|
||||
task.getArgsFile().set(neoDevBuildDir.map(dir -> dir.file("unix-server-args.txt")));
|
||||
});
|
||||
|
||||
for (var taskProvider : List.of(createWindowsServerArgsFile, createUnixServerArgsFile)) {
|
||||
taskProvider.configure(task -> {
|
||||
task.setGroup(INTERNAL_GROUP);
|
||||
task.getTemplate().set(project.getRootProject().file("server_files/args.txt"));
|
||||
task.getFmlVersion().set(fmlVersion);
|
||||
task.getMinecraftVersion().set(minecraftVersion);
|
||||
task.getNeoForgeVersion().set(neoForgeVersion);
|
||||
task.getRawNeoFormVersion().set(rawNeoFormVersion);
|
||||
// In theory, new BootstrapLauncher shouldn't need the module path in the ignore list anymore.
|
||||
// However, in server installs libraries are passed as relative paths here.
|
||||
// Module path detection doesn't currently work with relative paths (BootstrapLauncher #20).
|
||||
task.getIgnoreList().set(configurations.modulePath.getIncoming().getArtifacts().getResolvedArtifacts().map(results -> {
|
||||
return results.stream().map(r -> r.getFile().getName()).toList();
|
||||
}));
|
||||
task.getRawServerJar().set(createCleanArtifacts.flatMap(CreateCleanArtifacts::getRawServerJar));
|
||||
});
|
||||
}
|
||||
|
||||
var installerConfig = configurations.getExecutableTool(Tools.LEGACYINSTALLER);
|
||||
// TODO: signing?
|
||||
// We want to inherit the executable JAR manifest from LegacyInstaller.
|
||||
// - Jar tasks have special manifest handling, so use Zip.
|
||||
// - The manifest must be the first entry in the jar so LegacyInstaller has to be the first input.
|
||||
var installerJar = tasks.register("installerJar", Zip.class, task -> {
|
||||
task.setGroup(INTERNAL_GROUP);
|
||||
task.getArchiveClassifier().set("installer");
|
||||
task.getArchiveExtension().set("jar");
|
||||
task.setMetadataCharset("UTF-8");
|
||||
task.getDestinationDirectory().convention(project.getExtensions().getByType(BasePluginExtension.class).getLibsDirectory());
|
||||
|
||||
task.from(project.zipTree(project.provider(installerConfig::getSingleFile)), spec -> {
|
||||
spec.exclude("big_logo.png");
|
||||
});
|
||||
task.from(createLauncherProfile.flatMap(CreateLauncherProfile::getLauncherProfile), spec -> {
|
||||
spec.rename(s -> "version.json");
|
||||
});
|
||||
task.from(createInstallerProfile.flatMap(CreateInstallerProfile::getInstallerProfile), spec -> {
|
||||
spec.rename(s -> "install_profile.json");
|
||||
});
|
||||
task.from(project.getRootProject().file("src/main/resources/url.png"));
|
||||
task.from(project.getRootProject().file("src/main/resources/neoforged_logo.png"), spec -> {
|
||||
spec.rename(s -> "big_logo.png");
|
||||
});
|
||||
task.from(createUnixServerArgsFile.flatMap(CreateArgsFile::getArgsFile), spec -> {
|
||||
spec.into("data");
|
||||
spec.rename(s -> "unix_args.txt");
|
||||
});
|
||||
task.from(createWindowsServerArgsFile.flatMap(CreateArgsFile::getArgsFile), spec -> {
|
||||
spec.into("data");
|
||||
spec.rename(s -> "win_args.txt");
|
||||
});
|
||||
task.from(binaryPatchOutputs.binaryPatchesForClient(), spec -> {
|
||||
spec.into("data");
|
||||
spec.rename(s -> "client.lzma");
|
||||
});
|
||||
task.from(binaryPatchOutputs.binaryPatchesForServer(), spec -> {
|
||||
spec.into("data");
|
||||
spec.rename(s -> "server.lzma");
|
||||
});
|
||||
var mavenPath = neoForgeVersion.map(v -> "net/neoforged/neoforge/" + v);
|
||||
task.getInputs().property("mavenPath", mavenPath);
|
||||
task.from(project.getRootProject().files("server_files"), spec -> {
|
||||
spec.into("data");
|
||||
spec.exclude("args.txt");
|
||||
spec.filter(s -> {
|
||||
return s.replaceAll("@MAVEN_PATH@", mavenPath.get());
|
||||
});
|
||||
});
|
||||
|
||||
// This is true by default (see gradle.properties), and needs to be disabled explicitly when building (see release.yml).
|
||||
String installerDebugProperty = "neogradle.runtime.platform.installer.debug";
|
||||
if (project.getProperties().containsKey(installerDebugProperty) && Boolean.parseBoolean(project.getProperties().get(installerDebugProperty).toString())) {
|
||||
task.from(universalJar.flatMap(AbstractArchiveTask::getArchiveFile), spec -> {
|
||||
spec.into(String.format("/maven/net/neoforged/neoforge/%s/", neoForgeVersion.get()));
|
||||
spec.rename(name -> String.format("neoforge-%s-universal.jar", neoForgeVersion.get()));
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
var userdevJar = tasks.register("userdevJar", Jar.class, task -> {
|
||||
task.setGroup(INTERNAL_GROUP);
|
||||
task.getArchiveClassifier().set("userdev");
|
||||
|
||||
task.from(writeUserDevConfig.flatMap(CreateUserDevConfig::getUserDevConfig), spec -> {
|
||||
spec.rename(s -> "config.json");
|
||||
});
|
||||
task.from(atFile, spec -> {
|
||||
spec.into("ats/");
|
||||
});
|
||||
task.from(binaryPatchOutputs.binaryPatchesForMerged(), spec -> {
|
||||
spec.rename(s -> "joined.lzma");
|
||||
});
|
||||
task.from(project.zipTree(genSourcePatches.flatMap(GenerateSourcePatches::getPatchesJar)), spec -> {
|
||||
spec.into("patches/");
|
||||
});
|
||||
});
|
||||
|
||||
project.getExtensions().getByType(JavaPluginExtension.class).withSourcesJar();
|
||||
var sourcesJarProvider = project.getTasks().named("sourcesJar", Jar.class);
|
||||
sourcesJarProvider.configure(task -> {
|
||||
task.exclude("net/minecraft/**");
|
||||
task.exclude("com/**");
|
||||
task.exclude("mcp/**");
|
||||
});
|
||||
|
||||
tasks.named("assemble", task -> {
|
||||
task.dependsOn(installerJar);
|
||||
task.dependsOn(universalJar);
|
||||
task.dependsOn(userdevJar);
|
||||
task.dependsOn(sourcesJarProvider);
|
||||
});
|
||||
}
|
||||
|
||||
private static TaskProvider<ApplyAccessTransformer> configureAccessTransformer(
|
||||
Project project,
|
||||
NeoDevConfigurations configurations,
|
||||
TaskProvider<CreateMinecraftArtifacts> createSourceArtifacts,
|
||||
Provider<Directory> neoDevBuildDir,
|
||||
File atFile) {
|
||||
|
||||
// Pass -PvalidateAccessTransformers to validate ATs.
|
||||
var validateAts = project.getProviders().gradleProperty("validateAccessTransformers").map(p -> true).orElse(false);
|
||||
return project.getTasks().register("applyAccessTransformer", ApplyAccessTransformer.class, task -> {
|
||||
task.setGroup(INTERNAL_GROUP);
|
||||
task.classpath(configurations.getExecutableTool(Tools.JST));
|
||||
task.getInputJar().set(createSourceArtifacts.flatMap(CreateMinecraftArtifacts::getSourcesArtifact));
|
||||
task.getAccessTransformer().set(atFile);
|
||||
task.getValidate().set(validateAts);
|
||||
task.getOutputJar().set(neoDevBuildDir.map(dir -> dir.file("artifacts/access-transformed-sources.jar")));
|
||||
task.getLibraries().from(configurations.neoFormClasspath);
|
||||
task.getLibrariesFile().set(neoDevBuildDir.map(dir -> dir.file("minecraft-libraries-for-jst.txt")));
|
||||
});
|
||||
}
|
||||
|
||||
private static BinaryPatchOutputs configureBinaryPatchCreation(Project project,
|
||||
NeoDevConfigurations configurations,
|
||||
TaskProvider<CreateCleanArtifacts> createCleanArtifacts,
|
||||
Provider<Directory> neoDevBuildDir,
|
||||
File sourcesPatchesFolder) {
|
||||
var tasks = project.getTasks();
|
||||
|
||||
var artConfig = configurations.getExecutableTool(Tools.AUTO_RENAMING_TOOL);
|
||||
var remapClientJar = tasks.register("remapClientJar", RemapJar.class, task -> {
|
||||
task.setDescription("Creates a Minecraft client jar with the official mappings applied. Used as the base for generating binary patches for the client.");
|
||||
task.getInputJar().set(createCleanArtifacts.flatMap(CreateCleanArtifacts::getCleanClientJar));
|
||||
task.getOutputJar().set(neoDevBuildDir.map(dir -> dir.file("remapped-client.jar")));
|
||||
});
|
||||
var remapServerJar = tasks.register("remapServerJar", RemapJar.class, task -> {
|
||||
task.setDescription("Creates a Minecraft dedicated server jar with the official mappings applied. Used as the base for generating binary patches for the client.");
|
||||
task.getInputJar().set(createCleanArtifacts.flatMap(CreateCleanArtifacts::getCleanServerJar));
|
||||
task.getOutputJar().set(neoDevBuildDir.map(dir -> dir.file("remapped-server.jar")));
|
||||
});
|
||||
for (var remapTask : List.of(remapClientJar, remapServerJar)) {
|
||||
remapTask.configure(task -> {
|
||||
task.setGroup(INTERNAL_GROUP);
|
||||
task.classpath(artConfig);
|
||||
task.getMappings().set(createCleanArtifacts.flatMap(CreateCleanArtifacts::getMergedMappings));
|
||||
});
|
||||
}
|
||||
|
||||
var binpatcherConfig = configurations.getExecutableTool(Tools.BINPATCHER);
|
||||
var generateMergedBinPatches = tasks.register("generateMergedBinPatches", GenerateBinaryPatches.class, task -> {
|
||||
task.setDescription("Creates binary patch files by diffing a merged client/server jar-file and the compiled Minecraft classes in this project.");
|
||||
task.getCleanJar().set(createCleanArtifacts.flatMap(CreateCleanArtifacts::getCleanJoinedJar));
|
||||
task.getOutputFile().set(neoDevBuildDir.map(dir -> dir.file("merged-binpatches.lzma")));
|
||||
});
|
||||
var generateClientBinPatches = tasks.register("generateClientBinPatches", GenerateBinaryPatches.class, task -> {
|
||||
task.setDescription("Creates binary patch files by diffing a merged client jar-file and the compiled Minecraft classes in this project.");
|
||||
task.getCleanJar().set(remapClientJar.flatMap(RemapJar::getOutputJar));
|
||||
task.getOutputFile().set(neoDevBuildDir.map(dir -> dir.file("client-binpatches.lzma")));
|
||||
});
|
||||
var generateServerBinPatches = tasks.register("generateServerBinPatches", GenerateBinaryPatches.class, task -> {
|
||||
task.setDescription("Creates binary patch files by diffing a merged server jar-file and the compiled Minecraft classes in this project.");
|
||||
task.getCleanJar().set(remapServerJar.flatMap(RemapJar::getOutputJar));
|
||||
task.getOutputFile().set(neoDevBuildDir.map(dir -> dir.file("server-binpatches.lzma")));
|
||||
});
|
||||
for (var generateBinPatchesTask : List.of(generateMergedBinPatches, generateClientBinPatches, generateServerBinPatches)) {
|
||||
generateBinPatchesTask.configure(task -> {
|
||||
task.setGroup(INTERNAL_GROUP);
|
||||
task.classpath(binpatcherConfig);
|
||||
task.getPatchedJar().set(tasks.named("jar", Jar.class).flatMap(Jar::getArchiveFile));
|
||||
task.getSourcePatchesFolder().set(sourcesPatchesFolder);
|
||||
task.getMappings().set(createCleanArtifacts.flatMap(CreateCleanArtifacts::getMergedMappings));
|
||||
});
|
||||
}
|
||||
|
||||
return new BinaryPatchOutputs(
|
||||
generateMergedBinPatches.flatMap(GenerateBinaryPatches::getOutputFile),
|
||||
generateClientBinPatches.flatMap(GenerateBinaryPatches::getOutputFile),
|
||||
generateServerBinPatches.flatMap(GenerateBinaryPatches::getOutputFile)
|
||||
);
|
||||
}
|
||||
|
||||
private record BinaryPatchOutputs(
|
||||
Provider<RegularFile> binaryPatchesForMerged,
|
||||
Provider<RegularFile> binaryPatchesForClient,
|
||||
Provider<RegularFile> binaryPatchesForServer
|
||||
) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets up NFRT, and creates the sources and resources artifacts.
|
||||
*/
|
||||
static TaskProvider<CreateMinecraftArtifacts> configureMinecraftDecompilation(Project project) {
|
||||
project.getPlugins().apply(NeoFormRuntimePlugin.class);
|
||||
|
||||
var configurations = project.getConfigurations();
|
||||
var dependencyFactory = project.getDependencyFactory();
|
||||
var tasks = project.getTasks();
|
||||
var neoDevBuildDir = project.getLayout().getBuildDirectory().dir("neodev");
|
||||
|
||||
var rawNeoFormVersion = project.getProviders().gradleProperty("neoform_version");
|
||||
var minecraftVersion = project.getProviders().gradleProperty("minecraft_version");
|
||||
var mcAndNeoFormVersion = minecraftVersion.zip(rawNeoFormVersion, (mc, nf) -> mc + "-" + nf);
|
||||
|
||||
// Configuration for all artifacts that should be passed to NFRT to prevent repeated downloads
|
||||
var neoFormRuntimeArtifactManifestNeoForm = configurations.create("neoFormRuntimeArtifactManifestNeoForm", spec -> {
|
||||
spec.setCanBeConsumed(false);
|
||||
spec.setCanBeResolved(true);
|
||||
spec.getDependencies().addLater(mcAndNeoFormVersion.map(version -> {
|
||||
return dependencyFactory.create("net.neoforged:neoform:" + version);
|
||||
}));
|
||||
});
|
||||
|
||||
tasks.withType(NeoFormRuntimeTask.class, task -> {
|
||||
task.addArtifactsToManifest(neoFormRuntimeArtifactManifestNeoForm);
|
||||
});
|
||||
|
||||
return tasks.register("createSourceArtifacts", CreateMinecraftArtifacts.class, task -> {
|
||||
var minecraftArtifactsDir = neoDevBuildDir.map(dir -> dir.dir("artifacts"));
|
||||
task.getSourcesArtifact().set(minecraftArtifactsDir.map(dir -> dir.file("base-sources.jar")));
|
||||
task.getResourcesArtifact().set(minecraftArtifactsDir.map(dir -> dir.file("minecraft-resources.jar")));
|
||||
task.getNeoFormArtifact().set(mcAndNeoFormVersion.map(version -> "net.neoforged:neoform:" + version + "@zip"));
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,30 @@
|
||||
package net.neoforged.neodev;
|
||||
|
||||
import net.neoforged.moddevgradle.dsl.ModModel;
|
||||
import org.gradle.api.provider.Property;
|
||||
import org.gradle.api.provider.SetProperty;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
public abstract class NeoDevTestExtension {
|
||||
public static final String NAME = "neoDevTest";
|
||||
|
||||
@Inject
|
||||
public NeoDevTestExtension() {
|
||||
}
|
||||
|
||||
/**
|
||||
* The mod that will be loaded in JUnit tests.
|
||||
* The compiled classes from {@code src/test/java} and the resources from {@code src/test/resources}
|
||||
* will be added to that mod at runtime.
|
||||
*/
|
||||
public abstract Property<ModModel> getTestedMod();
|
||||
|
||||
/**
|
||||
* The mods to load when running unit tests. Defaults to all mods registered in the project.
|
||||
* This must contain {@link #getTestedMod()}.
|
||||
*
|
||||
* @see ModModel
|
||||
*/
|
||||
public abstract SetProperty<ModModel> getLoadedMods();
|
||||
}
|
||||
53
buildSrc/src/main/java/net/neoforged/neodev/RemapJar.java
Normal file
53
buildSrc/src/main/java/net/neoforged/neodev/RemapJar.java
Normal file
@ -0,0 +1,53 @@
|
||||
package net.neoforged.neodev;
|
||||
|
||||
import org.gradle.api.GradleException;
|
||||
import org.gradle.api.file.RegularFileProperty;
|
||||
import org.gradle.api.tasks.InputFile;
|
||||
import org.gradle.api.tasks.JavaExec;
|
||||
import org.gradle.api.tasks.OutputFile;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import java.io.BufferedOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* Produces a remapped jar-file that has almost no other changes applied with the intent of being
|
||||
* the base against which we {@link GenerateBinaryPatches generate binary patches}.
|
||||
* <p>
|
||||
* The installer produces the same Jar file as this task does and then applies the patches against that.
|
||||
* <p>
|
||||
* Any changes to the options used here have to be reflected in the {@link net.neoforged.neodev.installer.CreateInstallerProfile installer profile}
|
||||
* and vice versa, to ensure the patches are generated against the same binary files as they are applied to later.
|
||||
*/
|
||||
abstract class RemapJar extends JavaExec {
|
||||
@Inject
|
||||
public RemapJar() {}
|
||||
|
||||
@InputFile
|
||||
abstract RegularFileProperty getInputJar();
|
||||
|
||||
@InputFile
|
||||
abstract RegularFileProperty getMappings();
|
||||
|
||||
@OutputFile
|
||||
abstract RegularFileProperty getOutputJar();
|
||||
|
||||
@Override
|
||||
public void exec() {
|
||||
args("--input", getInputJar().get().getAsFile().getAbsolutePath());
|
||||
args("--output", getOutputJar().get().getAsFile().getAbsolutePath());
|
||||
args("--names", getMappings().get().getAsFile().getAbsolutePath());
|
||||
args("--ann-fix", "--ids-fix", "--src-fix", "--record-fix");
|
||||
|
||||
var logFile = new File(getTemporaryDir(), "console.log");
|
||||
try (var out = new BufferedOutputStream(new FileOutputStream(logFile))) {
|
||||
getLogger().info("Logging ART console output to {}", logFile.getAbsolutePath());
|
||||
setStandardOutput(out);
|
||||
super.exec();
|
||||
} catch (IOException e) {
|
||||
throw new GradleException("Failed to remap jar.", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
53
buildSrc/src/main/java/net/neoforged/neodev/Tools.java
Normal file
53
buildSrc/src/main/java/net/neoforged/neodev/Tools.java
Normal file
@ -0,0 +1,53 @@
|
||||
package net.neoforged.neodev;
|
||||
|
||||
import org.gradle.api.Project;
|
||||
|
||||
// If a GAV is changed, make sure to change the corresponding renovate comment in gradle.properties.
|
||||
public enum Tools {
|
||||
// Fatjar jst-cli-bundle instead of jst-cli because publication of the latter is currently broken.
|
||||
JST("net.neoforged.jst:jst-cli-bundle:%s", "jst_version", "toolJstClasspath", true),
|
||||
// Fatjar because the contents are copy/pasted into the installer jar which must be standalone.
|
||||
LEGACYINSTALLER("net.neoforged:legacyinstaller:%s:shrunk", "legacyinstaller_version", "toolLegacyinstallerClasspath", true),
|
||||
// Fatjar because the slim jar currently does not have the main class set in its manifest.
|
||||
AUTO_RENAMING_TOOL("net.neoforged:AutoRenamingTool:%s:all", "art_version", "toolAutoRenamingToolClasspath", true),
|
||||
INSTALLERTOOLS("net.neoforged.installertools:installertools:%s", "installertools_version", "toolInstallertoolsClasspath", false),
|
||||
JARSPLITTER("net.neoforged.installertools:jarsplitter:%s", "installertools_version", "toolJarsplitterClasspath", false),
|
||||
// Fatjar because it was like that in the userdev json in the past.
|
||||
// To reconsider, we need to get in touch with 3rd party plugin developers or wait for a BC window.
|
||||
BINPATCHER("net.neoforged.installertools:binarypatcher:%s:fatjar", "installertools_version", "toolBinpatcherClasspath", true);
|
||||
|
||||
private final String gavPattern;
|
||||
private final String versionProperty;
|
||||
private final String gradleConfigurationName;
|
||||
private final boolean requestFatJar;
|
||||
|
||||
Tools(String gavPattern, String versionProperty, String gradleConfigurationName, boolean requestFatJar) {
|
||||
this.gavPattern = gavPattern;
|
||||
this.versionProperty = versionProperty;
|
||||
this.gradleConfigurationName = gradleConfigurationName;
|
||||
this.requestFatJar = requestFatJar;
|
||||
}
|
||||
|
||||
/**
|
||||
* The name of the Gradle {@link org.gradle.api.artifacts.Configuration} used to resolve this particular tool.
|
||||
*/
|
||||
public String getGradleConfigurationName() {
|
||||
return gradleConfigurationName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Some tools may be incorrectly packaged and declare transitive dependencies even for their "fatjar" variants.
|
||||
* Gradle will not run these, so we ignore them.
|
||||
*/
|
||||
public boolean isRequestFatJar() {
|
||||
return requestFatJar;
|
||||
}
|
||||
|
||||
public String asGav(Project project) {
|
||||
var version = project.property(versionProperty);
|
||||
if (version == null) {
|
||||
throw new IllegalStateException("Could not find property " + versionProperty);
|
||||
}
|
||||
return gavPattern.formatted(version);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,126 @@
|
||||
package net.neoforged.neodev.installer;
|
||||
|
||||
import net.neoforged.neodev.utils.DependencyUtils;
|
||||
import org.gradle.api.DefaultTask;
|
||||
import org.gradle.api.artifacts.Configuration;
|
||||
import org.gradle.api.file.ArchiveOperations;
|
||||
import org.gradle.api.file.RegularFileProperty;
|
||||
import org.gradle.api.provider.ListProperty;
|
||||
import org.gradle.api.provider.Property;
|
||||
import org.gradle.api.tasks.Input;
|
||||
import org.gradle.api.tasks.InputFile;
|
||||
import org.gradle.api.tasks.OutputFile;
|
||||
import org.gradle.api.tasks.TaskAction;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
/**
|
||||
* Creates the JVM/program argument files used by the dedicated server launcher.
|
||||
*/
|
||||
public abstract class CreateArgsFile extends DefaultTask {
|
||||
@Inject
|
||||
public CreateArgsFile() {}
|
||||
|
||||
@InputFile
|
||||
public abstract RegularFileProperty getTemplate();
|
||||
|
||||
@Input
|
||||
public abstract Property<String> getFmlVersion();
|
||||
|
||||
@Input
|
||||
public abstract Property<String> getMinecraftVersion();
|
||||
|
||||
@Input
|
||||
public abstract Property<String> getNeoForgeVersion();
|
||||
|
||||
@Input
|
||||
public abstract Property<String> getRawNeoFormVersion();
|
||||
|
||||
@Input
|
||||
protected abstract Property<String> getPathSeparator();
|
||||
|
||||
@Input
|
||||
protected abstract Property<String> getModules();
|
||||
|
||||
@Input
|
||||
public abstract ListProperty<String> getIgnoreList();
|
||||
|
||||
@Input
|
||||
protected abstract Property<String> getClasspath();
|
||||
|
||||
public void setLibraries(String separator, Configuration classpath, Configuration modulePath) {
|
||||
getPathSeparator().set(separator);
|
||||
getClasspath().set(DependencyUtils.configurationToClasspath(classpath, "libraries/", separator));
|
||||
getModules().set(DependencyUtils.configurationToClasspath(modulePath, "libraries/", separator));
|
||||
}
|
||||
|
||||
@InputFile
|
||||
public abstract RegularFileProperty getRawServerJar();
|
||||
|
||||
@OutputFile
|
||||
public abstract RegularFileProperty getArgsFile();
|
||||
|
||||
@Inject
|
||||
protected abstract ArchiveOperations getArchiveOperations();
|
||||
|
||||
private String resolveClasspath() throws IOException {
|
||||
var ourClasspath = getClasspath().get() + getPathSeparator().get()
|
||||
+ "libraries/net/minecraft/server/%s/server-%s-extra.jar".formatted(
|
||||
getRawNeoFormVersion().get(), getRawNeoFormVersion().get());
|
||||
|
||||
// The raw server jar also contains its own classpath.
|
||||
// We want to make sure that our versions of the libraries are used when there is a conflict.
|
||||
var ourClasspathEntries = Stream.of(ourClasspath.split(getPathSeparator().get()))
|
||||
.map(CreateArgsFile::stripVersionSuffix)
|
||||
.collect(Collectors.toSet());
|
||||
|
||||
var serverClasspath = getArchiveOperations().zipTree(getRawServerJar())
|
||||
.filter(spec -> spec.getPath().endsWith("META-INF" + File.separator + "classpath-joined"))
|
||||
.getSingleFile();
|
||||
|
||||
var filteredServerClasspath = Stream.of(Files.readString(serverClasspath.toPath()).split(";"))
|
||||
.filter(path -> !ourClasspathEntries.contains(stripVersionSuffix(path)))
|
||||
// Exclude the actual MC server jar, which is under versions/
|
||||
.filter(path -> path.startsWith("libraries/"))
|
||||
.collect(Collectors.joining(getPathSeparator().get()));
|
||||
|
||||
return ourClasspath + getPathSeparator().get() + filteredServerClasspath;
|
||||
}
|
||||
|
||||
// Example:
|
||||
// Convert "libraries/com/github/oshi/oshi-core/6.4.10/oshi-core-6.4.10.jar"
|
||||
// to "libraries/com/github/oshi/oshi-core".
|
||||
private static String stripVersionSuffix(String classpathEntry) {
|
||||
var parts = classpathEntry.split("/");
|
||||
return String.join("/", List.of(parts).subList(0, parts.length - 2));
|
||||
}
|
||||
|
||||
@TaskAction
|
||||
public void createArgsFile() throws IOException {
|
||||
var replacements = new HashMap<String, String>();
|
||||
replacements.put("@MODULE_PATH@", getModules().get());
|
||||
replacements.put("@MODULES@", "ALL-MODULE-PATH");
|
||||
replacements.put("@IGNORE_LIST@", String.join(",", getIgnoreList().get()));
|
||||
replacements.put("@PLUGIN_LAYER_LIBRARIES@", "");
|
||||
replacements.put("@GAME_LAYER_LIBRARIES@", "");
|
||||
replacements.put("@CLASS_PATH@", resolveClasspath());
|
||||
replacements.put("@TASK@", "forgeserver");
|
||||
replacements.put("@FORGE_VERSION@", getNeoForgeVersion().get());
|
||||
replacements.put("@FML_VERSION@", getFmlVersion().get());
|
||||
replacements.put("@MC_VERSION@", getMinecraftVersion().get());
|
||||
replacements.put("@MCP_VERSION@", getRawNeoFormVersion().get());
|
||||
|
||||
var contents = Files.readString(getTemplate().get().getAsFile().toPath());
|
||||
for (var entry : replacements.entrySet()) {
|
||||
contents = contents.replaceAll(entry.getKey(), entry.getValue());
|
||||
}
|
||||
Files.writeString(getArgsFile().get().getAsFile().toPath(), contents);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,230 @@
|
||||
package net.neoforged.neodev.installer;
|
||||
|
||||
import com.google.gson.GsonBuilder;
|
||||
import net.neoforged.neodev.utils.FileUtils;
|
||||
import net.neoforged.neodev.utils.MavenIdentifier;
|
||||
import org.gradle.api.DefaultTask;
|
||||
import org.gradle.api.artifacts.Configuration;
|
||||
import org.gradle.api.file.RegularFileProperty;
|
||||
import org.gradle.api.provider.ListProperty;
|
||||
import org.gradle.api.provider.MapProperty;
|
||||
import org.gradle.api.provider.Property;
|
||||
import org.gradle.api.tasks.Input;
|
||||
import org.gradle.api.tasks.InputFile;
|
||||
import org.gradle.api.tasks.Nested;
|
||||
import org.gradle.api.tasks.OutputFile;
|
||||
import org.gradle.api.tasks.TaskAction;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import java.io.IOException;
|
||||
import java.net.URI;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Base64;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.function.BiConsumer;
|
||||
|
||||
/**
|
||||
* Creates the JSON profile used by legacyinstaller for installing the client into the vanilla launcher,
|
||||
* or installing a dedicated server.
|
||||
*/
|
||||
public abstract class CreateInstallerProfile extends DefaultTask {
|
||||
@Inject
|
||||
public CreateInstallerProfile() {}
|
||||
|
||||
@Input
|
||||
public abstract Property<String> getMinecraftVersion();
|
||||
|
||||
@Input
|
||||
public abstract Property<String> getNeoForgeVersion();
|
||||
|
||||
@Input
|
||||
public abstract Property<String> getMcAndNeoFormVersion();
|
||||
|
||||
@InputFile
|
||||
public abstract RegularFileProperty getIcon();
|
||||
|
||||
@Nested
|
||||
protected abstract ListProperty<IdentifiedFile> getLibraryFiles();
|
||||
|
||||
public void addLibraries(Configuration libraries) {
|
||||
getLibraryFiles().addAll(IdentifiedFile.listFromConfiguration(getProject(), libraries));
|
||||
}
|
||||
|
||||
@Input
|
||||
public abstract ListProperty<URI> getRepositoryURLs();
|
||||
|
||||
@Input
|
||||
public abstract MapProperty<InstallerProcessor, List<String>> getProcessorClasspaths();
|
||||
|
||||
@Input
|
||||
public abstract MapProperty<InstallerProcessor, String> getProcessorGavs();
|
||||
|
||||
@InputFile
|
||||
public abstract RegularFileProperty getUniversalJar();
|
||||
|
||||
@OutputFile
|
||||
public abstract RegularFileProperty getInstallerProfile();
|
||||
|
||||
private void addProcessor(List<ProcessorEntry> processors, @Nullable List<String> sides, InstallerProcessor processor, List<String> args) {
|
||||
var classpath = getProcessorClasspaths().get().get(processor);
|
||||
var mainJar = getProcessorGavs().get().get(processor);
|
||||
if (!classpath.contains(mainJar)) {
|
||||
throw new IllegalStateException("Processor %s is not included in its own classpath %s".formatted(mainJar, classpath));
|
||||
}
|
||||
processors.add(new ProcessorEntry(sides, mainJar, classpath, args));
|
||||
}
|
||||
|
||||
@TaskAction
|
||||
public void createInstallerProfile() throws IOException {
|
||||
var icon = "data:image/png;base64," + Base64.getEncoder().encodeToString(Files.readAllBytes(getIcon().getAsFile().get().toPath()));
|
||||
|
||||
var data = new LinkedHashMap<String, LauncherDataEntry>();
|
||||
var neoFormVersion = getMcAndNeoFormVersion().get();
|
||||
data.put("MAPPINGS", new LauncherDataEntry(String.format("[net.neoforged:neoform:%s:mappings@txt]", neoFormVersion), String.format("[net.neoforged:neoform:%s:mappings@txt]", neoFormVersion)));
|
||||
data.put("MOJMAPS", new LauncherDataEntry(String.format("[net.minecraft:client:%s:mappings@txt]", neoFormVersion), String.format("[net.minecraft:server:%s:mappings@txt]", neoFormVersion)));
|
||||
data.put("MERGED_MAPPINGS", new LauncherDataEntry(String.format("[net.neoforged:neoform:%s:mappings-merged@txt]", neoFormVersion), String.format("[net.neoforged:neoform:%s:mappings-merged@txt]", neoFormVersion)));
|
||||
data.put("BINPATCH", new LauncherDataEntry("/data/client.lzma", "/data/server.lzma"));
|
||||
data.put("MC_UNPACKED", new LauncherDataEntry(String.format("[net.minecraft:client:%s:unpacked]", neoFormVersion), String.format("[net.minecraft:server:%s:unpacked]", neoFormVersion)));
|
||||
data.put("MC_SLIM", new LauncherDataEntry(String.format("[net.minecraft:client:%s:slim]", neoFormVersion), String.format("[net.minecraft:server:%s:slim]", neoFormVersion)));
|
||||
data.put("MC_EXTRA", new LauncherDataEntry(String.format("[net.minecraft:client:%s:extra]", neoFormVersion), String.format("[net.minecraft:server:%s:extra]", neoFormVersion)));
|
||||
data.put("MC_SRG", new LauncherDataEntry(String.format("[net.minecraft:client:%s:srg]", neoFormVersion), String.format("[net.minecraft:server:%s:srg]", neoFormVersion)));
|
||||
data.put("PATCHED", new LauncherDataEntry(String.format("[%s:%s:%s:client]", "net.neoforged", "neoforge", getNeoForgeVersion().get()), String.format("[%s:%s:%s:server]", "net.neoforged", "neoforge", getNeoForgeVersion().get())));
|
||||
data.put("MCP_VERSION", new LauncherDataEntry(String.format("'%s'", neoFormVersion), String.format("'%s'", neoFormVersion)));
|
||||
|
||||
var processors = new ArrayList<ProcessorEntry>();
|
||||
BiConsumer<InstallerProcessor, List<String>> commonProcessor = (processor, args) -> addProcessor(processors, null, processor, args);
|
||||
BiConsumer<InstallerProcessor, List<String>> clientProcessor = (processor, args) -> addProcessor(processors, List.of("client"), processor, args);
|
||||
BiConsumer<InstallerProcessor, List<String>> serverProcessor = (processor, args) -> addProcessor(processors, List.of("server"), processor, args);
|
||||
|
||||
serverProcessor.accept(InstallerProcessor.INSTALLERTOOLS,
|
||||
List.of("--task", "EXTRACT_FILES", "--archive", "{INSTALLER}",
|
||||
|
||||
"--from", "data/run.sh", "--to", "{ROOT}/run.sh", "--exec", "{ROOT}/run.sh",
|
||||
|
||||
"--from", "data/run.bat", "--to", "{ROOT}/run.bat",
|
||||
|
||||
"--from", "data/user_jvm_args.txt", "--to", "{ROOT}/user_jvm_args.txt", "--optional", "{ROOT}/user_jvm_args.txt",
|
||||
|
||||
"--from", "data/win_args.txt", "--to", "{ROOT}/libraries/net/neoforged/neoforge/%s/win_args.txt".formatted(getNeoForgeVersion().get()),
|
||||
|
||||
"--from", "data/unix_args.txt", "--to", "{ROOT}/libraries/net/neoforged/neoforge/%s/unix_args.txt".formatted(getNeoForgeVersion().get()))
|
||||
);
|
||||
serverProcessor.accept(InstallerProcessor.INSTALLERTOOLS,
|
||||
List.of("--task", "BUNDLER_EXTRACT", "--input", "{MINECRAFT_JAR}", "--output", "{ROOT}/libraries/", "--libraries")
|
||||
);
|
||||
serverProcessor.accept(InstallerProcessor.INSTALLERTOOLS,
|
||||
List.of("--task", "BUNDLER_EXTRACT", "--input", "{MINECRAFT_JAR}", "--output", "{MC_UNPACKED}", "--jar-only")
|
||||
);
|
||||
var neoformDependency = "net.neoforged:neoform:" + getMcAndNeoFormVersion().get() + "@zip";;
|
||||
commonProcessor.accept(InstallerProcessor.INSTALLERTOOLS,
|
||||
List.of("--task", "MCP_DATA", "--input", String.format("[%s]", neoformDependency), "--output", "{MAPPINGS}", "--key", "mappings")
|
||||
);
|
||||
commonProcessor.accept(InstallerProcessor.INSTALLERTOOLS,
|
||||
List.of("--task", "DOWNLOAD_MOJMAPS", "--version", getMinecraftVersion().get(), "--side", "{SIDE}", "--output", "{MOJMAPS}")
|
||||
);
|
||||
commonProcessor.accept(InstallerProcessor.INSTALLERTOOLS,
|
||||
List.of("--task", "MERGE_MAPPING", "--left", "{MAPPINGS}", "--right", "{MOJMAPS}", "--output", "{MERGED_MAPPINGS}", "--classes", "--fields", "--methods", "--reverse-right")
|
||||
);
|
||||
clientProcessor.accept(InstallerProcessor.JARSPLITTER,
|
||||
List.of("--input", "{MINECRAFT_JAR}", "--slim", "{MC_SLIM}", "--extra", "{MC_EXTRA}", "--srg", "{MERGED_MAPPINGS}")
|
||||
);
|
||||
serverProcessor.accept(InstallerProcessor.JARSPLITTER,
|
||||
List.of("--input", "{MC_UNPACKED}", "--slim", "{MC_SLIM}", "--extra", "{MC_EXTRA}", "--srg", "{MERGED_MAPPINGS}")
|
||||
);
|
||||
// Note that the options supplied here have to match the ones used in the RemapJar task used to generate the binary patches
|
||||
commonProcessor.accept(InstallerProcessor.FART,
|
||||
List.of("--input", "{MC_SLIM}", "--output", "{MC_SRG}", "--names", "{MERGED_MAPPINGS}", "--ann-fix", "--ids-fix", "--src-fix", "--record-fix")
|
||||
);
|
||||
commonProcessor.accept(InstallerProcessor.BINPATCHER,
|
||||
List.of("--clean", "{MC_SRG}", "--output", "{PATCHED}", "--apply", "{BINPATCH}")
|
||||
);
|
||||
|
||||
getLogger().info("Collecting libraries for Installer Profile");
|
||||
// Remove potential duplicates.
|
||||
var libraryFilesToResolve = new LinkedHashMap<MavenIdentifier, IdentifiedFile>(getLibraryFiles().get().size());
|
||||
for (var libraryFile : getLibraryFiles().get()) {
|
||||
var existingFile = libraryFilesToResolve.putIfAbsent(libraryFile.getIdentifier().get(), libraryFile);
|
||||
if (existingFile != null) {
|
||||
var existing = existingFile.getFile().getAsFile().get();
|
||||
var duplicate = libraryFile.getFile().getAsFile().get();
|
||||
if (!existing.equals(duplicate)) {
|
||||
throw new IllegalArgumentException("Cannot resolve installer profile! Library %s has different files: %s and %s.".formatted(
|
||||
libraryFile.getIdentifier().get(),
|
||||
existing,
|
||||
duplicate));
|
||||
}
|
||||
}
|
||||
}
|
||||
var libraries = new ArrayList<>(
|
||||
LibraryCollector.resolveLibraries(getRepositoryURLs().get(), libraryFilesToResolve.values()));
|
||||
|
||||
var universalJar = getUniversalJar().getAsFile().get().toPath();
|
||||
libraries.add(new Library(
|
||||
"net.neoforged:neoforge:%s:universal".formatted(getNeoForgeVersion().get()),
|
||||
new LibraryDownload(new LibraryArtifact(
|
||||
LibraryCollector.sha1Hash(universalJar),
|
||||
Files.size(universalJar),
|
||||
"https://maven.neoforged.net/releases/net/neoforged/neoforge/%s/neoforge-%s-universal.jar".formatted(
|
||||
getNeoForgeVersion().get(),
|
||||
getNeoForgeVersion().get()),
|
||||
"net/neoforged/neoforge/%s/neoforge-%s-universal.jar".formatted(
|
||||
getNeoForgeVersion().get(),
|
||||
getNeoForgeVersion().get())
|
||||
))));
|
||||
|
||||
var profile = new InstallerProfile(
|
||||
"1",
|
||||
"NeoForge",
|
||||
"neoforge-%s".formatted(getNeoForgeVersion().get()),
|
||||
icon,
|
||||
getMinecraftVersion().get(),
|
||||
"/version.json",
|
||||
"/big_logo.png",
|
||||
"Welcome to the simple NeoForge installer",
|
||||
"https://mirrors.neoforged.net",
|
||||
true,
|
||||
data,
|
||||
processors,
|
||||
libraries,
|
||||
"{LIBRARY_DIR}/net/minecraft/server/{MINECRAFT_VERSION}/server-{MINECRAFT_VERSION}.jar"
|
||||
);
|
||||
|
||||
FileUtils.writeStringSafe(
|
||||
getInstallerProfile().getAsFile().get().toPath(),
|
||||
new GsonBuilder().setPrettyPrinting().disableHtmlEscaping().create().toJson(profile),
|
||||
StandardCharsets.UTF_8
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
record InstallerProfile(
|
||||
String spec,
|
||||
String profile,
|
||||
String version,
|
||||
String icon,
|
||||
String minecraft,
|
||||
String json,
|
||||
String logo,
|
||||
String welcome,
|
||||
String mirrorList,
|
||||
boolean hideExtract,
|
||||
Map<String, LauncherDataEntry> data,
|
||||
List<ProcessorEntry> processors,
|
||||
List<Library> libraries,
|
||||
String serverJarPath) {}
|
||||
|
||||
record LauncherDataEntry(
|
||||
String client,
|
||||
String server) {}
|
||||
|
||||
record ProcessorEntry(
|
||||
@Nullable
|
||||
List<String> sides,
|
||||
String jar,
|
||||
List<String> classpath,
|
||||
List<String> args) {}
|
||||
@ -0,0 +1,130 @@
|
||||
package net.neoforged.neodev.installer;
|
||||
|
||||
import com.google.gson.GsonBuilder;
|
||||
import net.neoforged.neodev.utils.DependencyUtils;
|
||||
import net.neoforged.neodev.utils.FileUtils;
|
||||
import org.gradle.api.DefaultTask;
|
||||
import org.gradle.api.artifacts.Configuration;
|
||||
import org.gradle.api.file.RegularFileProperty;
|
||||
import org.gradle.api.provider.ListProperty;
|
||||
import org.gradle.api.provider.Property;
|
||||
import org.gradle.api.tasks.Input;
|
||||
import org.gradle.api.tasks.Nested;
|
||||
import org.gradle.api.tasks.OutputFile;
|
||||
import org.gradle.api.tasks.TaskAction;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import java.io.IOException;
|
||||
import java.net.URI;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Creates the JSON file for running NeoForge via the Vanilla launcher.
|
||||
*/
|
||||
public abstract class CreateLauncherProfile extends DefaultTask {
|
||||
@Inject
|
||||
public CreateLauncherProfile() {}
|
||||
|
||||
@Input
|
||||
public abstract Property<String> getFmlVersion();
|
||||
|
||||
@Input
|
||||
public abstract Property<String> getMinecraftVersion();
|
||||
|
||||
@Input
|
||||
public abstract Property<String> getNeoForgeVersion();
|
||||
|
||||
@Input
|
||||
public abstract Property<String> getRawNeoFormVersion();
|
||||
|
||||
@Nested
|
||||
protected abstract ListProperty<IdentifiedFile> getLibraryFiles();
|
||||
|
||||
public void setLibraries(Configuration libraries) {
|
||||
getLibraryFiles().set(IdentifiedFile.listFromConfiguration(getProject(), libraries));
|
||||
}
|
||||
|
||||
@Input
|
||||
public abstract ListProperty<URI> getRepositoryURLs();
|
||||
|
||||
@Input
|
||||
public abstract ListProperty<String> getIgnoreList();
|
||||
|
||||
@Input
|
||||
protected abstract Property<String> getModulePath();
|
||||
|
||||
public void setModules(Configuration modules) {
|
||||
getModulePath().set(DependencyUtils.configurationToClasspath(modules, "${library_directory}/", "${classpath_separator}"));
|
||||
}
|
||||
|
||||
@OutputFile
|
||||
public abstract RegularFileProperty getLauncherProfile();
|
||||
|
||||
@TaskAction
|
||||
public void createLauncherProfile() throws IOException {
|
||||
var time = LocalDateTime.now().format(DateTimeFormatter.ISO_DATE_TIME);
|
||||
|
||||
getLogger().info("Collecting libraries for Launcher Profile");
|
||||
var libraries = LibraryCollector.resolveLibraries(getRepositoryURLs().get(), getLibraryFiles().get());
|
||||
|
||||
var gameArguments = new ArrayList<>(List.of(
|
||||
"--fml.neoForgeVersion", getNeoForgeVersion().get(),
|
||||
"--fml.fmlVersion", getFmlVersion().get(),
|
||||
"--fml.mcVersion", getMinecraftVersion().get(),
|
||||
"--fml.neoFormVersion", getRawNeoFormVersion().get(),
|
||||
"--launchTarget", "forgeclient"));
|
||||
|
||||
var jvmArguments = new ArrayList<>(List.of(
|
||||
"-Djava.net.preferIPv6Addresses=system",
|
||||
"-DignoreList=" + String.join(",", getIgnoreList().get()),
|
||||
"-DlibraryDirectory=${library_directory}"));
|
||||
|
||||
jvmArguments.add("-p");
|
||||
jvmArguments.add(getModulePath().get());
|
||||
|
||||
jvmArguments.addAll(List.of(
|
||||
"--add-modules", "ALL-MODULE-PATH",
|
||||
"--add-opens", "java.base/java.util.jar=cpw.mods.securejarhandler",
|
||||
"--add-opens", "java.base/java.lang.invoke=cpw.mods.securejarhandler",
|
||||
"--add-exports", "java.base/sun.security.util=cpw.mods.securejarhandler",
|
||||
"--add-exports", "jdk.naming.dns/com.sun.jndi.dns=java.naming"));
|
||||
|
||||
var arguments = new LinkedHashMap<String, List<String>>();
|
||||
arguments.put("game", gameArguments);
|
||||
arguments.put("jvm", jvmArguments);
|
||||
|
||||
var profile = new LauncherProfile(
|
||||
"neoforge-%s".formatted(getNeoForgeVersion().get()),
|
||||
time,
|
||||
time,
|
||||
"release",
|
||||
"cpw.mods.bootstraplauncher.BootstrapLauncher",
|
||||
getMinecraftVersion().get(),
|
||||
arguments,
|
||||
libraries
|
||||
);
|
||||
|
||||
FileUtils.writeStringSafe(
|
||||
getLauncherProfile().getAsFile().get().toPath(),
|
||||
new GsonBuilder().setPrettyPrinting().disableHtmlEscaping().create().toJson(profile),
|
||||
StandardCharsets.UTF_8
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
record LauncherProfile(
|
||||
String id,
|
||||
String time,
|
||||
String releaseTime,
|
||||
String type,
|
||||
String mainClass,
|
||||
String inheritsFrom,
|
||||
Map<String, List<String>> arguments,
|
||||
List<Library> libraries) {}
|
||||
|
||||
@ -0,0 +1,48 @@
|
||||
package net.neoforged.neodev.installer;
|
||||
|
||||
import net.neoforged.neodev.utils.DependencyUtils;
|
||||
import net.neoforged.neodev.utils.MavenIdentifier;
|
||||
import org.gradle.api.Project;
|
||||
import org.gradle.api.artifacts.Configuration;
|
||||
import org.gradle.api.artifacts.result.ResolvedArtifactResult;
|
||||
import org.gradle.api.file.RegularFileProperty;
|
||||
import org.gradle.api.provider.Property;
|
||||
import org.gradle.api.provider.Provider;
|
||||
import org.gradle.api.tasks.Input;
|
||||
import org.gradle.api.tasks.InputFile;
|
||||
import org.gradle.api.tasks.PathSensitive;
|
||||
import org.gradle.api.tasks.PathSensitivity;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import java.io.File;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Combines a {@link File} and its {@link MavenIdentifier maven identifier},
|
||||
* for usage as task inputs that will be passed to {@link LibraryCollector}.
|
||||
*/
|
||||
abstract class IdentifiedFile {
|
||||
static Provider<List<IdentifiedFile>> listFromConfiguration(Project project, Configuration configuration) {
|
||||
return configuration.getIncoming().getArtifacts().getResolvedArtifacts().map(
|
||||
artifacts -> artifacts.stream()
|
||||
.map(artifact -> IdentifiedFile.of(project, artifact))
|
||||
.toList());
|
||||
}
|
||||
|
||||
private static IdentifiedFile of(Project project, ResolvedArtifactResult resolvedArtifact) {
|
||||
var identifiedFile = project.getObjects().newInstance(IdentifiedFile.class);
|
||||
identifiedFile.getFile().set(resolvedArtifact.getFile());
|
||||
identifiedFile.getIdentifier().set(DependencyUtils.guessMavenIdentifier(resolvedArtifact));
|
||||
return identifiedFile;
|
||||
}
|
||||
|
||||
@Inject
|
||||
public IdentifiedFile() {}
|
||||
|
||||
@InputFile
|
||||
@PathSensitive(PathSensitivity.NONE)
|
||||
protected abstract RegularFileProperty getFile();
|
||||
|
||||
@Input
|
||||
protected abstract Property<MavenIdentifier> getIdentifier();
|
||||
}
|
||||
@ -0,0 +1,19 @@
|
||||
package net.neoforged.neodev.installer;
|
||||
|
||||
import net.neoforged.neodev.Tools;
|
||||
|
||||
/**
|
||||
* Identifies the tools used by the {@link InstallerProfile} to install NeoForge.
|
||||
*/
|
||||
public enum InstallerProcessor {
|
||||
BINPATCHER(Tools.BINPATCHER),
|
||||
FART(Tools.AUTO_RENAMING_TOOL),
|
||||
INSTALLERTOOLS(Tools.INSTALLERTOOLS),
|
||||
JARSPLITTER(Tools.JARSPLITTER);
|
||||
|
||||
public final Tools tool;
|
||||
|
||||
InstallerProcessor(Tools tool) {
|
||||
this.tool = tool;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,3 @@
|
||||
package net.neoforged.neodev.installer;
|
||||
|
||||
record Library(String name, LibraryDownload downloads) {}
|
||||
@ -0,0 +1,7 @@
|
||||
package net.neoforged.neodev.installer;
|
||||
|
||||
record LibraryArtifact(
|
||||
String sha1,
|
||||
long size,
|
||||
String url,
|
||||
String path) {}
|
||||
@ -0,0 +1,174 @@
|
||||
package net.neoforged.neodev.installer;
|
||||
|
||||
import net.neoforged.neodev.utils.MavenIdentifier;
|
||||
import org.gradle.api.logging.Logger;
|
||||
import org.gradle.api.logging.Logging;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.net.URI;
|
||||
import java.net.http.HttpClient;
|
||||
import java.net.http.HttpRequest;
|
||||
import java.net.http.HttpResponse;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.security.DigestInputStream;
|
||||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.HexFormat;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.function.Function;
|
||||
|
||||
/**
|
||||
* For each file in a collection, finds the repository that the file came from.
|
||||
*/
|
||||
class LibraryCollector {
|
||||
public static List<Library> resolveLibraries(List<URI> repositoryUrls, Collection<IdentifiedFile> libraries) throws IOException {
|
||||
var collector = new LibraryCollector(repositoryUrls);
|
||||
for (var library : libraries) {
|
||||
collector.addLibrary(library.getFile().getAsFile().get(), library.getIdentifier().get());
|
||||
}
|
||||
|
||||
var result = collector.libraries.stream().map(future -> {
|
||||
try {
|
||||
return future.get();
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}).toList();
|
||||
LOGGER.info("Collected %d libraries".formatted(result.size()));
|
||||
return result;
|
||||
}
|
||||
|
||||
private static final Logger LOGGER = Logging.getLogger(LibraryCollector.class);
|
||||
/**
|
||||
* Hosts from which we allow the installer to download.
|
||||
* We whitelist here to avoid redirecting player download traffic to anyone not affiliated with Mojang or us.
|
||||
*/
|
||||
private static final List<String> HOST_WHITELIST = List.of(
|
||||
"minecraft.net",
|
||||
"neoforged.net",
|
||||
"mojang.com"
|
||||
);
|
||||
|
||||
private static final URI MOJANG_MAVEN = URI.create("https://libraries.minecraft.net");
|
||||
private static final URI NEOFORGED_MAVEN = URI.create("https://maven.neoforged.net/releases");
|
||||
|
||||
private final List<URI> repositoryUrls;
|
||||
|
||||
private final List<Future<Library>> libraries = new ArrayList<>();
|
||||
|
||||
private final HttpClient httpClient = HttpClient.newBuilder().build();
|
||||
|
||||
private LibraryCollector(List<URI> repoUrl) {
|
||||
this.repositoryUrls = new ArrayList<>(repoUrl);
|
||||
|
||||
// Only remote repositories make sense (no maven local)
|
||||
repositoryUrls.removeIf(it -> {
|
||||
var lowercaseScheme = it.getScheme().toLowerCase(Locale.ROOT);
|
||||
return !lowercaseScheme.equals("https") && !lowercaseScheme.equals("http");
|
||||
});
|
||||
// Allow only URLs from whitelisted hosts
|
||||
repositoryUrls.removeIf(uri -> {
|
||||
var lowercaseHost = uri.getHost().toLowerCase(Locale.ROOT);
|
||||
return HOST_WHITELIST.stream().noneMatch(it -> lowercaseHost.equals(it) || lowercaseHost.endsWith("." + it));
|
||||
});
|
||||
// Always try Mojang Maven first, then our installer Maven
|
||||
repositoryUrls.removeIf(it -> it.getHost().equals(MOJANG_MAVEN.getHost()));
|
||||
repositoryUrls.removeIf(it -> it.getHost().equals(NEOFORGED_MAVEN.getHost()) && it.getPath().startsWith(NEOFORGED_MAVEN.getPath()));
|
||||
repositoryUrls.add(0, NEOFORGED_MAVEN);
|
||||
repositoryUrls.add(0, MOJANG_MAVEN);
|
||||
|
||||
LOGGER.info("Collecting libraries from:");
|
||||
for (var repo : repositoryUrls) {
|
||||
LOGGER.info(" - " + repo);
|
||||
}
|
||||
}
|
||||
|
||||
private void addLibrary(File file, MavenIdentifier identifier) throws IOException {
|
||||
final String name = identifier.artifactNotation();
|
||||
final String path = identifier.repositoryPath();
|
||||
|
||||
var sha1 = sha1Hash(file.toPath());
|
||||
var fileSize = Files.size(file.toPath());
|
||||
|
||||
// Try each configured repository in-order to find the file
|
||||
CompletableFuture<Library> libraryFuture = null;
|
||||
for (var repositoryUrl : repositoryUrls) {
|
||||
var artifactUri = joinUris(repositoryUrl, path);
|
||||
var request = HttpRequest.newBuilder(artifactUri)
|
||||
.method("HEAD", HttpRequest.BodyPublishers.noBody())
|
||||
.build();
|
||||
|
||||
Function<String, CompletableFuture<Library>> makeRequest = (String previousError) -> {
|
||||
return httpClient.sendAsync(request, HttpResponse.BodyHandlers.discarding())
|
||||
.thenApply(response -> {
|
||||
if (response.statusCode() != 200) {
|
||||
LOGGER.info(" Got %d for %s".formatted(response.statusCode(), artifactUri));
|
||||
String message = "Could not find %s: %d".formatted(artifactUri, response.statusCode());
|
||||
// Prepend error message from previous repo if they all fail
|
||||
if (previousError != null) {
|
||||
message = previousError + "\n" + message;
|
||||
}
|
||||
throw new RuntimeException(message);
|
||||
}
|
||||
LOGGER.info(" Found %s -> %s".formatted(name, artifactUri));
|
||||
return new Library(
|
||||
name,
|
||||
new LibraryDownload(new LibraryArtifact(
|
||||
sha1,
|
||||
fileSize,
|
||||
artifactUri.toString(),
|
||||
path)));
|
||||
});
|
||||
};
|
||||
|
||||
if (libraryFuture == null) {
|
||||
libraryFuture = makeRequest.apply(null);
|
||||
} else {
|
||||
libraryFuture = libraryFuture.exceptionallyCompose(error -> {
|
||||
return makeRequest.apply(error.getMessage());
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
libraries.add(libraryFuture);
|
||||
}
|
||||
|
||||
static String sha1Hash(Path path) throws IOException {
|
||||
MessageDigest digest;
|
||||
try {
|
||||
digest = MessageDigest.getInstance("SHA-1");
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
||||
try (var in = Files.newInputStream(path);
|
||||
var din = new DigestInputStream(in, digest)) {
|
||||
byte[] buffer = new byte[8192];
|
||||
while (din.read(buffer) != -1) {
|
||||
}
|
||||
}
|
||||
|
||||
return HexFormat.of().formatHex(digest.digest());
|
||||
}
|
||||
|
||||
private static URI joinUris(URI repositoryUrl, String path) {
|
||||
var baseUrl = repositoryUrl.toString();
|
||||
if (baseUrl.endsWith("/") && path.startsWith("/")) {
|
||||
while (path.startsWith("/")) {
|
||||
path = path.substring(1);
|
||||
}
|
||||
return URI.create(baseUrl + path);
|
||||
} else if (!baseUrl.endsWith("/") && !path.startsWith("/")) {
|
||||
return URI.create(baseUrl + "/" + path);
|
||||
} else {
|
||||
return URI.create(baseUrl + path);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,4 @@
|
||||
package net.neoforged.neodev.installer;
|
||||
|
||||
record LibraryDownload(
|
||||
LibraryArtifact artifact) {}
|
||||
@ -0,0 +1,84 @@
|
||||
package net.neoforged.neodev.utils;
|
||||
|
||||
import org.gradle.api.artifacts.Configuration;
|
||||
import org.gradle.api.artifacts.result.ResolvedArtifactResult;
|
||||
import org.gradle.api.provider.Provider;
|
||||
import org.gradle.internal.component.external.model.ModuleComponentArtifactIdentifier;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public final class DependencyUtils {
|
||||
private DependencyUtils() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a resolved artifact, try to guess which Maven GAV it was resolved from.
|
||||
*/
|
||||
public static MavenIdentifier guessMavenIdentifier(ResolvedArtifactResult result) {
|
||||
String group;
|
||||
String artifact;
|
||||
String version;
|
||||
String ext = "";
|
||||
String classifier = "";
|
||||
|
||||
var filename = result.getFile().getName();
|
||||
var startOfExt = filename.lastIndexOf('.');
|
||||
if (startOfExt != -1) {
|
||||
ext = filename.substring(startOfExt + 1);
|
||||
filename = filename.substring(0, startOfExt);
|
||||
}
|
||||
|
||||
if (result.getId() instanceof ModuleComponentArtifactIdentifier moduleId) {
|
||||
group = moduleId.getComponentIdentifier().getGroup();
|
||||
artifact = moduleId.getComponentIdentifier().getModule();
|
||||
version = moduleId.getComponentIdentifier().getVersion();
|
||||
var expectedBasename = artifact + "-" + version;
|
||||
|
||||
if (filename.startsWith(expectedBasename + "-")) {
|
||||
classifier = filename.substring((expectedBasename + "-").length());
|
||||
}
|
||||
} else {
|
||||
// When we encounter a project reference, the component identifier does not expose the group or module name.
|
||||
// But we can access the list of capabilities associated with the published variant the artifact originates from.
|
||||
// If the capability was not overridden, this will be the project GAV. If it is *not* the project GAV,
|
||||
// it will be at least in valid GAV format, not crashing NFRT when it parses the manifest. It will just be ignored.
|
||||
var capabilities = result.getVariant().getCapabilities();
|
||||
if (capabilities.size() == 1) {
|
||||
var capability = capabilities.get(0);
|
||||
group = capability.getGroup();
|
||||
artifact = capability.getName();
|
||||
version = capability.getVersion();
|
||||
} else {
|
||||
throw new IllegalArgumentException("Don't know how to break " + result.getId().getComponentIdentifier() + " into Maven components.");
|
||||
}
|
||||
}
|
||||
return new MavenIdentifier(group, artifact, version, classifier, ext);
|
||||
}
|
||||
|
||||
/**
|
||||
* Turns a configuration into a list of GAV entries.
|
||||
*/
|
||||
public static Provider<List<String>> configurationToGavList(Configuration configuration) {
|
||||
return configuration.getIncoming().getArtifacts().getResolvedArtifacts().map(results -> {
|
||||
// Using .toList() fails with the configuration cache - looks like Gradle can't deserialize the resulting list?
|
||||
return results.stream().map(artifact -> guessMavenIdentifier(artifact).artifactNotation()).collect(Collectors.toCollection(ArrayList::new));
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Turns a configuration into a classpath string,
|
||||
* assuming that the contents of the configuration are installed following the Maven directory layout.
|
||||
*
|
||||
* @param prefix string to add in front of each classpath entry
|
||||
* @param separator separator to add between each classpath entry
|
||||
*/
|
||||
public static Provider<String> configurationToClasspath(Configuration configuration, String prefix, String separator) {
|
||||
return configuration.getIncoming().getArtifacts().getResolvedArtifacts().map(
|
||||
results -> results.stream()
|
||||
.map(artifact -> prefix + guessMavenIdentifier(artifact).repositoryPath())
|
||||
.collect(Collectors.joining(separator))
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,72 @@
|
||||
package net.neoforged.neodev.utils;
|
||||
|
||||
import java.io.FilterOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.nio.charset.Charset;
|
||||
import java.nio.file.AtomicMoveNotSupportedException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.StandardCopyOption;
|
||||
import java.util.List;
|
||||
|
||||
public final class FileUtils {
|
||||
private FileUtils() {
|
||||
}
|
||||
|
||||
public static void writeStringSafe(Path destination, String content, Charset charset) throws IOException {
|
||||
if (!charset.newEncoder().canEncode(content)) {
|
||||
throw new IllegalArgumentException("The given character set " + charset
|
||||
+ " cannot represent this string: " + content);
|
||||
}
|
||||
|
||||
try (var out = newSafeFileOutputStream(destination)) {
|
||||
var encodedContent = content.getBytes(charset);
|
||||
out.write(encodedContent);
|
||||
}
|
||||
}
|
||||
|
||||
public static void writeLinesSafe(Path destination, List<String> lines, Charset charset) throws IOException {
|
||||
writeStringSafe(destination, String.join("\n", lines), charset);
|
||||
}
|
||||
|
||||
public static OutputStream newSafeFileOutputStream(Path destination) throws IOException {
|
||||
var uniqueId = ProcessHandle.current().pid() + "." + Thread.currentThread().getId();
|
||||
|
||||
var tempFile = destination.resolveSibling(destination.getFileName().toString() + "." + uniqueId + ".tmp");
|
||||
var closed = new boolean[1];
|
||||
return new FilterOutputStream(Files.newOutputStream(tempFile)) {
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
try {
|
||||
super.close();
|
||||
if (!closed[0]) {
|
||||
atomicMoveIfPossible(tempFile, destination);
|
||||
}
|
||||
} finally {
|
||||
try {
|
||||
Files.deleteIfExists(tempFile);
|
||||
} catch (IOException ignored) {
|
||||
}
|
||||
closed[0] = true;
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Atomically moves the given source file to the given destination file.
|
||||
* If the atomic move is not supported, the file will be moved normally.
|
||||
*
|
||||
* @param source The source file
|
||||
* @param destination The destination file
|
||||
* @throws IOException If an I/O error occurs
|
||||
*/
|
||||
private static void atomicMoveIfPossible(final Path source, final Path destination) throws IOException {
|
||||
try {
|
||||
Files.move(source, destination, StandardCopyOption.ATOMIC_MOVE, StandardCopyOption.REPLACE_EXISTING);
|
||||
} catch (AtomicMoveNotSupportedException ex) {
|
||||
Files.move(source, destination, StandardCopyOption.REPLACE_EXISTING);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,13 @@
|
||||
package net.neoforged.neodev.utils;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
public record MavenIdentifier(String group, String artifact, String version, String classifier, String extension) implements Serializable {
|
||||
public String artifactNotation() {
|
||||
return group + ":" + artifact + ":" + version + (classifier.isEmpty() ? "" : ":" + classifier) + ("jar".equals(extension) ? "" : "@" + extension);
|
||||
}
|
||||
|
||||
public String repositoryPath() {
|
||||
return group.replace(".", "/") + "/" + artifact + "/" + version + "/" + artifact + "-" + version + (classifier.isEmpty() ? "" : "-" + classifier) + "." + extension;
|
||||
}
|
||||
}
|
||||
@ -5,15 +5,6 @@ plugins {
|
||||
id 'neoforge.formatting-conventions'
|
||||
}
|
||||
|
||||
repositories {
|
||||
maven { url = 'https://maven.neoforged.net/releases' }
|
||||
maven {
|
||||
name 'Mojang'
|
||||
url 'https://libraries.minecraft.net'
|
||||
}
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
jar {
|
||||
manifest {
|
||||
attributes(
|
||||
|
||||
8
coremods/settings.gradle
Normal file
8
coremods/settings.gradle
Normal file
@ -0,0 +1,8 @@
|
||||
repositories {
|
||||
maven { url = 'https://maven.neoforged.net/releases' }
|
||||
maven {
|
||||
name 'Mojang'
|
||||
url 'https://libraries.minecraft.net'
|
||||
}
|
||||
mavenCentral()
|
||||
}
|
||||
@ -4,8 +4,14 @@ org.gradle.jvmargs=-Xmx3G
|
||||
org.gradle.daemon=true
|
||||
org.gradle.parallel=true
|
||||
org.gradle.caching=true
|
||||
org.gradle.configuration-cache=false
|
||||
org.gradle.configuration-cache=true
|
||||
org.gradle.debug=false
|
||||
#org.gradle.warning.mode=fail
|
||||
|
||||
# renovate: net.neoforged:moddev-gradle
|
||||
moddevgradle_plugin_version=2.0.46-beta
|
||||
# renovate: io.codechicken:DiffPatch
|
||||
diffpatch_version=2.0.0.35
|
||||
|
||||
java_version=21
|
||||
|
||||
@ -14,6 +20,14 @@ neoform_version=20241023.131943
|
||||
# on snapshot versions, used to prefix the version
|
||||
neoforge_snapshot_next_stable=21.4
|
||||
|
||||
# renovate: net.neoforged.jst:jst-cli-bundle
|
||||
jst_version=1.0.45
|
||||
legacyinstaller_version=3.0.+
|
||||
# renovate: net.neoforged:AutoRenamingTool
|
||||
art_version=2.0.3
|
||||
# renovate: net.neoforged.installertools:installertools
|
||||
installertools_version=2.1.2
|
||||
|
||||
mergetool_version=2.0.0
|
||||
accesstransformers_version=11.0.1
|
||||
coremods_version=6.0.4
|
||||
@ -22,7 +36,6 @@ modlauncher_version=11.0.4
|
||||
securejarhandler_version=3.0.8
|
||||
bootstraplauncher_version=2.0.2
|
||||
asm_version=9.7
|
||||
installer_version=2.1.+
|
||||
mixin_version=0.15.2+mixin.0.8.7
|
||||
terminalconsoleappender_version=1.3.0
|
||||
nightconfig_version=3.8.0
|
||||
@ -43,12 +56,8 @@ nashorn_core_version=15.3
|
||||
lwjgl_glfw_version=3.3.2
|
||||
mixin_extras_version=0.4.1
|
||||
|
||||
jupiter_api_version=5.7.0
|
||||
jupiter_api_version=5.10.2
|
||||
vintage_engine_version=5.+
|
||||
assertj_core=3.25.1
|
||||
|
||||
neogradle.runtime.platform.installer.debug=true
|
||||
# We want to be able to have a junit run disconnected from the test and main sourcesets
|
||||
neogradle.subsystems.conventions.sourcesets.automatic-inclusion=false
|
||||
neogradle.subsystems.conventions.enabled=false
|
||||
neogradle.subsystems.tools.jst=net.neoforged.jst:jst-cli-bundle:1.0.45
|
||||
|
||||
2
gradle/wrapper/gradle-wrapper.properties
vendored
2
gradle/wrapper/gradle-wrapper.properties
vendored
@ -1,6 +1,6 @@
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.10-bin.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.10.1-bin.zip
|
||||
networkTimeout=10000
|
||||
validateDistributionUrl=true
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
|
||||
@ -1,3 +1,15 @@
|
||||
dynamicProject {
|
||||
neoform("${project.minecraft_version}-${project.neoform_version}")
|
||||
|
||||
plugins {
|
||||
id 'java-library'
|
||||
}
|
||||
|
||||
apply plugin: net.neoforged.neodev.NeoDevBasePlugin
|
||||
|
||||
dependencies {
|
||||
implementation("net.neoforged:neoform:${project.minecraft_version}-${project.neoform_version}") {
|
||||
capabilities {
|
||||
requireCapability 'net.neoforged:neoform-dependencies'
|
||||
}
|
||||
endorseStrictVersions()
|
||||
}
|
||||
}
|
||||
|
||||
@ -10,6 +10,11 @@ plugins {
|
||||
id 'neoforge.versioning'
|
||||
}
|
||||
|
||||
apply plugin : net.neoforged.neodev.NeoDevPlugin
|
||||
|
||||
// Because of the source set reference.
|
||||
evaluationDependsOn(":neoforge-coremods")
|
||||
|
||||
gradleutils.setupSigning(project: project, signAllPublications: true)
|
||||
|
||||
changelog {
|
||||
@ -17,55 +22,6 @@ changelog {
|
||||
disableAutomaticPublicationRegistration()
|
||||
}
|
||||
|
||||
dynamicProject {
|
||||
runtime("${project.minecraft_version}-${project.neoform_version}",
|
||||
rootProject.layout.projectDirectory.dir('patches'),
|
||||
rootProject.layout.projectDirectory.dir('rejects'))
|
||||
}
|
||||
|
||||
final checkVersion = JCCPlugin.providePreviousVersion(
|
||||
project.providers, project.providers.provider({['https://maven.neoforged.net/releases']}), project.providers.provider({'net.neoforged:neoforge'}),
|
||||
project.provider { project.version }.map { ver -> CompatibilityTask.VersionComponentTest.MINOR.predicate(ver) }
|
||||
)
|
||||
final createCompatJar = tasks.register('createCompatibilityCheckJar', ProvideNeoForgeJarTask) {
|
||||
// Use the same jar that the patches were generated against
|
||||
cleanJar.set(tasks.generateClientBinaryPatches.clean)
|
||||
maven.set('https://maven.neoforged.net/releases')
|
||||
artifact.set('net.neoforged:neoforge')
|
||||
version.set(checkVersion)
|
||||
javaLauncher = javaToolchains.launcherFor {
|
||||
languageVersion = JavaLanguageVersion.of(java_version)
|
||||
}
|
||||
}
|
||||
checkJarCompatibility {
|
||||
isAPI = true
|
||||
baseJar = createCompatJar.flatMap { it.output }
|
||||
}
|
||||
|
||||
installerProfile {
|
||||
profile = 'NeoForge'
|
||||
}
|
||||
|
||||
minecraft {
|
||||
// FML looks for this mod id to find the minecraft classes
|
||||
modIdentifier 'minecraft'
|
||||
|
||||
accessTransformers {
|
||||
file rootProject.file('src/main/resources/META-INF/accesstransformer.cfg')
|
||||
}
|
||||
}
|
||||
|
||||
tasks.configureEach { tsk ->
|
||||
if (tsk.name == 'neoFormApplyUserAccessTransformer' && project.hasProperty('validateAccessTransformers')) {
|
||||
tsk.inputs.property('validation', 'error')
|
||||
tsk.logLevel('ERROR')
|
||||
tsk.doFirst {
|
||||
tsk.getRuntimeProgramArguments().addAll(tsk.getRuntimeProgramArguments().get())
|
||||
tsk.getRuntimeProgramArguments().add('--access-transformer-validation=error')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sourceSets {
|
||||
main {
|
||||
java {
|
||||
@ -77,12 +33,56 @@ sourceSets {
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
runtimeOnly "cpw.mods:bootstraplauncher:${project.bootstraplauncher_version}"
|
||||
final checkVersion = JCCPlugin.providePreviousVersion(
|
||||
project.providers, project.providers.provider({['https://maven.neoforged.net/releases']}), project.providers.provider({'net.neoforged:neoforge'}),
|
||||
project.provider { project.version }.map { ver -> CompatibilityTask.VersionComponentTest.MINOR.predicate(ver) }
|
||||
)
|
||||
final createCompatJar = tasks.register('createCompatibilityCheckJar', ProvideNeoForgeJarTask) {
|
||||
// Use the same jar that the patches were generated against
|
||||
cleanJar.set(tasks.generateClientBinPatches.cleanJar)
|
||||
maven.set('https://maven.neoforged.net/releases')
|
||||
artifact.set('net.neoforged:neoforge')
|
||||
version.set(checkVersion)
|
||||
javaLauncher = javaToolchains.launcherFor {
|
||||
languageVersion = JavaLanguageVersion.of(java_version)
|
||||
}
|
||||
}
|
||||
checkJarCompatibility {
|
||||
isAPI = true
|
||||
baseJar = createCompatJar.flatMap { it.output }
|
||||
}
|
||||
|
||||
moduleOnly "cpw.mods:securejarhandler:${project.securejarhandler_version}"
|
||||
neoDev {
|
||||
mods {
|
||||
minecraft {
|
||||
sourceSet sourceSets.main
|
||||
}
|
||||
"neoforge-coremods" {
|
||||
sourceSet project(":neoforge-coremods").sourceSets.main
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
// For an overview of what the nonstandard configurations do,
|
||||
// have a look at NeoDevConfigurations.java in the buildSrc folder.
|
||||
|
||||
neoFormData("net.neoforged:neoform:${project.minecraft_version}-${project.neoform_version}") {
|
||||
capabilities {
|
||||
requireCapability 'net.neoforged:neoform'
|
||||
}
|
||||
endorseStrictVersions()
|
||||
}
|
||||
neoFormDependencies("net.neoforged:neoform:${project.minecraft_version}-${project.neoform_version}") {
|
||||
capabilities {
|
||||
requireCapability 'net.neoforged:neoform-dependencies'
|
||||
}
|
||||
endorseStrictVersions()
|
||||
}
|
||||
|
||||
moduleLibraries "cpw.mods:securejarhandler:${project.securejarhandler_version}"
|
||||
for (var asmModule : ["org.ow2.asm:asm", "org.ow2.asm:asm-commons", "org.ow2.asm:asm-tree", "org.ow2.asm:asm-util", "org.ow2.asm:asm-analysis"]) {
|
||||
moduleOnly(asmModule) {
|
||||
moduleLibraries(asmModule) {
|
||||
// Vanilla ships with ASM 9.3 transitively (via their OpenID connect library dependency), we require
|
||||
// ASM in a more recent version and have to strictly require this to override the strict Minecraft version.
|
||||
version {
|
||||
@ -90,166 +90,72 @@ dependencies {
|
||||
}
|
||||
}
|
||||
}
|
||||
moduleOnly "cpw.mods:bootstraplauncher:${project.bootstraplauncher_version}"
|
||||
moduleOnly "net.neoforged:JarJarFileSystems:${project.jarjar_version}"
|
||||
moduleLibraries "cpw.mods:bootstraplauncher:${project.bootstraplauncher_version}"
|
||||
moduleLibraries "net.neoforged:JarJarFileSystems:${project.jarjar_version}"
|
||||
|
||||
installer ("net.neoforged.fancymodloader:loader:${project.fancy_mod_loader_version}") {
|
||||
libraries ("net.neoforged.fancymodloader:loader:${project.fancy_mod_loader_version}") {
|
||||
exclude group: 'org.slf4j'
|
||||
exclude group: 'net.fabricmc'
|
||||
}
|
||||
installer ("net.neoforged.fancymodloader:earlydisplay:${project.fancy_mod_loader_version}") {
|
||||
libraries ("net.neoforged.fancymodloader:earlydisplay:${project.fancy_mod_loader_version}") {
|
||||
exclude group: 'org.lwjgl'
|
||||
exclude group: 'org.slf4j'
|
||||
exclude group: 'net.fabricmc'
|
||||
}
|
||||
installer "cpw.mods:securejarhandler:${project.securejarhandler_version}"
|
||||
installer "org.ow2.asm:asm:${project.asm_version}"
|
||||
installer "org.ow2.asm:asm-commons:${project.asm_version}"
|
||||
installer "org.ow2.asm:asm-tree:${project.asm_version}"
|
||||
installer "org.ow2.asm:asm-util:${project.asm_version}"
|
||||
installer "org.ow2.asm:asm-analysis:${project.asm_version}"
|
||||
installer "net.neoforged:accesstransformers:${project.accesstransformers_version}"
|
||||
installer "net.neoforged:bus:${project.eventbus_version}"
|
||||
installer "net.neoforged:coremods:${project.coremods_version}"
|
||||
installer "cpw.mods:modlauncher:${project.modlauncher_version}"
|
||||
installer "net.neoforged:mergetool:${project.mergetool_version}:api"
|
||||
installer "com.electronwill.night-config:core:${project.nightconfig_version}"
|
||||
installer "com.electronwill.night-config:toml:${project.nightconfig_version}"
|
||||
installer "org.apache.maven:maven-artifact:${project.apache_maven_artifact_version}"
|
||||
installer "net.jodah:typetools:${project.typetools_version}"
|
||||
installer "net.minecrell:terminalconsoleappender:${project.terminalconsoleappender_version}"
|
||||
installer("net.fabricmc:sponge-mixin:${project.mixin_version}") { transitive = false }
|
||||
installer "org.openjdk.nashorn:nashorn-core:${project.nashorn_core_version}"
|
||||
installer ("net.neoforged:JarJarSelector:${project.jarjar_version}") {
|
||||
libraries "net.neoforged:accesstransformers:${project.accesstransformers_version}"
|
||||
libraries "net.neoforged:bus:${project.eventbus_version}"
|
||||
libraries "net.neoforged:coremods:${project.coremods_version}"
|
||||
libraries "cpw.mods:modlauncher:${project.modlauncher_version}"
|
||||
libraries "net.neoforged:mergetool:${project.mergetool_version}:api"
|
||||
libraries "com.electronwill.night-config:core:${project.nightconfig_version}"
|
||||
libraries "com.electronwill.night-config:toml:${project.nightconfig_version}"
|
||||
libraries "org.apache.maven:maven-artifact:${project.apache_maven_artifact_version}"
|
||||
libraries "net.jodah:typetools:${project.typetools_version}"
|
||||
libraries "net.minecrell:terminalconsoleappender:${project.terminalconsoleappender_version}"
|
||||
libraries("net.fabricmc:sponge-mixin:${project.mixin_version}") { transitive = false }
|
||||
libraries "org.openjdk.nashorn:nashorn-core:${project.nashorn_core_version}"
|
||||
libraries ("net.neoforged:JarJarSelector:${project.jarjar_version}") {
|
||||
exclude group: 'org.slf4j'
|
||||
}
|
||||
// We depend on apache commons directly as there is a difference between the version the server uses and the one the client does
|
||||
installer "org.apache.commons:commons-lang3:${project.apache_commons_lang3_version}"
|
||||
installer ("net.neoforged:JarJarMetadata:${project.jarjar_version}") {
|
||||
libraries "org.apache.commons:commons-lang3:${project.apache_commons_lang3_version}"
|
||||
libraries ("net.neoforged:JarJarMetadata:${project.jarjar_version}") {
|
||||
exclude group: 'org.slf4j'
|
||||
}
|
||||
// Manually override log4j since the version coming from other `installer` dependencies is outdated
|
||||
installer "org.apache.logging.log4j:log4j-api:${project.log4j_version}"
|
||||
installer "org.apache.logging.log4j:log4j-core:${project.log4j_version}"
|
||||
|
||||
compileOnly "org.jetbrains:annotations:${project.jetbrains_annotations_version}"
|
||||
|
||||
userdevCompileOnly jarJar("io.github.llamalad7:mixinextras-neoforge:${project.mixin_extras_version}"), {
|
||||
jarJar.ranged(it, "[${project.mixin_extras_version},)")
|
||||
userdevCompileOnly jarJar("io.github.llamalad7:mixinextras-neoforge:${project.mixin_extras_version}")
|
||||
|
||||
userdevTestFixtures("net.neoforged.fancymodloader:junit-fml:${project.fancy_mod_loader_version}") {
|
||||
endorseStrictVersions()
|
||||
}
|
||||
|
||||
userdevTestImplementation("net.neoforged.fancymodloader:junit-fml:${project.fancy_mod_loader_version}")
|
||||
compileOnly(jarJar(project(":neoforge-coremods")))
|
||||
// Must be implementation instead of compileOnly so that running dependent projects such as tests will trigger (re)compilation of coremods.
|
||||
// (Only needed when compiling through IntelliJ non-delegated builds - otherwise `compileOnly` would work).
|
||||
implementation(jarJar(project(":neoforge-coremods")))
|
||||
}
|
||||
|
||||
runTypes {
|
||||
client {
|
||||
singleInstance false
|
||||
client true
|
||||
|
||||
arguments.addAll '--fml.neoForgeVersion', project.version
|
||||
arguments.addAll '--fml.fmlVersion', project.fancy_mod_loader_version
|
||||
arguments.addAll '--fml.mcVersion', project.minecraft_version
|
||||
arguments.addAll '--fml.neoFormVersion', project.neoform_version
|
||||
}
|
||||
|
||||
server {
|
||||
server true
|
||||
|
||||
arguments.addAll '--fml.neoForgeVersion', project.version
|
||||
arguments.addAll '--fml.fmlVersion', project.fancy_mod_loader_version
|
||||
arguments.addAll '--fml.mcVersion', project.minecraft_version
|
||||
arguments.addAll '--fml.neoFormVersion', project.neoform_version
|
||||
}
|
||||
|
||||
gameTestServer {
|
||||
from project.runTypes.server
|
||||
|
||||
gameTest true
|
||||
}
|
||||
|
||||
gameTestClient {
|
||||
from project.runTypes.client
|
||||
|
||||
gameTest true
|
||||
}
|
||||
|
||||
data {
|
||||
dataGenerator true
|
||||
|
||||
// Don't set modid here so we can reuse this runType for test datagen
|
||||
arguments.addAll '--fml.neoForgeVersion', project.version
|
||||
arguments.addAll '--fml.fmlVersion', project.fancy_mod_loader_version
|
||||
arguments.addAll '--fml.mcVersion', project.minecraft_version
|
||||
arguments.addAll '--fml.neoFormVersion', project.neoform_version
|
||||
}
|
||||
|
||||
junit {
|
||||
junit true
|
||||
arguments.addAll '--fml.neoForgeVersion', project.version
|
||||
arguments.addAll '--fml.fmlVersion', project.fancy_mod_loader_version
|
||||
arguments.addAll '--fml.mcVersion', project.minecraft_version
|
||||
arguments.addAll '--fml.neoFormVersion', project.neoform_version
|
||||
}
|
||||
}
|
||||
|
||||
runs {
|
||||
client { }
|
||||
server { }
|
||||
gameTestServer { }
|
||||
gameTestClient { }
|
||||
data {
|
||||
arguments.addAll '--mod', 'neoforge'
|
||||
|
||||
modSources.add project.sourceSets.main
|
||||
|
||||
idea {
|
||||
primarySourceSet project.sourceSets.main
|
||||
neoDev {
|
||||
runs {
|
||||
client {
|
||||
client()
|
||||
}
|
||||
server {
|
||||
server()
|
||||
}
|
||||
gameTestServer {
|
||||
type = "gameTestServer"
|
||||
}
|
||||
data {
|
||||
data()
|
||||
programArguments.addAll '--mod', 'neoforge', '--flat', '--all', '--validate',
|
||||
'--existing', rootProject.file("src/main/resources").absolutePath,
|
||||
'--output', rootProject.file("src/generated/resources").absolutePath
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
runs.configureEach { it ->
|
||||
modSources.add project(":neoforge-coremods").sourceSets.main
|
||||
|
||||
final File gameDir = project.file("run/${it.name}") as File
|
||||
gameDir.mkdirs();
|
||||
|
||||
it.workingDirectory.set gameDir
|
||||
it.arguments.addAll '--gameDir', gameDir.absolutePath
|
||||
}
|
||||
|
||||
tasks.register("genPatches") {
|
||||
dependsOn tasks.unpackSourcePatches
|
||||
}
|
||||
|
||||
launcherProfile {
|
||||
arguments {
|
||||
game '--fml.neoForgeVersion'
|
||||
game project.version
|
||||
game '--fml.fmlVersion'
|
||||
game project.fancy_mod_loader_version
|
||||
game '--fml.mcVersion'
|
||||
game project.minecraft_version
|
||||
game '--fml.neoFormVersion'
|
||||
game project.neoform_version
|
||||
}
|
||||
}
|
||||
|
||||
userdevProfile {
|
||||
runTypes.configureEach {
|
||||
argument '--fml.neoForgeVersion'
|
||||
argument project.version
|
||||
argument '--fml.fmlVersion'
|
||||
argument project.fancy_mod_loader_version
|
||||
argument '--fml.mcVersion'
|
||||
argument project.minecraft_version
|
||||
argument '--fml.neoFormVersion'
|
||||
argument project.neoform_version
|
||||
}
|
||||
additionalTestDependencyArtifactCoordinate "net.neoforged:testframework:${project.version}"
|
||||
}
|
||||
|
||||
tasks.withType(Javadoc.class).configureEach {
|
||||
options.tags = [
|
||||
'apiNote:a:<em>API Note:</em>',
|
||||
@ -259,27 +165,6 @@ tasks.withType(Javadoc.class).configureEach {
|
||||
options.addStringOption('Xdoclint:all,-missing', '-public')
|
||||
}
|
||||
|
||||
configurations {
|
||||
forValidation {
|
||||
canBeConsumed = true
|
||||
canBeResolved = false
|
||||
attributes {
|
||||
attribute(Category.CATEGORY_ATTRIBUTE, objects.named(Category, Category.LIBRARY))
|
||||
attribute(Usage.USAGE_ATTRIBUTE, objects.named(Usage, Usage.JAVA_RUNTIME))
|
||||
attribute(Bundling.BUNDLING_ATTRIBUTE, objects.named(Bundling, Bundling.EXTERNAL))
|
||||
attribute(LibraryElements.LIBRARY_ELEMENTS_ATTRIBUTE, objects.named(LibraryElements, LibraryElements.JAR))
|
||||
}
|
||||
|
||||
extendsFrom api, runtimeOnly
|
||||
}
|
||||
}
|
||||
|
||||
artifacts {
|
||||
forValidation(jar.archiveFile) {
|
||||
builtBy(jar)
|
||||
}
|
||||
}
|
||||
|
||||
AdhocComponentWithVariants javaComponent = (AdhocComponentWithVariants) project.components.findByName("java")
|
||||
// Ensure the two default variants are not published, since they
|
||||
// contain Minecraft classes
|
||||
@ -290,10 +175,12 @@ javaComponent.withVariantsFromConfiguration(configurations.runtimeElements) {
|
||||
it.skip()
|
||||
}
|
||||
|
||||
// Resolvable configurations only
|
||||
configurations {
|
||||
modDevBundle {
|
||||
canBeDeclared = false
|
||||
canBeResolved = false
|
||||
canBeConsumed = true
|
||||
extendsFrom neoFormData
|
||||
attributes {
|
||||
attribute(Category.CATEGORY_ATTRIBUTE, objects.named(Category, "data"))
|
||||
attribute(Bundling.BUNDLING_ATTRIBUTE, objects.named(Bundling, Bundling.EXTERNAL))
|
||||
@ -304,8 +191,8 @@ configurations {
|
||||
javaComponent.addVariantsFromConfiguration(it) {} // Publish it
|
||||
}
|
||||
modDevConfig {
|
||||
canBeDeclared = false
|
||||
canBeResolved = false
|
||||
canBeConsumed = true
|
||||
attributes {
|
||||
attribute(Category.CATEGORY_ATTRIBUTE, objects.named(Category, "data"))
|
||||
attribute(Bundling.BUNDLING_ATTRIBUTE, objects.named(Bundling, Bundling.EXTERNAL))
|
||||
@ -316,8 +203,8 @@ configurations {
|
||||
javaComponent.addVariantsFromConfiguration(it) {} // Publish it
|
||||
}
|
||||
installerJar {
|
||||
canBeDeclared = false
|
||||
canBeResolved = false
|
||||
canBeConsumed = true
|
||||
attributes {
|
||||
attribute(Category.CATEGORY_ATTRIBUTE, objects.named(Category, Category.LIBRARY))
|
||||
attribute(Usage.USAGE_ATTRIBUTE, objects.named(Usage, Usage.JAVA_RUNTIME))
|
||||
@ -332,8 +219,8 @@ configurations {
|
||||
javaComponent.addVariantsFromConfiguration(it) {}
|
||||
}
|
||||
universalJar {
|
||||
canBeDeclared = false
|
||||
canBeResolved = false
|
||||
canBeConsumed = true
|
||||
attributes {
|
||||
attribute(Category.CATEGORY_ATTRIBUTE, objects.named(Category, Category.LIBRARY))
|
||||
attribute(Usage.USAGE_ATTRIBUTE, objects.named(Usage, Usage.JAVA_RUNTIME))
|
||||
@ -345,8 +232,8 @@ configurations {
|
||||
javaComponent.addVariantsFromConfiguration(it) {}
|
||||
}
|
||||
changelog {
|
||||
canBeDeclared = false
|
||||
canBeResolved = false
|
||||
canBeConsumed = true
|
||||
attributes {
|
||||
attribute(Category.CATEGORY_ATTRIBUTE, objects.named(Category, Category.DOCUMENTATION))
|
||||
attribute(DocsType.DOCS_TYPE_ATTRIBUTE, objects.named(DocsType, "changelog"))
|
||||
@ -357,11 +244,9 @@ configurations {
|
||||
}
|
||||
}
|
||||
modDevApiElements {
|
||||
canBeDeclared = false
|
||||
canBeResolved = false
|
||||
canBeConsumed = true
|
||||
afterEvaluate {
|
||||
extendsFrom userdevCompileOnly, installerLibraries, moduleOnly
|
||||
}
|
||||
extendsFrom libraries, moduleLibraries, userdevCompileOnly, neoFormDependencies
|
||||
attributes {
|
||||
attribute(Category.CATEGORY_ATTRIBUTE, objects.named(Category, Category.LIBRARY))
|
||||
attribute(Bundling.BUNDLING_ATTRIBUTE, objects.named(Bundling, Bundling.EXTERNAL))
|
||||
@ -373,11 +258,9 @@ configurations {
|
||||
javaComponent.addVariantsFromConfiguration(it) {}
|
||||
}
|
||||
modDevRuntimeElements {
|
||||
canBeDeclared = false
|
||||
canBeResolved = false
|
||||
canBeConsumed = true
|
||||
afterEvaluate {
|
||||
extendsFrom installerLibraries, moduleOnly
|
||||
}
|
||||
extendsFrom libraries, moduleLibraries, neoFormDependencies
|
||||
attributes {
|
||||
attribute(Category.CATEGORY_ATTRIBUTE, objects.named(Category, Category.LIBRARY))
|
||||
attribute(Bundling.BUNDLING_ATTRIBUTE, objects.named(Bundling, Bundling.EXTERNAL))
|
||||
@ -389,11 +272,9 @@ configurations {
|
||||
javaComponent.addVariantsFromConfiguration(it) {}
|
||||
}
|
||||
modDevModulePath {
|
||||
canBeDeclared = false
|
||||
canBeResolved = false
|
||||
canBeConsumed = true
|
||||
afterEvaluate {
|
||||
extendsFrom moduleOnly
|
||||
}
|
||||
extendsFrom moduleLibraries
|
||||
attributes {
|
||||
attribute(Category.CATEGORY_ATTRIBUTE, objects.named(Category, Category.LIBRARY))
|
||||
attribute(Bundling.BUNDLING_ATTRIBUTE, objects.named(Bundling, Bundling.EXTERNAL))
|
||||
@ -404,8 +285,9 @@ configurations {
|
||||
javaComponent.addVariantsFromConfiguration(it) {}
|
||||
}
|
||||
modDevTestFixtures {
|
||||
canBeDeclared = false
|
||||
canBeResolved = false
|
||||
canBeConsumed = true
|
||||
extendsFrom userdevTestFixtures
|
||||
attributes {
|
||||
attribute(Category.CATEGORY_ATTRIBUTE, objects.named(Category, Category.LIBRARY))
|
||||
attribute(Bundling.BUNDLING_ATTRIBUTE, objects.named(Bundling, Bundling.EXTERNAL))
|
||||
@ -418,63 +300,35 @@ configurations {
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
modDevBundle("net.neoforged:neoform:${project.minecraft_version}-${project.neoform_version}") {
|
||||
capabilities {
|
||||
requireCapability 'net.neoforged:neoform'
|
||||
}
|
||||
endorseStrictVersions()
|
||||
}
|
||||
modDevApiElements("net.neoforged:neoform:${project.minecraft_version}-${project.neoform_version}") {
|
||||
capabilities {
|
||||
requireCapability 'net.neoforged:neoform-dependencies'
|
||||
}
|
||||
endorseStrictVersions()
|
||||
}
|
||||
modDevRuntimeElements("net.neoforged:neoform:${project.minecraft_version}-${project.neoform_version}") {
|
||||
capabilities {
|
||||
requireCapability 'net.neoforged:neoform-dependencies'
|
||||
}
|
||||
endorseStrictVersions()
|
||||
}
|
||||
modDevTestFixtures("net.neoforged.fancymodloader:junit-fml:${project.fancy_mod_loader_version}") {
|
||||
endorseStrictVersions()
|
||||
}
|
||||
}
|
||||
|
||||
processResources {
|
||||
inputs.property("version", project.version)
|
||||
final version = project.version
|
||||
filesMatching("META-INF/neoforge.mods.toml") {
|
||||
expand([
|
||||
"global": [
|
||||
"neoForgeVersion": project.version
|
||||
"neoForgeVersion": version
|
||||
]
|
||||
])
|
||||
}
|
||||
}
|
||||
|
||||
afterEvaluate {
|
||||
artifacts {
|
||||
modDevBundle(userdevJar) {
|
||||
setClassifier("userdev") // Legacy
|
||||
}
|
||||
modDevConfig(createUserdevJson.output) {
|
||||
builtBy(createUserdevJson)
|
||||
setClassifier("moddev-config")
|
||||
}
|
||||
universalJar(signUniversalJar.output) {
|
||||
builtBy(signUniversalJar)
|
||||
setClassifier("universal")
|
||||
}
|
||||
installerJar(signInstallerJar.output) {
|
||||
builtBy(signInstallerJar)
|
||||
setClassifier("installer")
|
||||
}
|
||||
changelog(createChangelog.outputFile) {
|
||||
builtBy(createChangelog)
|
||||
setClassifier("changelog")
|
||||
setExtension("txt")
|
||||
}
|
||||
artifacts {
|
||||
modDevBundle(userdevJar) {
|
||||
setClassifier("userdev") // Legacy
|
||||
}
|
||||
modDevConfig(writeUserDevConfig.userDevConfig) {
|
||||
setClassifier("moddev-config")
|
||||
}
|
||||
universalJar(universalJar) {
|
||||
setClassifier("universal")
|
||||
}
|
||||
installerJar(installerJar) {
|
||||
setClassifier("installer")
|
||||
}
|
||||
changelog(createChangelog.outputFile) {
|
||||
builtBy(createChangelog)
|
||||
setClassifier("changelog")
|
||||
setExtension("txt")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -1,13 +1,28 @@
|
||||
pluginManagement {
|
||||
repositories {
|
||||
gradlePluginPortal()
|
||||
mavenLocal()
|
||||
maven { url = 'https://maven.neoforged.net/releases' }
|
||||
mavenLocal()
|
||||
}
|
||||
}
|
||||
|
||||
plugins {
|
||||
id 'net.neoforged.gradle.platform' version '7.0.171'
|
||||
id 'net.neoforged.moddev.repositories' version "${moddevgradle_plugin_version}"
|
||||
id 'org.gradle.toolchains.foojay-resolver-convention' version '0.8.0'
|
||||
}
|
||||
|
||||
// This makes the version available to buildSrc
|
||||
gradle.ext.moddevgradle_plugin_version = moddevgradle_plugin_version
|
||||
gradle.ext.gson_version = gson_version
|
||||
gradle.ext.diffpatch_version = diffpatch_version
|
||||
|
||||
dependencyResolutionManagement {
|
||||
repositoriesMode = RepositoriesMode.FAIL_ON_PROJECT_REPOS
|
||||
rulesMode = RulesMode.FAIL_ON_PROJECT_RULES
|
||||
repositories {
|
||||
mavenCentral()
|
||||
mavenLocal()
|
||||
}
|
||||
}
|
||||
|
||||
if (rootProject.name.toLowerCase() == "neoforge") {
|
||||
@ -15,13 +30,10 @@ if (rootProject.name.toLowerCase() == "neoforge") {
|
||||
rootProject.name = "NeoForge-Root"
|
||||
}
|
||||
|
||||
dynamicProjects {
|
||||
include ':base'
|
||||
include ':neoforge'
|
||||
|
||||
project(":base").projectDir = file("projects/base")
|
||||
project(":neoforge").projectDir = file("projects/neoforge")
|
||||
}
|
||||
include ':base'
|
||||
project(':base').projectDir = file('projects/base')
|
||||
include ':neoforge'
|
||||
project(':neoforge').projectDir = file('projects/neoforge')
|
||||
|
||||
include ':tests'
|
||||
project(":tests").projectDir = file("tests")
|
||||
|
||||
@ -3,25 +3,19 @@ plugins {
|
||||
id 'maven-publish'
|
||||
id 'com.diffplug.spotless'
|
||||
id 'net.neoforged.licenser'
|
||||
id 'net.neoforged.gradle.platform'
|
||||
id 'neoforge.formatting-conventions'
|
||||
}
|
||||
|
||||
java.withSourcesJar()
|
||||
|
||||
repositories {
|
||||
maven {
|
||||
name 'Mojang'
|
||||
url 'https://libraries.minecraft.net'
|
||||
}
|
||||
maven {
|
||||
name 'NeoForged'
|
||||
url 'https://maven.neoforged.net/releases'
|
||||
}
|
||||
}
|
||||
apply plugin : net.neoforged.minecraftdependencies.MinecraftDependenciesPlugin
|
||||
|
||||
dependencies {
|
||||
implementation project(path: ':neoforge', configuration: 'runtimeElements')
|
||||
// TODO: is this leaking in the POM? (most likely yes)
|
||||
// TODO: does this need to be changed back to runtimeDependencies?
|
||||
// TODO: should use attributes to resolve the right variant instead of hardcoding
|
||||
compileOnly project(path: ':neoforge', configuration: 'apiElements')
|
||||
runtimeOnly project(path: ':neoforge', configuration: 'runtimeElements')
|
||||
|
||||
compileOnly(platform("org.junit:junit-bom:${project.jupiter_api_version}"))
|
||||
compileOnly "org.junit.jupiter:junit-jupiter-params"
|
||||
@ -30,6 +24,14 @@ dependencies {
|
||||
compileOnly "com.google.code.findbugs:jsr305:3.0.2"
|
||||
}
|
||||
|
||||
sourceSets {
|
||||
main {
|
||||
// TODO: cursed
|
||||
compileClasspath += project(':neoforge').sourceSets.main.compileClasspath
|
||||
runtimeClasspath += project(':neoforge').sourceSets.main.runtimeClasspath
|
||||
}
|
||||
}
|
||||
|
||||
license {
|
||||
header = rootProject.file('codeformat/HEADER.txt')
|
||||
include '**/*.java'
|
||||
@ -39,6 +41,7 @@ tasks.withType(JavaCompile).configureEach {
|
||||
options.encoding = 'UTF-8'
|
||||
}
|
||||
|
||||
def version = project.version
|
||||
tasks.withType(ProcessResources).configureEach {
|
||||
inputs.properties version: version
|
||||
|
||||
|
||||
@ -1,27 +1,18 @@
|
||||
plugins {
|
||||
id 'java'
|
||||
id 'net.neoforged.gradle.platform'
|
||||
id 'com.diffplug.spotless'
|
||||
id 'net.neoforged.licenser'
|
||||
id 'neoforge.formatting-conventions'
|
||||
}
|
||||
|
||||
apply plugin : net.neoforged.neodev.NeoDevExtraPlugin
|
||||
|
||||
evaluationDependsOn(":neoforge")
|
||||
|
||||
def neoforgeProject = project(':neoforge')
|
||||
def testframeworkProject = project(':testframework')
|
||||
def coremodsProject = project(':neoforge-coremods')
|
||||
|
||||
repositories {
|
||||
mavenLocal()
|
||||
maven {
|
||||
name 'Mojang'
|
||||
url 'https://libraries.minecraft.net'
|
||||
}
|
||||
maven {
|
||||
name 'NeoForged'
|
||||
url 'https://maven.neoforged.net/releases'
|
||||
}
|
||||
}
|
||||
|
||||
sourceSets {
|
||||
main {
|
||||
resources {
|
||||
@ -31,10 +22,6 @@ sourceSets {
|
||||
junit {}
|
||||
}
|
||||
|
||||
configurations {
|
||||
junitImplementation.extendsFrom(implementation)
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation project(path: ':neoforge', configuration: 'runtimeElements')
|
||||
implementation(testframeworkProject)
|
||||
@ -45,74 +32,79 @@ dependencies {
|
||||
|
||||
junitImplementation("org.assertj:assertj-core:${project.assertj_core}")
|
||||
junitImplementation "net.neoforged.fancymodloader:junit-fml:${project.fancy_mod_loader_version}"
|
||||
junitImplementation project(path: ':neoforge', configuration: 'runtimeElements')
|
||||
junitImplementation(testframeworkProject)
|
||||
|
||||
compileOnly "org.jetbrains:annotations:${project.jetbrains_annotations_version}"
|
||||
}
|
||||
|
||||
runs {
|
||||
client {
|
||||
configure neoforgeProject.runTypes.client
|
||||
}
|
||||
junit {
|
||||
configure neoforgeProject.runTypes.junit
|
||||
unitTestSource sourceSets.junit
|
||||
}
|
||||
server {
|
||||
configure neoforgeProject.runTypes.server
|
||||
}
|
||||
gameTestServer {
|
||||
configure neoforgeProject.runTypes.gameTestServer
|
||||
}
|
||||
gameTestClient {
|
||||
configure neoforgeProject.runTypes.gameTestClient
|
||||
}
|
||||
data {
|
||||
configure neoforgeProject.runTypes.data
|
||||
junitTest {
|
||||
useJUnitPlatform()
|
||||
classpath = sourceSets.junit.output + sourceSets.junit.runtimeClasspath
|
||||
testClassesDirs = sourceSets.junit.output.classesDirs
|
||||
outputs.upToDateWhen { false }
|
||||
}
|
||||
|
||||
arguments.addAll '--flat', '--all', '--validate',
|
||||
'--mod', 'data_gen_test',
|
||||
'--mod', 'global_loot_test',
|
||||
'--mod', 'scaffolding_test',
|
||||
'--mod', 'custom_tag_types_test',
|
||||
'--mod', 'new_model_loader_test',
|
||||
'--mod', 'remove_tag_datagen_test',
|
||||
'--mod', 'tag_based_tool_types',
|
||||
'--mod', 'custom_transformtype_test',
|
||||
'--mod', 'data_pack_registries_test',
|
||||
'--mod', 'biome_modifiers_test',
|
||||
'--mod', 'structure_modifiers_test',
|
||||
'--mod', 'custom_preset_editor_test',
|
||||
'--mod', 'custom_predicate_test',
|
||||
'--mod', 'neotests',
|
||||
'--existing-mod', 'testframework',
|
||||
'--existing', sourceSets.main.resources.srcDirs[0].absolutePath
|
||||
neoDev {
|
||||
mods {
|
||||
neotests {
|
||||
sourceSet sourceSets.main
|
||||
}
|
||||
testframework {
|
||||
sourceSet project(":testframework").sourceSets.main
|
||||
}
|
||||
junit {
|
||||
sourceSet sourceSets.junit
|
||||
}
|
||||
coremods {
|
||||
sourceSet coremodsProject.sourceSets.main
|
||||
}
|
||||
}
|
||||
|
||||
final File gameDir = project.file("runs/${name}") as File
|
||||
gameDir.mkdirs();
|
||||
runs {
|
||||
client {
|
||||
client()
|
||||
}
|
||||
server {
|
||||
server()
|
||||
}
|
||||
gameTestServer {
|
||||
type = "gameTestServer"
|
||||
}
|
||||
data {
|
||||
data()
|
||||
|
||||
workingDirectory.set gameDir
|
||||
arguments.addAll '--gameDir', gameDir.absolutePath
|
||||
programArguments.addAll '--flat', '--all', '--validate',
|
||||
'--mod', 'data_gen_test',
|
||||
'--mod', 'global_loot_test',
|
||||
'--mod', 'scaffolding_test',
|
||||
'--mod', 'custom_tag_types_test',
|
||||
'--mod', 'new_model_loader_test',
|
||||
'--mod', 'remove_tag_datagen_test',
|
||||
'--mod', 'tag_based_tool_types',
|
||||
'--mod', 'custom_transformtype_test',
|
||||
'--mod', 'data_pack_registries_test',
|
||||
'--mod', 'biome_modifiers_test',
|
||||
'--mod', 'structure_modifiers_test',
|
||||
'--mod', 'custom_preset_editor_test',
|
||||
'--mod', 'custom_predicate_test',
|
||||
'--mod', 'neotests',
|
||||
'--existing-mod', 'testframework',
|
||||
'--existing', project.file("src/main/resources").absolutePath,
|
||||
'--output', project.file("src/generated/resources").absolutePath
|
||||
}
|
||||
}
|
||||
|
||||
runs.configureEach {
|
||||
// Add NeoForge and Minecraft (both under the "minecraft" mod), and exclude junit.
|
||||
loadedMods = [neoforgeProject.neoDev.mods.minecraft, mods.neotests, mods.testframework, mods.coremods]
|
||||
|
||||
gameDirectory.set project.file("runs/${it.name}") as File
|
||||
}
|
||||
}
|
||||
|
||||
//We need the assets and natives tasks from the forge project.
|
||||
runs.configureEach {
|
||||
dependsOn.add(neoforgeProject.runtime.assets)
|
||||
dependsOn.add(neoforgeProject.runtime.natives)
|
||||
modSource neoforgeProject.sourceSets.main
|
||||
modSource coremodsProject.sourceSets.main
|
||||
modSource testframeworkProject.sourceSets.main
|
||||
}
|
||||
|
||||
afterEvaluate {
|
||||
runs.data {
|
||||
// Override --output that forge already has
|
||||
def args = new ArrayList<String>(arguments.get());
|
||||
def outputIndex = args.indexOf('--output');
|
||||
args.set(outputIndex+1, file('src/generated/resources/').absolutePath);
|
||||
arguments.set(args);
|
||||
}
|
||||
runs.junit.modSources.all().get().values().remove(sourceSets.main)
|
||||
neoDevTest {
|
||||
loadedMods = [ project(":neoforge").neoDev.mods.minecraft, neoDev.mods.testframework, neoDev.mods.coremods, neoDev.mods.junit ]
|
||||
}
|
||||
|
||||
license {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user