diff --git a/.github/workflows/check-local-changes.yml b/.github/workflows/check-local-changes.yml index ef101ceacd..0609d67e89 100644 --- a/.github/workflows/check-local-changes.yml +++ b/.github/workflows/check-local-changes.yml @@ -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 diff --git a/.github/workflows/test-prs.yml b/.github/workflows/test-prs.yml index 51afdcddad..471304015a 100644 --- a/.github/workflows/test-prs.yml +++ b/.github/workflows/test-prs.yml @@ -47,6 +47,15 @@ jobs: - name: Run JUnit tests with Gradle run: ./gradlew :tests:runUnitTests + - name: Install software OpenGL rendering + run: sudo apt-get install xvfb libgl1-mesa-dri + + - name: Run production client self-test + run: xvfb-run ./gradlew :neoforge:testProductionClient + + - name: Run production server self-test + run: ./gradlew :neoforge:testProductionServer + - name: Store reports if: failure() uses: actions/upload-artifact@v4 diff --git a/.gitignore b/.gitignore index 883731fee7..b5c5b10b6f 100644 --- a/.gitignore +++ b/.gitignore @@ -44,4 +44,4 @@ build **/eclipse/ **/runs/ -**/out/ \ No newline at end of file +**/out/ diff --git a/build.gradle b/build.gradle index 6c46fb5ba6..fbc90b7d61 100644 --- a/build.gradle +++ b/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 diff --git a/buildSrc/README.md b/buildSrc/README.md new file mode 100644 index 0000000000..2e63a427c0 --- /dev/null +++ b/buildSrc/README.md @@ -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. diff --git a/buildSrc/build.gradle b/buildSrc/build.gradle index 6784052459..6d0ce4c5af 100644 --- a/buildSrc/build.gradle +++ b/buildSrc/build.gradle @@ -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}" +} diff --git a/buildSrc/settings.gradle b/buildSrc/settings.gradle new file mode 100644 index 0000000000..e69de29bb2 diff --git a/buildSrc/src/main/groovy/neoforge.formatting-conventions.gradle b/buildSrc/src/main/groovy/neoforge.formatting-conventions.gradle index 0ba7b48852..e0f554b820 100644 --- a/buildSrc/src/main/groovy/neoforge.formatting-conventions.gradle +++ b/buildSrc/src/main/groovy/neoforge.formatting-conventions.gradle @@ -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 { diff --git a/buildSrc/src/main/java/net/neoforged/neodev/ApplyAccessTransformer.java b/buildSrc/src/main/java/net/neoforged/neodev/ApplyAccessTransformer.java new file mode 100644 index 0000000000..7131cc8fdd --- /dev/null +++ b/buildSrc/src/main/java/net/neoforged/neodev/ApplyAccessTransformer.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 JavaSourceTransformer to apply + * access transformers to the Minecraft source code for extending the access level of existing classes/methods/etc. + *

+ * 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 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(); + } +} diff --git a/buildSrc/src/main/java/net/neoforged/neodev/ApplyPatches.java b/buildSrc/src/main/java/net/neoforged/neodev/ApplyPatches.java new file mode 100644 index 0000000000..037e5f9bf6 --- /dev/null +++ b/buildSrc/src/main/java/net/neoforged/neodev/ApplyPatches.java @@ -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 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."); + } + } +} diff --git a/buildSrc/src/main/java/net/neoforged/neodev/CreateCleanArtifacts.java b/buildSrc/src/main/java/net/neoforged/neodev/CreateCleanArtifacts.java new file mode 100644 index 0000000000..9854d597e4 --- /dev/null +++ b/buildSrc/src/main/java/net/neoforged/neodev/CreateCleanArtifacts.java @@ -0,0 +1,46 @@ +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 { + /** + * The unmodified downloaded client jar. + */ + @OutputFile + abstract RegularFileProperty getRawClientJar(); + + @OutputFile + abstract RegularFileProperty getCleanClientJar(); + + /** + * The unmodified downloaded server jar. + */ + @OutputFile + abstract RegularFileProperty getRawServerJar(); + + @OutputFile + abstract RegularFileProperty getCleanServerJar(); + + @OutputFile + abstract RegularFileProperty getCleanJoinedJar(); + + @OutputFile + abstract RegularFileProperty getMergedMappings(); + + @Inject + public CreateCleanArtifacts() { + getAdditionalResults().put("node.downloadClient.output.output", getRawClientJar().getAsFile()); + 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() + } +} diff --git a/buildSrc/src/main/java/net/neoforged/neodev/CreateUserDevConfig.java b/buildSrc/src/main/java/net/neoforged/neodev/CreateUserDevConfig.java new file mode 100644 index 0000000000..e046bf09c9 --- /dev/null +++ b/buildSrc/src/main/java/net/neoforged/neodev/CreateUserDevConfig.java @@ -0,0 +1,199 @@ +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 Architectury Loom, + * ModDevGradle + * or NeoGradle. + */ +abstract class CreateUserDevConfig extends DefaultTask { + @Inject + public CreateUserDevConfig() {} + + @Input + abstract Property getFmlVersion(); + + @Input + abstract Property getMinecraftVersion(); + + @Input + abstract Property getNeoForgeVersion(); + + @Input + abstract Property getRawNeoFormVersion(); + + @Input + abstract ListProperty getLibraries(); + + @Input + abstract ListProperty getModules(); + + @Input + abstract ListProperty getTestLibraries(); + + @Input + abstract ListProperty getIgnoreList(); + + @Input + abstract Property 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 -> "neoforgeclientdev"; + case CLIENT_DATA -> "neoforgeclientdatadev"; + case SERVER_DATA -> "neoforgeserverdatadev"; + case GAME_TEST_SERVER, SERVER -> "neoforgeserverdev"; + case JUNIT -> "neoforgejunitdev"; + }; + + List 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.CLIENT_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 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.CLIENT_DATA, + runType == RunType.GAME_TEST_SERVER || runType == RunType.SERVER || runType == RunType.SERVER_DATA, + runType == RunType.CLIENT_DATA || runType == RunType.SERVER_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"), + CLIENT_DATA("clientData"), + SERVER_DATA("serverData"), + 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 libraries, + List testLibraries, + Map runs, + List modules) {} + +record BinpatcherConfig( + String version, + List args) {} + +record UserDevRunType( + boolean singleInstance, + String main, + List args, + List jvmArgs, + boolean client, + boolean server, + boolean dataGenerator, + boolean gameTest, + boolean unitTest, + Map env, + Map props) {} diff --git a/buildSrc/src/main/java/net/neoforged/neodev/GenerateBinaryPatches.java b/buildSrc/src/main/java/net/neoforged/neodev/GenerateBinaryPatches.java new file mode 100644 index 0000000000..35a06a8c33 --- /dev/null +++ b/buildSrc/src/main/java/net/neoforged/neodev/GenerateBinaryPatches.java @@ -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); + } + } +} diff --git a/buildSrc/src/main/java/net/neoforged/neodev/GenerateSourcePatches.java b/buildSrc/src/main/java/net/neoforged/neodev/GenerateSourcePatches.java new file mode 100644 index 0000000000..c6e1c7e730 --- /dev/null +++ b/buildSrc/src/main/java/net/neoforged/neodev/GenerateSourcePatches.java @@ -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 result = builder.build().operate(); + + int exit = result.exit; + if (exit != 0 && exit != 1) { + throw new RuntimeException("DiffPatch failed with exit code: " + exit); + } + } +} diff --git a/buildSrc/src/main/java/net/neoforged/neodev/NeoDevBasePlugin.java b/buildSrc/src/main/java/net/neoforged/neodev/NeoDevBasePlugin.java new file mode 100644 index 0000000000..de480b5682 --- /dev/null +++ b/buildSrc/src/main/java/net/neoforged/neodev/NeoDevBasePlugin.java @@ -0,0 +1,65 @@ +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.plugins.JavaPlugin; +import org.gradle.api.tasks.Sync; + +public class NeoDevBasePlugin implements Plugin { + @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 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); + + var extension = project.getExtensions().create(NeoDevExtension.NAME, NeoDevExtension.class); + + var createSources = NeoDevPlugin.configureMinecraftDecompilation(project); + // Task must run on sync to have MC resources available for IDEA nondelegated builds. + NeoDevFacade.runTaskOnProjectSync(project, createSources); + + tasks.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/")); + }); + + var downloadAssets = tasks.register("downloadAssets", DownloadAssets.class, task -> { + task.setGroup(NeoDevPlugin.INTERNAL_GROUP); + task.getNeoFormArtifact().set(createSources.flatMap(CreateMinecraftArtifacts::getNeoFormArtifact)); + task.getAssetPropertiesFile().set(neoDevBuildDir.map(dir -> dir.file("minecraft_assets.properties"))); + }); + + // MC looks for its resources on the classpath. + var runtimeClasspath = project.getConfigurations().getByName(JavaPlugin.RUNTIME_ONLY_CONFIGURATION_NAME); + runtimeClasspath.getDependencies().add( + dependencyFactory.create( + project.files(createSources.flatMap(CreateMinecraftArtifacts::getResourcesArtifact)) + ) + ); + NeoDevFacade.setupRuns( + project, + neoDevBuildDir, + extension.getRuns(), + // Pass an empty file collection for the userdev config. + // This will cause MDG to generate a dummy config suitable for vanilla. + project.files(), + modulePath -> {}, + legacyClasspath -> {}, + downloadAssets.flatMap(DownloadAssets::getAssetPropertiesFile), + mcAndNeoFormVersion + ); + } +} diff --git a/buildSrc/src/main/java/net/neoforged/neodev/NeoDevConfigurations.java b/buildSrc/src/main/java/net/neoforged/neodev/NeoDevConfigurations.java new file mode 100644 index 0000000000..35eb5b570d --- /dev/null +++ b/buildSrc/src/main/java/net/neoforged/neodev/NeoDevConfigurations.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 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 createToolClasspaths(Project project) { + var configurations = project.getConfigurations(); + var dependencyFactory = project.getDependencyFactory(); + + var result = new HashMap(); + + 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)); + } +} diff --git a/buildSrc/src/main/java/net/neoforged/neodev/NeoDevExtension.java b/buildSrc/src/main/java/net/neoforged/neodev/NeoDevExtension.java new file mode 100644 index 0000000000..224c7ab7e9 --- /dev/null +++ b/buildSrc/src/main/java/net/neoforged/neodev/NeoDevExtension.java @@ -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 mods; + private final NamedDomainObjectContainer 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 getMods() { + return mods; + } + + public void mods(Action> action) { + action.execute(mods); + } + + public NamedDomainObjectContainer getRuns() { + return runs; + } + + public void runs(Action> action) { + action.execute(runs); + } +} diff --git a/buildSrc/src/main/java/net/neoforged/neodev/NeoDevExtraPlugin.java b/buildSrc/src/main/java/net/neoforged/neodev/NeoDevExtraPlugin.java new file mode 100644 index 0000000000..fd5c0d391e --- /dev/null +++ b/buildSrc/src/main/java/net/neoforged/neodev/NeoDevExtraPlugin.java @@ -0,0 +1,81 @@ +package net.neoforged.neodev; + +import net.neoforged.minecraftdependencies.MinecraftDependenciesPlugin; +import net.neoforged.moddevgradle.internal.NeoDevFacade; +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 { + @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 neoForgeConfigOnly = project.getConfigurations().create("neoForgeConfigOnly", spec -> { + spec.getDependencies().add(projectDep(dependencyFactory, neoForgeProject, "net.neoforged:neoforge-moddev-config")); + }); + + Consumer 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(), + neoForgeConfigOnly, + 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, + neoForgeConfigOnly, + 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; + } +} diff --git a/buildSrc/src/main/java/net/neoforged/neodev/NeoDevPlugin.java b/buildSrc/src/main/java/net/neoforged/neodev/NeoDevPlugin.java new file mode 100644 index 0000000000..d38f91f46b --- /dev/null +++ b/buildSrc/src/main/java/net/neoforged/neodev/NeoDevPlugin.java @@ -0,0 +1,616 @@ +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.e2e.InstallProductionClient; +import net.neoforged.neodev.e2e.InstallProductionServer; +import net.neoforged.neodev.e2e.RunProductionClient; +import net.neoforged.neodev.e2e.RunProductionServer; +import net.neoforged.neodev.e2e.TestProductionClient; +import net.neoforged.neodev.e2e.TestProductionServer; +import net.neoforged.neodev.installer.CreateArgsFile; +import net.neoforged.neodev.installer.CreateInstallerProfile; +import net.neoforged.neodev.installer.CreateLauncherProfile; +import net.neoforged.neodev.installer.IdentifiedFile; +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; +import java.util.function.Consumer; + +public class NeoDevPlugin implements Plugin { + 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 writeUserDevConfig = tasks.register("writeUserDevConfig", CreateUserDevConfig.class, task -> { + task.setGroup(INTERNAL_GROUP); + task.getUserDevConfig().set(neoDevBuildDir.map(dir -> dir.file("userdev-config.json"))); + 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(), + writeUserDevConfig, + 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); + task.setDescription("This task retrieves various files for the Minecraft version without applying NeoForge patches to them"); + var cleanArtifactsDir = neoDevBuildDir.map(dir -> dir.dir("artifacts/clean")); + task.getRawClientJar().set(cleanArtifactsDir.map(dir -> dir.file("raw-client.jar"))); + 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 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; + })); + // ${version_name}.jar will be filled out by the launcher. It corresponds to the raw SRG Minecraft client jar. + task.getIgnoreList().addAll("client-extra", "${version_name}.jar"); + 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 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); + }); + + // Set up E2E testing of the produced installer + setupProductionClientTest( + project, + configurations, + downloadAssets, + installerJar, + minecraftVersion, + neoForgeVersion, + createCleanArtifacts.flatMap(CreateCleanArtifacts::getRawClientJar) + ); + setupProductionServerTest(project, installerJar); + } + + private static TaskProvider configureAccessTransformer( + Project project, + NeoDevConfigurations configurations, + TaskProvider createSourceArtifacts, + Provider 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, + Provider 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 binaryPatchesForMerged, + Provider binaryPatchesForClient, + Provider binaryPatchesForServer + ) { + } + + /** + * Sets up NFRT, and creates the sources and resources artifacts. + */ + static TaskProvider 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")); + }); + } + + private void setupProductionClientTest(Project project, + NeoDevConfigurations configurations, + TaskProvider downloadAssets, + TaskProvider installer, + Provider minecraftVersion, + Provider neoForgeVersion, + Provider originalClientJar + ) { + + var installClient = project.getTasks().register("installProductionClient", InstallProductionClient.class, task -> { + task.setGroup(INTERNAL_GROUP); + task.setDescription("Runs the installer produced by this build and installs a production client."); + task.getInstaller().from(installer.flatMap(AbstractArchiveTask::getArchiveFile)); + + var destinationDir = project.getLayout().getBuildDirectory().dir("production-client"); + task.getInstallationDir().set(destinationDir); + }); + + Consumer configureRunProductionClient = task -> { + task.getLibraryFiles().addAll(IdentifiedFile.listFromConfiguration(project, configurations.neoFormClasspath)); + task.getLibraryFiles().addAll(IdentifiedFile.listFromConfiguration(project, configurations.launcherProfileClasspath)); + task.getAssetPropertiesFile().set(downloadAssets.flatMap(DownloadAssets::getAssetPropertiesFile)); + task.getMinecraftVersion().set(minecraftVersion); + task.getNeoForgeVersion().set(neoForgeVersion); + task.getInstallationDir().set(installClient.flatMap(InstallProductionClient::getInstallationDir)); + task.getOriginalClientJar().set(originalClientJar); + }; + project.getTasks().register("runProductionClient", RunProductionClient.class, task -> { + task.setGroup(INTERNAL_GROUP); + task.setDescription("Runs the production client installed by installProductionClient."); + configureRunProductionClient.accept(task); + }); + project.getTasks().register("testProductionClient", TestProductionClient.class, task -> { + task.setGroup(INTERNAL_GROUP); + task.setDescription("Tests the production client installed by installProductionClient."); + configureRunProductionClient.accept(task); + }); + } + + private void setupProductionServerTest(Project project, TaskProvider installer) { + var installServer = project.getTasks().register("installProductionServer", InstallProductionServer.class, task -> { + task.setGroup(INTERNAL_GROUP); + task.setDescription("Runs the installer produced by this build and installs a production server."); + task.getInstaller().from(installer.flatMap(AbstractArchiveTask::getArchiveFile)); + + var destinationDir = project.getLayout().getBuildDirectory().dir("production-server"); + task.getInstallationDir().set(destinationDir); + }); + + project.getTasks().register("runProductionServer", RunProductionServer.class, task -> { + task.setGroup(INTERNAL_GROUP); + task.setDescription("Runs the production server installed by installProductionServer."); + task.getInstallationDir().set(installServer.flatMap(InstallProductionServer::getInstallationDir)); + }); + + project.getTasks().register("testProductionServer", TestProductionServer.class, task -> { + task.setGroup(INTERNAL_GROUP); + task.setDescription("Tests the production server installed by installProductionServer."); + task.getInstallationDir().set(installServer.flatMap(InstallProductionServer::getInstallationDir)); + }); + } +} diff --git a/buildSrc/src/main/java/net/neoforged/neodev/NeoDevTestExtension.java b/buildSrc/src/main/java/net/neoforged/neodev/NeoDevTestExtension.java new file mode 100644 index 0000000000..57d3f7be33 --- /dev/null +++ b/buildSrc/src/main/java/net/neoforged/neodev/NeoDevTestExtension.java @@ -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 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 getLoadedMods(); +} diff --git a/buildSrc/src/main/java/net/neoforged/neodev/RemapJar.java b/buildSrc/src/main/java/net/neoforged/neodev/RemapJar.java new file mode 100644 index 0000000000..e93ccb8b94 --- /dev/null +++ b/buildSrc/src/main/java/net/neoforged/neodev/RemapJar.java @@ -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}. + *

+ * The installer produces the same Jar file as this task does and then applies the patches against that. + *

+ * 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); + } + } +} diff --git a/buildSrc/src/main/java/net/neoforged/neodev/Tools.java b/buildSrc/src/main/java/net/neoforged/neodev/Tools.java new file mode 100644 index 0000000000..a9f9106697 --- /dev/null +++ b/buildSrc/src/main/java/net/neoforged/neodev/Tools.java @@ -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); + } +} diff --git a/buildSrc/src/main/java/net/neoforged/neodev/e2e/InstallProductionClient.java b/buildSrc/src/main/java/net/neoforged/neodev/e2e/InstallProductionClient.java new file mode 100644 index 0000000000..bc16b4b386 --- /dev/null +++ b/buildSrc/src/main/java/net/neoforged/neodev/e2e/InstallProductionClient.java @@ -0,0 +1,64 @@ +package net.neoforged.neodev.e2e; + +import org.gradle.api.GradleException; +import org.gradle.api.file.ConfigurableFileCollection; +import org.gradle.api.file.DirectoryProperty; +import org.gradle.api.tasks.InputFiles; +import org.gradle.api.tasks.JavaExec; +import org.gradle.api.tasks.OutputDirectory; +import org.gradle.api.tasks.TaskAction; + +import javax.inject.Inject; +import java.io.BufferedOutputStream; +import java.io.IOException; +import java.io.UncheckedIOException; +import java.nio.file.Files; + +/** + * Downloads and installs a production NeoForge client. + * By extending this task from {@link JavaExec}, it's possible to debug the actual legacy installer + * via IntelliJ directly. + */ +public abstract class InstallProductionClient extends JavaExec { + /** + * This file collection should contain exactly one file: + * The NeoForge Installer Jar-File. + */ + @InputFiles + public abstract ConfigurableFileCollection getInstaller(); + + /** + * Where NeoForge should be installed. + */ + @OutputDirectory + public abstract DirectoryProperty getInstallationDir(); + + @Inject + public InstallProductionClient() { + classpath(getInstaller()); + } + + @TaskAction + @Override + public void exec() { + var installDir = getInstallationDir().getAsFile().get().toPath().toAbsolutePath(); + + // Installer looks for this file + var profilesJsonPath = installDir.resolve("launcher_profiles.json"); + try { + Files.writeString(profilesJsonPath, "{}"); + } catch (IOException e) { + throw new GradleException("Failed to write fake launcher profiles file.", e); + } + + setWorkingDir(installDir.toFile()); + args("--install-client", installDir.toString()); + try { + setStandardOutput(new BufferedOutputStream(Files.newOutputStream(installDir.resolve("install.log")))); + } catch (IOException e) { + throw new UncheckedIOException(e); + } + + super.exec(); + } +} diff --git a/buildSrc/src/main/java/net/neoforged/neodev/e2e/InstallProductionServer.java b/buildSrc/src/main/java/net/neoforged/neodev/e2e/InstallProductionServer.java new file mode 100644 index 0000000000..9360a88e88 --- /dev/null +++ b/buildSrc/src/main/java/net/neoforged/neodev/e2e/InstallProductionServer.java @@ -0,0 +1,63 @@ +package net.neoforged.neodev.e2e; + +import org.gradle.api.file.ConfigurableFileCollection; +import org.gradle.api.file.DirectoryProperty; +import org.gradle.api.file.RegularFileProperty; +import org.gradle.api.tasks.InputFiles; +import org.gradle.api.tasks.JavaExec; +import org.gradle.api.tasks.OutputDirectory; +import org.gradle.api.tasks.OutputFile; +import org.gradle.api.tasks.TaskAction; + +import javax.inject.Inject; +import java.io.BufferedOutputStream; +import java.io.IOException; +import java.io.UncheckedIOException; +import java.nio.file.Files; + +/** + * Runs the installer produced by the main build to install a dedicated server in a chosen directory. + */ +public abstract class InstallProductionServer extends JavaExec { + /** + * The NeoForge installer jar is expected to be the only file in this file collection. + */ + @InputFiles + public abstract ConfigurableFileCollection getInstaller(); + + /** + * Where the server should be installed. + */ + @OutputDirectory + public abstract DirectoryProperty getInstallationDir(); + + /** + * Points to the server.jar produced by the installer. + */ + @OutputFile + public abstract RegularFileProperty getServerLauncher(); + + @Inject + public InstallProductionServer() { + classpath(getInstaller()); + getServerLauncher().set(getInstallationDir().map(id -> id.file("server.jar"))); + getServerLauncher().finalizeValueOnRead(); + } + + @TaskAction + @Override + public void exec() { + var installDir = getInstallationDir().getAsFile().get().toPath().toAbsolutePath(); + + setWorkingDir(installDir.toFile()); + args("--install-server", installDir.toString()); + args("--server.jar"); + try { + setStandardOutput(new BufferedOutputStream(Files.newOutputStream(installDir.resolve("install.log")))); + } catch (IOException e) { + throw new UncheckedIOException(e); + } + + super.exec(); + } +} diff --git a/buildSrc/src/main/java/net/neoforged/neodev/e2e/RunProductionClient.java b/buildSrc/src/main/java/net/neoforged/neodev/e2e/RunProductionClient.java new file mode 100644 index 0000000000..e710999e70 --- /dev/null +++ b/buildSrc/src/main/java/net/neoforged/neodev/e2e/RunProductionClient.java @@ -0,0 +1,363 @@ +package net.neoforged.neodev.e2e; + +import com.google.gson.Gson; +import com.google.gson.JsonObject; +import net.neoforged.neodev.installer.IdentifiedFile; +import net.neoforged.neodev.utils.MavenIdentifier; +import org.apache.tools.ant.taskdefs.condition.Os; +import org.gradle.api.GradleException; +import org.gradle.api.file.DirectoryProperty; +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.InputDirectory; +import org.gradle.api.tasks.InputFile; +import org.gradle.api.tasks.JavaExec; +import org.gradle.api.tasks.Nested; +import org.gradle.api.tasks.TaskAction; +import org.gradle.process.ExecOperations; +import org.gradle.process.JavaExecSpec; + +import javax.inject.Inject; +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.StandardCopyOption; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Properties; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * Runs a production client previously installed by {@link InstallProductionClient}. + *

+ * This task has to extend from {@link JavaExec} instead of using {@link org.gradle.process.ExecOperations} internally + * to allow debugging it via IntelliJ directly. + * (Technically, implementing {@link org.gradle.process.JavaForkOptions} would suffice). + *

+ * The main complication of this task is evaluating the Vanilla version manifest and building a libraries + * directory and classpath as the Vanilla launcher would. + */ +public abstract class RunProductionClient extends JavaExec { + private final ExecOperations execOperations; + + /** + * The folder where the game was installed. + */ + @InputDirectory + public abstract DirectoryProperty getInstallationDir(); + + /** + * The pre-processed libraries as a file collection. + */ + @Nested + public abstract ListProperty getLibraryFiles(); + + /** + * The asset properties file produced by {@link net.neoforged.nfrtgradle.DownloadAssets}. + */ + @InputFile + public abstract RegularFileProperty getAssetPropertiesFile(); + + /** + * The Minecraft version matching the NeoForge version to install. + */ + @Input + public abstract Property getMinecraftVersion(); + + /** + * The NeoForge version, used for placeholders when launching the game. + * It needs to match the installer used. + */ + @Input + public abstract Property getNeoForgeVersion(); + + /** + * The original, unmodified client jar. + * The Vanilla launcher puts this on the classpath when it launches the game. + */ + @InputFile + public abstract RegularFileProperty getOriginalClientJar(); + + @Inject + public RunProductionClient(ExecOperations execOperations) { + this.execOperations = execOperations; + } + + @TaskAction + @Override + public void exec() { + var installDir = getInstallationDir().getAsFile().get().toPath(); + var nativesDir = installDir.resolve("natives"); + try { + Files.createDirectories(nativesDir); + } catch (IOException e) { + throw new GradleException("Failed to pre-create natives directory " + nativesDir, e); + } + var librariesDir = installDir.resolve("libraries"); + + var minecraftVersion = getMinecraftVersion().get(); + var versionId = "neoforge-" + getNeoForgeVersion().get(); + + var assetProperties = new Properties(); + try (var in = new FileInputStream(getAssetPropertiesFile().getAsFile().get())) { + assetProperties.load(in); + } catch (IOException e) { + throw new GradleException("Failed to read asset properties " + getAssetPropertiesFile(), e); + } + + var assetIndex = Objects.requireNonNull(assetProperties.getProperty("asset_index"), "asset_index"); + var assetsRoot = Objects.requireNonNull(assetProperties.getProperty("assets_root"), "assets_root"); + + // Set up the placeholders generally used by Vanilla profiles in their argument definitions. + var placeholders = new HashMap(); + placeholders.put("auth_player_name", "Dev"); + placeholders.put("version_name", minecraftVersion); + placeholders.put("game_directory", installDir.toAbsolutePath().toString()); + placeholders.put("auth_uuid", "00000000-0000-4000-8000-000000000000"); + placeholders.put("auth_access_token", "0"); + placeholders.put("clientid", "0"); + placeholders.put("auth_xuid", "0"); + placeholders.put("user_type", "legacy"); + placeholders.put("version_type", "release"); + placeholders.put("assets_index_name", assetIndex); + placeholders.put("assets_root", assetsRoot); + placeholders.put("launcher_name", "NeoForgeProdInstallation"); + placeholders.put("launcher_version", "1.0"); + placeholders.put("natives_directory", nativesDir.toAbsolutePath().toString()); + // These are used by NF but provided by the launcher + placeholders.put("library_directory", librariesDir.toAbsolutePath().toString()); + placeholders.put("classpath_separator", File.pathSeparator); + + execOperations.javaexec(spec -> { + // The JVM args at this point may include debugging options when started through IntelliJ + spec.jvmArgs(getJvmArguments().get()); + spec.workingDir(installDir); + + spec.environment(getEnvironment()); + applyVersionManifest(installDir, versionId, placeholders, librariesDir, spec); + }); + } + + /** + * Applies a Vanilla Launcher version manifest to the JavaForkOptions. + */ + private void applyVersionManifest(Path installDir, + String versionId, + Map placeholders, + Path librariesDir, + JavaExecSpec spec) { + var manifests = loadVersionManifests(installDir, versionId); + + var mergedProgramArgs = new ArrayList(); + var mergedJvmArgs = new ArrayList(); + + for (var manifest : manifests) { + var mainClass = manifest.getAsJsonPrimitive("mainClass"); + if (mainClass != null) { + spec.getMainClass().set(mainClass.getAsString()); + } + + mergedProgramArgs.addAll(getArguments(manifest, "game")); + mergedJvmArgs.addAll(getArguments(manifest, "jvm")); + } + + // Index all available libraries + var availableLibraries = new HashMap(); + for (var identifiedFile : getLibraryFiles().get()) { + availableLibraries.put( + identifiedFile.getIdentifier().get(), + identifiedFile.getFile().get().getAsFile().toPath() + ); + } + + // The libraries are built in reverse, and libraries already added are not added again from parent manifests + var librariesAdded = new HashSet(); + var classpathItems = new ArrayList(); + for (var i = manifests.size() - 1; i >= 0; i--) { + var manifest = manifests.get(i); + + var libraries = manifest.getAsJsonArray("libraries"); + for (var library : libraries) { + var libraryObj = library.getAsJsonObject(); + + // Skip if disabled by rule + if (isDisabledByRules(libraryObj)) { + getLogger().info("Skipping library {} since it's condition is not met.", libraryObj); + continue; + } + + var id = MavenIdentifier.parse(libraryObj.get("name").getAsString()); + + // We use this to deduplicate the same library in different versions across manifests + var idWithoutVersion = new MavenIdentifier( + id.group(), + id.artifact(), + "", + id.classifier(), + id.extension() + ); + + if (!librariesAdded.add(idWithoutVersion)) { + continue; // The library was overridden by a child profile + } + + // Try finding the library in the classpath we got from Gradle + var availableLibrary = availableLibraries.get(id); + if (availableLibrary == null) { + throw new GradleException("Version manifest asks for " + id + " but this library is not available through Gradle."); + } + + // Copy over the library to the libraries directory, since our loader only deduplicates class-path + // items with module-path items when they are at the same location (and the module-path is defined + // relative to the libraries directory). + Path destination = librariesDir.resolve(id.repositoryPath()); + copyIfNeeded(availableLibrary, destination); + classpathItems.add(destination.toAbsolutePath().toString()); + } + } + + // The Vanilla launcher adds the actual game jar (obfuscated) as the last classpath item + var gameJar = installDir.resolve("versions").resolve(versionId).resolve(versionId + ".jar"); + copyIfNeeded(getOriginalClientJar().get().getAsFile().toPath(), gameJar); + classpathItems.add(gameJar.toAbsolutePath().toString()); + placeholders.put("version_name", versionId); + + var classpath = String.join(File.pathSeparator, classpathItems); + placeholders.putIfAbsent("classpath", classpath); + + expandPlaceholders(mergedProgramArgs, placeholders); + spec.args(mergedProgramArgs); + expandPlaceholders(mergedJvmArgs, placeholders); + spec.jvmArgs(mergedJvmArgs); + } + + // Returns the inherited manifests first + private static List loadVersionManifests(Path installDir, String versionId) { + // Read back the version manifest and get the startup arguments + var manifestPath = installDir.resolve("versions").resolve(versionId).resolve(versionId + ".json"); + JsonObject manifest; + try { + manifest = readJson(manifestPath); + } catch (IOException e) { + throw new GradleException("Failed to read launcher profile " + manifestPath, e); + } + + var result = new ArrayList(); + var inheritsFrom = manifest.getAsJsonPrimitive("inheritsFrom"); + if (inheritsFrom != null) { + result.addAll(loadVersionManifests(installDir, inheritsFrom.getAsString())); + } + + result.add(manifest); + + return result; + } + + private static void expandPlaceholders(List args, Map variables) { + var pattern = Pattern.compile("\\$\\{([^}]+)}"); + + args.replaceAll(s -> { + var matcher = pattern.matcher(s); + return matcher.replaceAll(match -> { + var variable = match.group(1); + return Matcher.quoteReplacement(variables.getOrDefault(variable, matcher.group())); + }); + }); + } + + private static List getArguments(JsonObject manifest, String kind) { + var result = new ArrayList(); + + var gameArgs = manifest.getAsJsonObject("arguments").getAsJsonArray(kind); + for (var gameArg : gameArgs) { + if (gameArg.isJsonObject()) { + var conditionalArgument = gameArg.getAsJsonObject(); + if (!isDisabledByRules(conditionalArgument)) { + var value = conditionalArgument.get("value"); + if (value.isJsonPrimitive()) { + result.add(value.getAsString()); + } else { + for (var valueEl : value.getAsJsonArray()) { + result.add(valueEl.getAsString()); + } + } + } + } else { + result.add(gameArg.getAsString()); + } + } + + return result; + } + + private static boolean isDisabledByRules(JsonObject ruleObject) { + var rules = ruleObject.getAsJsonArray("rules"); + if (rules == null) { + return false; + } + + for (var ruleEl : rules) { + var rule = ruleEl.getAsJsonObject(); + boolean allow = "allow".equals(rule.getAsJsonPrimitive("action").getAsString()); + // We only care about "os" rules + if (rule.has("os")) { + var os = rule.getAsJsonObject("os"); + var name = os.getAsJsonPrimitive("name"); + var arch = os.getAsJsonPrimitive("arch"); + boolean ruleMatches = (name == null || isCurrentOsName(name.getAsString())) && (arch == null || isCurrentOsArch(arch.getAsString())); + if (ruleMatches != allow) { + return true; + } + } else { + // We assume unknown rules do not apply + return true; + } + } + return false; + } + + private static boolean isCurrentOsName(String os) { + return switch (os) { + case "windows" -> Os.isFamily(Os.FAMILY_WINDOWS); + case "osx" -> Os.isFamily(Os.FAMILY_MAC); + case "linux" -> Os.isFamily(Os.FAMILY_UNIX); + default -> false; + }; + } + + private static boolean isCurrentOsArch(String arch) { + return switch (arch) { + case "x86" -> System.getProperty("os.arch").equals("x86"); + default -> false; + }; + } + + private static JsonObject readJson(Path path) throws IOException { + try (var reader = Files.newBufferedReader(path, StandardCharsets.UTF_8)) { + return new Gson().fromJson(reader, JsonObject.class); + } + } + + private static void copyIfNeeded(Path source, Path destination) { + try { + if (!Files.exists(destination) + || !Objects.equals(Files.getLastModifiedTime(destination), Files.getLastModifiedTime(source)) + || Files.size(destination) != Files.size(source)) { + Files.createDirectories(destination.getParent()); + Files.copy(source, destination, StandardCopyOption.COPY_ATTRIBUTES, StandardCopyOption.REPLACE_EXISTING); + } + } catch (IOException e) { + throw new GradleException("Failed to copy " + source + " to " + destination + ": " + e, e); + } + } +} diff --git a/buildSrc/src/main/java/net/neoforged/neodev/e2e/RunProductionServer.java b/buildSrc/src/main/java/net/neoforged/neodev/e2e/RunProductionServer.java new file mode 100644 index 0000000000..e65ed58ecb --- /dev/null +++ b/buildSrc/src/main/java/net/neoforged/neodev/e2e/RunProductionServer.java @@ -0,0 +1,47 @@ +package net.neoforged.neodev.e2e; + +import org.gradle.api.file.DirectoryProperty; +import org.gradle.api.tasks.InputDirectory; +import org.gradle.api.tasks.JavaExec; +import org.gradle.api.tasks.TaskAction; +import org.gradle.process.ExecOperations; + +import javax.inject.Inject; + +/** + * Runs the {@code server.jar} installed by our installer using {@link InstallProductionServer}. + *

+ * This task has to extend from {@link JavaExec} instead of using {@link ExecOperations} internally + * to allow debugging the launched server with IntelliJ. + * (Technically, implementing {@link org.gradle.process.JavaForkOptions} would suffice). + */ +public abstract class RunProductionServer extends JavaExec { + private final ExecOperations execOperations; + + /** + * The folder where the game was installed. + */ + @InputDirectory + public abstract DirectoryProperty getInstallationDir(); + + @Inject + public RunProductionServer(ExecOperations execOperations) { + this.execOperations = execOperations; + } + + @TaskAction + @Override + public void exec() { + var installDir = getInstallationDir().getAsFile().get().toPath(); + + execOperations.javaexec(spec -> { + // The JVM args at this point may include debugging options when started through IntelliJ + spec.jvmArgs(getJvmArguments().get()); + spec.workingDir(installDir); + + spec.environment(getEnvironment()); + spec.classpath(installDir.resolve("server.jar")); + }); + + } +} diff --git a/buildSrc/src/main/java/net/neoforged/neodev/e2e/TestProductionClient.java b/buildSrc/src/main/java/net/neoforged/neodev/e2e/TestProductionClient.java new file mode 100644 index 0000000000..0c7ec7fde3 --- /dev/null +++ b/buildSrc/src/main/java/net/neoforged/neodev/e2e/TestProductionClient.java @@ -0,0 +1,38 @@ +package net.neoforged.neodev.e2e; + +import org.gradle.api.GradleException; +import org.gradle.process.ExecOperations; + +import javax.inject.Inject; +import java.io.File; +import java.time.Duration; +import java.time.temporal.ChronoUnit; + +/** + * Runs a production client using {@link RunProductionClient} and passes the environment variable + * to enable the {@link net.neoforged.neoforge.common.util.SelfTest self test}. + *

+ * Once the client exits, it validates that the self-test file was created, indicating the client successfully + * launched and started ticking. + */ +public abstract class TestProductionClient extends RunProductionClient { + @Inject + public TestProductionClient(ExecOperations execOperations) { + super(execOperations); + + getTimeout().set(Duration.of(5, ChronoUnit.MINUTES)); + } + + @Override + public void exec() { + var selfTestReport = new File(getTemporaryDir(), "client_self_test.txt"); + + environment("NEOFORGE_CLIENT_SELFTEST", selfTestReport.getAbsolutePath()); + + super.exec(); + + if (!selfTestReport.exists()) { + throw new GradleException("Missing self test report file after running client: " + selfTestReport); + } + } +} diff --git a/buildSrc/src/main/java/net/neoforged/neodev/e2e/TestProductionServer.java b/buildSrc/src/main/java/net/neoforged/neodev/e2e/TestProductionServer.java new file mode 100644 index 0000000000..9fc6ed293c --- /dev/null +++ b/buildSrc/src/main/java/net/neoforged/neodev/e2e/TestProductionServer.java @@ -0,0 +1,55 @@ +package net.neoforged.neodev.e2e; + +import org.gradle.api.GradleException; +import org.gradle.process.ExecOperations; + +import javax.inject.Inject; +import java.io.File; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.time.Duration; +import java.time.temporal.ChronoUnit; + +/** + * Runs a production server using {@link RunProductionServer} and passes the environment variable + * to enable the {@link net.neoforged.neoforge.common.util.SelfTest self test}. + *

+ * Once the server exits, it validates that the self-test file was created, indicating the server successfully + * launched and started ticking. + */ +public abstract class TestProductionServer extends RunProductionServer { + @Inject + public TestProductionServer(ExecOperations execOperations) { + super(execOperations); + + getTimeout().set(Duration.of(5, ChronoUnit.MINUTES)); + } + + @Override + public void exec() { + var selfTestReport = new File(getTemporaryDir(), "server_self_test.txt"); + + environment("NEOFORGE_DEDICATED_SERVER_SELFTEST", selfTestReport.getAbsolutePath()); + + var eulaFile = getInstallationDir().file("eula.txt").get().getAsFile().toPath(); + try { + Files.writeString(eulaFile, "eula=true", StandardCharsets.UTF_8); + } catch (IOException e) { + throw new GradleException("Failed writing eula acceptable to eula.txt", e); + } + + try { + super.exec(); + } finally { + try { + Files.deleteIfExists(eulaFile); + } catch (IOException ignored) { + } + } + + if (!selfTestReport.exists()) { + throw new GradleException("Missing self test report file after running server: " + selfTestReport); + } + } +} diff --git a/buildSrc/src/main/java/net/neoforged/neodev/installer/CreateArgsFile.java b/buildSrc/src/main/java/net/neoforged/neodev/installer/CreateArgsFile.java new file mode 100644 index 0000000000..cbae005d4a --- /dev/null +++ b/buildSrc/src/main/java/net/neoforged/neodev/installer/CreateArgsFile.java @@ -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 getFmlVersion(); + + @Input + public abstract Property getMinecraftVersion(); + + @Input + public abstract Property getNeoForgeVersion(); + + @Input + public abstract Property getRawNeoFormVersion(); + + @Input + protected abstract Property getPathSeparator(); + + @Input + protected abstract Property getModules(); + + @Input + public abstract ListProperty getIgnoreList(); + + @Input + protected abstract Property 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(); + 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@", "neoforgeserver"); + 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); + } +} diff --git a/buildSrc/src/main/java/net/neoforged/neodev/installer/CreateInstallerProfile.java b/buildSrc/src/main/java/net/neoforged/neodev/installer/CreateInstallerProfile.java new file mode 100644 index 0000000000..c9da8c83ec --- /dev/null +++ b/buildSrc/src/main/java/net/neoforged/neodev/installer/CreateInstallerProfile.java @@ -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 getMinecraftVersion(); + + @Input + public abstract Property getNeoForgeVersion(); + + @Input + public abstract Property getMcAndNeoFormVersion(); + + @InputFile + public abstract RegularFileProperty getIcon(); + + @Nested + protected abstract ListProperty getLibraryFiles(); + + public void addLibraries(Configuration libraries) { + getLibraryFiles().addAll(IdentifiedFile.listFromConfiguration(getProject(), libraries)); + } + + @Input + public abstract ListProperty getRepositoryURLs(); + + @Input + public abstract MapProperty> getProcessorClasspaths(); + + @Input + public abstract MapProperty getProcessorGavs(); + + @InputFile + public abstract RegularFileProperty getUniversalJar(); + + @OutputFile + public abstract RegularFileProperty getInstallerProfile(); + + private void addProcessor(List processors, @Nullable List sides, InstallerProcessor processor, List 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(); + 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(); + BiConsumer> commonProcessor = (processor, args) -> addProcessor(processors, null, processor, args); + BiConsumer> clientProcessor = (processor, args) -> addProcessor(processors, List.of("client"), processor, args); + BiConsumer> 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(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( + int spec, + String profile, + String version, + String icon, + String minecraft, + String json, + String logo, + String welcome, + String mirrorList, + boolean hideExtract, + Map data, + List processors, + List libraries, + String serverJarPath) {} + +record LauncherDataEntry( + String client, + String server) {} + +record ProcessorEntry( + @Nullable + List sides, + String jar, + List classpath, + List args) {} diff --git a/buildSrc/src/main/java/net/neoforged/neodev/installer/CreateLauncherProfile.java b/buildSrc/src/main/java/net/neoforged/neodev/installer/CreateLauncherProfile.java new file mode 100644 index 0000000000..c81a547564 --- /dev/null +++ b/buildSrc/src/main/java/net/neoforged/neodev/installer/CreateLauncherProfile.java @@ -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 getFmlVersion(); + + @Input + public abstract Property getMinecraftVersion(); + + @Input + public abstract Property getNeoForgeVersion(); + + @Input + public abstract Property getRawNeoFormVersion(); + + @Nested + protected abstract ListProperty getLibraryFiles(); + + public void setLibraries(Configuration libraries) { + getLibraryFiles().set(IdentifiedFile.listFromConfiguration(getProject(), libraries)); + } + + @Input + public abstract ListProperty getRepositoryURLs(); + + @Input + public abstract ListProperty getIgnoreList(); + + @Input + protected abstract Property 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", "neoforgeclient")); + + 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>(); + 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> arguments, + List libraries) {} + diff --git a/buildSrc/src/main/java/net/neoforged/neodev/installer/IdentifiedFile.java b/buildSrc/src/main/java/net/neoforged/neodev/installer/IdentifiedFile.java new file mode 100644 index 0000000000..ff75e1ba75 --- /dev/null +++ b/buildSrc/src/main/java/net/neoforged/neodev/installer/IdentifiedFile.java @@ -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}. + */ +public abstract class IdentifiedFile { + public static Provider> 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) + public abstract RegularFileProperty getFile(); + + @Input + public abstract Property getIdentifier(); +} diff --git a/buildSrc/src/main/java/net/neoforged/neodev/installer/InstallerProcessor.java b/buildSrc/src/main/java/net/neoforged/neodev/installer/InstallerProcessor.java new file mode 100644 index 0000000000..970fa6d1cb --- /dev/null +++ b/buildSrc/src/main/java/net/neoforged/neodev/installer/InstallerProcessor.java @@ -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; + } +} diff --git a/buildSrc/src/main/java/net/neoforged/neodev/installer/Library.java b/buildSrc/src/main/java/net/neoforged/neodev/installer/Library.java new file mode 100644 index 0000000000..46669967f4 --- /dev/null +++ b/buildSrc/src/main/java/net/neoforged/neodev/installer/Library.java @@ -0,0 +1,3 @@ +package net.neoforged.neodev.installer; + +record Library(String name, LibraryDownload downloads) {} diff --git a/buildSrc/src/main/java/net/neoforged/neodev/installer/LibraryArtifact.java b/buildSrc/src/main/java/net/neoforged/neodev/installer/LibraryArtifact.java new file mode 100644 index 0000000000..1e345980e9 --- /dev/null +++ b/buildSrc/src/main/java/net/neoforged/neodev/installer/LibraryArtifact.java @@ -0,0 +1,7 @@ +package net.neoforged.neodev.installer; + +record LibraryArtifact( + String sha1, + long size, + String url, + String path) {} diff --git a/buildSrc/src/main/java/net/neoforged/neodev/installer/LibraryCollector.java b/buildSrc/src/main/java/net/neoforged/neodev/installer/LibraryCollector.java new file mode 100644 index 0000000000..7417ac14c3 --- /dev/null +++ b/buildSrc/src/main/java/net/neoforged/neodev/installer/LibraryCollector.java @@ -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 resolveLibraries(List repositoryUrls, Collection 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 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 repositoryUrls; + + private final List> libraries = new ArrayList<>(); + + private final HttpClient httpClient = HttpClient.newBuilder().build(); + + private LibraryCollector(List 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 libraryFuture = null; + for (var repositoryUrl : repositoryUrls) { + var artifactUri = joinUris(repositoryUrl, path); + var request = HttpRequest.newBuilder(artifactUri) + .method("HEAD", HttpRequest.BodyPublishers.noBody()) + .build(); + + Function> 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); + } + } +} diff --git a/buildSrc/src/main/java/net/neoforged/neodev/installer/LibraryDownload.java b/buildSrc/src/main/java/net/neoforged/neodev/installer/LibraryDownload.java new file mode 100644 index 0000000000..38afe5d057 --- /dev/null +++ b/buildSrc/src/main/java/net/neoforged/neodev/installer/LibraryDownload.java @@ -0,0 +1,4 @@ +package net.neoforged.neodev.installer; + +record LibraryDownload( + LibraryArtifact artifact) {} diff --git a/buildSrc/src/main/java/net/neoforged/neodev/utils/DependencyUtils.java b/buildSrc/src/main/java/net/neoforged/neodev/utils/DependencyUtils.java new file mode 100644 index 0000000000..ee1bb65175 --- /dev/null +++ b/buildSrc/src/main/java/net/neoforged/neodev/utils/DependencyUtils.java @@ -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> 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 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)) + ); + } +} diff --git a/buildSrc/src/main/java/net/neoforged/neodev/utils/FileUtils.java b/buildSrc/src/main/java/net/neoforged/neodev/utils/FileUtils.java new file mode 100644 index 0000000000..d91996d32d --- /dev/null +++ b/buildSrc/src/main/java/net/neoforged/neodev/utils/FileUtils.java @@ -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 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); + } + } +} diff --git a/buildSrc/src/main/java/net/neoforged/neodev/utils/MavenIdentifier.java b/buildSrc/src/main/java/net/neoforged/neodev/utils/MavenIdentifier.java new file mode 100644 index 0000000000..21dc04655b --- /dev/null +++ b/buildSrc/src/main/java/net/neoforged/neodev/utils/MavenIdentifier.java @@ -0,0 +1,53 @@ +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; + } + + /** + * Valid forms: + *

+ */ + public static MavenIdentifier parse(String coordinate) { + var coordinateAndExt = coordinate.split("@"); + String extension = "jar"; + if (coordinateAndExt.length > 2) { + throw new IllegalArgumentException("Malformed Maven coordinate: " + coordinate); + } else if (coordinateAndExt.length == 2) { + extension = coordinateAndExt[1]; + coordinate = coordinateAndExt[0]; + } + + var parts = coordinate.split(":"); + if (parts.length != 3 && parts.length != 4) { + throw new IllegalArgumentException("Malformed Maven coordinate: " + coordinate); + } + + var groupId = parts[0]; + var artifactId = parts[1]; + var version = parts[2]; + var classifier = parts.length == 4 ? parts[3] : ""; + return new MavenIdentifier(groupId, artifactId, version, classifier, extension); + } + + @Override + public String toString() { + if (classifier != null) { + return group + ":" + artifact + ":" + version + ":" + classifier + "@" + extension; + } else { + return group + ":" + artifact + ":" + version + "@" + extension; + } + } +} diff --git a/coremods/build.gradle b/coremods/build.gradle index 3d942a4701..127e7ec9df 100644 --- a/coremods/build.gradle +++ b/coremods/build.gradle @@ -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( diff --git a/coremods/settings.gradle b/coremods/settings.gradle new file mode 100644 index 0000000000..06c2cf68e4 --- /dev/null +++ b/coremods/settings.gradle @@ -0,0 +1,8 @@ +repositories { + maven { url = 'https://maven.neoforged.net/releases' } + maven { + name 'Mojang' + url 'https://libraries.minecraft.net' + } + mavenCentral() +} diff --git a/docs/CONTRIBUTING.md b/docs/CONTRIBUTING.md index 80f55cebf7..00f2af575d 100644 --- a/docs/CONTRIBUTING.md +++ b/docs/CONTRIBUTING.md @@ -19,8 +19,11 @@ Contributing to NeoForge 8. Modify the patched Minecraft sources in `projects/neoforge/src/main/java` as needed. The unmodified sources are available in `projects/base/src/main/java` for your reference. Do not modify these. 9. Test your changes - Run the game (Runs are available in the IDE) - - Run `gradlew :tests:runGameTestServer` or `Tests: GameTestServer` from IDE - - Run `gradlew :tests:runGameTestClient` or `Tests: GameTestClient` from IDE + - Runs starting with `base -` run Vanilla without NeoForge or its patches. + - Runs starting with `neoforge -` run NeoForge. + - Runs starting with `tests -` run NeoForge along with the test mods in the `tests` project. + - Run `gradlew :tests:runGameTestServer` or `tests - GameTestServer` from IDE + - Run `gradlew :tests:runClient` or `tests - Client` from IDE - If possible, write an automated test under the tests project. See [NEOGAMETESTS.md](NEOGAMETESTS.md) for more info. 10. Run `gradlew genPatches` to generate patch-files from the patched sources 11. Run `gradlew applyAllFormatting` to automatically format sources diff --git a/gradle.properties b/gradle.properties index ee9ba5e243..3ab3c77e91 100644 --- a/gradle.properties +++ b/gradle.properties @@ -4,15 +4,29 @@ 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.48-beta +# renovate: io.codechicken:DiffPatch +diffpatch_version=2.0.0.35 java_version=21 -minecraft_version=1.21.3 -neoform_version=20241023.131943 +minecraft_version=1.21.4 +neoform_version=20241203.161809 # on snapshot versions, used to prefix the version -neoforge_snapshot_next_stable=21.4 +neoforge_snapshot_next_stable=21.5 + +# 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 @@ -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 @@ -30,7 +43,7 @@ jetbrains_annotations_version=24.0.1 slf4j_api_version=2.0.7 apache_maven_artifact_version=3.8.5 jarjar_version=0.4.1 -fancy_mod_loader_version=5.0.1 +fancy_mod_loader_version=6.0.4 mojang_logging_version=1.1.1 log4j_version=2.22.1 guava_version=31.1.2-jre @@ -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 diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 9355b41557..0aaefbcaf0 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -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 diff --git a/patches/com/mojang/blaze3d/platform/Window.java.patch b/patches/com/mojang/blaze3d/platform/Window.java.patch index f211b35fad..043b11d2cb 100644 --- a/patches/com/mojang/blaze3d/platform/Window.java.patch +++ b/patches/com/mojang/blaze3d/platform/Window.java.patch @@ -1,6 +1,6 @@ --- a/com/mojang/blaze3d/platform/Window.java +++ b/com/mojang/blaze3d/platform/Window.java -@@ -90,7 +_,8 @@ +@@ -91,7 +_,8 @@ GLFW.glfwWindowHint(139267, 2); GLFW.glfwWindowHint(139272, 204801); GLFW.glfwWindowHint(139270, 1); @@ -10,7 +10,7 @@ if (monitor != null) { VideoMode videomode = monitor.getPreferredVidMode(this.fullscreen ? this.preferredFullscreenVideoMode : Optional.empty()); this.windowedX = this.x = monitor.getX() + videomode.getWidth() / 2 - this.width / 2; -@@ -102,6 +_,7 @@ +@@ -103,6 +_,7 @@ this.windowedX = this.x = aint1[0]; this.windowedY = this.y = aint[0]; } @@ -18,7 +18,7 @@ GLFW.glfwMakeContextCurrent(this.window); GL.createCapabilities(); -@@ -269,6 +_,7 @@ +@@ -273,6 +_,7 @@ GLFW.glfwGetFramebufferSize(this.window, aint, aint1); this.framebufferWidth = aint[0] > 0 ? aint[0] : 1; this.framebufferHeight = aint1[0] > 0 ? aint1[0] : 1; diff --git a/patches/com/mojang/blaze3d/systems/RenderSystem.java.patch b/patches/com/mojang/blaze3d/systems/RenderSystem.java.patch index 756e40c7c9..28061afef1 100644 --- a/patches/com/mojang/blaze3d/systems/RenderSystem.java.patch +++ b/patches/com/mojang/blaze3d/systems/RenderSystem.java.patch @@ -1,6 +1,6 @@ --- a/com/mojang/blaze3d/systems/RenderSystem.java +++ b/com/mojang/blaze3d/systems/RenderSystem.java -@@ -822,4 +_,14 @@ +@@ -818,4 +_,14 @@ void accept(it.unimi.dsi.fastutil.ints.IntConsumer p_157488_, int p_157489_); } } diff --git a/patches/net/minecraft/Util.java.patch b/patches/net/minecraft/Util.java.patch index 06262a76e0..96bc929990 100644 --- a/patches/net/minecraft/Util.java.patch +++ b/patches/net/minecraft/Util.java.patch @@ -1,6 +1,6 @@ --- a/net/minecraft/Util.java +++ b/net/minecraft/Util.java -@@ -264,8 +_,8 @@ +@@ -268,8 +_,8 @@ .getSchema(DataFixUtils.makeKey(SharedConstants.getCurrentVersion().getDataVersion().getVersion())) .getChoiceType(p_137552_, p_137553_); } catch (IllegalArgumentException illegalargumentexception) { @@ -11,7 +11,7 @@ throw illegalargumentexception; } } -@@ -630,20 +_,20 @@ +@@ -634,20 +_,20 @@ public static void logAndPauseIfInIde(String p_143786_) { LOGGER.error(p_143786_); diff --git a/patches/net/minecraft/client/KeyMapping.java.patch b/patches/net/minecraft/client/KeyMapping.java.patch index 05cdbda228..3fb1afaedf 100644 --- a/patches/net/minecraft/client/KeyMapping.java.patch +++ b/patches/net/minecraft/client/KeyMapping.java.patch @@ -1,6 +1,6 @@ --- a/net/minecraft/client/KeyMapping.java +++ b/net/minecraft/client/KeyMapping.java -@@ -14,9 +_,9 @@ +@@ -15,9 +_,9 @@ import net.neoforged.api.distmarker.OnlyIn; @OnlyIn(Dist.CLIENT) @@ -12,7 +12,7 @@ private static final Set CATEGORIES = Sets.newHashSet(); public static final String CATEGORY_MOVEMENT = "key.categories.movement"; public static final String CATEGORY_MISC = "key.categories.misc"; -@@ -42,17 +_,17 @@ +@@ -43,17 +_,17 @@ private int clickCount; public static void click(InputConstants.Key p_90836_) { @@ -35,7 +35,7 @@ } public static void setAll() { -@@ -100,7 +_,7 @@ +@@ -101,7 +_,7 @@ } public boolean isDown() { @@ -44,7 +44,7 @@ } public String getCategory() { -@@ -134,9 +_,13 @@ +@@ -135,9 +_,13 @@ } public int compareTo(KeyMapping p_90841_) { @@ -61,7 +61,7 @@ } public static Supplier createNameSupplier(String p_90843_) { -@@ -145,6 +_,20 @@ +@@ -146,6 +_,20 @@ } public boolean same(KeyMapping p_90851_) { @@ -82,7 +82,7 @@ return this.key.equals(p_90851_.key); } -@@ -163,11 +_,13 @@ +@@ -164,11 +_,13 @@ } public Component getTranslatedKeyMessage() { @@ -97,10 +97,10 @@ } public String saveString() { -@@ -176,5 +_,86 @@ - - public void setDown(boolean p_90846_) { - this.isDown = p_90846_; +@@ -182,5 +_,86 @@ + @Nullable + public static KeyMapping get(String p_389468_) { + return ALL.get(p_389468_); + } + + // Neo: Injected Key Mapping controls diff --git a/patches/net/minecraft/client/Minecraft.java.patch b/patches/net/minecraft/client/Minecraft.java.patch index d73618ac55..c43825a350 100644 --- a/patches/net/minecraft/client/Minecraft.java.patch +++ b/patches/net/minecraft/client/Minecraft.java.patch @@ -1,6 +1,6 @@ --- a/net/minecraft/client/Minecraft.java +++ b/net/minecraft/client/Minecraft.java -@@ -253,7 +_,7 @@ +@@ -250,7 +_,7 @@ import org.slf4j.Logger; @OnlyIn(Dist.CLIENT) @@ -9,7 +9,7 @@ static Minecraft instance; private static final Logger LOGGER = LogUtils.getLogger(); public static final boolean ON_OSX = Util.getPlatform() == Util.OS.OSX; -@@ -437,7 +_,6 @@ +@@ -432,7 +_,6 @@ } }, Util.nonCriticalIoPool()); LOGGER.info("Setting user: {}", this.user.getName()); @@ -17,7 +17,7 @@ this.demo = p_91084_.game.demo; this.allowsMultiplayer = !p_91084_.game.disableMultiplayer; this.allowsChat = !p_91084_.game.disableChat; -@@ -488,15 +_,15 @@ +@@ -483,15 +_,17 @@ LOGGER.error("Couldn't set icon", (Throwable)ioexception); } @@ -32,18 +32,20 @@ this.mainRenderTarget.clear(); this.resourceManager = new ReloadableResourceManager(PackType.CLIENT_RESOURCES); + net.neoforged.neoforge.client.loading.ClientModLoader.begin(this, this.resourcePackRepository, this.resourceManager); ++ //Move client bootstrap to after mod loading so that events can be fired for it. ++ ClientBootstrap.bootstrap(); this.resourcePackRepository.reload(); this.options.loadSelectedResourcePacks(this.resourcePackRepository); this.languageManager = new LanguageManager(this.options.languageCode, p_344151_ -> { -@@ -582,6 +_,7 @@ +@@ -571,6 +_,7 @@ ); - this.resourceManager.registerReloadListener(this.entityRenderDispatcher); + this.resourceManager.registerReloadListener(this.blockEntityRenderDispatcher); this.particleEngine = new ParticleEngine(this.level, this.textureManager); + net.neoforged.neoforge.client.ClientHooks.onRegisterParticleProviders(this.particleEngine); this.resourceManager.registerReloadListener(this.particleEngine); this.paintingTextures = new PaintingTextureManager(this.textureManager); this.resourceManager.registerReloadListener(this.paintingTextures); -@@ -591,11 +_,15 @@ +@@ -580,11 +_,15 @@ this.resourceManager.registerReloadListener(this.guiSprites); this.gameRenderer = new GameRenderer(this, this.entityRenderDispatcher.getItemInHandRenderer(), this.resourceManager, this.renderBuffers); this.levelRenderer = new LevelRenderer(this, this.entityRenderDispatcher, this.blockEntityRenderDispatcher, this.renderBuffers); @@ -59,7 +61,7 @@ this.gui = new Gui(this); this.debugRenderer = new DebugRenderer(this); RealmsClient realmsclient = RealmsClient.create(this); -@@ -620,6 +_,7 @@ +@@ -609,6 +_,7 @@ this.options.fullscreen().set(this.window.isFullscreen()); } @@ -67,7 +69,7 @@ this.window.updateVsync(this.options.enableVsync().get()); this.window.updateRawMouseInput(this.options.rawMouseInput().get()); this.window.setDefaultErrorCallback(); -@@ -641,16 +_,18 @@ +@@ -631,16 +_,18 @@ GameLoadTimesEvent.INSTANCE.beginStep(TelemetryProperty.LOAD_TIME_LOADING_OVERLAY_MS); Minecraft.GameLoadCookie minecraft$gameloadcookie = new Minecraft.GameLoadCookie(realmsclient, p_91084_.quickPlay); this.setOverlay( @@ -90,7 +92,7 @@ ); this.quickPlayLog = QuickPlayLog.of(p_91084_.quickPlay.path()); this.framerateLimitTracker = new FramerateLimitTracker(this.options, this); -@@ -696,6 +_,8 @@ +@@ -686,6 +_,8 @@ runnable = () -> this.setScreen(screen); } @@ -99,7 +101,7 @@ return runnable; } -@@ -744,7 +_,7 @@ +@@ -734,7 +_,7 @@ private String createTitle() { StringBuilder stringbuilder = new StringBuilder("Minecraft"); if (checkModStatus().shouldReportAsModified()) { @@ -108,7 +110,7 @@ } stringbuilder.append(" "); -@@ -776,7 +_,7 @@ +@@ -766,7 +_,7 @@ } private void rollbackResourcePacks(Throwable p_91240_, @Nullable Minecraft.GameLoadCookie p_299846_) { @@ -117,7 +119,7 @@ this.clearResourcePacksOnError(p_91240_, null, p_299846_); } else { Util.throwAsRuntime(p_91240_); -@@ -935,7 +_,7 @@ +@@ -925,7 +_,7 @@ p_307414_.soundManager.emergencyShutdown(); } @@ -126,7 +128,7 @@ } public boolean isEnforceUnicode() { -@@ -1062,9 +_,7 @@ +@@ -1052,9 +_,7 @@ LOGGER.error("setScreen called from non-game thread"); } @@ -137,7 +139,7 @@ this.setLastInputType(InputType.NONE); } -@@ -1081,6 +_,19 @@ +@@ -1071,6 +_,19 @@ } } @@ -157,7 +159,7 @@ this.screen = p_91153_; if (this.screen != null) { this.screen.added(); -@@ -1234,9 +_,11 @@ +@@ -1224,9 +_,11 @@ this.mouseHandler.handleAccumulatedMovement(); profilerfiller.pop(); if (!this.noRender) { @@ -169,7 +171,7 @@ } profilerfiller.push("blit"); -@@ -1264,9 +_,13 @@ +@@ -1257,9 +_,13 @@ profilerfiller.pop(); this.window.setErrorSection("Post render"); this.frames++; @@ -184,7 +186,7 @@ this.deltaTracker.updatePauseState(this.pause); this.deltaTracker.updateFrozenState(!this.isLevelRunningNormally()); long l = Util.getNanos(); -@@ -1358,10 +_,12 @@ +@@ -1351,10 +_,12 @@ this.window.setGuiScale((double)i); if (this.screen != null) { this.screen.resize(this, this.window.getGuiScaledWidth(), this.window.getGuiScaledHeight()); @@ -197,7 +199,7 @@ this.gameRenderer.resize(this.window.getWidth(), this.window.getHeight()); this.mouseHandler.setIgnoreFirstMove(); } -@@ -1502,6 +_,7 @@ +@@ -1495,6 +_,7 @@ } public void stop() { @@ -205,7 +207,7 @@ this.running = false; } -@@ -1531,9 +_,17 @@ +@@ -1524,9 +_,17 @@ BlockHitResult blockhitresult = (BlockHitResult)this.hitResult; BlockPos blockpos = blockhitresult.getBlockPos(); if (!this.level.getBlockState(blockpos).isAir()) { @@ -225,7 +227,7 @@ this.player.swing(InteractionHand.MAIN_HAND); } } -@@ -1561,6 +_,8 @@ +@@ -1554,6 +_,8 @@ return false; } else { boolean flag = false; @@ -234,7 +236,7 @@ switch (this.hitResult.getType()) { case ENTITY: this.gameMode.attack(this.player, ((EntityHitResult)this.hitResult).getEntity()); -@@ -1581,8 +_,10 @@ +@@ -1574,8 +_,10 @@ } this.player.resetAttackStrengthTicker(); @@ -245,7 +247,7 @@ this.player.swing(InteractionHand.MAIN_HAND); return flag; } -@@ -1598,6 +_,11 @@ +@@ -1591,6 +_,11 @@ } for (InteractionHand interactionhand : InteractionHand.values()) { @@ -257,7 +259,7 @@ ItemStack itemstack = this.player.getItemInHand(interactionhand); if (!itemstack.isItemEnabled(this.level.enabledFeatures())) { return; -@@ -1618,7 +_,7 @@ +@@ -1611,7 +_,7 @@ } if (interactionresult instanceof InteractionResult.Success interactionresult$success2) { @@ -266,7 +268,7 @@ this.player.swing(interactionhand); } -@@ -1630,7 +_,7 @@ +@@ -1623,7 +_,7 @@ int i = itemstack.getCount(); InteractionResult interactionresult1 = this.gameMode.useItemOn(this.player, interactionhand, blockhitresult); if (interactionresult1 instanceof InteractionResult.Success interactionresult$success) { @@ -275,7 +277,7 @@ this.player.swing(interactionhand); if (!itemstack.isEmpty() && (itemstack.getCount() != i || this.gameMode.hasInfiniteItems())) { this.gameRenderer.itemInHandRenderer.itemUsed(interactionhand); -@@ -1646,6 +_,9 @@ +@@ -1639,6 +_,9 @@ } } @@ -285,7 +287,7 @@ if (!itemstack.isEmpty() && this.gameMode.useItem(this.player, interactionhand) instanceof InteractionResult.Success interactionresult$success1) { if (interactionresult$success1.swingSource() == InteractionResult.SwingSource.CLIENT) { -@@ -1666,6 +_,8 @@ +@@ -1659,6 +_,8 @@ public void tick() { this.clientTickCount++; @@ -294,7 +296,7 @@ if (this.level != null && !this.pause) { this.level.tickRateManager().tick(); } -@@ -1764,6 +_,7 @@ +@@ -1757,6 +_,7 @@ this.tutorial.tick(); @@ -302,7 +304,7 @@ try { this.level.tick(() -> true); } catch (Throwable throwable1) { -@@ -1777,6 +_,7 @@ +@@ -1770,6 +_,7 @@ throw new ReportedException(crashreport1); } @@ -310,7 +312,7 @@ } profilerfiller.popPush("animateTick"); -@@ -1801,6 +_,8 @@ +@@ -1794,6 +_,8 @@ profilerfiller.popPush("keyboard"); this.keyboardHandler.tick(); profilerfiller.pop(); @@ -319,7 +321,7 @@ } private boolean isLevelRunningNormally() { -@@ -2000,7 +_,8 @@ +@@ -1993,7 +_,8 @@ } public void setLevel(ClientLevel p_91157_, ReceivingLevelScreen.Reason p_341652_) { @@ -329,7 +331,7 @@ this.level = p_91157_; this.updateLevelInEngines(p_91157_); if (!this.isLocalServer) { -@@ -2037,6 +_,7 @@ +@@ -2030,6 +_,7 @@ IntegratedServer integratedserver = this.singleplayerServer; this.singleplayerServer = null; this.gameRenderer.resetData(); @@ -337,7 +339,7 @@ this.gameMode = null; this.narrator.clear(); this.clientLevelTeardownInProgress = true; -@@ -2044,6 +_,7 @@ +@@ -2037,6 +_,7 @@ try { this.updateScreenAndTick(p_320248_); if (this.level != null) { @@ -345,40 +347,11 @@ if (integratedserver != null) { ProfilerFiller profilerfiller = Profiler.get(); profilerfiller.push("waitForServer"); -@@ -2204,6 +_,7 @@ +@@ -2197,6 +_,7 @@ private void pickBlock() { if (this.hitResult != null && this.hitResult.getType() != HitResult.Type.MISS) { + if (net.neoforged.neoforge.client.ClientHooks.onClickInput(2, this.options.keyPickItem, InteractionHand.MAIN_HAND).isCanceled()) return; - boolean flag = this.player.getAbilities().instabuild; - BlockEntity blockentity = null; - HitResult.Type hitresult$type = this.hitResult.getType(); -@@ -2216,7 +_,7 @@ - } - - Block block = blockstate.getBlock(); -- itemstack = block.getCloneItemStack(this.level, blockpos, blockstate); -+ itemstack = blockstate.getCloneItemStack(this.hitResult, this.level, blockpos, this.player); - if (itemstack.isEmpty()) { - return; - } -@@ -2230,7 +_,7 @@ - } - - Entity entity = ((EntityHitResult)this.hitResult).getEntity(); -- itemstack = entity.getPickResult(); -+ itemstack = entity.getPickedResult(this.hitResult); - if (itemstack == null) { - return; - } -@@ -2757,6 +_,10 @@ - - public void updateMaxMipLevel(int p_91313_) { - this.modelManager.updateMaxMipLevel(p_91313_); -+ } -+ -+ public ItemColors getItemColors() { -+ return this.itemColors; - } - - public EntityModelSet getEntityModels() { + boolean flag = Screen.hasControlDown(); + HitResult hitresult = this.hitResult; + Objects.requireNonNull(this.hitResult); diff --git a/patches/net/minecraft/client/color/block/BlockColors.java.patch b/patches/net/minecraft/client/color/block/BlockColors.java.patch index 32c2a2e76b..9305fe8d82 100644 --- a/patches/net/minecraft/client/color/block/BlockColors.java.patch +++ b/patches/net/minecraft/client/color/block/BlockColors.java.patch @@ -1,16 +1,16 @@ --- a/net/minecraft/client/color/block/BlockColors.java +++ b/net/minecraft/client/color/block/BlockColors.java -@@ -29,7 +_,8 @@ - @OnlyIn(Dist.CLIENT) - public class BlockColors { +@@ -30,7 +_,8 @@ private static final int DEFAULT = -1; + public static final int LILY_PAD_IN_WORLD = -14647248; + public static final int LILY_PAD_DEFAULT = -9321636; - private final IdMapper blockColors = new IdMapper<>(32); + // Neo: Use the block instance directly as non-Vanilla block ids are not constant + private final java.util.Map blockColors = new java.util.IdentityHashMap<>(); private final Map>> coloringStates = Maps.newHashMap(); public static BlockColors createDefault() { -@@ -94,11 +_,12 @@ +@@ -95,11 +_,12 @@ }, Blocks.MELON_STEM, Blocks.PUMPKIN_STEM); blockcolors.addColoringState(StemBlock.AGE, Blocks.MELON_STEM, Blocks.PUMPKIN_STEM); blockcolors.register((p_92596_, p_92597_, p_92598_, p_92599_) -> p_92597_ != null && p_92598_ != null ? -14647248 : -9321636, Blocks.LILY_PAD); @@ -24,7 +24,7 @@ if (blockcolor != null) { return blockcolor.getColor(p_92583_, null, null, 0); } else { -@@ -108,13 +_,15 @@ +@@ -109,13 +_,15 @@ } public int getColor(BlockState p_92578_, @Nullable BlockAndTintGetter p_92579_, @Nullable BlockPos p_92580_, int p_92581_) { diff --git a/patches/net/minecraft/client/color/item/ItemColors.java.patch b/patches/net/minecraft/client/color/item/ItemColors.java.patch deleted file mode 100644 index afe4aac398..0000000000 --- a/patches/net/minecraft/client/color/item/ItemColors.java.patch +++ /dev/null @@ -1,35 +0,0 @@ ---- a/net/minecraft/client/color/item/ItemColors.java -+++ b/net/minecraft/client/color/item/ItemColors.java -@@ -26,7 +_,8 @@ - @OnlyIn(Dist.CLIENT) - public class ItemColors { - private static final int DEFAULT = -1; -- private final IdMapper itemColors = new IdMapper<>(32); -+ // Neo: Use the item instance directly as non-Vanilla item ids are not constant -+ private final java.util.Map itemColors = new java.util.IdentityHashMap<>(); - - public static ItemColors createDefault(BlockColors p_92684_) { - ItemColors itemcolors = new ItemColors(); -@@ -101,17 +_,20 @@ - (p_359075_, p_359076_) -> p_359076_ == 0 ? -1 : ARGB.opaque(p_359075_.getOrDefault(DataComponents.MAP_COLOR, MapItemColor.DEFAULT).rgb()), - Items.FILLED_MAP - ); -+ net.neoforged.neoforge.client.ClientHooks.onItemColorsInit(itemcolors, p_92684_); - return itemcolors; - } - - public int getColor(ItemStack p_92677_, int p_92678_) { -- ItemColor itemcolor = this.itemColors.byId(BuiltInRegistries.ITEM.getId(p_92677_.getItem())); -+ ItemColor itemcolor = this.itemColors.get(p_92677_.getItem()); - return itemcolor == null ? -1 : itemcolor.getColor(p_92677_, p_92678_); - } - -+ /** @deprecated Register via {@link net.neoforged.neoforge.client.event.RegisterColorHandlersEvent.Item} */ -+ @Deprecated - public void register(ItemColor p_92690_, ItemLike... p_92691_) { - for (ItemLike itemlike : p_92691_) { -- this.itemColors.addMapping(p_92690_, Item.getId(itemlike.asItem())); -+ this.itemColors.put(itemlike.asItem(), p_92690_); - } - } - } diff --git a/patches/net/minecraft/client/color/item/ItemTintSources.java.patch b/patches/net/minecraft/client/color/item/ItemTintSources.java.patch new file mode 100644 index 0000000000..d240b059e1 --- /dev/null +++ b/patches/net/minecraft/client/color/item/ItemTintSources.java.patch @@ -0,0 +1,10 @@ +--- a/net/minecraft/client/color/item/ItemTintSources.java ++++ b/net/minecraft/client/color/item/ItemTintSources.java +@@ -21,5 +_,7 @@ + ID_MAPPER.put(ResourceLocation.withDefaultNamespace("potion"), Potion.MAP_CODEC); + ID_MAPPER.put(ResourceLocation.withDefaultNamespace("map_color"), MapColor.MAP_CODEC); + ID_MAPPER.put(ResourceLocation.withDefaultNamespace("team"), TeamColor.MAP_CODEC); ++ ++ net.neoforged.fml.ModLoader.postEvent(new net.neoforged.neoforge.client.event.RegisterColorHandlersEvent.ItemTintSources(ID_MAPPER)); + } + } diff --git a/patches/net/minecraft/client/data/Main.java.patch b/patches/net/minecraft/client/data/Main.java.patch new file mode 100644 index 0000000000..5aa2dc3ded --- /dev/null +++ b/patches/net/minecraft/client/data/Main.java.patch @@ -0,0 +1,40 @@ +--- a/net/minecraft/client/data/Main.java ++++ b/net/minecraft/client/data/Main.java +@@ -30,16 +_,33 @@ + OptionSpec optionspec1 = optionparser.accepts("client", "Include client generators"); + OptionSpec optionspec2 = optionparser.accepts("all", "Include all generators"); + OptionSpec optionspec3 = optionparser.accepts("output", "Output folder").withRequiredArg().defaultsTo("generated"); ++ OptionSpec existing = optionparser.accepts("existing", "Existing resource packs that generated resources can reference").withRequiredArg(); ++ OptionSpec existingMod = optionparser.accepts("existing-mod", "Existing mods that generated resources can reference the resource packs of").withRequiredArg(); ++ OptionSpec gameDir = optionparser.accepts("gameDir").withRequiredArg().ofType(java.io.File.class).defaultsTo(new java.io.File(".")).required(); //Need by modlauncher, so lets just eat it ++ OptionSpec mod = optionparser.accepts("mod", "A modid to dump").withRequiredArg().withValuesSeparatedBy(","); ++ OptionSpec flat = optionparser.accepts("flat", "Do not append modid prefix to output directory when generating for multiple mods"); ++ OptionSpec assetIndex = optionparser.accepts("assetIndex").withRequiredArg(); ++ OptionSpec assetsDir = optionparser.accepts("assetsDir").withRequiredArg().ofType(java.io.File.class); ++ OptionSpec validateSpec = optionparser.accepts("validate", "Validate inputs"); + OptionSet optionset = optionparser.parse(p_388033_); + if (!optionset.has(optionspec) && optionset.hasOptions()) { + Path path = Paths.get(optionspec3.value(optionset)); + boolean flag = optionset.has(optionspec2); + boolean flag1 = flag || optionset.has(optionspec1); +- Bootstrap.bootStrap(); +- ClientBootstrap.bootstrap(); +- DataGenerator datagenerator = new DataGenerator(path, SharedConstants.getCurrentVersion(), true); ++ java.util.Collection existingPacks = optionset.valuesOf(existing).stream().map(Paths::get).toList(); ++ java.util.Set existingMods = new java.util.HashSet<>(optionset.valuesOf(existingMod)); ++ java.util.Set mods = new java.util.HashSet<>(optionset.valuesOf(mod)); ++ boolean isFlat = mods.isEmpty() || optionset.has(flat); ++ boolean validate = optionset.has(validateSpec); ++ DataGenerator datagenerator = new DataGenerator(isFlat ? path : path.resolve("minecraft"), SharedConstants.getCurrentVersion(), true); ++ if (mods.contains("minecraft") || mods.isEmpty()) { + addClientProviders(datagenerator, flag1); +- datagenerator.run(); ++ } ++ net.neoforged.neoforge.data.loading.DatagenModLoader.begin(mods, path, java.util.List.of(), existingPacks, existingMods, false, false, validate, isFlat, optionset.valueOf(assetIndex), optionset.valueOf(assetsDir), () -> { ++ ClientBootstrap.bootstrap(); ++ net.neoforged.neoforge.client.ClientHooks.registerSpriteSourceTypes(); ++ net.neoforged.neoforge.client.entity.animation.json.AnimationTypeManager.init(); ++ }, net.neoforged.neoforge.data.event.GatherDataEvent.Client::new, datagenerator); + } else { + optionparser.printHelpOn(System.out); + } diff --git a/patches/net/minecraft/client/gui/Font.java.patch b/patches/net/minecraft/client/gui/Font.java.patch index 92bc34f85a..f147a8bcdc 100644 --- a/patches/net/minecraft/client/gui/Font.java.patch +++ b/patches/net/minecraft/client/gui/Font.java.patch @@ -1,20 +1,56 @@ --- a/net/minecraft/client/gui/Font.java +++ b/net/minecraft/client/gui/Font.java -@@ -33,7 +_,7 @@ - import org.joml.Vector3f; +@@ -32,7 +_,7 @@ + import org.joml.Matrix4f; @OnlyIn(Dist.CLIENT) -public class Font { +public class Font implements net.neoforged.neoforge.client.extensions.IFontExtension { private static final float EFFECT_DEPTH = 0.01F; - private static final Vector3f SHADOW_OFFSET = new Vector3f(0.0F, 0.0F, 0.03F); - public static final int ALPHA_CUTOFF = 8; -@@ -309,6 +_,8 @@ - public StringSplitter getSplitter() { + public static final float SHADOW_DEPTH = 0.03F; + public static final int NO_SHADOW = 0; +@@ -42,6 +_,8 @@ + private final Function fonts; + final boolean filterFishyGlyphs; + private final StringSplitter splitter; ++ /** Neo: enables linear filtering on text */ ++ public boolean enableTextTextureLinearFiltering = false; + + public Font(Function p_243253_, boolean p_243245_) { + this.fonts = p_243253_; +@@ -298,6 +_,8 @@ return this.splitter; } -+ -+ @Override public Font self() { return this; } ++ @Override public Font self() { return this; } ++ @OnlyIn(Dist.CLIENT) public static enum DisplayMode { + NORMAL, +@@ -403,7 +_,7 @@ + p_381032_ - 1.0F, this.y + 9.0F, this.x, this.y - 1.0F, this.getUnderTextEffectDepth(), this.backgroundColor + ); + bakedglyph = Font.this.getFontSet(Style.DEFAULT_FONT).whiteGlyph(); +- VertexConsumer vertexconsumer = this.bufferSource.getBuffer(bakedglyph.renderType(this.mode)); ++ VertexConsumer vertexconsumer = this.bufferSource.getBuffer(bakedglyph.renderType(this.mode, Font.this.enableTextTextureLinearFiltering)); + bakedglyph.renderEffect(bakedglyph$effect, this.pose, vertexconsumer, this.packedLightCoords); + } + +@@ -413,7 +_,7 @@ + bakedglyph = Font.this.getFontSet(Style.DEFAULT_FONT).whiteGlyph(); + } + +- VertexConsumer vertexconsumer1 = this.bufferSource.getBuffer(bakedglyph.renderType(this.mode)); ++ VertexConsumer vertexconsumer1 = this.bufferSource.getBuffer(bakedglyph.renderType(this.mode, Font.this.enableTextTextureLinearFiltering)); + + for (BakedGlyph.Effect bakedglyph$effect1 : this.effects) { + bakedglyph.renderEffect(bakedglyph$effect1, this.pose, vertexconsumer1, this.packedLightCoords); +@@ -447,7 +_,7 @@ + void renderCharacters() { + for (BakedGlyph.GlyphInstance bakedglyph$glyphinstance : this.glyphInstances) { + BakedGlyph bakedglyph = bakedglyph$glyphinstance.glyph(); +- VertexConsumer vertexconsumer = this.bufferSource.getBuffer(bakedglyph.renderType(this.mode)); ++ VertexConsumer vertexconsumer = this.bufferSource.getBuffer(bakedglyph.renderType(this.mode, Font.this.enableTextTextureLinearFiltering)); + bakedglyph.renderChar(bakedglyph$glyphinstance, this.pose, vertexconsumer, this.packedLightCoords); + } + } diff --git a/patches/net/minecraft/client/gui/Gui.java.patch b/patches/net/minecraft/client/gui/Gui.java.patch index 57ea34601f..333bdeda69 100644 --- a/patches/net/minecraft/client/gui/Gui.java.patch +++ b/patches/net/minecraft/client/gui/Gui.java.patch @@ -386,7 +386,7 @@ this.toolHighlightTimer = (int)(40.0 * this.minecraft.options.notificationDisplayTime().get()); } else if (this.toolHighlightTimer > 0) { this.toolHighlightTimer--; -@@ -1319,8 +_,17 @@ +@@ -1321,8 +_,17 @@ } } @@ -405,7 +405,7 @@ CONTAINER( ResourceLocation.withDefaultNamespace("hud/heart/container"), ResourceLocation.withDefaultNamespace("hud/heart/container_blinking"), -@@ -1436,8 +_,13 @@ +@@ -1438,8 +_,13 @@ } else { gui$hearttype = NORMAL; } diff --git a/patches/net/minecraft/client/gui/GuiGraphics.java.patch b/patches/net/minecraft/client/gui/GuiGraphics.java.patch index caca78e363..472cc1b836 100644 --- a/patches/net/minecraft/client/gui/GuiGraphics.java.patch +++ b/patches/net/minecraft/client/gui/GuiGraphics.java.patch @@ -1,6 +1,6 @@ --- a/net/minecraft/client/gui/GuiGraphics.java +++ b/net/minecraft/client/gui/GuiGraphics.java -@@ -53,7 +_,7 @@ +@@ -52,7 +_,7 @@ import org.joml.Vector2ic; @OnlyIn(Dist.CLIENT) @@ -9,7 +9,7 @@ public static final float MAX_GUI_Z = 10000.0F; public static final float MIN_GUI_Z = -10000.0F; private static final int EXTRA_SPACE_AFTER_FIRST_TOOLTIP_LINE = 2; -@@ -227,6 +_,11 @@ +@@ -229,6 +_,11 @@ } public int drawString(Font p_283343_, @Nullable String p_281896_, int p_283569_, int p_283418_, int p_281560_, boolean p_282130_) { @@ -21,7 +21,7 @@ return p_281896_ == null ? 0 : p_283343_.drawInBatch( -@@ -248,6 +_,11 @@ +@@ -250,6 +_,11 @@ } public int drawString(Font p_282636_, FormattedCharSequence p_281596_, int p_281586_, int p_282816_, int p_281743_, boolean p_282394_) { @@ -33,7 +33,7 @@ return p_282636_.drawInBatch( p_281596_, (float)p_281586_, -@@ -870,10 +_,15 @@ +@@ -859,10 +_,15 @@ this.renderItemCount(p_282005_, p_283349_, p_282641_, p_282146_, p_282803_); this.renderItemCooldown(p_283349_, p_282641_, p_282146_); this.pose.popPose(); @@ -49,7 +49,7 @@ this.renderTooltip( p_282308_, Screen.getTooltipFromItem(this.minecraft, p_282781_), -@@ -882,6 +_,17 @@ +@@ -871,6 +_,17 @@ p_282292_, p_282781_.get(DataComponents.TOOLTIP_STYLE) ); @@ -67,7 +67,7 @@ } public void renderTooltip(Font p_283128_, List p_282716_, Optional p_281682_, int p_283678_, int p_281696_) { -@@ -891,11 +_,7 @@ +@@ -880,11 +_,7 @@ public void renderTooltip( Font p_371715_, List p_371741_, Optional p_371604_, int p_371500_, int p_371755_, @Nullable ResourceLocation p_371766_ ) { @@ -80,7 +80,7 @@ this.renderTooltipInternal(p_371715_, list, p_371500_, p_371755_, DefaultTooltipPositioner.INSTANCE, p_371766_); } -@@ -908,13 +_,14 @@ +@@ -897,13 +_,14 @@ } public void renderComponentTooltip(Font p_282739_, List p_281832_, int p_282191_, int p_282446_) { @@ -97,7 +97,7 @@ p_371314_, p_371389_, DefaultTooltipPositioner.INSTANCE, -@@ -922,6 +_,28 @@ +@@ -911,6 +_,28 @@ ); } @@ -126,7 +126,7 @@ public void renderTooltip(Font p_282192_, List p_282297_, int p_281680_, int p_283325_) { this.renderTooltip(p_282192_, p_282297_, p_281680_, p_283325_, null); } -@@ -954,41 +_,45 @@ +@@ -943,41 +_,45 @@ @Nullable ResourceLocation p_371327_ ) { if (!p_282615_.isEmpty()) { diff --git a/patches/net/minecraft/client/gui/components/AbstractWidget.java.patch b/patches/net/minecraft/client/gui/components/AbstractWidget.java.patch index 256cef46f4..6022c53dcf 100644 --- a/patches/net/minecraft/client/gui/components/AbstractWidget.java.patch +++ b/patches/net/minecraft/client/gui/components/AbstractWidget.java.patch @@ -19,7 +19,7 @@ } @@ -139,7 +_,7 @@ - boolean flag = this.clicked(p_93641_, p_93642_); + boolean flag = this.isMouseOver(p_93641_, p_93642_); if (flag) { this.playDownSound(Minecraft.getInstance().getSoundManager()); - this.onClick(p_93641_, p_93642_); @@ -27,7 +27,7 @@ return true; } } -@@ -257,6 +_,19 @@ +@@ -248,6 +_,19 @@ @Override public void setFocused(boolean p_93693_) { this.focused = p_93693_; diff --git a/patches/net/minecraft/client/gui/font/GlyphRenderTypes.java.patch b/patches/net/minecraft/client/gui/font/GlyphRenderTypes.java.patch new file mode 100644 index 0000000000..41aedd3241 --- /dev/null +++ b/patches/net/minecraft/client/gui/font/GlyphRenderTypes.java.patch @@ -0,0 +1,50 @@ +--- a/net/minecraft/client/gui/font/GlyphRenderTypes.java ++++ b/net/minecraft/client/gui/font/GlyphRenderTypes.java +@@ -7,22 +_,42 @@ + import net.neoforged.api.distmarker.OnlyIn; + + @OnlyIn(Dist.CLIENT) +-public record GlyphRenderTypes(RenderType normal, RenderType seeThrough, RenderType polygonOffset) { ++public record GlyphRenderTypes(RenderType normal, RenderType seeThrough, RenderType polygonOffset, RenderType normalBlur, RenderType seeThroughBlur, RenderType polygonOffsetBlur) { ++ /** @deprecated Neo: Use {@link GlyphRenderTypes(RenderType,RenderType,RenderType,RenderType,RenderType,RenderType)} instead */ ++ @Deprecated ++ public GlyphRenderTypes(RenderType normal, RenderType seeThrough, RenderType polygonOffset) { ++ this(normal, seeThrough, polygonOffset, normal, seeThrough, polygonOffset); ++ } ++ + public static GlyphRenderTypes createForIntensityTexture(ResourceLocation p_285411_) { + return new GlyphRenderTypes( + RenderType.textIntensity(p_285411_), RenderType.textIntensitySeeThrough(p_285411_), RenderType.textIntensityPolygonOffset(p_285411_) ++ , net.neoforged.neoforge.client.NeoForgeRenderTypes.getTextIntensityFiltered(p_285411_), ++ net.neoforged.neoforge.client.NeoForgeRenderTypes.getTextIntensitySeeThroughFiltered(p_285411_), ++ net.neoforged.neoforge.client.NeoForgeRenderTypes.getTextIntensityPolygonOffsetFiltered(p_285411_) + ); + } + + public static GlyphRenderTypes createForColorTexture(ResourceLocation p_285486_) { +- return new GlyphRenderTypes(RenderType.text(p_285486_), RenderType.textSeeThrough(p_285486_), RenderType.textPolygonOffset(p_285486_)); ++ return new GlyphRenderTypes(RenderType.text(p_285486_), RenderType.textSeeThrough(p_285486_), RenderType.textPolygonOffset(p_285486_), ++ net.neoforged.neoforge.client.NeoForgeRenderTypes.getTextFiltered(p_285486_), ++ net.neoforged.neoforge.client.NeoForgeRenderTypes.getTextSeeThroughFiltered(p_285486_), ++ net.neoforged.neoforge.client.NeoForgeRenderTypes.getTextPolygonOffsetFiltered(p_285486_) ++ ); + } + + public RenderType select(Font.DisplayMode p_285259_) { ++ return this.select(p_285259_, false); ++ } ++ ++ /** ++ * Neo: returns the {@link RenderType} to use for the given {@link Font.DisplayMode} and blur setting ++ */ ++ public RenderType select(Font.DisplayMode p_285259_, boolean blur) { + return switch (p_285259_) { +- case NORMAL -> this.normal; +- case SEE_THROUGH -> this.seeThrough; +- case POLYGON_OFFSET -> this.polygonOffset; ++ case NORMAL -> blur ? this.normalBlur : this.normal; ++ case SEE_THROUGH -> blur ? this.seeThroughBlur : this.seeThrough; ++ case POLYGON_OFFSET -> blur ? this.polygonOffsetBlur : this.polygonOffset; + }; + } + } diff --git a/patches/net/minecraft/client/gui/font/glyphs/BakedGlyph.java.patch b/patches/net/minecraft/client/gui/font/glyphs/BakedGlyph.java.patch new file mode 100644 index 0000000000..207270ffd3 --- /dev/null +++ b/patches/net/minecraft/client/gui/font/glyphs/BakedGlyph.java.patch @@ -0,0 +1,18 @@ +--- a/net/minecraft/client/gui/font/glyphs/BakedGlyph.java ++++ b/net/minecraft/client/gui/font/glyphs/BakedGlyph.java +@@ -139,6 +_,15 @@ + .setLight(p_382874_); + } + ++ /** ++ * Neo: returns the {@link RenderType} to use for the given {@link Font.DisplayMode} and blur setting ++ */ ++ public RenderType renderType(Font.DisplayMode p_181388_, boolean blur) { ++ return this.renderTypes.select(p_181388_, blur); ++ } ++ ++ /** @deprecated Neo: Use {@link #renderType(Font.DisplayMode, boolean)} instead */ ++ @Deprecated + public RenderType renderType(Font.DisplayMode p_181388_) { + return this.renderTypes.select(p_181388_); + } diff --git a/patches/net/minecraft/client/gui/screens/LoadingOverlay.java.patch b/patches/net/minecraft/client/gui/screens/LoadingOverlay.java.patch index b607643c50..d0c1f86d70 100644 --- a/patches/net/minecraft/client/gui/screens/LoadingOverlay.java.patch +++ b/patches/net/minecraft/client/gui/screens/LoadingOverlay.java.patch @@ -1,6 +1,6 @@ --- a/net/minecraft/client/gui/screens/LoadingOverlay.java +++ b/net/minecraft/client/gui/screens/LoadingOverlay.java -@@ -123,6 +_,7 @@ +@@ -122,6 +_,7 @@ } if (this.fadeOutStart == -1L && this.reload.isDone() && (!this.fadeIn || f1 >= 2.0F)) { @@ -8,7 +8,7 @@ try { this.reload.checkExceptions(); this.onFinish.accept(Optional.empty()); -@@ -130,7 +_,6 @@ +@@ -129,7 +_,6 @@ this.onFinish.accept(Optional.of(throwable)); } diff --git a/patches/net/minecraft/client/gui/screens/Screen.java.patch b/patches/net/minecraft/client/gui/screens/Screen.java.patch index e7d7751d22..8f599df40b 100644 --- a/patches/net/minecraft/client/gui/screens/Screen.java.patch +++ b/patches/net/minecraft/client/gui/screens/Screen.java.patch @@ -69,7 +69,7 @@ private void scheduleNarration(long p_169381_, boolean p_169382_) { this.nextNarrationTime = Util.getMillis() + p_169381_; if (p_169382_) { -@@ -642,5 +_,13 @@ +@@ -643,5 +_,13 @@ this.index = p_169425_; this.priority = p_169426_; } diff --git a/patches/net/minecraft/client/gui/screens/TitleScreen.java.patch b/patches/net/minecraft/client/gui/screens/TitleScreen.java.patch index 2038b23682..7ded730c48 100644 --- a/patches/net/minecraft/client/gui/screens/TitleScreen.java.patch +++ b/patches/net/minecraft/client/gui/screens/TitleScreen.java.patch @@ -1,6 +1,6 @@ --- a/net/minecraft/client/gui/screens/TitleScreen.java +++ b/net/minecraft/client/gui/screens/TitleScreen.java -@@ -114,11 +_,17 @@ +@@ -110,11 +_,17 @@ int i = this.font.width(COPYRIGHT_TEXT); int j = this.width - i - 2; int k = 24; @@ -19,7 +19,7 @@ } l = this.createTestWorldButton(l, 24); -@@ -160,7 +_,7 @@ +@@ -156,7 +_,7 @@ if (SharedConstants.IS_RUNNING_IN_IDE) { this.addRenderableWidget( Button.builder(Component.literal("Create Test World"), p_372504_ -> CreateWorldScreen.testWorld(this.minecraft, this)) @@ -28,7 +28,7 @@ .build() ); } -@@ -304,6 +_,7 @@ +@@ -300,6 +_,7 @@ if ((i & -67108864) != 0) { super.render(p_282860_, p_281753_, p_283539_, p_282628_); this.logoRenderer.renderLogo(p_282860_, this.width, f); @@ -36,7 +36,7 @@ if (this.splash != null && !this.minecraft.options.hideSplashTexts().get()) { this.splash.render(p_282860_, this.width, this.font, i); } -@@ -319,7 +_,13 @@ +@@ -315,7 +_,13 @@ s = s + I18n.get("menu.modded"); } diff --git a/patches/net/minecraft/client/gui/screens/inventory/AbstractContainerScreen.java.patch b/patches/net/minecraft/client/gui/screens/inventory/AbstractContainerScreen.java.patch index be4ec4baf1..872e89fb06 100644 --- a/patches/net/minecraft/client/gui/screens/inventory/AbstractContainerScreen.java.patch +++ b/patches/net/minecraft/client/gui/screens/inventory/AbstractContainerScreen.java.patch @@ -1,6 +1,6 @@ --- a/net/minecraft/client/gui/screens/inventory/AbstractContainerScreen.java +++ b/net/minecraft/client/gui/screens/inventory/AbstractContainerScreen.java -@@ -107,7 +_,12 @@ +@@ -105,7 +_,12 @@ public void render(GuiGraphics p_283479_, int p_283661_, int p_281248_, float p_281886_) { int i = this.leftPos; int j = this.topPos; @@ -14,7 +14,7 @@ p_283479_.pose().pushPose(); p_283479_.pose().translate((float)i, (float)j, 0.0F); Slot slot = this.hoveredSlot; -@@ -120,6 +_,7 @@ +@@ -118,6 +_,7 @@ } this.renderLabels(p_283479_, p_283661_, p_281248_); @@ -22,7 +22,7 @@ ItemStack itemstack = this.draggingItem.isEmpty() ? this.menu.getCarried() : this.draggingItem; if (!itemstack.isEmpty()) { int k = 8; -@@ -202,6 +_,7 @@ +@@ -200,6 +_,7 @@ this.font, this.getTooltipFromContainerItem(itemstack), itemstack.getTooltipImage(), @@ -30,7 +30,7 @@ p_282171_, p_281909_, itemstack.get(DataComponents.TOOLTIP_STYLE) -@@ -222,7 +_,8 @@ +@@ -220,7 +_,8 @@ p_282567_.pose().pushPose(); p_282567_.pose().translate(0.0F, 0.0F, 232.0F); p_282567_.renderItem(p_281330_, p_281772_, p_281689_); @@ -40,7 +40,7 @@ p_282567_.pose().popPose(); } -@@ -281,6 +_,14 @@ +@@ -278,6 +_,14 @@ p_281607_.fill(i, j, i + 16, j + 16, -2130706433); } @@ -55,7 +55,7 @@ int j1 = p_282613_.x + p_282613_.y * this.imageWidth; if (p_282613_.isFake()) { p_281607_.renderFakeItem(itemstack, i, j, j1); -@@ -289,9 +_,6 @@ +@@ -286,9 +_,6 @@ } p_281607_.renderItemDecorations(this.font, itemstack, i, j, s); @@ -65,7 +65,7 @@ } private void recalculateQuickCraftRemaining() { -@@ -329,7 +_,8 @@ +@@ -326,7 +_,8 @@ if (super.mouseClicked(p_97748_, p_97749_, p_97750_)) { return true; } else { @@ -75,7 +75,7 @@ Slot slot = this.getHoveredSlot(p_97748_, p_97749_); long i = Util.getMillis(); this.doubleclick = this.lastClickSlot == slot && i - this.lastClickTime < 250L && this.lastClickButton == p_97750_; -@@ -340,6 +_,7 @@ +@@ -337,6 +_,7 @@ int j = this.leftPos; int k = this.topPos; boolean flag1 = this.hasClickedOutside(p_97748_, p_97749_, j, k, p_97750_); @@ -83,7 +83,7 @@ int l = -1; if (slot != null) { l = slot.index; -@@ -365,7 +_,7 @@ +@@ -362,7 +_,7 @@ } } else if (!this.isQuickCrafting) { if (this.menu.getCarried().isEmpty()) { @@ -92,7 +92,7 @@ this.slotClicked(slot, l, p_97750_, ClickType.CLONE); } else { boolean flag2 = l != -999 -@@ -393,7 +_,7 @@ +@@ -390,7 +_,7 @@ this.quickCraftingType = 0; } else if (p_97750_ == 1) { this.quickCraftingType = 1; @@ -101,7 +101,7 @@ this.quickCraftingType = 2; } } -@@ -472,10 +_,13 @@ +@@ -469,10 +_,13 @@ @Override public boolean mouseReleased(double p_97812_, double p_97813_, int p_97814_) { @@ -115,7 +115,7 @@ int k = -1; if (slot != null) { k = slot.index; -@@ -492,7 +_,7 @@ +@@ -489,7 +_,7 @@ if (slot2 != null && slot2.mayPickup(this.minecraft.player) && slot2.hasItem() @@ -124,7 +124,7 @@ && AbstractContainerMenu.canItemQuickReplace(slot2, this.lastQuickMoved, true)) { this.slotClicked(slot2, slot2.index, p_97814_, ClickType.QUICK_MOVE); } -@@ -556,7 +_,7 @@ +@@ -553,7 +_,7 @@ this.slotClicked(null, -999, AbstractContainerMenu.getQuickcraftMask(2, this.quickCraftingType), ClickType.QUICK_CRAFT); } else if (!this.menu.getCarried().isEmpty()) { @@ -133,7 +133,7 @@ this.slotClicked(slot, k, p_97814_, ClickType.CLONE); } else { boolean flag1 = k != -999 -@@ -636,34 +_,39 @@ +@@ -633,34 +_,39 @@ @Override public boolean keyPressed(int p_97765_, int p_97766_, int p_97767_) { @@ -180,7 +180,7 @@ this.slotClicked(this.hoveredSlot, this.hoveredSlot.index, i, ClickType.SWAP); return true; } -@@ -701,6 +_,18 @@ +@@ -698,6 +_,18 @@ @Override public T getMenu() { return this.menu; diff --git a/patches/net/minecraft/client/gui/screens/inventory/CreativeModeInventoryScreen.java.patch b/patches/net/minecraft/client/gui/screens/inventory/CreativeModeInventoryScreen.java.patch index 37ccae8839..309b303ed2 100644 --- a/patches/net/minecraft/client/gui/screens/inventory/CreativeModeInventoryScreen.java.patch +++ b/patches/net/minecraft/client/gui/screens/inventory/CreativeModeInventoryScreen.java.patch @@ -1,6 +1,6 @@ --- a/net/minecraft/client/gui/screens/inventory/CreativeModeInventoryScreen.java +++ b/net/minecraft/client/gui/screens/inventory/CreativeModeInventoryScreen.java -@@ -115,6 +_,8 @@ +@@ -114,6 +_,8 @@ private final Set> visibleTags = new HashSet<>(); private final boolean displayOperatorCreativeTab; private final EffectsInInventory effects; @@ -9,7 +9,7 @@ public CreativeModeInventoryScreen(LocalPlayer p_346290_, FeatureFlagSet p_260074_, boolean p_259569_) { super(new CreativeModeInventoryScreen.ItemPickerMenu(p_346290_), p_346290_.getInventory(), CommonComponents.EMPTY); -@@ -151,9 +_,11 @@ +@@ -150,9 +_,11 @@ return false; } else { if (p_345591_ != null) { @@ -24,7 +24,7 @@ } return true; -@@ -163,7 +_,7 @@ +@@ -162,7 +_,7 @@ private void refreshCurrentTabContents(Collection p_261591_) { int i = this.menu.getRowIndexForScroll(this.scrollOffs); this.menu.items.clear(); @@ -33,14 +33,14 @@ this.refreshSearchResults(); } else { this.menu.items.addAll(p_261591_); -@@ -337,6 +_,34 @@ +@@ -336,6 +_,34 @@ protected void init() { if (this.minecraft.gameMode.hasInfiniteItems()) { super.init(); + this.pages.clear(); + int tabIndex = 0; + List currentPage = new java.util.ArrayList<>(); -+ for (CreativeModeTab sortedCreativeModeTab : net.neoforged.neoforge.common.CreativeModeTabRegistry.getSortedCreativeModeTabs()) { ++ for (CreativeModeTab sortedCreativeModeTab : net.neoforged.neoforge.common.CreativeModeTabRegistry.getSortedCreativeModeTabs().stream().filter(CreativeModeTab::hasAnyItems).toList()) { + currentPage.add(sortedCreativeModeTab); + tabIndex++; + if (tabIndex == 10) { @@ -68,7 +68,7 @@ this.searchBox = new EditBox(this.font, this.leftPos + 82, this.topPos + 6, 80, 9, Component.translatable("itemGroup.search")); this.searchBox.setMaxLength(50); this.searchBox.setBordered(false); -@@ -383,7 +_,7 @@ +@@ -382,7 +_,7 @@ public boolean charTyped(char p_98521_, int p_98522_) { if (this.ignoreTextInput) { return false; @@ -77,7 +77,7 @@ return false; } else { String s = this.searchBox.getValue(); -@@ -402,7 +_,7 @@ +@@ -401,7 +_,7 @@ @Override public boolean keyPressed(int p_98547_, int p_98548_, int p_98549_) { this.ignoreTextInput = false; @@ -86,7 +86,7 @@ if (this.minecraft.options.keyChat.matches(p_98547_, p_98548_)) { this.ignoreTextInput = true; this.selectTab(CreativeModeTabs.searchTab()); -@@ -438,6 +_,7 @@ +@@ -437,6 +_,7 @@ } private void refreshSearchResults() { @@ -94,7 +94,7 @@ this.menu.items.clear(); this.visibleTags.clear(); String s = this.searchBox.getValue(); -@@ -450,10 +_,10 @@ +@@ -449,10 +_,10 @@ SearchTree searchtree; if (s.startsWith("#")) { s = s.substring(1); @@ -107,7 +107,7 @@ } this.menu.items.addAll(searchtree.search(s.toLowerCase(Locale.ROOT))); -@@ -481,7 +_,8 @@ +@@ -480,7 +_,8 @@ @Override protected void renderLabels(GuiGraphics p_283168_, int p_281774_, int p_281466_) { if (selectedTab.showTitle()) { @@ -117,7 +117,7 @@ } } -@@ -491,7 +_,7 @@ +@@ -490,7 +_,7 @@ double d0 = p_98531_ - (double)this.leftPos; double d1 = p_98532_ - (double)this.topPos; @@ -126,7 +126,7 @@ if (this.checkTabClicked(creativemodetab, d0, d1)) { return true; } -@@ -513,7 +_,7 @@ +@@ -512,7 +_,7 @@ double d1 = p_98623_ - (double)this.topPos; this.scrolling = false; @@ -135,7 +135,7 @@ if (this.checkTabClicked(creativemodetab, d0, d1)) { this.selectTab(creativemodetab); return true; -@@ -531,6 +_,7 @@ +@@ -530,6 +_,7 @@ private void selectTab(CreativeModeTab p_98561_) { CreativeModeTab creativemodetab = selectedTab; selectedTab = p_98561_; @@ -143,7 +143,7 @@ this.quickCraftSlots.clear(); this.menu.items.clear(); this.clearDraggingState(); -@@ -607,13 +_,15 @@ +@@ -606,13 +_,15 @@ this.originalSlots = null; } @@ -160,7 +160,7 @@ this.refreshSearchResults(); } else { -@@ -679,18 +_,27 @@ +@@ -678,18 +_,27 @@ super.render(p_283000_, p_281317_, p_282770_, p_281295_); this.effects.render(p_283000_, p_281317_, p_282770_, p_281295_); @@ -194,7 +194,7 @@ this.renderTooltip(p_283000_, p_281317_, p_282770_); } -@@ -703,10 +_,10 @@ +@@ -702,10 +_,10 @@ public List getTooltipFromContainerItem(ItemStack p_281769_) { boolean flag = this.hoveredSlot != null && this.hoveredSlot instanceof CreativeModeInventoryScreen.CustomCreativeSlot; boolean flag1 = selectedTab.getType() == CreativeModeTab.Type.CATEGORY; @@ -207,7 +207,7 @@ if (flag1 && flag) { return list; } else { -@@ -722,7 +_,7 @@ +@@ -721,7 +_,7 @@ int i = 1; for (CreativeModeTab creativemodetab : CreativeModeTabs.tabs()) { @@ -216,7 +216,7 @@ list1.add(i++, creativemodetab.getDisplayName().copy().withStyle(ChatFormatting.BLUE)); } } -@@ -733,7 +_,7 @@ +@@ -732,7 +_,7 @@ @Override protected void renderBg(GuiGraphics p_282663_, float p_282504_, int p_282089_, int p_282249_) { @@ -225,7 +225,7 @@ if (creativemodetab != selectedTab) { this.renderTabButton(p_282663_, creativemodetab); } -@@ -747,10 +_,11 @@ +@@ -746,10 +_,11 @@ int k = this.topPos + 18; int i = k + 112; if (selectedTab.canScroll()) { @@ -238,7 +238,7 @@ this.renderTabButton(p_282663_, selectedTab); if (selectedTab.getType() == CreativeModeTab.Type.INVENTORY) { InventoryScreen.renderEntityInInventoryFollowsMouse( -@@ -769,7 +_,7 @@ +@@ -768,7 +_,7 @@ } private int getTabX(CreativeModeTab p_260136_) { @@ -247,7 +247,7 @@ int j = 27; int k = 27 * i; if (p_260136_.isAlignedRight()) { -@@ -781,7 +_,7 @@ +@@ -780,7 +_,7 @@ private int getTabY(CreativeModeTab p_260181_) { int i = 0; @@ -256,7 +256,7 @@ i -= 32; } else { i += this.imageHeight; -@@ -809,8 +_,8 @@ +@@ -808,8 +_,8 @@ protected void renderTabButton(GuiGraphics p_283590_, CreativeModeTab p_283489_) { boolean flag = p_283489_ == selectedTab; @@ -267,7 +267,7 @@ int j = this.leftPos + this.getTabX(p_283489_); int k = this.topPos - (flag1 ? 28 : -(this.imageHeight - 4)); ResourceLocation[] aresourcelocation; -@@ -820,6 +_,7 @@ +@@ -819,6 +_,7 @@ aresourcelocation = flag ? SELECTED_BOTTOM_TABS : UNSELECTED_BOTTOM_TABS; } @@ -275,7 +275,7 @@ p_283590_.blitSprite(RenderType::guiTextured, aresourcelocation[Mth.clamp(i, 0, aresourcelocation.length)], j, k, 26, 32); p_283590_.pose().pushPose(); p_283590_.pose().translate(0.0F, 0.0F, 100.0F); -@@ -861,6 +_,14 @@ +@@ -860,6 +_,14 @@ } } @@ -290,7 +290,7 @@ @OnlyIn(Dist.CLIENT) static class CustomCreativeSlot extends Slot { public CustomCreativeSlot(Container p_98633_, int p_98634_, int p_98635_, int p_98636_) { -@@ -1042,6 +_,22 @@ +@@ -1041,6 +_,22 @@ @Override public boolean mayPickup(Player p_98665_) { return this.target.mayPickup(p_98665_); @@ -307,8 +307,8 @@ + } + + @Override -+ public Slot setBackground(ResourceLocation atlas, ResourceLocation sprite) { -+ this.target.setBackground(atlas, sprite); ++ public Slot setBackground(ResourceLocation sprite) { ++ this.target.setBackground(sprite); + return this; } } diff --git a/patches/net/minecraft/client/gui/screens/inventory/EnchantmentScreen.java.patch b/patches/net/minecraft/client/gui/screens/inventory/EnchantmentScreen.java.patch index 66f9e706cc..0369823bed 100644 --- a/patches/net/minecraft/client/gui/screens/inventory/EnchantmentScreen.java.patch +++ b/patches/net/minecraft/client/gui/screens/inventory/EnchantmentScreen.java.patch @@ -8,7 +8,7 @@ + if (((k < l + 1 || this.minecraft.player.experienceLevel < k1) && !this.minecraft.player.getAbilities().instabuild) || this.menu.enchantClue[l] == -1) { // Forge: render buttons as disabled when enchantable but enchantability not met on lower levels p_282430_.blitSprite(RenderType::guiTextured, ENCHANTMENT_SLOT_DISABLED_SPRITE, i1, j + 14 + 19 * l, 108, 19); p_282430_.blitSprite(RenderType::guiTextured, DISABLED_LEVEL_SPRITES[l], i1 + 1, j + 15 + 19 * l, 16, 16); - p_282430_.drawWordWrap(this.font, formattedtext, j1, j + 16 + 19 * l, l1, (i2 & 16711422) >> 1); + p_282430_.drawWordWrap(this.font, formattedtext, j1, j + 16 + 19 * l, l1, (i2 & 16711422) >> 1, false); @@ -179,13 +_,16 @@ .registryAccess() .lookupOrThrow(Registries.ENCHANTMENT) diff --git a/patches/net/minecraft/client/gui/screens/options/controls/KeyBindsList.java.patch b/patches/net/minecraft/client/gui/screens/options/controls/KeyBindsList.java.patch index 789a7732ae..844e9dadbd 100644 --- a/patches/net/minecraft/client/gui/screens/options/controls/KeyBindsList.java.patch +++ b/patches/net/minecraft/client/gui/screens/options/controls/KeyBindsList.java.patch @@ -1,6 +1,6 @@ --- a/net/minecraft/client/gui/screens/options/controls/KeyBindsList.java +++ b/net/minecraft/client/gui/screens/options/controls/KeyBindsList.java -@@ -158,6 +_,7 @@ +@@ -156,6 +_,7 @@ ) .build(); this.resetButton = Button.builder(RESET_BUTTON_TITLE, p_359096_ -> { @@ -8,7 +8,7 @@ p_345998_.setKey(p_345998_.getDefaultKey()); KeyBindsList.this.resetMappingAndUpdateButtons(); }).bounds(0, 0, 50, 20).createNarration(p_344899_ -> Component.translatable("narrator.controls.reset", p_345196_)).build(); -@@ -210,7 +_,7 @@ +@@ -208,7 +_,7 @@ MutableComponent mutablecomponent = Component.empty(); if (!this.key.isUnbound()) { for (KeyMapping keymapping : KeyBindsList.this.minecraft.options.keyMappings) { diff --git a/patches/net/minecraft/client/gui/screens/worldselection/CreateWorldScreen.java.patch b/patches/net/minecraft/client/gui/screens/worldselection/CreateWorldScreen.java.patch index 55fa739634..1e9418a335 100644 --- a/patches/net/minecraft/client/gui/screens/worldselection/CreateWorldScreen.java.patch +++ b/patches/net/minecraft/client/gui/screens/worldselection/CreateWorldScreen.java.patch @@ -1,6 +1,6 @@ --- a/net/minecraft/client/gui/screens/worldselection/CreateWorldScreen.java +++ b/net/minecraft/client/gui/screens/worldselection/CreateWorldScreen.java -@@ -169,6 +_,7 @@ +@@ -168,6 +_,7 @@ ) { queueLoadScreen(p_372818_, PREPARING_WORLD_DATA); PackRepository packrepository = new PackRepository(new ServerPacksSource(p_372818_.directoryValidator())); @@ -8,7 +8,7 @@ WorldLoader.InitConfig worldloader$initconfig = createDefaultLoadConfig(packrepository, WorldDataConfiguration.DEFAULT); CompletableFuture completablefuture = WorldLoader.load( worldloader$initconfig, -@@ -307,6 +_,10 @@ +@@ -306,6 +_,10 @@ SystemToast.onPackCopyFailure(this.minecraft, s); return false; } else { @@ -19,7 +19,7 @@ this.minecraft .createWorldOpenFlows() .createLevelFromExistingSettings(optional.get(), worldcreationcontext.dataPackResources(), p_249152_, p_374211_); -@@ -491,7 +_,7 @@ +@@ -480,7 +_,7 @@ if (p_269627_) { p_270552_.accept(this.uiState.getSettings().dataConfiguration()); } else { @@ -28,7 +28,7 @@ } }, Component.translatable("dataPack.validation.failed"), -@@ -605,6 +_,7 @@ +@@ -594,6 +_,7 @@ if (path != null) { if (this.tempDataPackRepository == null) { this.tempDataPackRepository = ServerPacksSource.createPackRepository(path, this.packValidator); diff --git a/patches/net/minecraft/client/gui/screens/worldselection/ExperimentsScreen.java.patch b/patches/net/minecraft/client/gui/screens/worldselection/ExperimentsScreen.java.patch new file mode 100644 index 0000000000..c34df49406 --- /dev/null +++ b/patches/net/minecraft/client/gui/screens/worldselection/ExperimentsScreen.java.patch @@ -0,0 +1,14 @@ +--- a/net/minecraft/client/gui/screens/worldselection/ExperimentsScreen.java ++++ b/net/minecraft/client/gui/screens/worldselection/ExperimentsScreen.java +@@ -62,6 +_,11 @@ + + @Override + protected void init() { ++ if (net.minecraft.world.flag.FeatureFlags.REGISTRY.hasAnyModdedFlags()) { ++ this.minecraft.setScreen(new net.neoforged.neoforge.client.gui.ScrollableExperimentsScreen(this.parent, this.packRepository, this.output)); ++ return; ++ } ++ + this.layout.addTitleHeader(TITLE, this.font); + LinearLayout linearlayout = this.layout.addToContents(LinearLayout.vertical()); + linearlayout.addChild(new MultiLineTextWidget(INFO, this.font).setMaxWidth(310), p_293611_ -> p_293611_.paddingBottom(15)); diff --git a/patches/net/minecraft/client/gui/screens/worldselection/WorldSelectionList.java.patch b/patches/net/minecraft/client/gui/screens/worldselection/WorldSelectionList.java.patch index bd2c7fd8b2..ff9e5dff5c 100644 --- a/patches/net/minecraft/client/gui/screens/worldselection/WorldSelectionList.java.patch +++ b/patches/net/minecraft/client/gui/screens/worldselection/WorldSelectionList.java.patch @@ -9,8 +9,8 @@ static final Component FROM_NEWER_TOOLTIP_1 = Component.translatable("selectWorld.tooltip.fromNewerVersion1").withStyle(ChatFormatting.RED); static final Component FROM_NEWER_TOOLTIP_2 = Component.translatable("selectWorld.tooltip.fromNewerVersion2").withStyle(ChatFormatting.RED); @@ -403,6 +_,7 @@ - p_281612_.drawString(this.minecraft.font, s1, p_282820_ + 32 + 3, p_283181_ + 9 + 3, -8355712, false); - p_281612_.drawString(this.minecraft.font, component, p_282820_ + 32 + 3, p_283181_ + 9 + 9 + 3, -8355712, false); + p_281612_.drawString(this.minecraft.font, s1, p_282820_ + 32 + 3, p_283181_ + 9 + 3, -8355712); + p_281612_.drawString(this.minecraft.font, component, p_282820_ + 32 + 3, p_283181_ + 9 + 9 + 3, -8355712); p_281612_.blit(RenderType::guiTextured, this.icon.textureLocation(), p_282820_, p_283181_, 0.0F, 0.0F, 32, 32, 32, 32); + renderExperimentalWarning(p_281612_, p_283204_, p_283025_, p_283181_, p_282820_); if (this.minecraft.options.touchscreen().get() || p_283396_) { diff --git a/patches/net/minecraft/client/main/Main.java.patch b/patches/net/minecraft/client/main/Main.java.patch index c66fe4ba49..af5d923dcf 100644 --- a/patches/net/minecraft/client/main/Main.java.patch +++ b/patches/net/minecraft/client/main/Main.java.patch @@ -1,11 +1,12 @@ --- a/net/minecraft/client/main/Main.java +++ b/net/minecraft/client/main/Main.java -@@ -120,7 +_,7 @@ +@@ -121,8 +_,7 @@ CrashReport.preload(); logger = LogUtils.getLogger(); s1 = "Bootstrap"; - Bootstrap.bootStrap(); -+ net.neoforged.fml.loading.BackgroundWaiter.runAndTick(()->Bootstrap.bootStrap(), net.neoforged.fml.loading.FMLLoader.progressWindowTick); +- ClientBootstrap.bootstrap(); ++ net.neoforged.fml.loading.BackgroundWaiter.runAndTick(() -> Bootstrap.bootStrap(), net.neoforged.fml.loading.FMLLoader.progressWindowTick); GameLoadTimesEvent.INSTANCE.setBootstrapTime(Bootstrap.bootstrapDuration.get()); Bootstrap.validate(); s1 = "Argument parsing"; diff --git a/patches/net/minecraft/client/model/HumanoidModel.java.patch b/patches/net/minecraft/client/model/HumanoidModel.java.patch index 5d2e521e41..94cf1ee81c 100644 --- a/patches/net/minecraft/client/model/HumanoidModel.java.patch +++ b/patches/net/minecraft/client/model/HumanoidModel.java.patch @@ -1,6 +1,6 @@ --- a/net/minecraft/client/model/HumanoidModel.java +++ b/net/minecraft/client/model/HumanoidModel.java -@@ -257,6 +_,8 @@ +@@ -253,6 +_,8 @@ case BRUSH: this.rightArm.xRot = this.rightArm.xRot * 0.5F - (float) (Math.PI / 5); this.rightArm.yRot = 0.0F; @@ -9,7 +9,7 @@ } } -@@ -299,6 +_,8 @@ +@@ -295,6 +_,8 @@ case BRUSH: this.leftArm.xRot = this.leftArm.xRot * 0.5F - (float) (Math.PI / 5); this.leftArm.yRot = 0.0F; @@ -18,7 +18,7 @@ } } -@@ -376,7 +_,7 @@ +@@ -372,7 +_,7 @@ } @OnlyIn(Dist.CLIENT) @@ -27,7 +27,7 @@ EMPTY(false), ITEM(false), BLOCK(false), -@@ -389,13 +_,31 @@ +@@ -385,13 +_,31 @@ BRUSH(false); private final boolean twoHanded; diff --git a/patches/net/minecraft/client/model/geom/LayerDefinitions.java.patch b/patches/net/minecraft/client/model/geom/LayerDefinitions.java.patch index de5171aeea..3e54603301 100644 --- a/patches/net/minecraft/client/model/geom/LayerDefinitions.java.patch +++ b/patches/net/minecraft/client/model/geom/LayerDefinitions.java.patch @@ -1,8 +1,8 @@ --- a/net/minecraft/client/model/geom/LayerDefinitions.java +++ b/net/minecraft/client/model/geom/LayerDefinitions.java -@@ -454,6 +_,7 @@ - builder.put(ModelLayers.createWallSignModelName(p_359128_), layerdefinition54); - builder.put(ModelLayers.createHangingSignModelName(p_359128_), layerdefinition55); +@@ -468,6 +_,7 @@ + builder.put(ModelLayers.createHangingSignModelName(p_382521_, hangingsignrenderer$attachmenttype), layerdefinition55); + } }); + net.neoforged.neoforge.client.ClientHooks.loadLayerDefinitions(builder); ImmutableMap immutablemap = builder.build(); diff --git a/patches/net/minecraft/client/model/geom/ModelLayers.java.patch b/patches/net/minecraft/client/model/geom/ModelLayers.java.patch index 9b9691073a..2e84c89ca5 100644 --- a/patches/net/minecraft/client/model/geom/ModelLayers.java.patch +++ b/patches/net/minecraft/client/model/geom/ModelLayers.java.patch @@ -1,6 +1,6 @@ --- a/net/minecraft/client/model/geom/ModelLayers.java +++ b/net/minecraft/client/model/geom/ModelLayers.java -@@ -301,15 +_,18 @@ +@@ -308,15 +_,18 @@ } public static ModelLayerLocation createStandingSignModelName(WoodType p_171292_) { @@ -15,10 +15,10 @@ + return new ModelLayerLocation(location.withPrefix("sign/wall/"), "main"); } - public static ModelLayerLocation createHangingSignModelName(WoodType p_252225_) { -- return createLocation("hanging_sign/" + p_252225_.name(), "main"); + public static ModelLayerLocation createHangingSignModelName(WoodType p_252225_, HangingSignRenderer.AttachmentType p_382987_) { +- return createLocation("hanging_sign/" + p_252225_.name() + "/" + p_382987_.getSerializedName(), "main"); + ResourceLocation location = ResourceLocation.parse(p_252225_.name()); -+ return new ModelLayerLocation(location.withPrefix("hanging_sign/"), "main"); ++ return new ModelLayerLocation(location.withPrefix("hanging_sign/").withSuffix("/" + p_382987_.getSerializedName()), "main"); } public static Stream getKnownLocations() { diff --git a/patches/net/minecraft/client/multiplayer/ClientChunkCache.java.patch b/patches/net/minecraft/client/multiplayer/ClientChunkCache.java.patch index 768dd4d9eb..3aa9874e4a 100644 --- a/patches/net/minecraft/client/multiplayer/ClientChunkCache.java.patch +++ b/patches/net/minecraft/client/multiplayer/ClientChunkCache.java.patch @@ -8,7 +8,7 @@ this.storage.drop(i, levelchunk); } } -@@ -125,6 +_,7 @@ +@@ -126,6 +_,7 @@ } this.level.onChunkLoaded(chunkpos); diff --git a/patches/net/minecraft/client/multiplayer/ClientLevel.java.patch b/patches/net/minecraft/client/multiplayer/ClientLevel.java.patch index a1d3332be3..34cbc348c3 100644 --- a/patches/net/minecraft/client/multiplayer/ClientLevel.java.patch +++ b/patches/net/minecraft/client/multiplayer/ClientLevel.java.patch @@ -1,6 +1,15 @@ --- a/net/minecraft/client/multiplayer/ClientLevel.java +++ b/net/minecraft/client/multiplayer/ClientLevel.java -@@ -126,6 +_,7 @@ +@@ -115,7 +_,7 @@ + private final TickRateManager tickRateManager; + private final Minecraft minecraft = Minecraft.getInstance(); + final List players = Lists.newArrayList(); +- final List dragonParts = Lists.newArrayList(); ++ final List> dragonParts = Lists.newArrayList(); + private final Map mapData = Maps.newHashMap(); + private static final int CLOUD_COLOR = -1; + private int skyFlashTime; +@@ -131,6 +_,7 @@ p_194170_.put( BiomeColors.WATER_COLOR_RESOLVER, new BlockTintCache(p_194168_ -> this.calculateBlockTint(p_194168_, BiomeColors.WATER_COLOR_RESOLVER)) ); @@ -8,16 +17,15 @@ } ); private final ClientChunkCache chunkSource; -@@ -135,6 +_,8 @@ +@@ -140,6 +_,7 @@ private final int seaLevel; private boolean tickDayTime; private static final Set MARKER_PARTICLE_ITEMS = Set.of(Items.BARRIER, Items.LIGHT); -+ private final it.unimi.dsi.fastutil.ints.Int2ObjectMap> partEntities = new it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap<>(); + private final net.neoforged.neoforge.client.model.data.ModelDataManager modelDataManager = new net.neoforged.neoforge.client.model.data.ModelDataManager(this); public void handleBlockChangedAck(int p_233652_) { this.blockStatePredictionHandler.endPredictionsUpTo(p_233652_, this); -@@ -164,10 +_,15 @@ +@@ -169,10 +_,15 @@ @Override public boolean setBlock(BlockPos p_233643_, BlockState p_233644_, int p_233645_, int p_233646_) { if (this.blockStatePredictionHandler.isPredicting()) { @@ -33,7 +41,7 @@ } return flag; -@@ -201,6 +_,7 @@ +@@ -206,6 +_,7 @@ this.serverSimulationDistance = p_363776_; this.updateSkyBrightness(); this.prepareWeather(); @@ -41,7 +49,7 @@ } public void queueLightUpdate(Runnable p_194172_) { -@@ -244,7 +_,7 @@ +@@ -249,7 +_,7 @@ private void tickTime() { this.clientLevelData.setGameTime(this.clientLevelData.getGameTime() + 1L); if (this.tickDayTime) { @@ -50,7 +58,7 @@ } } -@@ -283,7 +_,11 @@ +@@ -288,7 +_,11 @@ p_104640_.setOldPosAndRot(); p_104640_.tickCount++; Profiler.get().push(() -> BuiltInRegistries.ENTITY_TYPE.getKey(p_104640_.getType()).toString()); @@ -63,7 +71,7 @@ Profiler.get().pop(); for (Entity entity : p_104640_.getPassengers()) { -@@ -335,8 +_,10 @@ +@@ -339,8 +_,10 @@ } public void addEntity(Entity p_104741_) { @@ -74,7 +82,7 @@ } public void removeEntity(int p_171643_, Entity.RemovalReason p_171644_) { -@@ -507,6 +_,13 @@ +@@ -511,6 +_,13 @@ float p_263349_, long p_263408_ ) { @@ -88,7 +96,7 @@ if (p_263381_ == this.minecraft.player) { this.playSound(p_263372_, p_263404_, p_263365_, p_263335_.value(), p_263417_, p_263416_, p_263349_, false, p_263408_); } -@@ -516,6 +_,12 @@ +@@ -520,6 +_,12 @@ public void playSeededSound( @Nullable Player p_263514_, Entity p_263536_, Holder p_263518_, SoundSource p_263487_, float p_263538_, float p_263524_, long p_263509_ ) { @@ -101,7 +109,16 @@ if (p_263514_ == this.minecraft.player) { this.minecraft.getSoundManager().play(new EntityBoundSoundInstance(p_263518_.value(), p_263487_, p_263538_, p_263524_, p_263536_, p_263509_)); } -@@ -1045,6 +_,7 @@ +@@ -731,7 +_,7 @@ + return this.players; + } + +- public List dragonParts() { ++ public List> dragonParts() { + return this.dragonParts; + } + +@@ -1056,6 +_,7 @@ } public void setDifficulty(Difficulty p_104852_) { @@ -109,29 +126,27 @@ this.difficulty = p_104852_; } -@@ -1081,14 +_,75 @@ - if (p_171712_ instanceof AbstractClientPlayer) { - ClientLevel.this.players.add((AbstractClientPlayer)p_171712_); +@@ -1098,6 +_,9 @@ + ClientLevel.this.dragonParts.addAll(Arrays.asList(enderdragon.getSubEntities())); + break; + default: ++ if (p_171712_.isMultipartEntity()) { ++ ClientLevel.this.dragonParts.addAll(Arrays.asList(p_171712_.getParts())); ++ } } -+ if (p_171712_.isMultipartEntity()) { -+ for (net.neoforged.neoforge.entity.PartEntity part : p_171712_.getParts()) { -+ ClientLevel.this.partEntities.put(part.getId(), part); -+ } -+ } } - public void onTrackingEnd(Entity p_171716_) { - p_171716_.unRide(); - ClientLevel.this.players.remove(p_171716_); +@@ -1112,10 +_,58 @@ + ClientLevel.this.dragonParts.removeAll(Arrays.asList(enderdragon.getSubEntities())); + break; + default: ++ if (p_171716_.isMultipartEntity()) { ++ ClientLevel.this.dragonParts.removeAll(Arrays.asList(p_171716_.getParts())); ++ } + } + + p_171716_.onRemovedFromLevel(); + net.neoforged.neoforge.common.NeoForge.EVENT_BUS.post(new net.neoforged.neoforge.event.entity.EntityLeaveLevelEvent(p_171716_, ClientLevel.this)); -+ -+ if (p_171716_.isMultipartEntity()) { -+ for (net.neoforged.neoforge.entity.PartEntity part : p_171716_.getParts()) { -+ ClientLevel.this.partEntities.remove(part.getId()); -+ } -+ } } public void onSectionChange(Entity p_233660_) { @@ -139,11 +154,6 @@ + } + + @Override -+ public java.util.Collection> getPartEntities() { -+ return this.partEntities.values(); -+ } -+ -+ @Override + public net.neoforged.neoforge.client.model.data.ModelDataManager getModelDataManager() { + return modelDataManager; + } diff --git a/patches/net/minecraft/client/multiplayer/ClientPacketListener.java.patch b/patches/net/minecraft/client/multiplayer/ClientPacketListener.java.patch index a1ad4df3f6..dab1a825b8 100644 --- a/patches/net/minecraft/client/multiplayer/ClientPacketListener.java.patch +++ b/patches/net/minecraft/client/multiplayer/ClientPacketListener.java.patch @@ -1,6 +1,6 @@ --- a/net/minecraft/client/multiplayer/ClientPacketListener.java +++ b/net/minecraft/client/multiplayer/ClientPacketListener.java -@@ -367,6 +_,7 @@ +@@ -368,6 +_,7 @@ private final ChunkBatchSizeCalculator chunkBatchSizeCalculator = new ChunkBatchSizeCalculator(); private final PingDebugMonitor pingDebugMonitor; private final DebugSampleSubscriber debugSampleSubscriber; @@ -8,7 +8,7 @@ @Nullable private LevelLoadStatusManager levelLoadStatusManager; private boolean serverEnforcesSecureChat; -@@ -388,7 +_,8 @@ +@@ -389,7 +_,8 @@ p_253924_.gui.getChat().restoreState(p_295121_.chatState()); } @@ -18,7 +18,7 @@ this.fuelValues = FuelValues.vanillaBurnTimes(p_295121_.receivedRegistries(), this.enabledFeatures); } -@@ -451,12 +_,13 @@ +@@ -452,12 +_,13 @@ this.minecraft.debugRenderer.clear(); this.minecraft.player.resetPos(); @@ -33,7 +33,7 @@ this.minecraft.player.setReducedDebugInfo(p_105030_.reducedDebugInfo()); this.minecraft.player.setShowDeathScreen(p_105030_.showDeathScreen()); this.minecraft.player.setDoLimitedCrafting(p_105030_.doLimitedCrafting()); -@@ -900,7 +_,8 @@ +@@ -902,7 +_,8 @@ this.serverCookies, chatcomponent$state, this.customReportDetails, @@ -126,7 +126,7 @@ } @Override -@@ -2526,6 +_,8 @@ +@@ -2540,6 +_,8 @@ } public void sendChat(String p_249888_) { @@ -135,7 +135,7 @@ Instant instant = Instant.now(); long i = Crypt.SaltSupplier.getLong(); LastSeenMessagesTracker.Update lastseenmessagestracker$update = this.lastSeenMessages.generateAndApplyUpdate(); -@@ -2535,6 +_,7 @@ +@@ -2549,6 +_,7 @@ } public void sendCommand(String p_250092_) { @@ -143,7 +143,7 @@ SignableCommand signablecommand = SignableCommand.of(this.parseCommand(p_250092_)); if (signablecommand.arguments().isEmpty()) { this.send(new ServerboundChatCommandPacket(p_250092_)); -@@ -2622,6 +_,10 @@ +@@ -2640,6 +_,10 @@ public Scoreboard scoreboard() { return this.scoreboard; diff --git a/patches/net/minecraft/client/multiplayer/MultiPlayerGameMode.java.patch b/patches/net/minecraft/client/multiplayer/MultiPlayerGameMode.java.patch index 4a88a77d66..128eb6a4de 100644 --- a/patches/net/minecraft/client/multiplayer/MultiPlayerGameMode.java.patch +++ b/patches/net/minecraft/client/multiplayer/MultiPlayerGameMode.java.patch @@ -1,6 +1,6 @@ --- a/net/minecraft/client/multiplayer/MultiPlayerGameMode.java +++ b/net/minecraft/client/multiplayer/MultiPlayerGameMode.java -@@ -120,11 +_,12 @@ +@@ -121,11 +_,12 @@ } else if (blockstate.isAir()) { return false; } else { @@ -15,7 +15,7 @@ } return flag; -@@ -143,6 +_,7 @@ +@@ -144,6 +_,7 @@ BlockState blockstate = this.minecraft.level.getBlockState(p_105270_); this.minecraft.getTutorial().onDestroyBlock(this.minecraft.level, p_105270_, blockstate, 1.0F); this.startPrediction(this.minecraft.level, p_233757_ -> { @@ -23,7 +23,7 @@ this.destroyBlock(p_105270_); return new ServerboundPlayerActionPacket(ServerboundPlayerActionPacket.Action.START_DESTROY_BLOCK, p_105270_, p_105271_, p_233757_); }); -@@ -152,15 +_,19 @@ +@@ -153,15 +_,19 @@ this.connection .send(new ServerboundPlayerActionPacket(ServerboundPlayerActionPacket.Action.ABORT_DESTROY_BLOCK, this.destroyBlockPos, p_105271_)); } @@ -43,7 +43,7 @@ if (flag && blockstate1.getDestroyProgress(this.minecraft.player, this.minecraft.player.level(), p_105270_) >= 1.0F) { this.destroyBlock(p_105270_); } else { -@@ -172,7 +_,7 @@ +@@ -173,7 +_,7 @@ this.minecraft.level.destroyBlockProgress(this.minecraft.player.getId(), this.destroyBlockPos, this.getDestroyStage()); } @@ -52,7 +52,7 @@ }); } -@@ -203,6 +_,7 @@ +@@ -204,6 +_,7 @@ BlockState blockstate1 = this.minecraft.level.getBlockState(p_105284_); this.minecraft.getTutorial().onDestroyBlock(this.minecraft.level, p_105284_, blockstate1, 1.0F); this.startPrediction(this.minecraft.level, p_233753_ -> { @@ -60,7 +60,7 @@ this.destroyBlock(p_105284_); return new ServerboundPlayerActionPacket(ServerboundPlayerActionPacket.Action.START_DESTROY_BLOCK, p_105284_, p_105285_, p_233753_); }); -@@ -215,7 +_,7 @@ +@@ -216,7 +_,7 @@ } else { this.destroyProgress = this.destroyProgress + blockstate.getDestroyProgress(this.minecraft.player, this.minecraft.player.level(), p_105284_); if (this.destroyTicks % 4.0F == 0.0F) { @@ -69,7 +69,7 @@ this.minecraft .getSoundManager() .play( -@@ -232,6 +_,7 @@ +@@ -233,6 +_,7 @@ this.destroyTicks++; this.minecraft.getTutorial().onDestroyBlock(this.minecraft.level, p_105284_, blockstate, Mth.clamp(this.destroyProgress, 0.0F, 1.0F)); @@ -77,7 +77,7 @@ if (this.destroyProgress >= 1.0F) { this.isDestroying = false; this.startPrediction(this.minecraft.level, p_233739_ -> { -@@ -270,7 +_,7 @@ +@@ -271,7 +_,7 @@ private boolean sameDestroyTarget(BlockPos p_105282_) { ItemStack itemstack = this.minecraft.player.getMainHandItem(); @@ -86,7 +86,7 @@ } private void ensureHasSentCarriedItem() { -@@ -298,12 +_,23 @@ +@@ -299,12 +_,23 @@ private InteractionResult performUseItemOn(LocalPlayer p_233747_, InteractionHand p_233748_, BlockHitResult p_233749_) { BlockPos blockpos = p_233749_.getBlockPos(); ItemStack itemstack = p_233747_.getItemInHand(p_233748_); @@ -112,7 +112,7 @@ BlockState blockstate = this.minecraft.level.getBlockState(blockpos); if (!this.connection.isFeatureEnabled(blockstate.getBlock().requiredFeatures())) { return InteractionResult.FAIL; -@@ -324,8 +_,10 @@ +@@ -325,8 +_,10 @@ } } @@ -125,7 +125,7 @@ InteractionResult interactionresult2; if (this.localPlayerMode.isCreative()) { int i = itemstack.getCount(); -@@ -359,6 +_,11 @@ +@@ -360,6 +_,11 @@ mutableobject.setValue(InteractionResult.PASS); return serverbounduseitempacket; } else { @@ -137,7 +137,7 @@ InteractionResult interactionresult = itemstack.use(this.minecraft.level, p_233722_, p_233723_); ItemStack itemstack1; if (interactionresult instanceof InteractionResult.Success interactionresult$success) { -@@ -371,6 +_,8 @@ +@@ -372,6 +_,8 @@ if (itemstack1 != itemstack) { p_233722_.setItemInHand(p_233723_, itemstack1); @@ -146,7 +146,7 @@ } mutableobject.setValue(interactionresult); -@@ -409,6 +_,9 @@ +@@ -410,6 +_,9 @@ this.ensureHasSentCarriedItem(); Vec3 vec3 = p_105233_.getLocation().subtract(p_105232_.getX(), p_105232_.getY(), p_105232_.getZ()); this.connection.send(ServerboundInteractPacket.createInteractionPacket(p_105232_, p_105231_.isShiftKeyDown(), p_105234_, vec3)); diff --git a/patches/net/minecraft/client/multiplayer/PlayerInfo.java.patch b/patches/net/minecraft/client/multiplayer/PlayerInfo.java.patch index 27972460ce..0be4333b3b 100644 --- a/patches/net/minecraft/client/multiplayer/PlayerInfo.java.patch +++ b/patches/net/minecraft/client/multiplayer/PlayerInfo.java.patch @@ -1,6 +1,6 @@ --- a/net/minecraft/client/multiplayer/PlayerInfo.java +++ b/net/minecraft/client/multiplayer/PlayerInfo.java -@@ -86,6 +_,7 @@ +@@ -88,6 +_,7 @@ } protected void setGameMode(GameType p_105318_) { diff --git a/patches/net/minecraft/client/particle/BreakingItemParticle.java.patch b/patches/net/minecraft/client/particle/BreakingItemParticle.java.patch deleted file mode 100644 index 3d9b32361f..0000000000 --- a/patches/net/minecraft/client/particle/BreakingItemParticle.java.patch +++ /dev/null @@ -1,11 +0,0 @@ ---- a/net/minecraft/client/particle/BreakingItemParticle.java -+++ b/net/minecraft/client/particle/BreakingItemParticle.java -@@ -33,7 +_,7 @@ - - protected BreakingItemParticle(ClientLevel p_105665_, double p_105666_, double p_105667_, double p_105668_, ItemStack p_105669_) { - super(p_105665_, p_105666_, p_105667_, p_105668_, 0.0, 0.0, 0.0); -- this.setSprite(Minecraft.getInstance().getItemRenderer().getModel(p_105669_, p_105665_, null, 0).getParticleIcon()); -+ this.setSprite(Minecraft.getInstance().getItemRenderer().getModel(p_105669_, p_105665_, null, 0).getParticleIcon(net.neoforged.neoforge.client.model.data.ModelData.EMPTY)); - this.gravity = 1.0F; - this.quadSize /= 2.0F; - this.uo = this.random.nextFloat() * 3.0F; diff --git a/patches/net/minecraft/client/particle/ItemPickupParticle.java.patch b/patches/net/minecraft/client/particle/ItemPickupParticle.java.patch index fbf0a1e746..8cea2aa848 100644 --- a/patches/net/minecraft/client/particle/ItemPickupParticle.java.patch +++ b/patches/net/minecraft/client/particle/ItemPickupParticle.java.patch @@ -1,6 +1,6 @@ --- a/net/minecraft/client/particle/ItemPickupParticle.java +++ b/net/minecraft/client/particle/ItemPickupParticle.java -@@ -102,4 +_,11 @@ +@@ -99,4 +_,11 @@ this.targetYOld = this.targetY; this.targetZOld = this.targetZ; } diff --git a/patches/net/minecraft/client/particle/Particle.java.patch b/patches/net/minecraft/client/particle/Particle.java.patch index a3587365f5..b74356986f 100644 --- a/patches/net/minecraft/client/particle/Particle.java.patch +++ b/patches/net/minecraft/client/particle/Particle.java.patch @@ -1,6 +1,6 @@ --- a/net/minecraft/client/particle/Particle.java +++ b/net/minecraft/client/particle/Particle.java -@@ -245,6 +_,18 @@ +@@ -250,6 +_,18 @@ return Optional.empty(); } diff --git a/patches/net/minecraft/client/particle/ParticleEngine.java.patch b/patches/net/minecraft/client/particle/ParticleEngine.java.patch index de43669f57..cb59bf982d 100644 --- a/patches/net/minecraft/client/particle/ParticleEngine.java.patch +++ b/patches/net/minecraft/client/particle/ParticleEngine.java.patch @@ -1,7 +1,7 @@ --- a/net/minecraft/client/particle/ParticleEngine.java +++ b/net/minecraft/client/particle/ParticleEngine.java -@@ -77,11 +_,11 @@ - ParticleRenderType.TERRAIN_SHEET, ParticleRenderType.PARTICLE_SHEET_OPAQUE, ParticleRenderType.PARTICLE_SHEET_TRANSLUCENT, ParticleRenderType.CUSTOM +@@ -74,11 +_,11 @@ + ParticleRenderType.TERRAIN_SHEET, ParticleRenderType.PARTICLE_SHEET_OPAQUE, ParticleRenderType.PARTICLE_SHEET_TRANSLUCENT ); protected ClientLevel level; - private final Map> particles = Maps.newIdentityHashMap(); @@ -14,7 +14,7 @@ private final Queue particlesToAdd = Queues.newArrayDeque(); private final Map spriteSets = Maps.newHashMap(); private final TextureAtlas textureAtlas; -@@ -214,10 +_,14 @@ +@@ -207,10 +_,14 @@ this.register(ParticleTypes.BLOCK_CRUMBLE, new TerrainParticle.CrumblingProvider()); } @@ -30,7 +30,7 @@ public void register(ParticleType p_273423_, ParticleProvider.Sprite p_273134_) { this.register( p_273423_, -@@ -234,10 +_,12 @@ +@@ -227,10 +_,12 @@ ); } @@ -44,7 +44,7 @@ } @Override -@@ -357,7 +_,7 @@ +@@ -350,7 +_,7 @@ private Particle makeParticle( T p_107396_, double p_107397_, double p_107398_, double p_107399_, double p_107400_, double p_107401_, double p_107402_ ) { @@ -53,36 +53,81 @@ return particleprovider == null ? null : particleprovider.createParticle(p_107396_, this.level, p_107397_, p_107398_, p_107399_, p_107400_, p_107401_, p_107402_); -@@ -433,17 +_,27 @@ +@@ -426,28 +_,48 @@ } } + @Deprecated - public void render(LightTexture p_107339_, Camera p_107340_, float p_107341_) { -+ render(p_107339_, p_107340_, p_107341_, null, type -> true); + public void render(Camera p_107340_, float p_107341_, MultiBufferSource.BufferSource p_383193_) { +- for (ParticleRenderType particlerendertype : RENDER_ORDER) { ++ render(p_107340_, p_107341_, p_383193_, null, type -> true); + } + -+ public void render(LightTexture p_107339_, Camera p_107340_, float p_107341_, @Nullable net.minecraft.client.renderer.culling.Frustum frustum, java.util.function.Predicate renderTypePredicate) { - p_107339_.turnOnLightLayer(); - RenderSystem.enableDepthTest(); ++ public void render(Camera p_107340_, float p_107341_, MultiBufferSource.BufferSource p_383193_, @Nullable net.minecraft.client.renderer.culling.Frustum frustum, java.util.function.Predicate renderTypePredicate) { + //TODO porting: is this even needed with the particle render order fix??? -+ RenderSystem.activeTexture(org.lwjgl.opengl.GL13.GL_TEXTURE2); -+ RenderSystem.activeTexture(org.lwjgl.opengl.GL13.GL_TEXTURE0); - -- for (ParticleRenderType particlerendertype : RENDER_ORDER) { ++ com.mojang.blaze3d.systems.RenderSystem.activeTexture(org.lwjgl.opengl.GL13.GL_TEXTURE2); ++ com.mojang.blaze3d.systems.RenderSystem.activeTexture(org.lwjgl.opengl.GL13.GL_TEXTURE0); + for (ParticleRenderType particlerendertype : this.particles.keySet()) { // Neo: allow custom IParticleRenderType's -+ if (particlerendertype == ParticleRenderType.NO_RENDER || !renderTypePredicate.test(particlerendertype)) continue; ++ if (particlerendertype == ParticleRenderType.NO_RENDER || particlerendertype == ParticleRenderType.CUSTOM || !renderTypePredicate.test(particlerendertype)) continue; Queue queue = this.particles.get(particlerendertype); if (queue != null && !queue.isEmpty()) { - Tesselator tesselator = Tesselator.getInstance(); - BufferBuilder bufferbuilder = particlerendertype.begin(tesselator, this.textureManager); - if (bufferbuilder != null) { - for (Particle particle : queue) { -+ if (frustum != null && !frustum.isVisible(particle.getRenderBoundingBox(p_107341_))) continue; - try { - particle.render(bufferbuilder, p_107340_, p_107341_); - } catch (Throwable throwable) { -@@ -475,7 +_,7 @@ +- renderParticleType(p_107340_, p_107341_, p_383193_, particlerendertype, queue); ++ renderParticleType(p_107340_, p_107341_, p_383193_, particlerendertype, queue, frustum); + } + } + + Queue queue1 = this.particles.get(ParticleRenderType.CUSTOM); + if (queue1 != null && !queue1.isEmpty()) { +- renderCustomParticles(p_107340_, p_107341_, p_383193_, queue1); ++ renderCustomParticles(p_107340_, p_107341_, p_383193_, queue1, frustum); + } + + p_383193_.endBatch(); + } + +- private static void renderParticleType( +- Camera p_382847_, float p_383032_, MultiBufferSource.BufferSource p_383105_, ParticleRenderType p_383179_, Queue p_383046_ ++ /** ++ * @deprecated Neo: use {@link #renderParticleType(Camera, float, MultiBufferSource.BufferSource, ParticleRenderType, Queue, net.minecraft.client.renderer.culling.Frustum)} instead ++ */ ++ @Deprecated ++ private static void renderParticleType( ++ Camera p_382847_, float p_383032_, MultiBufferSource.BufferSource p_383105_, ParticleRenderType p_383179_, Queue p_383046_ ++ ) { ++ renderParticleType(p_382847_, p_383032_, p_383105_, p_383179_, p_383046_, null); ++ } ++ ++ private static void renderParticleType( ++ Camera p_382847_, float p_383032_, MultiBufferSource.BufferSource p_383105_, ParticleRenderType p_383179_, Queue p_383046_, @Nullable net.minecraft.client.renderer.culling.Frustum frustum + ) { + VertexConsumer vertexconsumer = p_383105_.getBuffer(Objects.requireNonNull(p_383179_.renderType())); + + for (Particle particle : p_383046_) { ++ if (frustum != null && !frustum.isVisible(particle.getRenderBoundingBox(p_383032_))) continue; + try { + particle.render(vertexconsumer, p_382847_, p_383032_); + } catch (Throwable throwable) { +@@ -460,10 +_,19 @@ + } + } + ++ /** ++ * @deprecated Neo: use {@link #renderCustomParticles(Camera, float, MultiBufferSource.BufferSource, Queue, net.minecraft.client.renderer.culling.Frustum)} instead ++ */ ++ @Deprecated + private static void renderCustomParticles(Camera p_383089_, float p_383167_, MultiBufferSource.BufferSource p_382990_, Queue p_383010_) { ++ renderCustomParticles(p_383089_, p_383167_, p_382990_, p_383010_, null); ++ } ++ ++ private static void renderCustomParticles(Camera p_383089_, float p_383167_, MultiBufferSource.BufferSource p_382990_, Queue p_383010_, @Nullable net.minecraft.client.renderer.culling.Frustum frustum) { + PoseStack posestack = new PoseStack(); + + for (Particle particle : p_383010_) { ++ if (frustum != null && !frustum.isVisible(particle.getRenderBoundingBox(p_383167_))) continue; + try { + particle.renderCustom(posestack, p_382990_, p_383089_, p_383167_); + } catch (Throwable throwable) { +@@ -483,7 +_,7 @@ } public void destroy(BlockPos p_107356_, BlockState p_107357_) { @@ -91,7 +136,7 @@ VoxelShape voxelshape = p_107357_.getShape(this.level, p_107356_); double d0 = 0.25; voxelshape.forAllBoxes( -@@ -507,7 +_,7 @@ +@@ -515,7 +_,7 @@ d6 - 0.5, p_107357_, p_107356_ @@ -100,7 +145,7 @@ ); } } -@@ -552,12 +_,28 @@ +@@ -560,12 +_,28 @@ d0 = (double)i + aabb.maxX + 0.1F; } diff --git a/patches/net/minecraft/client/particle/ParticleRenderType.java.patch b/patches/net/minecraft/client/particle/ParticleRenderType.java.patch index e3d114e526..9066738f82 100644 --- a/patches/net/minecraft/client/particle/ParticleRenderType.java.patch +++ b/patches/net/minecraft/client/particle/ParticleRenderType.java.patch @@ -1,24 +1,23 @@ --- a/net/minecraft/client/particle/ParticleRenderType.java +++ b/net/minecraft/client/particle/ParticleRenderType.java -@@ -44,6 +_,11 @@ - public String toString() { - return "PARTICLE_SHEET_OPAQUE"; - } -+ -+ @Override -+ public boolean isTranslucent() { -+ return false; -+ } - }; - ParticleRenderType PARTICLE_SHEET_TRANSLUCENT = new ParticleRenderType() { - @Override -@@ -89,4 +_,9 @@ +@@ -7,14 +_,18 @@ + import net.neoforged.api.distmarker.OnlyIn; - @Nullable - BufferBuilder begin(Tesselator p_350949_, TextureManager p_107437_); + @OnlyIn(Dist.CLIENT) +-public record ParticleRenderType(String name, @Nullable RenderType renderType) { ++public record ParticleRenderType(String name, @Nullable RenderType renderType, boolean translucent) { + public static final ParticleRenderType TERRAIN_SHEET = new ParticleRenderType("TERRAIN_SHEET", RenderType.translucentParticle(TextureAtlas.LOCATION_BLOCKS)); + public static final ParticleRenderType PARTICLE_SHEET_OPAQUE = new ParticleRenderType( +- "PARTICLE_SHEET_OPAQUE", RenderType.opaqueParticle(TextureAtlas.LOCATION_PARTICLES) ++ "PARTICLE_SHEET_OPAQUE", RenderType.opaqueParticle(TextureAtlas.LOCATION_PARTICLES), false + ); + public static final ParticleRenderType PARTICLE_SHEET_TRANSLUCENT = new ParticleRenderType( + "PARTICLE_SHEET_TRANSLUCENT", RenderType.translucentParticle(TextureAtlas.LOCATION_PARTICLES) + ); + public static final ParticleRenderType CUSTOM = new ParticleRenderType("CUSTOM", null); + public static final ParticleRenderType NO_RENDER = new ParticleRenderType("NO_RENDER", null); + -+ /** {@return whether this type renders before or after the translucent chunk layer} */ -+ default boolean isTranslucent() { -+ return true; ++ public ParticleRenderType(String name, @Nullable RenderType renderType) { ++ this(name, renderType, true); + } } diff --git a/patches/net/minecraft/client/player/LocalPlayer.java.patch b/patches/net/minecraft/client/player/LocalPlayer.java.patch index d772181669..7d98bb110e 100644 --- a/patches/net/minecraft/client/player/LocalPlayer.java.patch +++ b/patches/net/minecraft/client/player/LocalPlayer.java.patch @@ -1,6 +1,6 @@ --- a/net/minecraft/client/player/LocalPlayer.java +++ b/net/minecraft/client/player/LocalPlayer.java -@@ -302,6 +_,7 @@ +@@ -307,6 +_,7 @@ ServerboundPlayerActionPacket.Action serverboundplayeractionpacket$action = p_108701_ ? ServerboundPlayerActionPacket.Action.DROP_ALL_ITEMS : ServerboundPlayerActionPacket.Action.DROP_ITEM; @@ -8,7 +8,7 @@ ItemStack itemstack = this.getInventory().removeFromSelected(p_108701_); this.connection.send(new ServerboundPlayerActionPacket(serverboundplayeractionpacket$action, BlockPos.ZERO, Direction.DOWN)); return !itemstack.isEmpty(); -@@ -482,7 +_,14 @@ +@@ -487,7 +_,14 @@ @Override public void playSound(SoundEvent p_108651_, float p_108652_, float p_108653_) { @@ -24,43 +24,43 @@ } @Override -@@ -676,6 +_,7 @@ +@@ -680,6 +_,7 @@ + && this.canPlayerFitWithinBlocksAndEntitiesWhen(Pose.CROUCHING) && (this.isShiftKeyDown() || !this.isSleeping() && !this.canPlayerFitWithinBlocksAndEntitiesWhen(Pose.STANDING)); - float f = (float)this.getAttributeValue(Attributes.SNEAKING_SPEED); - this.input.tick(this.isMovingSlowly(), f); + this.input.tick(); + net.neoforged.neoforge.client.ClientHooks.onMovementInputUpdate(this, this.input); this.minecraft.getTutorial().onInput(this.input); - if (this.isUsingItem() && !this.isPassenger()) { - this.input.leftImpulse *= 0.2F; -@@ -704,7 +_,7 @@ - boolean flag4 = this.canStartSprinting(); - boolean flag5 = this.isPassenger() ? this.getVehicle().onGround() : this.onGround(); - boolean flag6 = !flag1 && !flag2; -- if ((flag5 || this.isUnderWater()) && flag6 && flag4) { -+ if ((flag5 || this.isUnderWater() || this.canStartSwimming()) && flag6 && flag4) { + if (this.shouldStopSprinting()) { + this.setSprinting(false); +@@ -718,7 +_,7 @@ + boolean flag3 = this.canStartSprinting(); + boolean flag4 = this.isPassenger() ? this.getVehicle().onGround() : this.onGround(); + boolean flag5 = !flag1 && !flag2; +- if ((flag4 || this.isUnderWater()) && flag5 && flag3) { ++ if ((flag4 || this.isUnderWater() || this.canStartSwimming()) && flag5 && flag3) { if (this.sprintTriggerTime <= 0 && !this.minecraft.options.keySprint.isDown()) { this.sprintTriggerTime = 7; } else { -@@ -712,15 +_,15 @@ +@@ -726,15 +_,15 @@ } } -- if ((!this.isInWater() || this.isUnderWater()) && flag4 && this.minecraft.options.keySprint.isDown()) { -+ if (!this.isSprinting() && (!(this.isInWater() || this.isInFluidType((fluidType, height) -> this.canSwimInFluidType(fluidType))) || (this.isUnderWater() || this.canStartSwimming())) && this.hasEnoughImpulseToStartSprinting() && flag4 && !this.isUsingItem() && !this.hasEffect(MobEffects.BLINDNESS) && this.minecraft.options.keySprint.isDown()) { +- if ((!this.isInWater() || this.isUnderWater()) && flag3 && this.minecraft.options.keySprint.isDown()) { ++ if (!this.isSprinting() && (!(this.isInWater() || this.isInFluidType((fluidType, height) -> this.canSwimInFluidType(fluidType))) || (this.isUnderWater() || this.canStartSwimming())) && this.hasEnoughImpulseToStartSprinting() && flag3 && !this.isUsingItem() && !this.hasEffect(MobEffects.BLINDNESS) && this.minecraft.options.keySprint.isDown()) { this.setSprinting(true); } if (this.isSprinting()) { - boolean flag7 = !this.input.hasForwardImpulse() || !this.hasEnoughFoodToStartSprinting(); -- boolean flag8 = flag7 || this.horizontalCollision && !this.minorHorizontalCollision || this.isInWater() && !this.isUnderWater(); -+ boolean flag8 = flag7 || this.horizontalCollision && !this.minorHorizontalCollision || this.isInWater() && !this.isUnderWater() || (this.isInFluidType((fluidType, height) -> this.canSwimInFluidType(fluidType)) && !this.canStartSwimming()); + boolean flag6 = !this.input.hasForwardImpulse() || !this.hasEnoughFoodToStartSprinting(); +- boolean flag7 = flag6 || this.horizontalCollision && !this.minorHorizontalCollision || this.isInWater() && !this.isUnderWater(); ++ boolean flag7 = flag6 || this.horizontalCollision && !this.minorHorizontalCollision || this.isInWater() && !this.isUnderWater() || (this.isInFluidType((fluidType, height) -> this.canSwimInFluidType(fluidType)) && !this.canStartSwimming()); if (this.isSwimming()) { -- if (!this.onGround() && !this.input.keyPresses.shift() && flag7 || !this.isInWater()) { -+ if (!this.onGround() && !this.input.keyPresses.shift() && flag7 || !(this.isInWater() || this.isInFluidType((fluidType, height) -> this.canSwimInFluidType(fluidType)))) { +- if (!this.onGround() && !this.input.keyPresses.shift() && flag6 || !this.isInWater()) { ++ if (!this.onGround() && !this.input.keyPresses.shift() && flag6 || !(this.isInWater() || this.isInFluidType((fluidType, height) -> this.canSwimInFluidType(fluidType)))) { this.setSprinting(false); } - } else if (flag8) { -@@ -729,7 +_,7 @@ + } else if (flag7) { +@@ -743,7 +_,7 @@ } boolean flag9 = false; @@ -69,7 +69,7 @@ if (this.minecraft.gameMode.isAlwaysFlying()) { if (!abilities.flying) { abilities.flying = true; -@@ -866,6 +_,10 @@ +@@ -896,6 +_,10 @@ @Override public void rideTick() { super.rideTick(); @@ -80,7 +80,7 @@ this.handsBusy = false; if (this.getControlledVehicle() instanceof AbstractBoat abstractboat) { abstractboat.setInput( -@@ -1063,7 +_,7 @@ +@@ -1094,7 +_,7 @@ } private boolean hasEnoughFoodToStartSprinting() { diff --git a/patches/net/minecraft/client/renderer/GameRenderer.java.patch b/patches/net/minecraft/client/renderer/GameRenderer.java.patch index db82c7ecba..783162e3ba 100644 --- a/patches/net/minecraft/client/renderer/GameRenderer.java.patch +++ b/patches/net/minecraft/client/renderer/GameRenderer.java.patch @@ -29,7 +29,7 @@ f2 /= (float)livingentity.hurtDuration; f2 = Mth.sin(f2 * f2 * f2 * f2 * (float) Math.PI); float f3 = livingentity.getHurtDir(); -@@ -474,12 +_,12 @@ +@@ -476,12 +_,12 @@ (float)((double)window.getHeight() / window.getGuiScale()), 0.0F, 1000.0F, @@ -44,7 +44,7 @@ Lighting.setupFor3DItems(); GuiGraphics guigraphics = new GuiGraphics(this.minecraft, this.renderBuffers.bufferSource()); if (flag && p_109096_ && this.minecraft.level != null) { -@@ -505,7 +_,8 @@ +@@ -507,7 +_,8 @@ } } else if (flag && this.minecraft.screen != null) { try { @@ -54,10 +54,10 @@ } catch (Throwable throwable1) { CrashReport crashreport1 = CrashReport.forThrowable(throwable1, "Rendering screen"); CrashReportCategory crashreportcategory1 = crashreport1.addCategory("Screen render details"); -@@ -687,6 +_,8 @@ +@@ -689,6 +_,8 @@ this.minecraft.levelRenderer.prepareCullFrustum(camera.getPosition(), matrix4f2, matrix4f1); this.minecraft.getMainRenderTarget().bindWrite(true); - this.minecraft.levelRenderer.renderLevel(this.resourcePool, p_348589_, flag, camera, this, this.lightTexture, matrix4f2, matrix4f); + this.minecraft.levelRenderer.renderLevel(this.resourcePool, p_348589_, flag, camera, this, matrix4f2, matrix4f); + profilerfiller.popPush("neoforge_render_last"); + net.neoforged.neoforge.client.ClientHooks.dispatchRenderStage(net.neoforged.neoforge.client.event.RenderLevelStageEvent.Stage.AFTER_LEVEL, this.minecraft.levelRenderer, null, matrix4f1, matrix4f, this.minecraft.levelRenderer.getTicks(), camera, this.minecraft.levelRenderer.getFrustum()); profilerfiller.popPush("hand"); diff --git a/patches/net/minecraft/client/renderer/ItemBlockRenderTypes.java.patch b/patches/net/minecraft/client/renderer/ItemBlockRenderTypes.java.patch index 1bf5aead8b..81aaa5cdef 100644 --- a/patches/net/minecraft/client/renderer/ItemBlockRenderTypes.java.patch +++ b/patches/net/minecraft/client/renderer/ItemBlockRenderTypes.java.patch @@ -5,18 +5,18 @@ @OnlyIn(Dist.CLIENT) public class ItemBlockRenderTypes { + @Deprecated - private static final Map TYPE_BY_BLOCK = Util.make(Maps.newHashMap(), p_378824_ -> { + private static final Map TYPE_BY_BLOCK = Util.make(Maps.newHashMap(), p_382527_ -> { RenderType rendertype = RenderType.tripwire(); - p_378824_.put(Blocks.TRIPWIRE, rendertype); -@@ -340,6 +_,7 @@ - p_378824_.put(Blocks.BUBBLE_COLUMN, rendertype3); - p_378824_.put(Blocks.TINTED_GLASS, rendertype3); + p_382527_.put(Blocks.TRIPWIRE, rendertype); +@@ -345,6 +_,7 @@ + p_382527_.put(Blocks.BUBBLE_COLUMN, rendertype3); + p_382527_.put(Blocks.TINTED_GLASS, rendertype3); }); + @Deprecated private static final Map TYPE_BY_FLUID = Util.make(Maps.newHashMap(), p_109290_ -> { RenderType rendertype = RenderType.translucent(); p_109290_.put(Fluids.FLOWING_WATER, rendertype); -@@ -347,6 +_,8 @@ +@@ -352,6 +_,8 @@ }); private static boolean renderCutout; @@ -25,7 +25,7 @@ public static RenderType getChunkRenderType(BlockState p_109283_) { Block block = p_109283_.getBlock(); if (block instanceof LeavesBlock) { -@@ -357,6 +_,8 @@ +@@ -362,6 +_,8 @@ } } @@ -34,7 +34,7 @@ public static RenderType getMovingBlockRenderType(BlockState p_109294_) { Block block = p_109294_.getBlock(); if (block instanceof LeavesBlock) { -@@ -371,11 +_,15 @@ +@@ -376,11 +_,15 @@ } } @@ -50,7 +50,7 @@ public static RenderType getRenderType(ItemStack p_366701_) { if (p_366701_.getItem() instanceof BlockItem blockitem) { Block block = blockitem.getBlock(); -@@ -392,5 +_,78 @@ +@@ -397,5 +_,78 @@ public static void setFancy(boolean p_109292_) { renderCutout = p_109292_; diff --git a/patches/net/minecraft/client/renderer/ItemInHandRenderer.java.patch b/patches/net/minecraft/client/renderer/ItemInHandRenderer.java.patch index 50e02aa55f..cc0c374d6d 100644 --- a/patches/net/minecraft/client/renderer/ItemInHandRenderer.java.patch +++ b/patches/net/minecraft/client/renderer/ItemInHandRenderer.java.patch @@ -1,6 +1,6 @@ --- a/net/minecraft/client/renderer/ItemInHandRenderer.java +++ b/net/minecraft/client/renderer/ItemInHandRenderer.java -@@ -168,11 +_,11 @@ +@@ -172,11 +_,11 @@ ResourceLocation resourcelocation = this.minecraft.player.getSkin().texture(); if (p_109365_ == HumanoidArm.RIGHT) { playerrenderer.renderRightHand( @@ -14,7 +14,7 @@ ); } -@@ -234,7 +_,7 @@ +@@ -238,7 +_,7 @@ p_109367_.translate(-0.5F, -0.5F, 0.0F); p_109367_.scale(0.0078125F, 0.0078125F, 0.0078125F); MapId mapid = p_109370_.get(DataComponents.MAP_ID); @@ -23,7 +23,7 @@ VertexConsumer vertexconsumer = p_109368_.getBuffer(mapitemsaveddata == null ? MAP_BACKGROUND : MAP_BACKGROUND_CHECKERBOARD); Matrix4f matrix4f = p_109367_.last().pose(); vertexconsumer.addVertex(matrix4f, -7.0F, 135.0F, 0.0F).setColor(-1).setUv(0.0F, 1.0F).setLight(p_109369_); -@@ -271,10 +_,10 @@ +@@ -275,10 +_,10 @@ ResourceLocation resourcelocation = abstractclientplayer.getSkin().texture(); if (flag) { playerrenderer.renderRightHand( @@ -36,7 +36,7 @@ } } -@@ -347,12 +_,14 @@ +@@ -351,12 +_,14 @@ if (iteminhandrenderer$handrenderselection.renderMainHand) { float f4 = interactionhand == InteractionHand.MAIN_HAND ? f : 0.0F; float f5 = 1.0F - Mth.lerp(p_109315_, this.oMainHandHeight, this.mainHandHeight); @@ -51,7 +51,7 @@ this.renderArmWithItem(p_109318_, p_109315_, f1, InteractionHand.OFF_HAND, f6, this.offHandItem, f7, p_109316_, p_109317_, p_109319_); } -@@ -412,13 +_,13 @@ +@@ -416,13 +_,13 @@ if (flag && !p_109372_.isInvisible()) { this.renderPlayerArm(p_109379_, p_109380_, p_109381_, p_109378_, p_109376_, humanoidarm); } @@ -67,30 +67,12 @@ boolean flag1 = CrossbowItem.isCharged(p_109377_); boolean flag2 = humanoidarm == HumanoidArm.RIGHT; int i = flag2 ? 1 : -1; -@@ -468,6 +_,7 @@ - ); +@@ -468,6 +_,8 @@ } else { boolean flag3 = humanoidarm == HumanoidArm.RIGHT; -+ if (!net.neoforged.neoforge.client.extensions.common.IClientItemExtensions.of(p_109377_).applyForgeHandTransform(p_109379_, minecraft.player, humanoidarm, p_109377_, p_109373_, p_109378_, p_109376_)) // FORGE: Allow items to define custom arm animation + int j = flag3 ? 1 : -1; ++ // Neo: Allow items to define custom arm animation ++ if (!net.neoforged.neoforge.client.extensions.common.IClientItemExtensions.of(p_109377_).applyForgeHandTransform(p_109379_, minecraft.player, humanoidarm, p_109377_, p_109373_, p_109378_, p_109376_)) if (p_109372_.isUsingItem() && p_109372_.getUseItemRemainingTicks() > 0 && p_109372_.getUsedItemHand() == p_109375_) { - int k = flag3 ? 1 : -1; switch (p_109377_.getUseAnimation()) { -@@ -582,8 +_,16 @@ - this.offHandHeight = Mth.clamp(this.offHandHeight - 0.4F, 0.0F, 1.0F); - } else { - float f = localplayer.getAttackStrengthScale(1.0F); -- this.mainHandHeight = this.mainHandHeight + Mth.clamp((this.mainHandItem == itemstack ? f * f * f : 0.0F) - this.mainHandHeight, -0.4F, 0.4F); -- this.offHandHeight = this.offHandHeight + Mth.clamp((float)(this.offHandItem == itemstack1 ? 1 : 0) - this.offHandHeight, -0.4F, 0.4F); -+ boolean requipM = net.neoforged.neoforge.client.ClientHooks.shouldCauseReequipAnimation(this.mainHandItem, itemstack, localplayer.getInventory().selected); -+ boolean requipO = net.neoforged.neoforge.client.ClientHooks.shouldCauseReequipAnimation(this.offHandItem, itemstack1, -1); -+ -+ if (!requipM && this.mainHandItem != itemstack) -+ this.mainHandItem = itemstack; -+ if (!requipO && this.offHandItem != itemstack1) -+ this.offHandItem = itemstack1; -+ -+ this.mainHandHeight += Mth.clamp((!requipM ? f * f * f : 0.0F) - this.mainHandHeight, -0.4F, 0.4F); -+ this.offHandHeight += Mth.clamp((float)(!requipO ? 1 : 0) - this.offHandHeight, -0.4F, 0.4F); - } - - if (this.mainHandHeight < 0.1F) { + case NONE: diff --git a/patches/net/minecraft/client/renderer/LevelRenderer.java.patch b/patches/net/minecraft/client/renderer/LevelRenderer.java.patch index f5f9a14c0d..8bb0f0e49c 100644 --- a/patches/net/minecraft/client/renderer/LevelRenderer.java.patch +++ b/patches/net/minecraft/client/renderer/LevelRenderer.java.patch @@ -1,6 +1,6 @@ --- a/net/minecraft/client/renderer/LevelRenderer.java +++ b/net/minecraft/client/renderer/LevelRenderer.java -@@ -485,7 +_,7 @@ +@@ -480,7 +_,7 @@ RenderSystem.clear(16640); }); if (!flag1) { @@ -9,25 +9,25 @@ } this.addMainPass(framegraphbuilder, frustum, p_109604_, p_254120_, p_323920_, fogparameters, p_109603_, flag2, p_348530_, profilerfiller); -@@ -494,7 +_,7 @@ +@@ -489,7 +_,7 @@ postchain1.addToFrame(framegraphbuilder, i, j, this.targets); } -- this.addParticlesPass(framegraphbuilder, p_109604_, p_109606_, f, fogparameters); -+ this.addParticlesPass(framegraphbuilder, p_109604_, p_109606_, f, fogparameters, frustum, p_254120_, p_323920_); +- this.addParticlesPass(framegraphbuilder, p_109604_, f, fogparameters); ++ this.addParticlesPass(framegraphbuilder, p_109604_, f, fogparameters, frustum, p_254120_, p_323920_); CloudStatus cloudstatus = this.minecraft.options.getCloudsType(); if (cloudstatus != CloudStatus.OFF) { float f2 = this.level.effects().getCloudHeight(); -@@ -505,7 +_,7 @@ +@@ -500,7 +_,7 @@ } } -- this.addWeatherPass(framegraphbuilder, p_109606_, p_109604_.getPosition(), f, fogparameters); -+ this.addWeatherPass(framegraphbuilder, p_109606_, p_109604_.getPosition(), f, fogparameters, p_254120_, p_323920_, p_109604_); +- this.addWeatherPass(framegraphbuilder, p_109604_.getPosition(), f, fogparameters); ++ this.addWeatherPass(framegraphbuilder, p_109604_.getPosition(), f, fogparameters, p_254120_, p_323920_, p_109604_); if (postchain != null) { postchain.addToFrame(framegraphbuilder, i, j, this.targets); } -@@ -576,7 +_,9 @@ +@@ -571,7 +_,9 @@ double d2 = vec3.z(); p_362234_.push("terrain"); this.renderSectionLayer(RenderType.solid(), d0, d1, d2, p_362420_, p_361272_); @@ -37,7 +37,7 @@ this.renderSectionLayer(RenderType.cutout(), d0, d1, d2, p_362420_, p_361272_); if (this.level.effects().constantAmbientLight()) { Lighting.setupNetherLevel(); -@@ -608,6 +_,7 @@ +@@ -603,6 +_,7 @@ p_362234_.popPush("entities"); this.renderEntities(posestack, multibuffersource$buffersource, p_363453_, p_360931_, this.visibleEntities); multibuffersource$buffersource.endLastBatch(); @@ -45,7 +45,7 @@ this.checkPoseStack(posestack); p_362234_.popPush("blockentities"); this.renderBlockEntities(posestack, multibuffersource$buffersource, multibuffersource$buffersource1, p_363453_, f); -@@ -624,6 +_,7 @@ +@@ -619,6 +_,7 @@ multibuffersource$buffersource.endBatch(Sheets.hangingSignSheet()); multibuffersource$buffersource.endBatch(Sheets.chestSheet()); this.renderBuffers.outlineBufferSource().endOutlineBatch(); @@ -53,45 +53,43 @@ if (p_363964_) { this.renderBlockOutline(p_363453_, multibuffersource$buffersource, posestack, false); } -@@ -644,6 +_,11 @@ +@@ -639,6 +_,11 @@ multibuffersource$buffersource1.endBatch(); this.checkPoseStack(posestack); multibuffersource$buffersource.endBatch(RenderType.waterMask()); + // Neo: in Fast/Fancy, render solid particles before translucent geometry so they don't disappear underwater (MC-161917) + if (this.targets.particles == null) { + p_362234_.popPush("solid_particles"); -+ this.minecraft.particleEngine.render(this.minecraft.gameRenderer.lightTexture(), p_363453_, f, p_366590_, type -> !type.isTranslucent()); ++ this.minecraft.particleEngine.render(p_363453_, f, this.renderBuffers.bufferSource(), p_366590_, type -> !type.translucent()); + } multibuffersource$buffersource.endBatch(); if (resourcehandle1 != null) { resourcehandle1.get().setClearColor(0.0F, 0.0F, 0.0F, 0.0F); -@@ -664,7 +_,15 @@ +@@ -659,7 +_,15 @@ }); } + /** -+ * @deprecated Neo: use {@link #addParticlesPass(FrameGraphBuilder, Camera, LightTexture, float, FogParameters, Frustum, Matrix4f, Matrix4f)} instead ++ * @deprecated Neo: use {@link #addParticlesPass(FrameGraphBuilder, Camera, float, FogParameters, Frustum, Matrix4f, Matrix4f)} instead + */ + @Deprecated - private void addParticlesPass(FrameGraphBuilder p_363357_, Camera p_365299_, LightTexture p_364308_, float p_364282_, FogParameters p_362149_) { -+ addParticlesPass(p_363357_, p_365299_, p_364308_, p_364282_, p_362149_, this.capturedFrustum != null ? this.capturedFrustum : this.cullingFrustum, RenderSystem.getModelViewMatrix(), RenderSystem.getProjectionMatrix()); + private void addParticlesPass(FrameGraphBuilder p_363357_, Camera p_365299_, float p_364282_, FogParameters p_362149_) { ++ addParticlesPass(p_363357_, p_365299_, p_364282_, p_362149_, this.capturedFrustum != null ? this.capturedFrustum : this.cullingFrustum, RenderSystem.getModelViewMatrix(), RenderSystem.getProjectionMatrix()); + } + -+ private void addParticlesPass(FrameGraphBuilder p_363357_, Camera p_365299_, LightTexture p_364308_, float p_364282_, FogParameters p_362149_, Frustum frustum, Matrix4f modelViewMatrix, Matrix4f projectionMatrix) { ++ private void addParticlesPass(FrameGraphBuilder p_363357_, Camera p_365299_, float p_364282_, FogParameters p_362149_, Frustum frustum, Matrix4f modelViewMatrix, Matrix4f projectionMatrix) { FramePass framepass = p_363357_.addPass("particles"); if (this.targets.particles != null) { this.targets.particles = framepass.readsAndWrites(this.targets.particles); -@@ -684,7 +_,8 @@ +@@ -679,6 +_,7 @@ } - RenderStateShard.PARTICLES_TARGET.setupRenderState(); -- this.minecraft.particleEngine.render(p_364308_, p_365299_, p_364282_); -+ this.minecraft.particleEngine.render(p_364308_, p_365299_, p_364282_, frustum, resourcehandle1 == null ? type -> type.isTranslucent() : type -> true); // Neo: only render translucent particles here in Fast/Fancy + this.minecraft.particleEngine.render(p_365299_, p_364282_, this.renderBuffers.bufferSource()); + net.neoforged.neoforge.client.ClientHooks.dispatchRenderStage(net.neoforged.neoforge.client.event.RenderLevelStageEvent.Stage.AFTER_PARTICLES, this, null, modelViewMatrix, projectionMatrix, this.ticks, p_365299_, getFrustum()); - RenderStateShard.PARTICLES_TARGET.clearRenderState(); }); } -@@ -713,11 +_,20 @@ + +@@ -706,11 +_,20 @@ resourcehandle.get().clear(); } @@ -101,26 +99,26 @@ } + /** -+ * @deprecated Neo: use {@link #addWeatherPass(FrameGraphBuilder, LightTexture, Vec3, float, FogParameters, Matrix4f, Matrix4f, Camera)} instead ++ * @deprecated Neo: use {@link #addWeatherPass(FrameGraphBuilder, Vec3, float, FogParameters, Matrix4f, Matrix4f, Camera)} instead + */ + @Deprecated - private void addWeatherPass(FrameGraphBuilder p_364025_, LightTexture p_361536_, Vec3 p_360771_, float p_362434_, FogParameters p_360974_) { -+ addWeatherPass(p_364025_, p_361536_, p_360771_, p_362434_, p_360974_, RenderSystem.getModelViewMatrix(), RenderSystem.getProjectionMatrix(), this.minecraft.gameRenderer.getMainCamera()); + private void addWeatherPass(FrameGraphBuilder p_364025_, Vec3 p_360771_, float p_362434_, FogParameters p_360974_) { ++ addWeatherPass(p_364025_, p_360771_, p_362434_, p_360974_, RenderSystem.getModelViewMatrix(), RenderSystem.getProjectionMatrix(), this.minecraft.gameRenderer.getMainCamera()); + } + -+ private void addWeatherPass(FrameGraphBuilder p_364025_, LightTexture p_361536_, Vec3 p_360771_, float p_362434_, FogParameters p_360974_, Matrix4f modelViewMatrix, Matrix4f projectionMatrix, Camera camera) { ++ private void addWeatherPass(FrameGraphBuilder p_364025_, Vec3 p_360771_, float p_362434_, FogParameters p_360974_, Matrix4f modelViewMatrix, Matrix4f projectionMatrix, Camera camera) { int i = this.minecraft.options.getEffectiveRenderDistance() * 16; float f = this.minecraft.gameRenderer.getDepthFar(); FramePass framepass = p_364025_.addPass("weather"); -@@ -731,6 +_,7 @@ +@@ -724,6 +_,7 @@ RenderSystem.setShaderFog(p_360974_); - RenderStateShard.WEATHER_TARGET.setupRenderState(); - this.weatherEffectRenderer.render(this.minecraft.level, p_361536_, this.ticks, p_362434_, p_360771_); + MultiBufferSource.BufferSource multibuffersource$buffersource = this.renderBuffers.bufferSource(); + this.weatherEffectRenderer.render(this.minecraft.level, multibuffersource$buffersource, this.ticks, p_362434_, p_360771_); + net.neoforged.neoforge.client.ClientHooks.dispatchRenderStage(net.neoforged.neoforge.client.event.RenderLevelStageEvent.Stage.AFTER_WEATHER, this, null, modelViewMatrix, projectionMatrix, this.ticks, camera, getFrustum()); this.worldBorderRenderer.render(this.level.getWorldBorder(), p_360771_, (double)i, (double)f); - RenderStateShard.WEATHER_TARGET.clearRenderState(); + multibuffersource$buffersource.endBatch(); }); -@@ -775,11 +_,14 @@ +@@ -768,11 +_,14 @@ || p_363510_.isDetached() || p_363510_.getEntity() instanceof LivingEntity && ((LivingEntity)p_363510_.getEntity()).isSleeping() ) @@ -136,7 +134,7 @@ } } } -@@ -825,10 +_,12 @@ +@@ -818,10 +_,12 @@ double d1 = vec3.y(); double d2 = vec3.z(); @@ -149,7 +147,7 @@ BlockPos blockpos = blockentity.getBlockPos(); MultiBufferSource multibuffersource = p_363819_; p_362832_.pushPose(); -@@ -856,6 +_,7 @@ +@@ -849,6 +_,7 @@ synchronized (this.globalBlockEntities) { for (BlockEntity blockentity1 : this.globalBlockEntities) { @@ -157,7 +155,7 @@ BlockPos blockpos1 = blockentity1.getBlockPos(); p_362832_.pushPose(); p_362832_.translate((double)blockpos1.getX() - d0, (double)blockpos1.getY() - d1, (double)blockpos1.getZ() - d2); -@@ -883,9 +_,10 @@ +@@ -876,9 +_,10 @@ VertexConsumer vertexconsumer = new SheetedDecalTextureGenerator( p_365216_.getBuffer(ModelBakery.DESTROY_TYPES.get(i)), posestack$pose, 1.0F ); @@ -169,7 +167,7 @@ p_363901_.popPose(); } } -@@ -897,8 +_,9 @@ +@@ -890,8 +_,9 @@ if (blockhitresult.getType() != HitResult.Type.MISS) { BlockPos blockpos = blockhitresult.getBlockPos(); BlockState blockstate = this.level.getBlockState(blockpos); @@ -180,7 +178,7 @@ if (flag != p_361698_) { return; } -@@ -1026,6 +_,7 @@ +@@ -1019,6 +_,7 @@ compiledshaderprogram.clear(); VertexBuffer.unbind(); zone.close(); @@ -188,7 +186,7 @@ p_294513_.clearRenderState(); } } -@@ -1066,7 +_,15 @@ +@@ -1059,7 +_,15 @@ } } @@ -204,15 +202,15 @@ FogType fogtype = p_362177_.getFluidInCamera(); if (fogtype != FogType.POWDER_SNOW && fogtype != FogType.LAVA && !this.doesMobEffectBlockSky(p_362177_)) { DimensionSpecialEffects dimensionspecialeffects = this.level.effects(); -@@ -1075,6 +_,7 @@ +@@ -1068,6 +_,7 @@ FramePass framepass = p_362870_.addPass("sky"); this.targets.main = framepass.readsAndWrites(this.targets.main); framepass.executes(() -> { + if (!level.effects().renderSky(level, ticks, p_363799_, modelViewMatrix, p_362177_, projectionMatrix, () -> RenderSystem.setShaderFog(p_364999_))) { RenderSystem.setShaderFog(p_364999_); - RenderStateShard.MAIN_TARGET.setupRenderState(); - PoseStack posestack = new PoseStack(); -@@ -1102,6 +_,8 @@ + if (dimensionspecialeffects$skytype == DimensionSpecialEffects.SkyType.END) { + this.skyRenderer.renderEndSky(); +@@ -1095,6 +_,8 @@ this.skyRenderer.renderDarkDisc(posestack); } } @@ -221,7 +219,7 @@ }); } } -@@ -1413,7 +_,7 @@ +@@ -1387,7 +_,7 @@ } else { int i = p_109538_.getBrightness(LightLayer.SKY, p_109540_); int j = p_109538_.getBrightness(LightLayer.BLOCK, p_109540_); @@ -230,7 +228,7 @@ if (j < k) { j = k; } -@@ -1475,5 +_,22 @@ +@@ -1449,5 +_,22 @@ public CloudRenderer getCloudRenderer() { return this.cloudRenderer; diff --git a/patches/net/minecraft/client/renderer/MapRenderer.java.patch b/patches/net/minecraft/client/renderer/MapRenderer.java.patch index 8d0c96067c..8a46ee0351 100644 --- a/patches/net/minecraft/client/renderer/MapRenderer.java.patch +++ b/patches/net/minecraft/client/renderer/MapRenderer.java.patch @@ -11,7 +11,16 @@ p_362483_.pushPose(); p_362483_.translate( (float)maprenderstate$mapdecorationrenderstate.x / 2.0F + 64.0F, (float)maprenderstate$mapdecorationrenderstate.y / 2.0F + 64.0F, -0.02F -@@ -116,6 +_,7 @@ +@@ -109,13 +_,15 @@ + p_364922_.texture = this.mapTextureManager.prepareMapTexture(p_361383_, p_363500_); + p_364922_.decorations.clear(); + ++ net.neoforged.neoforge.client.renderstate.RenderStateExtensions.onUpdateMapRenderState(p_363500_, p_364922_); + for (MapDecoration mapdecoration : p_363500_.getDecorations()) { +- p_364922_.decorations.add(this.extractDecorationRenderState(mapdecoration)); ++ p_364922_.decorations.add(net.neoforged.neoforge.client.renderstate.RenderStateExtensions.onUpdateMapDecorationRenderState(mapdecoration.type(), p_363500_, p_364922_, this.extractDecorationRenderState(mapdecoration))); + } + } private MapRenderState.MapDecorationRenderState extractDecorationRenderState(MapDecoration p_364175_) { MapRenderState.MapDecorationRenderState maprenderstate$mapdecorationrenderstate = new MapRenderState.MapDecorationRenderState(); diff --git a/patches/net/minecraft/client/renderer/RenderType.java.patch b/patches/net/minecraft/client/renderer/RenderType.java.patch index 3186edd211..cff5c841ea 100644 --- a/patches/net/minecraft/client/renderer/RenderType.java.patch +++ b/patches/net/minecraft/client/renderer/RenderType.java.patch @@ -1,52 +1,9 @@ --- a/net/minecraft/client/renderer/RenderType.java +++ b/net/minecraft/client/renderer/RenderType.java -@@ -1098,7 +_,7 @@ - } - - public static RenderType text(ResourceLocation p_110498_) { -- return TEXT.apply(p_110498_); -+ return net.neoforged.neoforge.client.NeoForgeRenderTypes.getText(p_110498_); - } - - public static RenderType textBackground() { -@@ -1106,19 +_,19 @@ - } - - public static RenderType textIntensity(ResourceLocation p_173238_) { -- return TEXT_INTENSITY.apply(p_173238_); -+ return net.neoforged.neoforge.client.NeoForgeRenderTypes.getTextIntensity(p_173238_); - } - - public static RenderType textPolygonOffset(ResourceLocation p_181445_) { -- return TEXT_POLYGON_OFFSET.apply(p_181445_); -+ return net.neoforged.neoforge.client.NeoForgeRenderTypes.getTextPolygonOffset(p_181445_); - } - - public static RenderType textIntensityPolygonOffset(ResourceLocation p_181447_) { -- return TEXT_INTENSITY_POLYGON_OFFSET.apply(p_181447_); -+ return net.neoforged.neoforge.client.NeoForgeRenderTypes.getTextIntensityPolygonOffset(p_181447_); - } - - public static RenderType textSeeThrough(ResourceLocation p_110501_) { -- return TEXT_SEE_THROUGH.apply(p_110501_); -+ return net.neoforged.neoforge.client.NeoForgeRenderTypes.getTextSeeThrough(p_110501_); - } - - public static RenderType textBackgroundSeeThrough() { -@@ -1126,7 +_,7 @@ - } - - public static RenderType textIntensitySeeThrough(ResourceLocation p_173241_) { -- return TEXT_INTENSITY_SEE_THROUGH.apply(p_173241_); -+ return net.neoforged.neoforge.client.NeoForgeRenderTypes.getTextIntensitySeeThrough(p_173241_); - } - - public static RenderType lightning() { -@@ -1622,5 +_,17 @@ - public String toString() { +@@ -1847,4 +_,16 @@ return this.name; } -+ } + } + + // Neo: Assign internal IDs for RenderType to be used in rendering + private int chunkLayerId = -1; @@ -58,5 +15,5 @@ + int i = 0; + for (var layer : chunkBufferLayers()) + layer.chunkLayerId = i++; - } ++ } } diff --git a/patches/net/minecraft/client/renderer/ScreenEffectRenderer.java.patch b/patches/net/minecraft/client/renderer/ScreenEffectRenderer.java.patch index 901854e375..e2f0f92603 100644 --- a/patches/net/minecraft/client/renderer/ScreenEffectRenderer.java.patch +++ b/patches/net/minecraft/client/renderer/ScreenEffectRenderer.java.patch @@ -1,32 +1,32 @@ --- a/net/minecraft/client/renderer/ScreenEffectRenderer.java +++ b/net/minecraft/client/renderer/ScreenEffectRenderer.java -@@ -30,18 +_,22 @@ - public static void renderScreenEffect(Minecraft p_110719_, PoseStack p_110720_) { +@@ -26,18 +_,22 @@ + public static void renderScreenEffect(Minecraft p_110719_, PoseStack p_110720_, MultiBufferSource p_382889_) { Player player = p_110719_.player; if (!player.noPhysics) { - BlockState blockstate = getViewBlockingState(player); - if (blockstate != null) { -- renderTex(p_110719_.getBlockRenderer().getBlockModelShaper().getParticleIcon(blockstate), p_110720_); +- renderTex(p_110719_.getBlockRenderer().getBlockModelShaper().getParticleIcon(blockstate), p_110720_, p_382889_); + org.apache.commons.lang3.tuple.Pair overlay = getOverlayBlock(player); + if (overlay != null) { + if (!net.neoforged.neoforge.client.ClientHooks.renderBlockOverlay(player, p_110720_, net.neoforged.neoforge.client.event.RenderBlockScreenEffectEvent.OverlayType.BLOCK, overlay.getLeft(), overlay.getRight())) -+ renderTex(p_110719_.getBlockRenderer().getBlockModelShaper().getTexture(overlay.getLeft(), p_110719_.level, overlay.getRight()), p_110720_); ++ renderTex(p_110719_.getBlockRenderer().getBlockModelShaper().getTexture(overlay.getLeft(), p_110719_.level, overlay.getRight()), p_110720_, p_382889_); } } if (!p_110719_.player.isSpectator()) { if (p_110719_.player.isEyeInFluid(FluidTags.WATER)) { + if (!net.neoforged.neoforge.client.ClientHooks.renderWaterOverlay(player, p_110720_)) - renderWater(p_110719_, p_110720_); + renderWater(p_110719_, p_110720_, p_382889_); } -+ else if (!player.getEyeInFluidType().isAir()) net.neoforged.neoforge.client.extensions.common.IClientFluidTypeExtensions.of(player.getEyeInFluidType()).renderOverlay(p_110719_, p_110720_); ++ else if (!player.getEyeInFluidType().isAir()) net.neoforged.neoforge.client.extensions.common.IClientFluidTypeExtensions.of(player.getEyeInFluidType()).renderOverlay(p_110719_, p_110720_, p_382889_); if (p_110719_.player.isOnFire()) { + if (!net.neoforged.neoforge.client.ClientHooks.renderFireOverlay(player, p_110720_)) - renderFire(p_110719_, p_110720_); + renderFire(p_110720_, p_382889_); } } -@@ -49,6 +_,11 @@ +@@ -45,6 +_,11 @@ @Nullable private static BlockState getViewBlockingState(Player p_110717_) { @@ -38,7 +38,7 @@ BlockPos.MutableBlockPos blockpos$mutableblockpos = new BlockPos.MutableBlockPos(); for (int i = 0; i < 8; i++) { -@@ -58,7 +_,7 @@ +@@ -54,7 +_,7 @@ blockpos$mutableblockpos.set(d0, d1, d2); BlockState blockstate = p_110717_.level().getBlockState(blockpos$mutableblockpos); if (blockstate.getRenderShape() != RenderShape.INVISIBLE && blockstate.isViewBlocking(p_110717_.level(), blockpos$mutableblockpos)) { @@ -47,17 +47,23 @@ } } -@@ -88,8 +_,12 @@ +@@ -82,6 +_,10 @@ } - private static void renderWater(Minecraft p_110726_, PoseStack p_110727_) { -+ renderFluid(p_110726_, p_110727_, UNDERWATER_LOCATION); + private static void renderWater(Minecraft p_110726_, PoseStack p_110727_, MultiBufferSource p_383128_) { ++ renderFluid(p_110726_, p_110727_, p_383128_, UNDERWATER_LOCATION); + } + -+ public static void renderFluid(Minecraft p_110726_, PoseStack p_110727_, ResourceLocation texture) { - RenderSystem.setShader(CoreShaders.POSITION_TEX); -- RenderSystem.setShaderTexture(0, UNDERWATER_LOCATION); -+ RenderSystem.setShaderTexture(0, texture); ++ public static void renderFluid(Minecraft p_110726_, PoseStack p_110727_, MultiBufferSource p_383128_, ResourceLocation texture) { BlockPos blockpos = BlockPos.containing(p_110726_.player.getX(), p_110726_.player.getEyeY(), p_110726_.player.getZ()); float f = LightTexture.getBrightness(p_110726_.player.level().dimensionType(), p_110726_.player.level().getMaxLocalRawBrightness(blockpos)); - RenderSystem.enableBlend(); + int i = ARGB.colorFromFloat(0.1F, f, f, f); +@@ -94,7 +_,7 @@ + float f7 = -p_110726_.player.getYRot() / 64.0F; + float f8 = p_110726_.player.getXRot() / 64.0F; + Matrix4f matrix4f = p_110727_.last().pose(); +- VertexConsumer vertexconsumer = p_383128_.getBuffer(RenderType.blockScreenEffect(UNDERWATER_LOCATION)); ++ VertexConsumer vertexconsumer = p_383128_.getBuffer(RenderType.blockScreenEffect(texture)); + vertexconsumer.addVertex(matrix4f, -1.0F, -1.0F, -0.5F).setUv(4.0F + f7, 4.0F + f8).setColor(i); + vertexconsumer.addVertex(matrix4f, 1.0F, -1.0F, -0.5F).setUv(0.0F + f7, 4.0F + f8).setColor(i); + vertexconsumer.addVertex(matrix4f, 1.0F, 1.0F, -0.5F).setUv(0.0F + f7, 0.0F + f8).setColor(i); diff --git a/patches/net/minecraft/client/renderer/ShaderManager.java.patch b/patches/net/minecraft/client/renderer/ShaderManager.java.patch index 4e634d23e7..d6621b2f20 100644 --- a/patches/net/minecraft/client/renderer/ShaderManager.java.patch +++ b/patches/net/minecraft/client/renderer/ShaderManager.java.patch @@ -1,11 +1,11 @@ --- a/net/minecraft/client/renderer/ShaderManager.java +++ b/net/minecraft/client/renderer/ShaderManager.java @@ -169,7 +_,7 @@ + protected void apply(ShaderManager.Configs p_366597_, ResourceManager p_366533_, ProfilerFiller p_366866_) { ShaderManager.CompilationCache shadermanager$compilationcache = new ShaderManager.CompilationCache(p_366597_); Map map = new HashMap<>(); +- Set set = new HashSet<>(CoreShaders.getProgramsToPreload()); ++ Set set = new HashSet<>(net.neoforged.neoforge.client.CoreShaderManager.getProgramsToPreload()); -- for (ShaderProgram shaderprogram : CoreShaders.getProgramsToPreload()) { -+ for (ShaderProgram shaderprogram : net.neoforged.neoforge.client.CoreShaderManager.getProgramsToPreload()) { - try { - shadermanager$compilationcache.programs.put(shaderprogram, Optional.of(shadermanager$compilationcache.compileProgram(shaderprogram))); - } catch (ShaderManager.CompilationException shadermanager$compilationexception) { + for (PostChainConfig postchainconfig : p_366597_.postChains.values()) { + for (PostChainConfig.Pass postchainconfig$pass : postchainconfig.passes()) { diff --git a/patches/net/minecraft/client/renderer/Sheets.java.patch b/patches/net/minecraft/client/renderer/Sheets.java.patch index cb01eb86db..16c8df9f7a 100644 --- a/patches/net/minecraft/client/renderer/Sheets.java.patch +++ b/patches/net/minecraft/client/renderer/Sheets.java.patch @@ -1,22 +1,24 @@ --- a/net/minecraft/client/renderer/Sheets.java +++ b/net/minecraft/client/renderer/Sheets.java -@@ -141,11 +_,13 @@ +@@ -156,7 +_,7 @@ } private static Material createSignMaterial(WoodType p_173386_) { -- return new Material(SIGN_SHEET, ResourceLocation.withDefaultNamespace("entity/signs/" + p_173386_.name())); -+ ResourceLocation location = ResourceLocation.parse(p_173386_.name()); -+ return new Material(SIGN_SHEET, ResourceLocation.fromNamespaceAndPath(location.getNamespace(), "entity/signs/" + location.getPath())); +- return createSignMaterial(ResourceLocation.withDefaultNamespace(p_173386_.name())); ++ return createSignMaterial(ResourceLocation.parse(p_173386_.name())); + } + + public static Material createSignMaterial(ResourceLocation p_389416_) { +@@ -164,7 +_,7 @@ } private static Material createHangingSignMaterial(WoodType p_251735_) { -- return new Material(SIGN_SHEET, ResourceLocation.withDefaultNamespace("entity/signs/hanging/" + p_251735_.name())); -+ ResourceLocation location = ResourceLocation.parse(p_251735_.name()); -+ return new Material(SIGN_SHEET, ResourceLocation.fromNamespaceAndPath(location.getNamespace(), "entity/signs/hanging/" + location.getPath())); +- return createHangingSignMaterial(ResourceLocation.withDefaultNamespace(p_251735_.name())); ++ return createHangingSignMaterial(ResourceLocation.parse(p_251735_.name())); } - public static Material getSignMaterial(WoodType p_173382_) { -@@ -204,6 +_,23 @@ + public static Material createHangingSignMaterial(ResourceLocation p_389413_) { +@@ -231,6 +_,23 @@ case SINGLE: default: return p_110773_; diff --git a/patches/net/minecraft/client/renderer/WeatherEffectRenderer.java.patch b/patches/net/minecraft/client/renderer/WeatherEffectRenderer.java.patch index 180f282591..9d71f4e7ca 100644 --- a/patches/net/minecraft/client/renderer/WeatherEffectRenderer.java.patch +++ b/patches/net/minecraft/client/renderer/WeatherEffectRenderer.java.patch @@ -1,15 +1,15 @@ --- a/net/minecraft/client/renderer/WeatherEffectRenderer.java +++ b/net/minecraft/client/renderer/WeatherEffectRenderer.java -@@ -61,6 +_,8 @@ +@@ -56,6 +_,8 @@ } - public void render(Level p_364994_, LightTexture p_363130_, int p_363664_, float p_361655_, Vec3 p_363375_) { -+ if (((ClientLevel) p_364994_).effects().renderSnowAndRain((ClientLevel) p_364994_, p_363664_, p_361655_, p_363130_, p_363375_.x, p_363375_.y, p_363375_.z)) + public void render(Level p_364994_, MultiBufferSource p_383071_, int p_363664_, float p_361655_, Vec3 p_363375_) { ++ if (((ClientLevel) p_364994_).effects().renderSnowAndRain((ClientLevel) p_364994_, p_363664_, p_361655_, p_363375_.x, p_363375_.y, p_363375_.z)) + return; float f = p_364994_.getRainLevel(p_361655_); if (!(f <= 0.0F)) { int i = Minecraft.useFancyGraphics() ? 10 : 5; -@@ -201,6 +_,8 @@ +@@ -180,6 +_,8 @@ } public void tickRainParticles(ClientLevel p_361823_, Camera p_364990_, int p_361788_, ParticleStatus p_363302_) { diff --git a/patches/net/minecraft/client/renderer/block/BlockRenderDispatcher.java.patch b/patches/net/minecraft/client/renderer/block/BlockRenderDispatcher.java.patch index 5ee7f47292..144c236c6b 100644 --- a/patches/net/minecraft/client/renderer/block/BlockRenderDispatcher.java.patch +++ b/patches/net/minecraft/client/renderer/block/BlockRenderDispatcher.java.patch @@ -2,7 +2,7 @@ +++ b/net/minecraft/client/renderer/block/BlockRenderDispatcher.java @@ -37,7 +_,7 @@ this.blockModelShaper = p_173399_; - this.blockEntityRenderer = p_173400_; + this.specialBlockModelRenderer = p_386717_; this.blockColors = p_173401_; - this.modelRenderer = new ModelBlockRenderer(this.blockColors); + this.modelRenderer = new net.neoforged.neoforge.client.model.lighting.LightPipelineAwareModelBlockRenderer(this.blockColors); @@ -71,7 +71,7 @@ this.liquidBlockRenderer.tesselate(p_234365_, p_234364_, p_234366_, p_234367_, p_234368_); } catch (Throwable throwable) { CrashReport crashreport = CrashReport.forThrowable(throwable, "Tesselating liquid in world"); -@@ -104,7 +_,11 @@ +@@ -104,25 +_,33 @@ return this.blockModelShaper.getBlockModel(p_110911_); } @@ -79,41 +79,38 @@ public void renderSingleBlock(BlockState p_110913_, PoseStack p_110914_, MultiBufferSource p_110915_, int p_110916_, int p_110917_) { + renderSingleBlock(p_110913_, p_110914_, p_110915_, p_110916_, p_110917_, net.neoforged.neoforge.client.model.data.ModelData.EMPTY, null); + } -+ public void renderSingleBlock(BlockState p_110913_, PoseStack p_110914_, MultiBufferSource p_110915_, int p_110916_, int p_110917_, net.neoforged.neoforge.client.model.data.ModelData modelData, net.minecraft.client.renderer.RenderType renderType) { ++ ++ public void renderSingleBlock(BlockState p_110913_, PoseStack p_110914_, MultiBufferSource p_110915_, int p_110916_, int p_110917_, net.neoforged.neoforge.client.model.data.ModelData modelData, @org.jetbrains.annotations.Nullable net.minecraft.client.renderer.RenderType renderType) { RenderShape rendershape = p_110913_.getRenderShape(); if (rendershape != RenderShape.INVISIBLE) { - switch (rendershape) { -@@ -114,22 +_,25 @@ - float f = (float)(i >> 16 & 0xFF) / 255.0F; - float f1 = (float)(i >> 8 & 0xFF) / 255.0F; - float f2 = (float)(i & 0xFF) / 255.0F; -+ for (net.minecraft.client.renderer.RenderType rt : bakedmodel.getRenderTypes(p_110913_, RandomSource.create(42), modelData)) - this.modelRenderer - .renderModel( - p_110914_.last(), -- p_110915_.getBuffer(ItemBlockRenderTypes.getRenderType(p_110913_)), -+ p_110915_.getBuffer(renderType != null ? renderType : net.neoforged.neoforge.client.RenderTypeHelper.getEntityRenderType(rt)), - p_110913_, - bakedmodel, - f, - f1, - f2, - p_110916_, -- p_110917_ -+ p_110917_, -+ modelData, -+ rt - ); - break; - case ENTITYBLOCK_ANIMATED: -- this.blockEntityRenderer -- .renderByItem(new ItemStack(p_110913_.getBlock()), ItemDisplayContext.NONE, p_110914_, p_110915_, p_110916_, p_110917_); -+ ItemStack stack = new ItemStack(p_110913_.getBlock()); -+ net.neoforged.neoforge.client.extensions.common.IClientItemExtensions.of(stack).getCustomRenderer().renderByItem(stack, ItemDisplayContext.NONE, p_110914_, p_110915_, p_110916_, p_110917_); - } + BakedModel bakedmodel = this.getBlockModel(p_110913_); + int i = this.blockColors.getColor(p_110913_, null, null, 0); +- float f = (float)(i >> 16 & 0xFF) / 255.0F; +- float f1 = (float)(i >> 8 & 0xFF) / 255.0F; +- float f2 = (float)(i & 0xFF) / 255.0F; ++ float f = (float) (i >> 16 & 0xFF) / 255.0F; ++ float f1 = (float) (i >> 8 & 0xFF) / 255.0F; ++ float f2 = (float) (i & 0xFF) / 255.0F; ++ for (net.minecraft.client.renderer.RenderType rt : bakedmodel.getRenderTypes(p_110913_, RandomSource.create(42), modelData)) + this.modelRenderer + .renderModel( + p_110914_.last(), +- p_110915_.getBuffer(ItemBlockRenderTypes.getRenderType(p_110913_)), ++ p_110915_.getBuffer(renderType != null ? renderType : net.neoforged.neoforge.client.RenderTypeHelper.getEntityRenderType(rt)), + p_110913_, + bakedmodel, + f, + f1, + f2, + p_110916_, +- p_110917_ ++ p_110917_, ++ modelData, ++ rt + ); + this.specialBlockModelRenderer.get().renderByBlock(p_110913_.getBlock(), ItemDisplayContext.NONE, p_110914_, p_110915_, p_110916_, p_110917_); } - } -@@ -137,5 +_,9 @@ +@@ -131,5 +_,9 @@ @Override public void onResourceManagerReload(ResourceManager p_110909_) { this.liquidBlockRenderer.setupSprites(); diff --git a/patches/net/minecraft/client/renderer/block/LiquidBlockRenderer.java.patch b/patches/net/minecraft/client/renderer/block/LiquidBlockRenderer.java.patch index f2ca146f9c..72aee1fb3e 100644 --- a/patches/net/minecraft/client/renderer/block/LiquidBlockRenderer.java.patch +++ b/patches/net/minecraft/client/renderer/block/LiquidBlockRenderer.java.patch @@ -1,6 +1,6 @@ --- a/net/minecraft/client/renderer/block/LiquidBlockRenderer.java +++ b/net/minecraft/client/renderer/block/LiquidBlockRenderer.java -@@ -37,6 +_,7 @@ +@@ -37,12 +_,17 @@ this.waterIcons[0] = Minecraft.getInstance().getModelManager().getBlockModelShaper().getBlockModel(Blocks.WATER.defaultBlockState()).getParticleIcon(); this.waterIcons[1] = ModelBakery.WATER_FLOW.sprite(); this.waterOverlay = ModelBakery.WATER_OVERLAY.sprite(); @@ -8,8 +8,29 @@ } private static boolean isNeighborSameFluid(FluidState p_203186_, FluidState p_203187_) { -@@ -70,8 +_,9 @@ + return p_203187_.getType().isSame(p_203186_.getType()); + } + ++ private static boolean isNeighborStateHidingOverlay(FluidState selfState, BlockState otherState, Direction neighborFace) { ++ return otherState.shouldHideAdjacentFluidFace(neighborFace, selfState); ++ } ++ + private static boolean isFaceOccludedByState(Direction p_110980_, float p_110981_, BlockState p_110983_) { + VoxelShape voxelshape = p_110983_.getFaceOcclusionShape(p_110980_.getOpposite()); + if (voxelshape == Shapes.empty()) { +@@ -64,14 +_,20 @@ + return isFaceOccludedByState(p_110963_.getOpposite(), 1.0F, p_110962_); + } ++ /** @deprecated Neo: use overload that accepts BlockState */ + public static boolean shouldRenderFace(FluidState p_203169_, BlockState p_203170_, Direction p_203171_, FluidState p_203172_) { + return !isFaceOccludedBySelf(p_203170_, p_203171_) && !isNeighborSameFluid(p_203169_, p_203172_); + } + ++ public static boolean shouldRenderFace(FluidState fluidState, BlockState selfState, Direction direction, BlockState otherState) { ++ return !isFaceOccludedBySelf(selfState, direction) && !isNeighborStateHidingOverlay(fluidState, otherState, direction.getOpposite()); ++ } ++ public void tesselate(BlockAndTintGetter p_234370_, BlockPos p_234371_, VertexConsumer p_234372_, BlockState p_234373_, FluidState p_234374_) { boolean flag = p_234374_.is(FluidTags.LAVA); - TextureAtlasSprite[] atextureatlassprite = flag ? this.lavaIcons : this.waterIcons; @@ -20,6 +41,25 @@ float f = (float)(i >> 16 & 0xFF) / 255.0F; float f1 = (float)(i >> 8 & 0xFF) / 255.0F; float f2 = (float)(i & 0xFF) / 255.0F; +@@ -87,12 +_,12 @@ + FluidState fluidstate4 = blockstate4.getFluidState(); + BlockState blockstate5 = p_234370_.getBlockState(p_234371_.relative(Direction.EAST)); + FluidState fluidstate5 = blockstate5.getFluidState(); +- boolean flag1 = !isNeighborSameFluid(p_234374_, fluidstate1); +- boolean flag2 = shouldRenderFace(p_234374_, p_234373_, Direction.DOWN, fluidstate) && !isFaceOccludedByNeighbor(Direction.DOWN, 0.8888889F, blockstate); +- boolean flag3 = shouldRenderFace(p_234374_, p_234373_, Direction.NORTH, fluidstate2); +- boolean flag4 = shouldRenderFace(p_234374_, p_234373_, Direction.SOUTH, fluidstate3); +- boolean flag5 = shouldRenderFace(p_234374_, p_234373_, Direction.WEST, fluidstate4); +- boolean flag6 = shouldRenderFace(p_234374_, p_234373_, Direction.EAST, fluidstate5); ++ boolean flag1 = !isNeighborStateHidingOverlay(p_234374_, blockstate1, Direction.DOWN); ++ boolean flag2 = shouldRenderFace(p_234374_, p_234373_, Direction.DOWN, blockstate) && !isFaceOccludedByNeighbor(Direction.DOWN, 0.8888889F, blockstate); ++ boolean flag3 = shouldRenderFace(p_234374_, p_234373_, Direction.NORTH, blockstate2); ++ boolean flag4 = shouldRenderFace(p_234374_, p_234373_, Direction.SOUTH, blockstate3); ++ boolean flag5 = shouldRenderFace(p_234374_, p_234373_, Direction.WEST, blockstate4); ++ boolean flag6 = shouldRenderFace(p_234374_, p_234373_, Direction.EAST, blockstate5); + if (flag1 || flag2 || flag6 || flag5 || flag3 || flag4) { + float f3 = p_234370_.getShade(Direction.DOWN, true); + float f4 = p_234370_.getShade(Direction.UP, true); @@ -180,15 +_,15 @@ float f57 = f4 * f; float f29 = f4 * f1; diff --git a/patches/net/minecraft/client/renderer/block/model/BakedOverrides.java.patch b/patches/net/minecraft/client/renderer/block/model/BakedOverrides.java.patch deleted file mode 100644 index 60cb1a713d..0000000000 --- a/patches/net/minecraft/client/renderer/block/model/BakedOverrides.java.patch +++ /dev/null @@ -1,38 +0,0 @@ ---- a/net/minecraft/client/renderer/block/model/BakedOverrides.java -+++ b/net/minecraft/client/renderer/block/model/BakedOverrides.java -@@ -29,7 +_,15 @@ - this.properties = new ResourceLocation[0]; - } - -+ /** -+ * @deprecated Neo: Use {@link #BakedOverrides(ModelBaker, List, java.util.function.Function)} -+ */ -+ @Deprecated - public BakedOverrides(ModelBaker p_371950_, List p_371198_) { -+ this(p_371950_, p_371198_, p_371950_.getModelTextureGetter()); -+ } -+ -+ public BakedOverrides(ModelBaker p_371950_, List p_371198_, java.util.function.Function spriteGetter) { - this.properties = p_371198_.stream() - .flatMap(p_371945_ -> p_371945_.predicates().stream()) - .map(ItemOverride.Predicate::property) -@@ -45,7 +_,7 @@ - - for (int j = p_371198_.size() - 1; j >= 0; j--) { - ItemOverride itemoverride = p_371198_.get(j); -- BakedModel bakedmodel = p_371950_.bake(itemoverride.model(), BlockModelRotation.X0_Y0); -+ BakedModel bakedmodel = p_371950_.bake(itemoverride.model(), BlockModelRotation.X0_Y0, spriteGetter); - BakedOverrides.PropertyMatcher[] abakedoverrides$propertymatcher = itemoverride.predicates().stream().map(p_371644_ -> { - int k = object2intmap.getInt(p_371644_.property()); - return new BakedOverrides.PropertyMatcher(k, p_371644_.value()); -@@ -80,6 +_,10 @@ - } - - return null; -+ } -+ -+ public com.google.common.collect.ImmutableList getOverrides() { -+ return com.google.common.collect.ImmutableList.copyOf(overrides); - } - - @OnlyIn(Dist.CLIENT) diff --git a/patches/net/minecraft/client/renderer/block/model/BlockElement.java.patch b/patches/net/minecraft/client/renderer/block/model/BlockElement.java.patch index c19fb03a3e..fbef04d799 100644 --- a/patches/net/minecraft/client/renderer/block/model/BlockElement.java.patch +++ b/patches/net/minecraft/client/renderer/block/model/BlockElement.java.patch @@ -1,6 +1,6 @@ --- a/net/minecraft/client/renderer/block/model/BlockElement.java +++ b/net/minecraft/client/renderer/block/model/BlockElement.java -@@ -30,6 +_,7 @@ +@@ -31,6 +_,7 @@ public final BlockElementRotation rotation; public final boolean shade; public final int lightEmission; @@ -8,7 +8,7 @@ public BlockElement(Vector3f p_253626_, Vector3f p_254426_, Map p_254454_) { this(p_253626_, p_254426_, p_254454_, null, true, 0); -@@ -43,6 +_,10 @@ +@@ -44,6 +_,10 @@ boolean p_361372_, int p_361908_ ) { @@ -19,7 +19,7 @@ this.from = p_361324_; this.to = p_363867_; this.faces = p_362722_; -@@ -50,6 +_,8 @@ +@@ -51,6 +_,8 @@ this.shade = p_361372_; this.lightEmission = p_361908_; this.fillUvs(); @@ -28,7 +28,7 @@ } private void fillUvs() { -@@ -104,7 +_,8 @@ +@@ -98,7 +_,8 @@ } } @@ -38,7 +38,7 @@ } } -@@ -215,5 +_,13 @@ +@@ -209,5 +_,13 @@ return new Vector3f(afloat[0], afloat[1], afloat[2]); } } diff --git a/patches/net/minecraft/client/renderer/block/model/BlockModel.java.patch b/patches/net/minecraft/client/renderer/block/model/BlockModel.java.patch index 767bdb3611..cfe8c768a8 100644 --- a/patches/net/minecraft/client/renderer/block/model/BlockModel.java.patch +++ b/patches/net/minecraft/client/renderer/block/model/BlockModel.java.patch @@ -1,104 +1,119 @@ --- a/net/minecraft/client/renderer/block/model/BlockModel.java +++ b/net/minecraft/client/renderer/block/model/BlockModel.java -@@ -72,9 +_,10 @@ - public BlockModel parent; - @Nullable - protected ResourceLocation parentLocation; -+ public final net.neoforged.neoforge.client.model.geometry.BlockGeometryBakingContext customData = new net.neoforged.neoforge.client.model.geometry.BlockGeometryBakingContext(this); +@@ -26,15 +_,17 @@ + import net.neoforged.api.distmarker.OnlyIn; - public static BlockModel fromStream(Reader p_111462_) { -- return GsonHelper.fromJson(GSON, p_111462_, BlockModel.class); -+ return GsonHelper.fromJson(net.neoforged.neoforge.client.model.ExtendedBlockModelDeserializer.INSTANCE, p_111462_, BlockModel.class); - } - - public BlockModel( -@@ -95,10 +_,15 @@ - this.overrides = p_273099_; - } + @OnlyIn(Dist.CLIENT) +-public class BlockModel implements UnbakedModel { ++public class BlockModel implements UnbakedModel, net.neoforged.neoforge.client.model.ExtendedUnbakedModel { + @VisibleForTesting + public static final Gson GSON = new GsonBuilder() ++ .registerTypeHierarchyAdapter(UnbakedModel.class, new net.neoforged.neoforge.client.model.UnbakedModelParser.Deserializer()) + .registerTypeAdapter(BlockModel.class, new BlockModel.Deserializer()) + .registerTypeAdapter(BlockElement.class, new BlockElement.Deserializer()) + .registerTypeAdapter(BlockElementFace.class, new BlockElementFace.Deserializer()) + .registerTypeAdapter(BlockFaceUV.class, new BlockFaceUV.Deserializer()) + .registerTypeAdapter(ItemTransform.class, new ItemTransform.Deserializer()) + .registerTypeAdapter(ItemTransforms.class, new ItemTransforms.Deserializer()) ++ .registerTypeAdapter(com.mojang.math.Transformation.class, new net.neoforged.neoforge.common.util.TransformationHelper.Deserializer()) + .create(); + private final List elements; + @Nullable +@@ -49,7 +_,14 @@ + private UnbakedModel parent; + @Nullable + private final ResourceLocation parentLocation; ++ @Nullable ++ private final com.mojang.math.Transformation transformation; ++ private final net.neoforged.neoforge.client.RenderTypeGroup renderTypeGroup; ++ /** ++ * @deprecated Neo: use {@link net.neoforged.neoforge.client.model.UnbakedModelParser#parse(Reader)} instead ++ */ + @Deprecated - public List getElements() { -+ if (customData.hasCustomGeometry()) return java.util.Collections.emptyList(); - return this.elements.isEmpty() && this.parent != null ? this.parent.getElements() : this.elements; + public static BlockModel fromStream(Reader p_111462_) { + return GsonHelper.fromJson(GSON, p_111462_, BlockModel.class); } - -+ @Nullable -+ public ResourceLocation getParentLocation() { return parentLocation; } +@@ -62,12 +_,27 @@ + @Nullable UnbakedModel.GuiLight p_387948_, + @Nullable ItemTransforms p_273480_ + ) { ++ this(p_273263_, p_272668_, p_386899_, p_272676_, p_387948_, p_273480_, null, net.neoforged.neoforge.client.RenderTypeGroup.EMPTY); ++ } + - public boolean hasAmbientOcclusion() { - if (this.hasAmbientOcclusion != null) { - return this.hasAmbientOcclusion; -@@ -132,10 +_,18 @@ ++ public BlockModel( ++ @Nullable ResourceLocation p_273263_, ++ List p_272668_, ++ TextureSlots.Data p_386899_, ++ @Nullable Boolean p_272676_, ++ @Nullable UnbakedModel.GuiLight p_387948_, ++ @Nullable ItemTransforms p_273480_, ++ @Nullable com.mojang.math.Transformation transformation, ++ net.neoforged.neoforge.client.RenderTypeGroup renderTypeGroup ++ ) { + this.elements = p_272668_; + this.hasAmbientOcclusion = p_272676_; + this.guiLight = p_387948_; + this.textureSlots = p_386899_; + this.parentLocation = p_273263_; + this.transforms = p_273480_; ++ this.transformation = transformation; ++ this.renderTypeGroup = renderTypeGroup; + } - this.parent = blockmodel; - } -+ -+ if (customData.hasCustomGeometry()) { -+ customData.getCustomGeometry().resolveDependencies(p_361203_, customData); -+ } + @Nullable +@@ -107,10 +_,12 @@ } @Override - public BakedModel bake(ModelBaker p_252120_, Function p_250023_, ModelState p_251130_) { -+ var customGeometry = this.customData.getCustomGeometry(); -+ if (customGeometry != null) { -+ return customGeometry.bake(this.customData, p_252120_, p_250023_, p_251130_, this.overrides); -+ } - return this.bake(p_250023_, p_251130_, true); +- public BakedModel bake(TextureSlots p_387258_, ModelBaker p_388168_, ModelState p_111453_, boolean p_111455_, boolean p_387632_, ItemTransforms p_386577_) { ++ public BakedModel bake(TextureSlots p_387258_, ModelBaker p_388168_, ModelState p_111453_, boolean p_111455_, boolean p_387632_, ItemTransforms p_386577_, net.minecraft.util.context.ContextMap additionalProperties) { + return this.elements.isEmpty() && this.parent != null +- ? this.parent.bake(p_387258_, p_388168_, p_111453_, p_111455_, p_387632_, p_386577_) +- : SimpleBakedModel.bakeElements(this.elements, p_387258_, p_388168_.sprites(), p_111453_, p_111455_, p_387632_, true, p_386577_); ++ ? this.parent.bake(p_387258_, p_388168_, p_111453_, p_111455_, p_387632_, p_386577_, additionalProperties) ++ : SimpleBakedModel.bakeElements(this.elements, p_387258_, p_388168_.sprites(), p_111453_, p_111455_, p_387632_, true, p_386577_, ++ additionalProperties.getOrDefault(net.neoforged.neoforge.client.model.NeoForgeModelProperties.TRANSFORM, com.mojang.math.Transformation.identity()), ++ additionalProperties.getOrDefault(net.neoforged.neoforge.client.model.NeoForgeModelProperties.RENDER_TYPE, net.neoforged.neoforge.client.RenderTypeGroup.EMPTY)); } -@@ -146,6 +_,10 @@ - } else { - SimpleBakedModel.Builder simplebakedmodel$builder = new SimpleBakedModel.Builder(this, p_111455_).particle(textureatlassprite); + @Nullable +@@ -125,6 +_,16 @@ + return this.parentLocation; + } -+ if (!this.customData.getRootTransform().isIdentity()) { -+ p_111453_ = net.neoforged.neoforge.client.model.geometry.UnbakedGeometryHelper.composeRootTransformIntoModelState(p_111453_, this.customData.getRootTransform()); -+ } ++ @Override ++ public void fillAdditionalProperties(net.minecraft.util.context.ContextMap.Builder propertiesBuilder) { ++ if (this.transformation != null) { ++ propertiesBuilder.withParameter(net.neoforged.neoforge.client.model.NeoForgeModelProperties.TRANSFORM, this.transformation); ++ } ++ if (!this.renderTypeGroup.isEmpty()) { ++ propertiesBuilder.withParameter(net.neoforged.neoforge.client.model.NeoForgeModelProperties.RENDER_TYPE, this.renderTypeGroup); ++ } ++ } + - for (BlockElement blockelement : this.getElements()) { - for (Direction direction : blockelement.faces.keySet()) { - BlockElementFace blockelementface = blockelement.faces.get(direction); -@@ -161,7 +_,11 @@ - } + @OnlyIn(Dist.CLIENT) + public static class Deserializer implements JsonDeserializer { + public BlockModel deserialize(JsonElement p_111498_, Type p_111499_, JsonDeserializationContext p_111500_) throws JsonParseException { +@@ -145,7 +_,20 @@ } -- return simplebakedmodel$builder.build(); -+ var renderTypes = net.neoforged.neoforge.client.RenderTypeGroup.EMPTY; -+ if (this.customData.getRenderTypeHint() != null) { -+ renderTypes = this.customData.getRenderType(this.customData.getRenderTypeHint()); -+ } -+ return simplebakedmodel$builder.build(renderTypes); - } - } - -@@ -229,7 +_,18 @@ - ItemTransform itemtransform5 = this.getTransform(ItemDisplayContext.GUI); - ItemTransform itemtransform6 = this.getTransform(ItemDisplayContext.GROUND); - ItemTransform itemtransform7 = this.getTransform(ItemDisplayContext.FIXED); -- return new ItemTransforms(itemtransform, itemtransform1, itemtransform2, itemtransform3, itemtransform4, itemtransform5, itemtransform6, itemtransform7); + ResourceLocation resourcelocation = s.isEmpty() ? null : ResourceLocation.parse(s); +- return new BlockModel(resourcelocation, list, textureslots$data, obool, unbakedmodel$guilight, itemtransforms); + -+ var builder = com.google.common.collect.ImmutableMap.builder(); -+ for(ItemDisplayContext type : ItemDisplayContext.values()) { -+ if (type.isModded()) { -+ var transform = this.getTransform(type); -+ if (transform != ItemTransform.NO_TRANSFORM) { -+ builder.put(type, transform); -+ } ++ com.mojang.math.Transformation rootTransform = null; ++ if (jsonobject.has("transform")) { ++ JsonElement transform = jsonobject.get("transform"); ++ rootTransform = p_111500_.deserialize(transform, com.mojang.math.Transformation.class); + } -+ } + -+ return new ItemTransforms(itemtransform, itemtransform1, itemtransform2, itemtransform3, itemtransform4, itemtransform5, itemtransform6, itemtransform7, builder.build()); - } - - private ItemTransform getTransform(ItemDisplayContext p_270662_) { -@@ -347,6 +_,10 @@ - - public boolean lightLikeBlock() { - return this == SIDE; -+ } ++ var renderTypeGroup = net.neoforged.neoforge.client.RenderTypeGroup.EMPTY; ++ if (jsonobject.has("render_type")) { ++ var renderTypeHintName = GsonHelper.getAsString(jsonobject, "render_type"); ++ renderTypeGroup = net.neoforged.neoforge.client.NamedRenderTypeManager.get(ResourceLocation.parse(renderTypeHintName)); ++ } + -+ public String getSerializedName() { -+ return name; ++ return new BlockModel(resourcelocation, list, textureslots$data, obool, unbakedmodel$guilight, itemtransforms, rootTransform, renderTypeGroup); } - } - } + + private TextureSlots.Data getTextureMap(JsonObject p_111510_) { diff --git a/patches/net/minecraft/client/renderer/block/model/FaceBakery.java.patch b/patches/net/minecraft/client/renderer/block/model/FaceBakery.java.patch index 71b9c9943e..bcf2bbca9c 100644 --- a/patches/net/minecraft/client/renderer/block/model/FaceBakery.java.patch +++ b/patches/net/minecraft/client/renderer/block/model/FaceBakery.java.patch @@ -1,7 +1,7 @@ --- a/net/minecraft/client/renderer/block/model/FaceBakery.java +++ b/net/minecraft/client/renderer/block/model/FaceBakery.java @@ -58,7 +_,14 @@ - this.recalculateWinding(aint, direction); + recalculateWinding(aint, direction); } - return new BakedQuad(aint, p_111603_.tintIndex(), direction, p_111604_, p_111608_, p_364857_); diff --git a/patches/net/minecraft/client/renderer/block/model/ItemModelGenerator.java.patch b/patches/net/minecraft/client/renderer/block/model/ItemModelGenerator.java.patch index 199561dd6b..4028b1df80 100644 --- a/patches/net/minecraft/client/renderer/block/model/ItemModelGenerator.java.patch +++ b/patches/net/minecraft/client/renderer/block/model/ItemModelGenerator.java.patch @@ -1,21 +1,52 @@ --- a/net/minecraft/client/renderer/block/model/ItemModelGenerator.java +++ b/net/minecraft/client/renderer/block/model/ItemModelGenerator.java -@@ -32,13 +_,16 @@ +@@ -20,7 +_,7 @@ + import org.joml.Vector3f; - Material material = p_111672_.getMaterial(s); - map.put(s, Either.left(material)); -- SpriteContents spritecontents = p_111671_.apply(material).contents(); + @OnlyIn(Dist.CLIENT) +-public class ItemModelGenerator implements UnbakedModel { ++public class ItemModelGenerator implements UnbakedModel, net.neoforged.neoforge.client.model.ExtendedUnbakedModel { + public static final ResourceLocation GENERATED_ITEM_MODEL_ID = ResourceLocation.withDefaultNamespace("builtin/generated"); + public static final List LAYERS = List.of("layer0", "layer1", "layer2", "layer3", "layer4"); + private static final float MIN_Z = 7.5F; +@@ -43,13 +_,23 @@ + } + + @Override +- public BakedModel bake(TextureSlots p_386773_, ModelBaker p_386770_, ModelState p_386536_, boolean p_388612_, boolean p_387457_, ItemTransforms p_388565_) { +- return this.bake(p_386773_, p_386770_.sprites(), p_386536_, p_388612_, p_387457_, p_388565_); ++ public BakedModel bake(TextureSlots p_386773_, ModelBaker p_386770_, ModelState p_386536_, boolean p_388612_, boolean p_387457_, ItemTransforms p_388565_, net.minecraft.util.context.ContextMap additionalProperties) { ++ return this.bake(p_386773_, p_386770_.sprites(), p_386536_, p_388612_, p_387457_, p_388565_, additionalProperties.getOrDefault(net.neoforged.neoforge.client.model.NeoForgeModelProperties.RENDER_TYPE, net.neoforged.neoforge.client.RenderTypeGroup.EMPTY)); + } + ++ /** ++ * @deprecated Neo: use {@link #bake(TextureSlots, SpriteGetter, ModelState, boolean, boolean, ItemTransforms, net.neoforged.neoforge.client.RenderTypeGroup)} instead ++ */ ++ @Deprecated + private BakedModel bake( + TextureSlots p_387202_, SpriteGetter p_387257_, ModelState p_387172_, boolean p_388328_, boolean p_387288_, ItemTransforms p_388238_ + ) { ++ return this.bake(p_387202_, p_387257_, p_387172_, p_388328_, p_387288_, p_388238_, net.neoforged.neoforge.client.RenderTypeGroup.EMPTY); ++ } ++ ++ private BakedModel bake( ++ TextureSlots p_387202_, SpriteGetter p_387257_, ModelState p_387172_, boolean p_388328_, boolean p_387288_, ItemTransforms p_388238_, net.neoforged.neoforge.client.RenderTypeGroup renderTypes ++ ) { + TextureSlots.Data.Builder textureslots$data$builder = new TextureSlots.Data.Builder(); + List list = new ArrayList<>(); + +@@ -61,11 +_,11 @@ + } + + textureslots$data$builder.addTexture(s, material); +- SpriteContents spritecontents = p_387257_.get(material).contents(); - list.addAll(this.processFrames(i, s, spritecontents)); -+ TextureAtlasSprite sprite = p_111671_.apply(material); -+ // Neo: fix MC-73186 on generated item models ++ net.minecraft.client.renderer.texture.TextureAtlasSprite sprite = p_387257_.get(material); + list.addAll(net.neoforged.neoforge.client.ClientHooks.fixItemModelSeams(this.processFrames(i, s, sprite.contents()), sprite)); } - map.put("particle", p_111672_.hasTexture("particle") ? Either.left(p_111672_.getMaterial("particle")) : map.get("layer0")); - BlockModel blockmodel = new BlockModel(null, list, map, false, p_111672_.getGuiLight(), p_111672_.getTransforms(), p_111672_.getOverrides()); - blockmodel.name = p_111672_.name; -+ blockmodel.customData.copyFrom(p_111672_.customData); -+ blockmodel.customData.setGui3d(false); - return blockmodel; +- return SimpleBakedModel.bakeElements(list, p_387202_, p_387257_, p_387172_, p_388328_, p_387288_, false, p_388238_); ++ return SimpleBakedModel.bakeElements(list, p_387202_, p_387257_, p_387172_, p_388328_, p_387288_, false, p_388238_, com.mojang.math.Transformation.identity(), renderTypes); } + public List processFrames(int p_111639_, String p_111640_, SpriteContents p_251768_) { diff --git a/patches/net/minecraft/client/renderer/block/model/ItemTransforms.java.patch b/patches/net/minecraft/client/renderer/block/model/ItemTransforms.java.patch index a341611fbb..9ca5df5e9c 100644 --- a/patches/net/minecraft/client/renderer/block/model/ItemTransforms.java.patch +++ b/patches/net/minecraft/client/renderer/block/model/ItemTransforms.java.patch @@ -1,51 +1,32 @@ --- a/net/minecraft/client/renderer/block/model/ItemTransforms.java +++ b/net/minecraft/client/renderer/block/model/ItemTransforms.java -@@ -21,6 +_,7 @@ - public final ItemTransform gui; - public final ItemTransform ground; - public final ItemTransform fixed; -+ public final com.google.common.collect.ImmutableMap moddedTransforms; - - private ItemTransforms() { - this( -@@ -44,8 +_,10 @@ - this.gui = p_111807_.gui; - this.ground = p_111807_.ground; - this.fixed = p_111807_.fixed; -+ this.moddedTransforms = p_111807_.moddedTransforms; - } - +@@ -19,8 +_,23 @@ + ItemTransform head, + ItemTransform gui, + ItemTransform ground, +- ItemTransform fixed ++ ItemTransform fixed, ++ com.google.common.collect.ImmutableMap moddedTransforms + ) { + @Deprecated - public ItemTransforms( - ItemTransform p_111798_, - ItemTransform p_111799_, -@@ -56,6 +_,20 @@ - ItemTransform p_111804_, - ItemTransform p_111805_ - ) { -+ this(p_111798_, p_111799_, p_111800_, p_111801_, p_111802_, p_111803_, p_111804_, p_111805_, com.google.common.collect.ImmutableMap.of()); -+ } -+ + public ItemTransforms( -+ ItemTransform p_111798_, -+ ItemTransform p_111799_, -+ ItemTransform p_111800_, -+ ItemTransform p_111801_, -+ ItemTransform p_111802_, -+ ItemTransform p_111803_, -+ ItemTransform p_111804_, -+ ItemTransform p_111805_, -+ com.google.common.collect.ImmutableMap moddedTransforms ++ ItemTransform thirdPersonLeftHand, ++ ItemTransform thirdPersonRightHand, ++ ItemTransform firstPersonLeftHand, ++ ItemTransform firstPersonRightHand, ++ ItemTransform head, ++ ItemTransform gui, ++ ItemTransform ground, ++ ItemTransform fixed + ) { - this.thirdPersonLeftHand = p_111798_; - this.thirdPersonRightHand = p_111799_; - this.firstPersonLeftHand = p_111800_; -@@ -64,9 +_,21 @@ - this.gui = p_111803_; - this.ground = p_111804_; - this.fixed = p_111805_; -+ this.moddedTransforms = moddedTransforms; - } ++ this(thirdPersonLeftHand, thirdPersonRightHand, firstPersonLeftHand, firstPersonRightHand, head, gui, ground, fixed, com.google.common.collect.ImmutableMap.of()); ++ } ++ + public static final ItemTransforms NO_TRANSFORMS = new ItemTransforms( + ItemTransform.NO_TRANSFORM, + ItemTransform.NO_TRANSFORM, +@@ -33,6 +_,17 @@ + ); public ItemTransform getTransform(ItemDisplayContext p_270619_) { + if (p_270619_.isModded()) { @@ -62,7 +43,7 @@ return switch (p_270619_) { case THIRD_PERSON_LEFT_HAND -> this.thirdPersonLeftHand; case THIRD_PERSON_RIGHT_HAND -> this.thirdPersonRightHand; -@@ -104,9 +_,23 @@ +@@ -66,9 +_,23 @@ ItemTransform itemtransform5 = this.getTransform(p_111822_, jsonobject, ItemDisplayContext.GUI); ItemTransform itemtransform6 = this.getTransform(p_111822_, jsonobject, ItemDisplayContext.GROUND); ItemTransform itemtransform7 = this.getTransform(p_111822_, jsonobject, ItemDisplayContext.FIXED); diff --git a/patches/net/minecraft/client/renderer/block/model/MultiVariant.java.patch b/patches/net/minecraft/client/renderer/block/model/MultiVariant.java.patch deleted file mode 100644 index bd74eaf20d..0000000000 --- a/patches/net/minecraft/client/renderer/block/model/MultiVariant.java.patch +++ /dev/null @@ -1,17 +0,0 @@ ---- a/net/minecraft/client/renderer/block/model/MultiVariant.java -+++ b/net/minecraft/client/renderer/block/model/MultiVariant.java -@@ -45,12 +_,12 @@ - public BakedModel bake(ModelBaker p_249016_, Function p_111851_, ModelState p_111852_) { - if (this.variants.size() == 1) { - Variant variant1 = this.variants.getFirst(); -- return p_249016_.bake(variant1.getModelLocation(), variant1); -+ return p_249016_.bake(variant1.getModelLocation(), variant1, p_111851_); - } else { - SimpleWeightedRandomList.Builder builder = SimpleWeightedRandomList.builder(); - - for (Variant variant : this.variants) { -- BakedModel bakedmodel = p_249016_.bake(variant.getModelLocation(), variant); -+ BakedModel bakedmodel = p_249016_.bake(variant.getModelLocation(), variant, p_111851_); - builder.add(bakedmodel, variant.getWeight()); - } - diff --git a/patches/net/minecraft/client/renderer/blockentity/SignRenderer.java.patch b/patches/net/minecraft/client/renderer/blockentity/AbstractSignRenderer.java.patch similarity index 52% rename from patches/net/minecraft/client/renderer/blockentity/SignRenderer.java.patch rename to patches/net/minecraft/client/renderer/blockentity/AbstractSignRenderer.java.patch index 23ffcf89c2..658b82318d 100644 --- a/patches/net/minecraft/client/renderer/blockentity/SignRenderer.java.patch +++ b/patches/net/minecraft/client/renderer/blockentity/AbstractSignRenderer.java.patch @@ -1,18 +1,16 @@ ---- a/net/minecraft/client/renderer/blockentity/SignRenderer.java -+++ b/net/minecraft/client/renderer/blockentity/SignRenderer.java -@@ -251,6 +_,15 @@ - return LayerDefinition.create(meshdefinition, 64, 32); +--- a/net/minecraft/client/renderer/blockentity/AbstractSignRenderer.java ++++ b/net/minecraft/client/renderer/blockentity/AbstractSignRenderer.java +@@ -195,4 +_,13 @@ + return ARGB.color(0, j, k, l); + } } - ++ + @Override + public net.minecraft.world.phys.AABB getRenderBoundingBox(SignBlockEntity blockEntity) { -+ if (blockEntity.getBlockState().getBlock() instanceof StandingSignBlock) { ++ if (blockEntity.getBlockState().getBlock() instanceof net.minecraft.world.level.block.StandingSignBlock) { + net.minecraft.core.BlockPos pos = blockEntity.getBlockPos(); + return new net.minecraft.world.phys.AABB(pos.getX(), pos.getY(), pos.getZ(), pos.getX() + 1.0, pos.getY() + 1.125, pos.getZ() + 1.0); + } + return BlockEntityRenderer.super.getRenderBoundingBox(blockEntity); + } -+ - @OnlyIn(Dist.CLIENT) - static record Models(Model standing, Model wall) { - } + } diff --git a/patches/net/minecraft/client/renderer/blockentity/BannerRenderer.java.patch b/patches/net/minecraft/client/renderer/blockentity/BannerRenderer.java.patch index 81cc3164d7..43ec30aece 100644 --- a/patches/net/minecraft/client/renderer/blockentity/BannerRenderer.java.patch +++ b/patches/net/minecraft/client/renderer/blockentity/BannerRenderer.java.patch @@ -1,6 +1,6 @@ --- a/net/minecraft/client/renderer/blockentity/BannerRenderer.java +++ b/net/minecraft/client/renderer/blockentity/BannerRenderer.java -@@ -141,4 +_,11 @@ +@@ -135,4 +_,11 @@ int i = p_332728_.getTextureDiffuseColor(); p_332732_.render(p_332737_, p_332704_.buffer(p_332758_, RenderType::entityNoOutline), p_332821_, p_332828_, i); } diff --git a/patches/net/minecraft/client/renderer/blockentity/DecoratedPotRenderer.java.patch b/patches/net/minecraft/client/renderer/blockentity/DecoratedPotRenderer.java.patch index 279790622c..54c1ba440f 100644 --- a/patches/net/minecraft/client/renderer/blockentity/DecoratedPotRenderer.java.patch +++ b/patches/net/minecraft/client/renderer/blockentity/DecoratedPotRenderer.java.patch @@ -1,6 +1,6 @@ --- a/net/minecraft/client/renderer/blockentity/DecoratedPotRenderer.java +++ b/net/minecraft/client/renderer/blockentity/DecoratedPotRenderer.java -@@ -141,4 +_,10 @@ +@@ -153,4 +_,10 @@ private void renderSide(ModelPart p_273495_, PoseStack p_272899_, MultiBufferSource p_273582_, int p_273242_, int p_273108_, Material p_273173_) { p_273495_.render(p_272899_, p_273173_.buffer(p_273582_, RenderType::entitySolid), p_273242_, p_273108_); } diff --git a/patches/net/minecraft/client/renderer/blockentity/ShulkerBoxRenderer.java.patch b/patches/net/minecraft/client/renderer/blockentity/ShulkerBoxRenderer.java.patch index b1bd51f952..c5bdbc66de 100644 --- a/patches/net/minecraft/client/renderer/blockentity/ShulkerBoxRenderer.java.patch +++ b/patches/net/minecraft/client/renderer/blockentity/ShulkerBoxRenderer.java.patch @@ -1,7 +1,7 @@ --- a/net/minecraft/client/renderer/blockentity/ShulkerBoxRenderer.java +++ b/net/minecraft/client/renderer/blockentity/ShulkerBoxRenderer.java -@@ -55,6 +_,12 @@ - p_112480_.popPose(); +@@ -57,6 +_,12 @@ + p_388735_.popPose(); } + @Override diff --git a/patches/net/minecraft/client/renderer/blockentity/SkullBlockRenderer.java.patch b/patches/net/minecraft/client/renderer/blockentity/SkullBlockRenderer.java.patch index d738262caa..2d8d009060 100644 --- a/patches/net/minecraft/client/renderer/blockentity/SkullBlockRenderer.java.patch +++ b/patches/net/minecraft/client/renderer/blockentity/SkullBlockRenderer.java.patch @@ -1,18 +1,9 @@ --- a/net/minecraft/client/renderer/blockentity/SkullBlockRenderer.java +++ b/net/minecraft/client/renderer/blockentity/SkullBlockRenderer.java -@@ -55,6 +_,7 @@ - builder.put(SkullBlock.Types.CREEPER, new SkullModel(p_173662_.bakeLayer(ModelLayers.CREEPER_HEAD))); - builder.put(SkullBlock.Types.DRAGON, new DragonHeadModel(p_173662_.bakeLayer(ModelLayers.DRAGON_SKULL))); - builder.put(SkullBlock.Types.PIGLIN, new PiglinHeadModel(p_173662_.bakeLayer(ModelLayers.PIGLIN_HEAD))); -+ net.neoforged.fml.ModLoader.postEvent(new net.neoforged.neoforge.client.event.EntityRenderersEvent.CreateSkullModels(builder, p_173662_)); - return builder.build(); +@@ -115,4 +_,14 @@ + ) + : RenderType.entityCutoutNoCullZOffset(p_389624_ != null ? p_389624_ : SKIN_BY_TYPE.get(p_389566_)); } - -@@ -108,5 +_,15 @@ - } else { - return RenderType.entityCutoutNoCullZOffset(resourcelocation); - } -+ } + + @Override + public net.minecraft.world.phys.AABB getRenderBoundingBox(SkullBlockEntity blockEntity) { @@ -22,5 +13,5 @@ + return new net.minecraft.world.phys.AABB(pos.getX() - .75, pos.getY() - .35, pos.getZ() - .75, pos.getX() + 1.75, pos.getY() + 1.0, pos.getZ() + 1.75); + } + return BlockEntityRenderer.super.getRenderBoundingBox(blockEntity); - } ++ } } diff --git a/patches/net/minecraft/client/renderer/chunk/SectionRenderDispatcher.java.patch b/patches/net/minecraft/client/renderer/chunk/SectionRenderDispatcher.java.patch index 83ea06baae..5b7ff9be4f 100644 --- a/patches/net/minecraft/client/renderer/chunk/SectionRenderDispatcher.java.patch +++ b/patches/net/minecraft/client/renderer/chunk/SectionRenderDispatcher.java.patch @@ -1,6 +1,6 @@ --- a/net/minecraft/client/renderer/chunk/SectionRenderDispatcher.java +++ b/net/minecraft/client/renderer/chunk/SectionRenderDispatcher.java -@@ -393,9 +_,10 @@ +@@ -399,9 +_,10 @@ public SectionRenderDispatcher.RenderSection.CompileTask createCompileTask(RenderRegionCache p_295324_) { this.cancelTasks(); @@ -13,7 +13,7 @@ return this.lastRebuildTask; } -@@ -464,10 +_,17 @@ +@@ -470,10 +_,17 @@ class RebuildTask extends SectionRenderDispatcher.RenderSection.CompileTask { @Nullable protected volatile RenderChunkRegion region; @@ -31,7 +31,7 @@ } @Override -@@ -498,7 +_,7 @@ +@@ -499,7 +_,7 @@ SectionCompiler.Results sectioncompiler$results; try (Zone zone = Profiler.get().zone("Compile Section")) { sectioncompiler$results = SectionRenderDispatcher.this.sectionCompiler diff --git a/patches/net/minecraft/client/renderer/entity/EntityRenderDispatcher.java.patch b/patches/net/minecraft/client/renderer/entity/EntityRenderDispatcher.java.patch index 49c5906036..aaca1f7592 100644 --- a/patches/net/minecraft/client/renderer/entity/EntityRenderDispatcher.java.patch +++ b/patches/net/minecraft/client/renderer/entity/EntityRenderDispatcher.java.patch @@ -1,6 +1,6 @@ --- a/net/minecraft/client/renderer/entity/EntityRenderDispatcher.java +++ b/net/minecraft/client/renderer/entity/EntityRenderDispatcher.java -@@ -243,12 +_,12 @@ +@@ -246,12 +_,12 @@ ) { AABB aabb = p_114444_.getBoundingBox().move(-p_114444_.getX(), -p_114444_.getY(), -p_114444_.getZ()); ShapeRenderer.renderLineBox(p_114442_, p_114443_, aabb, p_353064_, p_353059_, p_353042_, 1.0F); @@ -15,7 +15,7 @@ p_114442_.pushPose(); double d3 = d0 + Mth.lerp((double)p_114445_, enderdragonpart.xOld, enderdragonpart.getX()); double d4 = d1 + Mth.lerp((double)p_114445_, enderdragonpart.yOld, enderdragonpart.getY()); -@@ -474,6 +_,10 @@ +@@ -477,6 +_,10 @@ return this.itemInHandRenderer; } @@ -26,7 +26,7 @@ @Override public void onResourceManagerReload(ResourceManager p_174004_) { EntityRendererProvider.Context entityrendererprovider$context = new EntityRendererProvider.Context( -@@ -481,5 +_,6 @@ +@@ -484,5 +_,6 @@ ); this.renderers = EntityRenderers.createEntityRenderers(entityrendererprovider$context); this.playerRenderers = EntityRenderers.createPlayerRenderers(entityrendererprovider$context); diff --git a/patches/net/minecraft/client/renderer/entity/EntityRenderer.java.patch b/patches/net/minecraft/client/renderer/entity/EntityRenderer.java.patch index 2e7b86b284..4381fedeb5 100644 --- a/patches/net/minecraft/client/renderer/entity/EntityRenderer.java.patch +++ b/patches/net/minecraft/client/renderer/entity/EntityRenderer.java.patch @@ -9,21 +9,29 @@ this.renderNameTag(p_364816_, p_364816_.nameTag, p_114488_, p_114489_, p_114490_); } } -@@ -270,7 +_,12 @@ +@@ -249,6 +_,7 @@ + public final S createRenderState(T p_361382_, float p_360885_) { + S s = this.reusedState; + this.extractRenderState(p_361382_, s, p_360885_); ++ net.neoforged.neoforge.client.renderstate.RenderStateExtensions.onUpdateEntityRenderState(this, p_361382_, s); + return s; + } + +@@ -274,9 +_,11 @@ } p_361028_.distanceToCameraSq = this.entityRenderDispatcher.distanceToSqr(p_362104_); - boolean flag = p_361028_.distanceToCameraSq < 4096.0 && this.shouldShowName(p_362104_, p_361028_.distanceToCameraSq); -+ boolean flag = p_361028_.distanceToCameraSq < 4096.0; -+ if (flag) { -+ var event = new net.neoforged.neoforge.client.event.RenderNameTagEvent.CanRender(p_362104_, p_361028_, p_362104_.getDisplayName(), this, p_362204_); -+ net.neoforged.neoforge.common.NeoForge.EVENT_BUS.post(event); -+ flag = event.canRender().isTrue() || (event.canRender().isDefault() && this.shouldShowName(p_362104_, p_361028_.distanceToCameraSq)); -+ } ++ var event = new net.neoforged.neoforge.client.event.RenderNameTagEvent.CanRender(p_362104_, p_361028_, this.getNameTag(p_362104_), this, p_362204_); ++ net.neoforged.neoforge.common.NeoForge.EVENT_BUS.post(event); ++ boolean flag = event.canRender().isTrue() || (event.canRender().isDefault() && p_361028_.distanceToCameraSq < 4096.0 && this.shouldShowName(p_362104_, p_361028_.distanceToCameraSq)); if (flag) { - p_361028_.nameTag = this.getNameTag(p_362104_); +- p_361028_.nameTag = this.getNameTag(p_362104_); ++ p_361028_.nameTag = event.getContent(); p_361028_.nameTagAttachment = p_362104_.getAttachments().getNullable(EntityAttachment.NAME_TAG, 0, p_362104_.getYRot(p_362204_)); -@@ -302,5 +_,7 @@ + } else { + p_361028_.nameTag = null; +@@ -306,5 +_,7 @@ } p_361028_.displayFireAnimation = p_362104_.displayFireAnimation(); diff --git a/patches/net/minecraft/client/renderer/entity/FishingHookRenderer.java.patch b/patches/net/minecraft/client/renderer/entity/FishingHookRenderer.java.patch index 6747d9fda7..fead956b8d 100644 --- a/patches/net/minecraft/client/renderer/entity/FishingHookRenderer.java.patch +++ b/patches/net/minecraft/client/renderer/entity/FishingHookRenderer.java.patch @@ -1,11 +1,11 @@ --- a/net/minecraft/client/renderer/entity/FishingHookRenderer.java +++ b/net/minecraft/client/renderer/entity/FishingHookRenderer.java -@@ -63,7 +_,7 @@ - private Vec3 getPlayerHandPos(Player p_340935_, float p_340872_, float p_341261_) { - int i = p_340935_.getMainArm() == HumanoidArm.RIGHT ? 1 : -1; - ItemStack itemstack = p_340935_.getMainHandItem(); -- if (!itemstack.is(Items.FISHING_ROD)) { -+ if (!itemstack.canPerformAction(net.neoforged.neoforge.common.ItemAbilities.FISHING_ROD_CAST)) { - i = -i; - } +@@ -60,7 +_,7 @@ + } + + public static HumanoidArm getHoldingArm(Player p_386900_) { +- return p_386900_.getMainHandItem().getItem() instanceof FishingRodItem ? p_386900_.getMainArm() : p_386900_.getMainArm().getOpposite(); ++ return p_386900_.getMainHandItem().canPerformAction(net.neoforged.neoforge.common.ItemAbilities.FISHING_ROD_CAST) ? p_386900_.getMainArm() : p_386900_.getMainArm().getOpposite(); + } + private Vec3 getPlayerHandPos(Player p_340935_, float p_340872_, float p_341261_) { diff --git a/patches/net/minecraft/client/renderer/entity/HumanoidMobRenderer.java.patch b/patches/net/minecraft/client/renderer/entity/HumanoidMobRenderer.java.patch index fa5624769b..e9e02707fe 100644 --- a/patches/net/minecraft/client/renderer/entity/HumanoidMobRenderer.java.patch +++ b/patches/net/minecraft/client/renderer/entity/HumanoidMobRenderer.java.patch @@ -1,6 +1,6 @@ --- a/net/minecraft/client/renderer/entity/HumanoidMobRenderer.java +++ b/net/minecraft/client/renderer/entity/HumanoidMobRenderer.java -@@ -40,7 +_,7 @@ +@@ -51,7 +_,7 @@ p_362998_.isCrouching = p_365104_.isCrouching(); p_362998_.isFallFlying = p_365104_.isFallFlying(); p_362998_.isVisuallySwimming = p_365104_.isVisuallySwimming(); diff --git a/patches/net/minecraft/client/renderer/entity/ItemEntityRenderer.java.patch b/patches/net/minecraft/client/renderer/entity/ItemEntityRenderer.java.patch index 52951841f3..1e35d7dac3 100644 --- a/patches/net/minecraft/client/renderer/entity/ItemEntityRenderer.java.patch +++ b/patches/net/minecraft/client/renderer/entity/ItemEntityRenderer.java.patch @@ -1,24 +1,29 @@ --- a/net/minecraft/client/renderer/entity/ItemEntityRenderer.java +++ b/net/minecraft/client/renderer/entity/ItemEntityRenderer.java -@@ -54,7 +_,8 @@ - this.random.setSeed((long)getSeedForItemStack(itemstack)); - boolean flag = bakedmodel.isGui3d(); +@@ -38,6 +_,8 @@ + super.extractRenderState(p_362393_, p_361441_, p_360849_); + p_361441_.ageInTicks = (float)p_362393_.getAge() + p_360849_; + p_361441_.bobOffset = p_362393_.bobOffs; ++ p_361441_.shouldBob = net.neoforged.neoforge.client.extensions.common.IClientItemExtensions.of(p_362393_.getItem()).shouldBobAsEntity(p_362393_.getItem()); ++ p_361441_.shouldSpread = net.neoforged.neoforge.client.extensions.common.IClientItemExtensions.of(p_362393_.getItem()).shouldSpreadAsEntity(p_362393_.getItem()); + p_361441_.extractItemGroupRenderState(p_362393_, p_362393_.getItem(), this.itemModelResolver); + } + +@@ -45,7 +_,7 @@ + if (!p_362172_.item.isEmpty()) { + p_115030_.pushPose(); float f = 0.25F; - float f1 = Mth.sin(p_362172_.ageInTicks / 10.0F + p_362172_.bobOffset) * 0.1F + 0.1F; -+ boolean shouldBob = net.neoforged.neoforge.client.extensions.common.IClientItemExtensions.of(itemstack).shouldBobAsEntity(itemstack); -+ float f1 = shouldBob ? Mth.sin(p_362172_.ageInTicks / 10.0F + p_362172_.bobOffset) * 0.1F + 0.1F : 0; - float f2 = bakedmodel.getTransforms().getTransform(ItemDisplayContext.GROUND).scale.y(); ++ float f1 = p_362172_.shouldBob ? Mth.sin(p_362172_.ageInTicks / 10.0F + p_362172_.bobOffset) * 0.1F + 0.1F : 0; + float f2 = p_362172_.item.transform().scale.y(); p_115030_.translate(0.0F, f1 + 0.25F * f2, 0.0F); float f3 = ItemEntity.getSpin(p_362172_.ageInTicks, p_362172_.bobOffset); -@@ -110,9 +_,10 @@ - p_323733_.translate(f3, f4, f5); - } +@@ -75,7 +_,7 @@ -+ boolean shouldSpread = net.neoforged.neoforge.client.extensions.common.IClientItemExtensions.of(p_323718_).shouldSpreadAsEntity(p_323718_); for (int j = 0; j < i; j++) { - p_323733_.pushPose(); + p_323763_.pushPose(); - if (j > 0) { -+ if (j > 0 && shouldSpread) { - if (p_324462_) { - float f7 = (p_324565_.nextFloat() * 2.0F - 1.0F) * 0.15F; - float f9 = (p_324565_.nextFloat() * 2.0F - 1.0F) * 0.15F; ++ if (j > 0 && p_388704_.shouldSpread) { + if (flag) { + float f7 = (p_324507_.nextFloat() * 2.0F - 1.0F) * 0.15F; + float f9 = (p_324507_.nextFloat() * 2.0F - 1.0F) * 0.15F; diff --git a/patches/net/minecraft/client/renderer/entity/ItemFrameRenderer.java.patch b/patches/net/minecraft/client/renderer/entity/ItemFrameRenderer.java.patch index 7fe0bf497d..02db78f347 100644 --- a/patches/net/minecraft/client/renderer/entity/ItemFrameRenderer.java.patch +++ b/patches/net/minecraft/client/renderer/entity/ItemFrameRenderer.java.patch @@ -1,23 +1,23 @@ --- a/net/minecraft/client/renderer/entity/ItemFrameRenderer.java +++ b/net/minecraft/client/renderer/entity/ItemFrameRenderer.java -@@ -100,6 +_,7 @@ +@@ -95,6 +_,7 @@ + p_115079_.translate(0.0F, 0.0F, 0.4375F); + } - int j = mapid != null ? p_364723_.rotation % 4 * 2 : p_364723_.rotation; ++ if (!net.neoforged.neoforge.common.NeoForge.EVENT_BUS.post(new net.neoforged.neoforge.client.event.RenderItemInFrameEvent(p_364723_, this, p_115079_, p_115080_, p_115081_)).isCanceled()) { + if (p_364723_.mapId != null) { + int j = p_364723_.rotation % 4 * 2; p_115079_.mulPose(Axis.ZP.rotationDegrees((float)j * 360.0F / 8.0F)); -+ if (!net.neoforged.neoforge.common.NeoForge.EVENT_BUS.post(new net.neoforged.neoforge.client.event.RenderItemInFrameEvent(p_364723_, this, p_115079_, p_115080_, p_115081_)).isCanceled()) { - if (mapid != null) { - p_115079_.mulPose(Axis.ZP.rotationDegrees(180.0F)); - float f2 = 0.0078125F; -@@ -113,6 +_,7 @@ - p_115079_.scale(0.5F, 0.5F, 0.5F); - this.itemRenderer.render(itemstack, ItemDisplayContext.FIXED, false, p_115079_, p_115080_, k, OverlayTexture.NO_OVERLAY, p_364723_.itemModel); - } -+ } +@@ -111,6 +_,7 @@ + p_115079_.scale(0.5F, 0.5F, 0.5F); + p_364723_.item.render(p_115079_, p_115080_, k, OverlayTexture.NO_OVERLAY); } ++ } p_115079_.popPose(); -@@ -161,7 +_,7 @@ - if (!p_362907_.itemStack.isEmpty()) { + } +@@ -154,7 +_,7 @@ + if (!itemstack.isEmpty()) { MapId mapid = p_363125_.getFramedMapId(itemstack); if (mapid != null) { - MapItemSavedData mapitemsaveddata = p_363125_.level().getMapData(mapid); diff --git a/patches/net/minecraft/client/renderer/entity/ItemRenderer.java.patch b/patches/net/minecraft/client/renderer/entity/ItemRenderer.java.patch index a53ce7e08e..714221b7bc 100644 --- a/patches/net/minecraft/client/renderer/entity/ItemRenderer.java.patch +++ b/patches/net/minecraft/client/renderer/entity/ItemRenderer.java.patch @@ -1,54 +1,11 @@ --- a/net/minecraft/client/renderer/entity/ItemRenderer.java +++ b/net/minecraft/client/renderer/entity/ItemRenderer.java -@@ -156,7 +_,7 @@ - float p_371782_ - ) { - p_371635_.pushPose(); -- p_371384_.getTransforms().getTransform(p_371250_).apply(p_371248_, p_371635_); -+ p_371384_ = net.neoforged.neoforge.client.ClientHooks.handleCameraTransforms(p_371635_, p_371384_, p_371250_, p_371248_); - p_371635_.translate(-0.5F, -0.5F, p_371782_); - this.renderItem(p_371318_, p_371250_, p_371635_, p_371946_, p_371752_, p_371508_, p_371384_, p_371718_); - p_371635_.popPose(); -@@ -173,7 +_,8 @@ - boolean p_364829_ - ) { - if (!p_363970_.isCustomRenderer() && (!p_361397_.is(Items.TRIDENT) || p_364829_)) { -- RenderType rendertype = ItemBlockRenderTypes.getRenderType(p_361397_); -+ for (var model : p_363970_.getRenderPasses(p_361397_)) { -+ for (var rendertype : model.getRenderTypes(p_361397_)) { - VertexConsumer vertexconsumer; - if (hasAnimatedTexture(p_361397_) && p_361397_.hasFoil()) { - PoseStack.Pose posestack$pose = p_360423_.last().copy(); -@@ -188,9 +_,11 @@ - vertexconsumer = getFoilBuffer(p_360415_, rendertype, true, p_361397_.hasFoil()); +@@ -132,7 +_,7 @@ + f3 = 1.0F; } -- this.renderModelLists(p_363970_, p_361397_, p_361265_, p_364771_, p_360423_, vertexconsumer); -+ this.renderModelLists(model, p_361397_, p_361265_, p_364771_, p_360423_, vertexconsumer); -+ } -+ } - } else { -- this.blockEntityRenderer.renderByItem(p_361397_, p_361627_, p_360423_, p_360415_, p_361265_, p_364771_); -+ net.neoforged.neoforge.client.extensions.common.IClientItemExtensions.of(p_361397_).getCustomRenderer().renderByItem(p_361397_, p_361627_, p_360423_, p_360415_, p_361265_, p_364771_); - } - } - -@@ -238,7 +_,7 @@ - float f1 = (float)ARGB.red(i) / 255.0F; - float f2 = (float)ARGB.green(i) / 255.0F; - float f3 = (float)ARGB.blue(i) / 255.0F; - p_115164_.putBulkData(posestack$pose, bakedquad, f1, f2, f3, f, p_115167_, p_115168_); -+ p_115164_.putBulkData(posestack$pose, bakedquad, f1, f2, f3, f, p_115167_, p_115168_, true); // Neo: pass readExistingColor=true ++ p_115164_.putBulkData(posestack$pose, bakedquad, f1, f2, f3, f, p_115167_, p_115168_, true); } } -@@ -294,5 +_,9 @@ - ClientLevel clientlevel = p_372942_ instanceof ClientLevel ? (ClientLevel)p_372942_ : null; - BakedModel bakedmodel = p_373094_.overrides().findOverride(p_372923_, clientlevel, p_373099_, p_372933_); - return bakedmodel == null ? p_373094_ : bakedmodel; -+ } -+ -+ public BlockEntityWithoutLevelRenderer getBlockEntityRenderer() { -+ return blockEntityRenderer; - } - } diff --git a/patches/net/minecraft/client/renderer/entity/LivingEntityRenderer.java.patch b/patches/net/minecraft/client/renderer/entity/LivingEntityRenderer.java.patch index 8322d59b63..8998226b08 100644 --- a/patches/net/minecraft/client/renderer/entity/LivingEntityRenderer.java.patch +++ b/patches/net/minecraft/client/renderer/entity/LivingEntityRenderer.java.patch @@ -1,6 +1,6 @@ --- a/net/minecraft/client/renderer/entity/LivingEntityRenderer.java +++ b/net/minecraft/client/renderer/entity/LivingEntityRenderer.java -@@ -69,6 +_,7 @@ +@@ -73,6 +_,7 @@ } public void render(S p_361886_, PoseStack p_115311_, MultiBufferSource p_115312_, int p_115313_) { @@ -8,7 +8,7 @@ p_115311_.pushPose(); if (p_361886_.hasPose(Pose.SLEEPING)) { Direction direction = p_361886_.bedOrientation; -@@ -104,6 +_,7 @@ +@@ -108,6 +_,7 @@ p_115311_.popPose(); super.render(p_361886_, p_115311_, p_115312_, p_115313_); @@ -16,7 +16,7 @@ } protected boolean shouldRenderLayers(S p_364697_) { -@@ -201,7 +_,7 @@ +@@ -205,7 +_,7 @@ protected boolean shouldShowName(T p_363517_, double p_365448_) { if (p_363517_.isDiscrete()) { float f = 32.0F; @@ -25,12 +25,12 @@ return false; } } -@@ -284,7 +_,7 @@ - - p_360515_.isFullyFrozen = p_362733_.isFullyFrozen(); - p_360515_.isBaby = p_362733_.isBaby(); -- p_360515_.isInWater = p_362733_.isInWater(); -+ p_360515_.isInWater = p_362733_.isInWater() || p_362733_.isInFluidType((fluidType, height) -> p_362733_.canSwimInFluidType(fluidType)); - p_360515_.isAutoSpinAttack = p_362733_.isAutoSpinAttack(); - p_360515_.hasRedOverlay = p_362733_.hurtTime > 0 || p_362733_.deathTime > 0; - ItemStack itemstack1 = p_362733_.getItemBySlot(EquipmentSlot.HEAD); +@@ -289,7 +_,7 @@ + label48: { + p_360515_.isFullyFrozen = p_362733_.isFullyFrozen(); + p_360515_.isBaby = p_362733_.isBaby(); +- p_360515_.isInWater = p_362733_.isInWater(); ++ p_360515_.isInWater = p_362733_.isInWater() || p_362733_.isInFluidType((fluidType, height) -> p_362733_.canSwimInFluidType(fluidType)); + p_360515_.isAutoSpinAttack = p_362733_.isAutoSpinAttack(); + p_360515_.hasRedOverlay = p_362733_.hurtTime > 0 || p_362733_.deathTime > 0; + ItemStack itemstack = p_362733_.getItemBySlot(EquipmentSlot.HEAD); diff --git a/patches/net/minecraft/client/renderer/entity/layers/EquipmentLayerRenderer.java.patch b/patches/net/minecraft/client/renderer/entity/layers/EquipmentLayerRenderer.java.patch index d9bc50306b..54ebe1b713 100644 --- a/patches/net/minecraft/client/renderer/entity/layers/EquipmentLayerRenderer.java.patch +++ b/patches/net/minecraft/client/renderer/entity/layers/EquipmentLayerRenderer.java.patch @@ -1,11 +1,11 @@ --- a/net/minecraft/client/renderer/entity/layers/EquipmentLayerRenderer.java +++ b/net/minecraft/client/renderer/entity/layers/EquipmentLayerRenderer.java -@@ -65,21 +_,26 @@ +@@ -66,21 +_,26 @@ int p_371309_, - @Nullable ResourceLocation p_371587_ + @Nullable ResourceLocation p_371639_ ) { -+ p_371731_ = getArmorModelHook(p_371670_, p_371854_, p_371731_); - List list = this.equipmentModels.get(p_371639_).getLayers(p_371854_); ++ p_371731_ = getArmorModelHook(p_371670_, p_387484_, p_371731_); + List list = this.equipmentAssets.get(p_387603_).getLayers(p_387484_); if (!list.isEmpty()) { - int i = p_371670_.is(ItemTags.DYEABLE) ? DyedItemColor.getOrDefault(p_371670_, 0) : 0; + net.neoforged.neoforge.client.extensions.common.IClientItemExtensions extensions = net.neoforged.neoforge.client.extensions.common.IClientItemExtensions.of(p_371670_); @@ -13,14 +13,14 @@ boolean flag = p_371670_.hasFoil(); + int idx = 0; - for (EquipmentModel.Layer equipmentmodel$layer : list) { -- int j = getColorForLayer(equipmentmodel$layer, i); -+ int j = extensions.getArmorLayerTintColor(p_371670_, equipmentmodel$layer, idx, i); + for (EquipmentClientInfo.Layer equipmentclientinfo$layer : list) { +- int j = getColorForLayer(equipmentclientinfo$layer, i); ++ int j = extensions.getArmorLayerTintColor(p_371670_, equipmentclientinfo$layer, idx, i); if (j != 0) { - ResourceLocation resourcelocation = equipmentmodel$layer.usePlayerTexture() && p_371587_ != null - ? p_371587_ - : this.layerTextureLookup.apply(new EquipmentLayerRenderer.LayerTextureKey(p_371854_, equipmentmodel$layer)); -+ resourcelocation = net.neoforged.neoforge.client.ClientHooks.getArmorTexture(p_371670_, p_371854_, equipmentmodel$layer, resourcelocation); + ResourceLocation resourcelocation = equipmentclientinfo$layer.usePlayerTexture() && p_371639_ != null + ? p_371639_ + : this.layerTextureLookup.apply(new EquipmentLayerRenderer.LayerTextureKey(p_387484_, equipmentclientinfo$layer)); ++ resourcelocation = net.neoforged.neoforge.client.ClientHooks.getArmorTexture(p_371670_, p_387484_, equipmentclientinfo$layer, resourcelocation); VertexConsumer vertexconsumer = ItemRenderer.getArmorFoilBuffer(p_371286_, RenderType.armorCutoutNoCull(resourcelocation), flag); p_371731_.renderToBuffer(p_371767_, vertexconsumer, p_371309_, OverlayTexture.NO_OVERLAY, j); flag = false; @@ -29,7 +29,7 @@ } ArmorTrim armortrim = p_371670_.get(DataComponents.TRIM); -@@ -99,6 +_,13 @@ +@@ -100,6 +_,13 @@ } else { return -1; } @@ -38,7 +38,7 @@ + /** + * Hook to allow item-sensitive armor model. for HumanoidArmorLayer. + */ -+ protected net.minecraft.client.model.Model getArmorModelHook(ItemStack itemStack, EquipmentModel.LayerType layerType, Model model) { ++ protected net.minecraft.client.model.Model getArmorModelHook(ItemStack itemStack, EquipmentClientInfo.LayerType layerType, Model model) { + return net.neoforged.neoforge.client.ClientHooks.getArmorModel(itemStack, layerType, model); } diff --git a/patches/net/minecraft/client/renderer/entity/player/PlayerRenderer.java.patch b/patches/net/minecraft/client/renderer/entity/player/PlayerRenderer.java.patch index d2fb4f4901..a36dc98a5e 100644 --- a/patches/net/minecraft/client/renderer/entity/player/PlayerRenderer.java.patch +++ b/patches/net/minecraft/client/renderer/entity/player/PlayerRenderer.java.patch @@ -1,16 +1,39 @@ --- a/net/minecraft/client/renderer/entity/player/PlayerRenderer.java +++ b/net/minecraft/client/renderer/entity/player/PlayerRenderer.java -@@ -127,6 +_,9 @@ - } else if (!p_363098_.swinging && p_364742_.holdsChargedCrossbow) { - return HumanoidModel.ArmPose.CROSSBOW_HOLD; - } -+ if (p_364742_.customArmPose != null) { -+ return p_364742_.customArmPose; -+ } - - return HumanoidModel.ArmPose.ITEM; +@@ -86,8 +_,10 @@ + private static HumanoidModel.ArmPose getArmPose(AbstractClientPlayer p_386861_, HumanoidArm p_373044_) { + ItemStack itemstack = p_386861_.getItemInHand(InteractionHand.MAIN_HAND); + ItemStack itemstack1 = p_386861_.getItemInHand(InteractionHand.OFF_HAND); +- HumanoidModel.ArmPose humanoidmodel$armpose = getArmPose(p_386861_, itemstack, InteractionHand.MAIN_HAND); +- HumanoidModel.ArmPose humanoidmodel$armpose1 = getArmPose(p_386861_, itemstack1, InteractionHand.OFF_HAND); ++ var extensionsMainHand = net.neoforged.neoforge.client.extensions.common.IClientItemExtensions.of(itemstack); ++ var extensionsOffHand = net.neoforged.neoforge.client.extensions.common.IClientItemExtensions.of(itemstack1); ++ HumanoidModel.ArmPose humanoidmodel$armpose = getArmPose(p_386861_, itemstack, InteractionHand.MAIN_HAND, extensionsMainHand.getArmPose(p_386861_, InteractionHand.MAIN_HAND, itemstack)); ++ HumanoidModel.ArmPose humanoidmodel$armpose1 = getArmPose(p_386861_, itemstack1, InteractionHand.OFF_HAND, extensionsOffHand.getArmPose(p_386861_, InteractionHand.OFF_HAND, itemstack1)); + if (humanoidmodel$armpose.isTwoHanded()) { + humanoidmodel$armpose1 = itemstack1.isEmpty() ? HumanoidModel.ArmPose.EMPTY : HumanoidModel.ArmPose.ITEM; } -@@ -141,6 +_,13 @@ +@@ -95,7 +_,19 @@ + return p_386861_.getMainArm() == p_373044_ ? humanoidmodel$armpose : humanoidmodel$armpose1; + } + ++ /** ++ * @deprecated Neo: use {@link #getArmPose(Player, ItemStack, InteractionHand, HumanoidModel.ArmPose)} instead ++ */ ++ @Deprecated + private static HumanoidModel.ArmPose getArmPose(Player p_386775_, ItemStack p_388403_, InteractionHand p_117796_) { ++ return getArmPose(p_386775_, p_388403_, p_117796_, null); ++ } ++ ++ private static HumanoidModel.ArmPose getArmPose(Player p_386775_, ItemStack p_388403_, InteractionHand p_117796_, @Nullable HumanoidModel.ArmPose pose) { ++ if (pose != null) { ++ return pose; ++ } ++ + if (p_388403_.isEmpty()) { + return HumanoidModel.ArmPose.EMPTY; + } else { +@@ -145,6 +_,13 @@ p_117799_.scale(0.9375F, 0.9375F, 0.9375F); } @@ -24,17 +47,7 @@ protected void renderNameTag(PlayerRenderState p_363185_, Component p_117809_, PoseStack p_117810_, MultiBufferSource p_117811_, int p_117812_) { p_117810_.pushPose(); if (p_363185_.scoreText != null) { -@@ -217,7 +_,8 @@ - ItemStack itemstack = p_364516_.getItemInHand(p_364304_); - p_360817_.isEmpty = itemstack.isEmpty(); - p_360817_.useAnimation = !itemstack.isEmpty() ? itemstack.getUseAnimation() : null; -- p_360817_.holdsChargedCrossbow = itemstack.is(Items.CROSSBOW) && CrossbowItem.isCharged(itemstack); -+ p_360817_.holdsChargedCrossbow = itemstack.getItem() instanceof CrossbowItem && CrossbowItem.isCharged(itemstack); -+ p_360817_.customArmPose = net.neoforged.neoforge.client.extensions.common.IClientItemExtensions.of(itemstack).getArmPose(p_364516_, p_364304_, itemstack); - } - - private static void extractCapeState(AbstractClientPlayer p_364691_, PlayerRenderState p_360814_, float p_364460_) { -@@ -247,11 +_,29 @@ +@@ -251,11 +_,29 @@ : null; } diff --git a/patches/net/minecraft/client/renderer/entity/state/EntityRenderState.java.patch b/patches/net/minecraft/client/renderer/entity/state/EntityRenderState.java.patch index a9fd80707c..7614a11b9f 100644 --- a/patches/net/minecraft/client/renderer/entity/state/EntityRenderState.java.patch +++ b/patches/net/minecraft/client/renderer/entity/state/EntityRenderState.java.patch @@ -1,5 +1,14 @@ --- a/net/minecraft/client/renderer/entity/state/EntityRenderState.java +++ b/net/minecraft/client/renderer/entity/state/EntityRenderState.java +@@ -7,7 +_,7 @@ + import net.neoforged.api.distmarker.OnlyIn; + + @OnlyIn(Dist.CLIENT) +-public class EntityRenderState { ++public class EntityRenderState extends net.neoforged.neoforge.client.renderstate.BaseRenderState { + public double x; + public double y; + public double z; @@ -27,6 +_,7 @@ public Vec3 nameTagAttachment; @Nullable diff --git a/patches/net/minecraft/client/renderer/entity/state/ItemClusterRenderState.java.patch b/patches/net/minecraft/client/renderer/entity/state/ItemClusterRenderState.java.patch new file mode 100644 index 0000000000..1de5b0c358 --- /dev/null +++ b/patches/net/minecraft/client/renderer/entity/state/ItemClusterRenderState.java.patch @@ -0,0 +1,10 @@ +--- a/net/minecraft/client/renderer/entity/state/ItemClusterRenderState.java ++++ b/net/minecraft/client/renderer/entity/state/ItemClusterRenderState.java +@@ -14,6 +_,7 @@ + public final ItemStackRenderState item = new ItemStackRenderState(); + public int count; + public int seed; ++ public boolean shouldSpread; + + public void extractItemGroupRenderState(Entity p_386526_, ItemStack p_386486_, ItemModelResolver p_387036_) { + p_387036_.updateForNonLiving(this.item, p_386486_, ItemDisplayContext.GROUND, p_386526_); diff --git a/patches/net/minecraft/client/renderer/entity/state/ItemEntityRenderState.java.patch b/patches/net/minecraft/client/renderer/entity/state/ItemEntityRenderState.java.patch new file mode 100644 index 0000000000..7b3eb62dce --- /dev/null +++ b/patches/net/minecraft/client/renderer/entity/state/ItemEntityRenderState.java.patch @@ -0,0 +1,8 @@ +--- a/net/minecraft/client/renderer/entity/state/ItemEntityRenderState.java ++++ b/net/minecraft/client/renderer/entity/state/ItemEntityRenderState.java +@@ -6,4 +_,5 @@ + @OnlyIn(Dist.CLIENT) + public class ItemEntityRenderState extends ItemClusterRenderState { + public float bobOffset; ++ public boolean shouldBob; + } diff --git a/patches/net/minecraft/client/renderer/entity/state/PlayerRenderState.java.patch b/patches/net/minecraft/client/renderer/entity/state/PlayerRenderState.java.patch deleted file mode 100644 index 870f5bd850..0000000000 --- a/patches/net/minecraft/client/renderer/entity/state/PlayerRenderState.java.patch +++ /dev/null @@ -1,10 +0,0 @@ ---- a/net/minecraft/client/renderer/entity/state/PlayerRenderState.java -+++ b/net/minecraft/client/renderer/entity/state/PlayerRenderState.java -@@ -52,5 +_,7 @@ - @Nullable - public ItemUseAnimation useAnimation; - public boolean holdsChargedCrossbow; -+ @Nullable -+ public net.minecraft.client.model.HumanoidModel.ArmPose customArmPose; - } - } diff --git a/patches/net/minecraft/client/renderer/item/BlockModelWrapper.java.patch b/patches/net/minecraft/client/renderer/item/BlockModelWrapper.java.patch new file mode 100644 index 0000000000..0ffe05b0dc --- /dev/null +++ b/patches/net/minecraft/client/renderer/item/BlockModelWrapper.java.patch @@ -0,0 +1,44 @@ +--- a/net/minecraft/client/renderer/item/BlockModelWrapper.java ++++ b/net/minecraft/client/renderer/item/BlockModelWrapper.java +@@ -41,22 +_,25 @@ + @Nullable LivingEntity p_387263_, + int p_388300_ + ) { +- ItemStackRenderState.LayerRenderState itemstackrenderstate$layerrenderstate = p_386488_.newLayer(); +- if (p_386443_.hasFoil()) { +- itemstackrenderstate$layerrenderstate.setFoilType( +- hasSpecialAnimatedTexture(p_386443_) ? ItemStackRenderState.FoilType.SPECIAL : ItemStackRenderState.FoilType.STANDARD +- ); +- } +- +- int i = this.tints.size(); +- int[] aint = itemstackrenderstate$layerrenderstate.prepareTintLayers(i); +- +- for (int j = 0; j < i; j++) { +- aint[j] = this.tints.get(j).calculate(p_386443_, p_387522_, p_387263_); +- } +- +- RenderType rendertype = ItemBlockRenderTypes.getRenderType(p_386443_); +- itemstackrenderstate$layerrenderstate.setupBlockModel(this.model, rendertype); ++ final int[] tints = new int[this.tints.size()]; ++ for (int j = 0; j < tints.length; j++) { ++ tints[j] = this.tints.get(j).calculate(p_386443_, p_387522_, p_387263_); ++ } ++ final ItemStackRenderState.FoilType foilType = hasSpecialAnimatedTexture(p_386443_) ? ItemStackRenderState.FoilType.SPECIAL : ItemStackRenderState.FoilType.STANDARD; ++ ++ this.model.getRenderPasses(p_386443_).forEach(pass -> { ++ ItemStackRenderState.LayerRenderState itemstackrenderstate$layerrenderstate = p_386488_.newLayer(); ++ if (p_386443_.hasFoil()) { ++ itemstackrenderstate$layerrenderstate.setFoilType( ++ foilType ++ ); ++ } ++ ++ int[] aint = itemstackrenderstate$layerrenderstate.prepareTintLayers(tints.length); ++ System.arraycopy(tints, 0, aint, 0, tints.length); ++ ++ itemstackrenderstate$layerrenderstate.setupBlockModel(this.model, pass.getRenderType(p_386443_)); ++ }); + } + + private static boolean hasSpecialAnimatedTexture(ItemStack p_387217_) { diff --git a/patches/net/minecraft/client/renderer/item/ItemModels.java.patch b/patches/net/minecraft/client/renderer/item/ItemModels.java.patch new file mode 100644 index 0000000000..7e2649b4c9 --- /dev/null +++ b/patches/net/minecraft/client/renderer/item/ItemModels.java.patch @@ -0,0 +1,10 @@ +--- a/net/minecraft/client/renderer/item/ItemModels.java ++++ b/net/minecraft/client/renderer/item/ItemModels.java +@@ -21,5 +_,7 @@ + ID_MAPPER.put(ResourceLocation.withDefaultNamespace("bundle/selected_item"), BundleSelectedItemSpecialRenderer.Unbaked.MAP_CODEC); + ID_MAPPER.put(ResourceLocation.withDefaultNamespace("select"), SelectItemModel.Unbaked.MAP_CODEC); + ID_MAPPER.put(ResourceLocation.withDefaultNamespace("condition"), ConditionalItemModel.Unbaked.MAP_CODEC); ++ ++ net.neoforged.fml.ModLoader.postEvent(new net.neoforged.neoforge.client.event.RegisterItemModelsEvent(ID_MAPPER)); + } + } diff --git a/patches/net/minecraft/client/renderer/item/ItemProperties.java.patch b/patches/net/minecraft/client/renderer/item/ItemProperties.java.patch deleted file mode 100644 index 95f1563e80..0000000000 --- a/patches/net/minecraft/client/renderer/item/ItemProperties.java.patch +++ /dev/null @@ -1,23 +0,0 @@ ---- a/net/minecraft/client/renderer/item/ItemProperties.java -+++ b/net/minecraft/client/renderer/item/ItemProperties.java -@@ -42,6 +_,9 @@ - private static final Map> PROPERTIES = Maps.newHashMap(); - - private static ClampedItemPropertyFunction registerGeneric(ResourceLocation p_174582_, ClampedItemPropertyFunction p_174583_) { -+ return (ClampedItemPropertyFunction) registerGeneric(p_174582_, (ItemPropertyFunction) p_174583_); -+ } -+ public static ItemPropertyFunction registerGeneric(ResourceLocation p_174582_, ItemPropertyFunction p_174583_) { - GENERIC_PROPERTIES.put(p_174582_, p_174583_); - return p_174583_; - } -@@ -51,6 +_,10 @@ - } - - private static void register(Item p_174571_, ResourceLocation p_174572_, ClampedItemPropertyFunction p_174573_) { -+ register(p_174571_, p_174572_, (ItemPropertyFunction) p_174573_); -+ } -+ -+ public static void register(Item p_174571_, ResourceLocation p_174572_, ItemPropertyFunction p_174573_) { - PROPERTIES.computeIfAbsent(p_174571_, p_117828_ -> Maps.newHashMap()).put(p_174572_, p_174573_); - } - diff --git a/patches/net/minecraft/client/renderer/item/ItemStackRenderState.java.patch b/patches/net/minecraft/client/renderer/item/ItemStackRenderState.java.patch new file mode 100644 index 0000000000..9f4cd214c1 --- /dev/null +++ b/patches/net/minecraft/client/renderer/item/ItemStackRenderState.java.patch @@ -0,0 +1,21 @@ +--- a/net/minecraft/client/renderer/item/ItemStackRenderState.java ++++ b/net/minecraft/client/renderer/item/ItemStackRenderState.java +@@ -72,7 +_,7 @@ + return null; + } else { + BakedModel bakedmodel = this.layers[p_387539_.nextInt(this.activeLayerCount)].model; +- return bakedmodel == null ? null : bakedmodel.getParticleIcon(); ++ return bakedmodel == null ? null : bakedmodel.getParticleIcon(net.neoforged.neoforge.client.model.data.ModelData.EMPTY); + } + } + +@@ -149,6 +_,9 @@ + + void render(PoseStack p_387607_, MultiBufferSource p_386763_, int p_387589_, int p_388775_) { + p_387607_.pushPose(); ++ if (model != null) ++ net.neoforged.neoforge.client.ClientHooks.handleCameraTransforms(p_387607_, model, displayContext, ItemStackRenderState.this.isLeftHand); ++ else + this.transform().apply(ItemStackRenderState.this.isLeftHand, p_387607_); + p_387607_.translate(-0.5F, -0.5F, -0.5F); + if (this.specialRenderer != null) { diff --git a/patches/net/minecraft/client/renderer/item/properties/conditional/ConditionalItemModelProperties.java.patch b/patches/net/minecraft/client/renderer/item/properties/conditional/ConditionalItemModelProperties.java.patch new file mode 100644 index 0000000000..080464ae1f --- /dev/null +++ b/patches/net/minecraft/client/renderer/item/properties/conditional/ConditionalItemModelProperties.java.patch @@ -0,0 +1,10 @@ +--- a/net/minecraft/client/renderer/item/properties/conditional/ConditionalItemModelProperties.java ++++ b/net/minecraft/client/renderer/item/properties/conditional/ConditionalItemModelProperties.java +@@ -25,5 +_,7 @@ + ID_MAPPER.put(ResourceLocation.withDefaultNamespace("extended_view"), ExtendedView.MAP_CODEC); + ID_MAPPER.put(ResourceLocation.withDefaultNamespace("keybind_down"), IsKeybindDown.MAP_CODEC); + ID_MAPPER.put(ResourceLocation.withDefaultNamespace("view_entity"), IsViewEntity.MAP_CODEC); ++ ++ net.neoforged.fml.ModLoader.postEvent(new net.neoforged.neoforge.client.event.RegisterConditionalItemModelPropertyEvent(ID_MAPPER)); + } + } diff --git a/patches/net/minecraft/client/renderer/item/properties/numeric/RangeSelectItemModelProperties.java.patch b/patches/net/minecraft/client/renderer/item/properties/numeric/RangeSelectItemModelProperties.java.patch new file mode 100644 index 0000000000..cd56d12d98 --- /dev/null +++ b/patches/net/minecraft/client/renderer/item/properties/numeric/RangeSelectItemModelProperties.java.patch @@ -0,0 +1,10 @@ +--- a/net/minecraft/client/renderer/item/properties/numeric/RangeSelectItemModelProperties.java ++++ b/net/minecraft/client/renderer/item/properties/numeric/RangeSelectItemModelProperties.java +@@ -23,5 +_,7 @@ + ID_MAPPER.put(ResourceLocation.withDefaultNamespace("use_cycle"), UseCycle.MAP_CODEC); + ID_MAPPER.put(ResourceLocation.withDefaultNamespace("use_duration"), UseDuration.MAP_CODEC); + ID_MAPPER.put(ResourceLocation.withDefaultNamespace("count"), Count.MAP_CODEC); ++ ++ net.neoforged.fml.ModLoader.postEvent(new net.neoforged.neoforge.client.event.RegisterRangeSelectItemModelPropertyEvent(ID_MAPPER)); + } + } diff --git a/patches/net/minecraft/client/renderer/item/properties/select/SelectItemModelProperties.java.patch b/patches/net/minecraft/client/renderer/item/properties/select/SelectItemModelProperties.java.patch new file mode 100644 index 0000000000..7ba6478101 --- /dev/null +++ b/patches/net/minecraft/client/renderer/item/properties/select/SelectItemModelProperties.java.patch @@ -0,0 +1,10 @@ +--- a/net/minecraft/client/renderer/item/properties/select/SelectItemModelProperties.java ++++ b/net/minecraft/client/renderer/item/properties/select/SelectItemModelProperties.java +@@ -21,5 +_,7 @@ + ID_MAPPER.put(ResourceLocation.withDefaultNamespace("local_time"), LocalTime.TYPE); + ID_MAPPER.put(ResourceLocation.withDefaultNamespace("context_entity_type"), ContextEntityType.TYPE); + ID_MAPPER.put(ResourceLocation.withDefaultNamespace("context_dimension"), ContextDimension.TYPE); ++ ++ net.neoforged.fml.ModLoader.postEvent(new net.neoforged.neoforge.client.event.RegisterSelectItemModelPropertyEvent(ID_MAPPER)); + } + } diff --git a/patches/net/minecraft/client/renderer/special/SpecialModelRenderers.java.patch b/patches/net/minecraft/client/renderer/special/SpecialModelRenderers.java.patch new file mode 100644 index 0000000000..ea6de530f0 --- /dev/null +++ b/patches/net/minecraft/client/renderer/special/SpecialModelRenderers.java.patch @@ -0,0 +1,11 @@ +--- a/net/minecraft/client/renderer/special/SpecialModelRenderers.java ++++ b/net/minecraft/client/renderer/special/SpecialModelRenderers.java +@@ -171,6 +_,8 @@ + ID_MAPPER.put(ResourceLocation.withDefaultNamespace("decorated_pot"), DecoratedPotSpecialRenderer.Unbaked.MAP_CODEC); + ID_MAPPER.put(ResourceLocation.withDefaultNamespace("standing_sign"), StandingSignSpecialRenderer.Unbaked.MAP_CODEC); + ID_MAPPER.put(ResourceLocation.withDefaultNamespace("hanging_sign"), HangingSignSpecialRenderer.Unbaked.MAP_CODEC); ++ ++ net.neoforged.fml.ModLoader.postEvent(new net.neoforged.neoforge.client.event.RegisterSpecialModelRendererEvent(ID_MAPPER)); + } + + public static Map> createBlockRenderers(EntityModelSet p_387779_) { diff --git a/patches/net/minecraft/client/renderer/state/MapRenderState.java.patch b/patches/net/minecraft/client/renderer/state/MapRenderState.java.patch index 2ac034ae3d..cadb13bc87 100644 --- a/patches/net/minecraft/client/renderer/state/MapRenderState.java.patch +++ b/patches/net/minecraft/client/renderer/state/MapRenderState.java.patch @@ -1,9 +1,18 @@ --- a/net/minecraft/client/renderer/state/MapRenderState.java +++ b/net/minecraft/client/renderer/state/MapRenderState.java -@@ -17,6 +_,7 @@ +@@ -10,13 +_,14 @@ + import net.neoforged.api.distmarker.OnlyIn; + + @OnlyIn(Dist.CLIENT) +-public class MapRenderState { ++public class MapRenderState extends net.neoforged.neoforge.client.renderstate.BaseRenderState { + @Nullable + public ResourceLocation texture; + public final List decorations = new ArrayList<>(); @OnlyIn(Dist.CLIENT) - public static class MapDecorationRenderState { +- public static class MapDecorationRenderState { ++ public static class MapDecorationRenderState extends net.neoforged.neoforge.client.renderstate.BaseRenderState { + public net.minecraft.core.Holder type; @Nullable public TextureAtlasSprite atlasSprite; diff --git a/patches/net/minecraft/client/renderer/texture/AbstractTexture.java.patch b/patches/net/minecraft/client/renderer/texture/AbstractTexture.java.patch index ce14e13dcf..ad9784c4ea 100644 --- a/patches/net/minecraft/client/renderer/texture/AbstractTexture.java.patch +++ b/patches/net/minecraft/client/renderer/texture/AbstractTexture.java.patch @@ -1,11 +1,15 @@ --- a/net/minecraft/client/renderer/texture/AbstractTexture.java +++ b/net/minecraft/client/renderer/texture/AbstractTexture.java -@@ -15,9 +_,13 @@ +@@ -12,6 +_,8 @@ public static final int NOT_ASSIGNED = -1; protected int id = -1; protected boolean defaultBlur; + protected boolean blur; + protected boolean mipmap; + private int wrapS = 10497; + private int wrapT = 10497; + private int minFilter = 9986; +@@ -51,6 +_,8 @@ public void setFilter(boolean p_117961_, boolean p_117962_) { RenderSystem.assertOnRenderThreadOrInit(); @@ -14,10 +18,10 @@ int i; int j; if (p_117961_) { -@@ -31,6 +_,20 @@ - this.bind(); - GlStateManager._texParameter(3553, 10241, i); - GlStateManager._texParameter(3553, 10240, j); +@@ -75,6 +_,20 @@ + this.magFilter = j; + } + } + } + + // FORGE: This seems to have been stripped out, but we need it diff --git a/patches/net/minecraft/client/renderer/texture/SpriteContents.java.patch b/patches/net/minecraft/client/renderer/texture/SpriteContents.java.patch index 115bee7e58..5505e4800a 100644 --- a/patches/net/minecraft/client/renderer/texture/SpriteContents.java.patch +++ b/patches/net/minecraft/client/renderer/texture/SpriteContents.java.patch @@ -1,6 +1,6 @@ --- a/net/minecraft/client/renderer/texture/SpriteContents.java +++ b/net/minecraft/client/renderer/texture/SpriteContents.java -@@ -46,6 +_,10 @@ +@@ -47,6 +_,10 @@ this.byMipLevel = new NativeImage[]{this.originalImage}; } @@ -11,18 +11,16 @@ public void increaseMipLevel(int p_248864_) { try { this.byMipLevel = MipmapGenerator.generateMipLevels(this.byMipLevel, p_248864_); -@@ -120,6 +_,10 @@ +@@ -130,6 +_,8 @@ void upload(int p_248895_, int p_250245_, int p_250458_, int p_251337_, NativeImage[] p_248825_) { for (int i = 0; i < this.byMipLevel.length; i++) { -+ // Forge: Skip uploading if the texture would be made invalid by mip level -+ if ((this.width >> i) <= 0 || (this.height >> i) <= 0) -+ break; -+ - p_248825_[i] - .upload(i, p_248895_ >> i, p_250245_ >> i, p_250458_ >> i, p_251337_ >> i, this.width >> i, this.height >> i, this.byMipLevel.length > 1, false); ++ // NeoForge: Skip uploading if the texture would be made invalid by mip level ++ if ((this.width >> i) <= 0 || (this.height >> i) <= 0) break; + p_248825_[i].upload(i, p_248895_ >> i, p_250245_ >> i, p_250458_ >> i, p_251337_ >> i, this.width >> i, this.height >> i, false); } -@@ -242,7 +_,8 @@ + } +@@ -244,7 +_,8 @@ for (int i = 0; i < this.activeFrame.length; i++) { int j = SpriteContents.this.width >> i; int k = SpriteContents.this.height >> i; @@ -32,7 +30,7 @@ } } -@@ -257,6 +_,9 @@ +@@ -259,6 +_,9 @@ for (int k = 0; k < this.activeFrame.length; k++) { int l = SpriteContents.this.width >> k; int i1 = SpriteContents.this.height >> k; diff --git a/patches/net/minecraft/client/renderer/texture/TextureAtlas.java.patch b/patches/net/minecraft/client/renderer/texture/TextureAtlas.java.patch index ffc654c26a..f0b8aec905 100644 --- a/patches/net/minecraft/client/renderer/texture/TextureAtlas.java.patch +++ b/patches/net/minecraft/client/renderer/texture/TextureAtlas.java.patch @@ -1,6 +1,6 @@ --- a/net/minecraft/client/renderer/texture/TextureAtlas.java +++ b/net/minecraft/client/renderer/texture/TextureAtlas.java -@@ -87,6 +_,8 @@ +@@ -82,6 +_,8 @@ this.sprites = List.copyOf(list); this.animatedTextures = List.copyOf(list1); } @@ -9,10 +9,10 @@ } @Override -@@ -168,5 +_,9 @@ +@@ -159,5 +_,9 @@ - public void updateFilter(SpriteLoader.Preparations p_251993_) { - this.setFilter(false, p_251993_.mipLevel() > 0); + int getHeight() { + return this.height; + } + + public Map getTextures() { diff --git a/patches/net/minecraft/client/renderer/texture/atlas/SpriteResourceLoader.java.patch b/patches/net/minecraft/client/renderer/texture/atlas/SpriteResourceLoader.java.patch index 9051e59ca1..c281c0ab4b 100644 --- a/patches/net/minecraft/client/renderer/texture/atlas/SpriteResourceLoader.java.patch +++ b/patches/net/minecraft/client/renderer/texture/atlas/SpriteResourceLoader.java.patch @@ -1,24 +1,21 @@ --- a/net/minecraft/client/renderer/texture/atlas/SpriteResourceLoader.java +++ b/net/minecraft/client/renderer/texture/atlas/SpriteResourceLoader.java -@@ -24,7 +_,7 @@ +@@ -25,7 +_,7 @@ Logger LOGGER = LogUtils.getLogger(); - static SpriteResourceLoader create(Collection> p_296204_) { -- return (p_293680_, p_293681_) -> { -+ return (p_293680_, p_293681_, constructor) -> { + static SpriteResourceLoader create(Collection> p_296204_) { +- return (p_389362_, p_389363_) -> { ++ return (p_389362_, p_389363_, constructor) -> { ResourceMetadata resourcemetadata; try { - resourcemetadata = p_293681_.metadata().copySections(p_296204_); -@@ -45,7 +_,7 @@ - .orElse(AnimationMetadataSection.EMPTY); - FrameSize framesize = animationmetadatasection.calculateFrameSize(nativeimage.getWidth(), nativeimage.getHeight()); - if (Mth.isMultipleOf(nativeimage.getWidth(), framesize.width()) && Mth.isMultipleOf(nativeimage.getHeight(), framesize.height())) { -- return new SpriteContents(p_293680_, framesize, nativeimage, resourcemetadata); -+ return constructor.create(p_293680_, framesize, nativeimage, resourcemetadata); - } else { - LOGGER.error( - "Image {} size {},{} is not multiple of frame size {},{}", -@@ -62,5 +_,10 @@ + resourcemetadata = p_389363_.metadata().copySections(p_296204_); +@@ -62,10 +_,15 @@ + framesize = new FrameSize(nativeimage.getWidth(), nativeimage.getHeight()); + } + +- return new SpriteContents(p_389362_, framesize, nativeimage, resourcemetadata); ++ return constructor.create(p_389362_, framesize, nativeimage, resourcemetadata); + }; } @Nullable diff --git a/patches/net/minecraft/client/resources/model/BakedModel.java.patch b/patches/net/minecraft/client/resources/model/BakedModel.java.patch index 15490942f9..e2ff981485 100644 --- a/patches/net/minecraft/client/resources/model/BakedModel.java.patch +++ b/patches/net/minecraft/client/resources/model/BakedModel.java.patch @@ -1,6 +1,6 @@ --- a/net/minecraft/client/resources/model/BakedModel.java +++ b/net/minecraft/client/resources/model/BakedModel.java -@@ -13,7 +_,9 @@ +@@ -12,7 +_,9 @@ import net.neoforged.api.distmarker.OnlyIn; @OnlyIn(Dist.CLIENT) @@ -11,18 +11,15 @@ List getQuads(@Nullable BlockState p_235039_, @Nullable Direction p_235040_, RandomSource p_235041_); boolean useAmbientOcclusion(); -@@ -24,9 +_,13 @@ +@@ -21,7 +_,11 @@ - boolean isCustomRenderer(); + boolean usesBlockLight(); + /**@deprecated Forge: Use {@link #getParticleIcon(net.neoforged.neoforge.client.model.data.ModelData)}*/ + @Deprecated TextureAtlasSprite getParticleIcon(); -- ItemTransforms getTransforms(); + /**@deprecated Forge: Use {@link #applyTransform(net.minecraft.world.item.ItemDisplayContext, com.mojang.blaze3d.vertex.PoseStack, boolean)} instead */ + @Deprecated -+ default ItemTransforms getTransforms() { return ItemTransforms.NO_TRANSFORMS; } - - default BakedOverrides overrides() { - return BakedOverrides.EMPTY; + ItemTransforms getTransforms(); + } diff --git a/patches/net/minecraft/client/resources/model/DelegateBakedModel.java.patch b/patches/net/minecraft/client/resources/model/DelegateBakedModel.java.patch index 5c8eacbbe9..572e01296c 100644 --- a/patches/net/minecraft/client/resources/model/DelegateBakedModel.java.patch +++ b/patches/net/minecraft/client/resources/model/DelegateBakedModel.java.patch @@ -28,7 +28,7 @@ public boolean isGui3d() { return this.parent.isGui3d(); } -@@ -45,12 +_,44 @@ +@@ -40,12 +_,44 @@ } @Override @@ -64,8 +64,8 @@ + } + + @Override -+ public List getRenderTypes(net.minecraft.world.item.ItemStack itemStack) { -+ return this.parent.getRenderTypes(itemStack); ++ public net.minecraft.client.renderer.RenderType getRenderType(net.minecraft.world.item.ItemStack itemStack) { ++ return this.parent.getRenderType(itemStack); + } + + @Override diff --git a/patches/net/minecraft/client/resources/model/ItemModel.java.patch b/patches/net/minecraft/client/resources/model/ItemModel.java.patch deleted file mode 100644 index 10d96b4e0a..0000000000 --- a/patches/net/minecraft/client/resources/model/ItemModel.java.patch +++ /dev/null @@ -1,16 +0,0 @@ ---- a/net/minecraft/client/resources/model/ItemModel.java -+++ b/net/minecraft/client/resources/model/ItemModel.java -@@ -29,11 +_,11 @@ - - @Override - public BakedModel bake(ModelBaker p_371426_, Function p_371750_, ModelState p_371674_) { -- BakedModel bakedmodel = p_371426_.bake(this.id, p_371674_); -+ BakedModel bakedmodel = p_371426_.bake(this.id, p_371674_, p_371750_); - if (this.overrides.isEmpty()) { - return bakedmodel; - } else { -- BakedOverrides bakedoverrides = new BakedOverrides(p_371426_, this.overrides); -+ BakedOverrides bakedoverrides = new BakedOverrides(p_371426_, this.overrides, p_371750_); - return new ItemModel.BakedModelWithOverrides(bakedmodel, bakedoverrides); - } - } diff --git a/patches/net/minecraft/client/resources/model/ModelBaker.java.patch b/patches/net/minecraft/client/resources/model/ModelBaker.java.patch index 36016793f0..db7b52b0bf 100644 --- a/patches/net/minecraft/client/resources/model/ModelBaker.java.patch +++ b/patches/net/minecraft/client/resources/model/ModelBaker.java.patch @@ -1,14 +1,11 @@ --- a/net/minecraft/client/resources/model/ModelBaker.java +++ b/net/minecraft/client/resources/model/ModelBaker.java -@@ -5,6 +_,10 @@ +@@ -6,7 +_,7 @@ import net.neoforged.api.distmarker.OnlyIn; @OnlyIn(Dist.CLIENT) -public interface ModelBaker { +public interface ModelBaker extends net.neoforged.neoforge.client.extensions.IModelBakerExtension { -+ /** -+ * @deprecated Forge: Use {@link #bake(ResourceLocation, ModelState, java.util.function.Function)} instead. -+ */ -+ @Deprecated BakedModel bake(ResourceLocation p_250776_, ModelState p_251280_); - } + + SpriteGetter sprites(); diff --git a/patches/net/minecraft/client/resources/model/ModelBakery.java.patch b/patches/net/minecraft/client/resources/model/ModelBakery.java.patch index ed116d3ffe..694309384e 100644 --- a/patches/net/minecraft/client/resources/model/ModelBakery.java.patch +++ b/patches/net/minecraft/client/resources/model/ModelBakery.java.patch @@ -1,52 +1,84 @@ --- a/net/minecraft/client/resources/model/ModelBakery.java +++ b/net/minecraft/client/resources/model/ModelBakery.java -@@ -94,25 +_,46 @@ - } - - @Override -+ @org.jetbrains.annotations.Nullable -+ public UnbakedModel getTopLevelModel(ModelResourceLocation location) { -+ return topModels.get(location); -+ } -+ -+ @Override -+ public Function getModelTextureGetter() { -+ return this.modelTextureGetter; -+ } +@@ -46,19 +_,36 @@ + private final Map clientInfos; + final Map unbakedPlainModels; + final UnbakedModel missingModel; ++ private final Map standaloneModels; + -+ @Override - public BakedModel bake(ResourceLocation p_252176_, ModelState p_249765_) { -+ return bake(p_252176_, p_249765_, this.modelTextureGetter); -+ } -+ -+ @Override -+ public BakedModel bake(ResourceLocation p_252176_, ModelState p_249765_, Function sprites) { - ModelBakery.BakedCacheKey modelbakery$bakedcachekey = new ModelBakery.BakedCacheKey(p_252176_, p_249765_.getRotation(), p_249765_.isUvLocked()); - BakedModel bakedmodel = ModelBakery.this.bakedCache.get(modelbakery$bakedcachekey); - if (bakedmodel != null) { - return bakedmodel; - } else { - UnbakedModel unbakedmodel = this.getModel(p_252176_); -- BakedModel bakedmodel1 = this.bakeUncached(unbakedmodel, p_249765_); -+ BakedModel bakedmodel1 = this.bakeUncached(unbakedmodel, p_249765_, sprites); - ModelBakery.this.bakedCache.put(modelbakery$bakedcachekey, bakedmodel1); - return bakedmodel1; - } - } ++ /** ++ * @deprecated Neo: use {@link #ModelBakery(EntityModelSet, Map, Map, Map, UnbakedModel, Map)} ModelBakery instead} ++ */ ++ @Deprecated ++ public ModelBakery( ++ EntityModelSet p_388903_, ++ Map p_251087_, ++ Map p_250416_, ++ Map p_388404_, ++ UnbakedModel p_360944_ ++ ) { ++ this(p_388903_, p_251087_, p_250416_, p_388404_, p_360944_, Map.of()); ++ } - BakedModel bakeUncached(UnbakedModel p_352386_, ModelState p_352194_) { -+ return bakeUncached(p_352386_, p_352194_, this.modelTextureGetter); -+ } -+ -+ @Override -+ public BakedModel bakeUncached(UnbakedModel p_352386_, ModelState p_352194_, Function sprites) { - if (p_352386_ instanceof BlockModel blockmodel && blockmodel.getRootModel() == SpecialModels.GENERATED_MARKER) { -- return ModelBakery.ITEM_MODEL_GENERATOR.generateBlockModel(this.modelTextureGetter, blockmodel).bake(this.modelTextureGetter, p_352194_, false); -+ return ModelBakery.ITEM_MODEL_GENERATOR.generateBlockModel(sprites, blockmodel).bake(sprites, p_352194_, false); + public ModelBakery( + EntityModelSet p_388903_, + Map p_251087_, + Map p_250416_, + Map p_388404_, +- UnbakedModel p_360944_ ++ UnbakedModel p_360944_, ++ Map standaloneModels + ) { + this.entityModelSet = p_388903_; + this.unbakedBlockStateModels = p_251087_; + this.clientInfos = p_250416_; + this.unbakedPlainModels = p_388404_; + this.missingModel = p_360944_; ++ this.standaloneModels = standaloneModels; + } + + public ModelBakery.BakingResult bakeModels(ModelBakery.TextureGetter p_352431_) { +@@ -92,7 +_,18 @@ + LOGGER.warn("Unable to bake item model: '{}'", p_390101_, exception); } + }); +- return new ModelBakery.BakingResult(bakedmodel, map, itemmodel, map1, map2); ++ Map bakedStandaloneModels = new HashMap<>(this.standaloneModels.size()); ++ this.standaloneModels.forEach((location, model) -> { ++ try { ++ ModelDebugName debugName = () -> location + "#standalone"; ++ ModelBakerImpl modelBaker = new ModelBakerImpl(p_352431_, debugName); ++ BakedModel bakedModel = UnbakedModel.bakeWithTopModelValues(model, modelBaker, BlockModelRotation.X0_Y0); ++ bakedStandaloneModels.put(location, bakedModel); ++ } catch (Exception exception) { ++ LOGGER.warn("Unable to bake standalone model: '{}': {}", location, exception); ++ } ++ }); ++ return new ModelBakery.BakingResult(bakedmodel, map, itemmodel, map1, map2, bakedStandaloneModels); + } -- return p_352386_.bake(this, this.modelTextureGetter, p_352194_); -+ return p_352386_.bake(this, sprites, p_352194_); - } + @OnlyIn(Dist.CLIENT) +@@ -105,8 +_,22 @@ + Map blockStateModels, + ItemModel missingItemModel, + Map itemStackModels, +- Map itemProperties ++ Map itemProperties, ++ Map standaloneModels + ) { ++ /** ++ * @deprecated Neo: use {@link #BakingResult(BakedModel, Map, ItemModel, Map, Map, Map)}} instead ++ */ ++ @Deprecated ++ public BakingResult( ++ BakedModel missingModel, ++ Map blockStateModels, ++ ItemModel missingItemModel, ++ Map itemStackModels, ++ Map itemProperties ++ ) { ++ this(missingModel, blockStateModels, missingItemModel, itemStackModels, itemProperties, Map.of()); ++ } } + @OnlyIn(Dist.CLIENT) diff --git a/patches/net/minecraft/client/resources/model/ModelDiscovery.java.patch b/patches/net/minecraft/client/resources/model/ModelDiscovery.java.patch index 7c9de8482a..dba9824817 100644 --- a/patches/net/minecraft/client/resources/model/ModelDiscovery.java.patch +++ b/patches/net/minecraft/client/resources/model/ModelDiscovery.java.patch @@ -1,26 +1,23 @@ --- a/net/minecraft/client/resources/model/ModelDiscovery.java +++ b/net/minecraft/client/resources/model/ModelDiscovery.java -@@ -49,6 +_,7 @@ - }); - set.add(ItemRenderer.TRIDENT_MODEL); - set.add(ItemRenderer.SPYGLASS_MODEL); -+ net.neoforged.neoforge.client.ClientHooks.onRegisterAdditionalModels(set); - return set; +@@ -22,6 +_,7 @@ + final UnbakedModel missingModel; + private final List topModels = new ArrayList<>(); + private final Map referencedModels = new HashMap<>(); ++ final Map standaloneModels = new HashMap<>(); + + public ModelDiscovery(Map p_360750_, UnbakedModel p_365355_) { + this.inputModels = p_360750_; +@@ -31,6 +_,12 @@ + + public void registerSpecialModels() { + this.referencedModels.put(ItemModelGenerator.GENERATED_ITEM_MODEL_ID, new ItemModelGenerator()); ++ ++ net.neoforged.neoforge.client.ClientHooks.onRegisterAdditionalModels(path -> { ++ UnbakedModel model = getBlockModel(path); ++ this.addRoot(model); ++ this.standaloneModels.put(path, model); ++ }); } -@@ -77,6 +_,15 @@ - } - } - ); -+ // Neo: ensure standalone models registered in ModelEvent.RegisterAdditional are loaded -+ var it = set.iterator(); -+ while (it.hasNext()) { -+ ModelResourceLocation mrl = it.next(); -+ if (mrl.getVariant().equals(ModelResourceLocation.STANDALONE_VARIANT)) { -+ registerTopModel(mrl, getBlockModel(mrl.id())); -+ it.remove(); -+ } -+ } - if (!set.isEmpty()) { - LOGGER.warn("Missing mandatory models: {}", set.stream().map(p_370354_ -> "\n\t" + p_370354_).collect(Collectors.joining())); - } + public void addRoot(ResolvableModel p_388596_) { diff --git a/patches/net/minecraft/client/resources/model/ModelManager.java.patch b/patches/net/minecraft/client/resources/model/ModelManager.java.patch index 2795dad254..0586b6732f 100644 --- a/patches/net/minecraft/client/resources/model/ModelManager.java.patch +++ b/patches/net/minecraft/client/resources/model/ModelManager.java.patch @@ -1,48 +1,75 @@ --- a/net/minecraft/client/resources/model/ModelManager.java +++ b/net/minecraft/client/resources/model/ModelManager.java -@@ -71,13 +_,14 @@ - TextureAtlas.LOCATION_BLOCKS, - ResourceLocation.withDefaultNamespace("blocks") - ); -- private Map bakedRegistry; -+ private Map bakedRegistry = new java.util.HashMap<>(); - private final AtlasSet atlases; - private final BlockModelShaper blockModelShaper; - private final BlockColors blockColors; - private int maxMipmapLevels; +@@ -83,11 +_,13 @@ private BakedModel missingModel; - private Object2IntMap modelGroups; -+ private ModelBakery modelBakery; + private ItemModel missingItemModel; + private Object2IntMap modelGroups = Object2IntMaps.emptyMap(); ++ private final java.util.concurrent.atomic.AtomicReference modelBakery = new java.util.concurrent.atomic.AtomicReference<>(null); public ModelManager(TextureManager p_119406_, BlockColors p_119407_, int p_119408_) { this.blockColors = p_119407_; -@@ -102,6 +_,7 @@ + this.maxMipmapLevels = p_119408_; + this.blockModelShaper = new BlockModelShaper(this); ++ Map VANILLA_ATLASES = net.neoforged.neoforge.client.ClientHooks.gatherMaterialAtlases(ModelManager.VANILLA_ATLASES); + this.atlases = new AtlasSet(VANILLA_ATLASES, p_119406_); + } + +@@ -115,6 +_,7 @@ public final CompletableFuture reload( PreparableReloadListener.PreparationBarrier p_249079_, ResourceManager p_251134_, Executor p_250550_, Executor p_249221_ ) { -+ net.neoforged.neoforge.client.model.geometry.GeometryLoaderManager.init(); ++ net.neoforged.neoforge.client.model.UnbakedModelParser.init(); UnbakedModel unbakedmodel = MissingBlockModel.missingModel(); - BlockStateModelLoader blockstatemodelloader = new BlockStateModelLoader(unbakedmodel); - CompletableFuture> completablefuture = loadBlockModels(p_251134_, p_250550_); -@@ -258,6 +_,8 @@ - .collect(Collectors.joining("\n")) + CompletableFuture completablefuture = CompletableFuture.supplyAsync(EntityModelSet::vanilla, p_250550_); + CompletableFuture completablefuture1 = completablefuture.thenApplyAsync(SpecialBlockModelRenderer::vanilla, p_250550_); +@@ -154,8 +_,10 @@ + completablefuture3.join().plainModels(), + completablefuture4.join().contents(), + modeldiscovery.getReferencedModels(), +- unbakedmodel ++ unbakedmodel, ++ modeldiscovery.standaloneModels + ); ++ this.modelBakery.set(modelbakery); + return loadModels(Profiler.get(), map1, modelbakery, object2intmap, completablefuture.join(), completablefuture1.join()); + }, + p_250550_ +@@ -169,7 +_,7 @@ + return CompletableFuture.>supplyAsync(() -> MODEL_LISTER.listMatchingResources(p_251361_), p_252189_) + .thenCompose( + p_250597_ -> { +- List>> list = new ArrayList<>(p_250597_.size()); ++ List>> list = new ArrayList<>(p_250597_.size()); + + for (Entry entry : p_250597_.entrySet()) { + list.add(CompletableFuture.supplyAsync(() -> { +@@ -178,7 +_,7 @@ + try { + Pair pair; + try (Reader reader = entry.getValue().openAsReader()) { +- pair = Pair.of(resourcelocation, BlockModel.fromStream(reader)); ++ pair = Pair.of(resourcelocation, net.neoforged.neoforge.client.model.UnbakedModelParser.parse(reader)); + } + + return pair; +@@ -261,6 +_,8 @@ + p_386267_.stream().sorted().map(p_386265_ -> " " + p_386265_).collect(Collectors.joining("\n")) ) ); -+ p_252136_.popPush("forge_modify_baking_result"); -+ net.neoforged.neoforge.client.ClientHooks.onModifyBakingResult(p_248945_.getBakedTopLevelModels(), p_250646_, p_248945_); ++ p_252136_.push("neoforge_modify_baking_result"); ++ net.neoforged.neoforge.client.ClientHooks.onModifyBakingResult(modelbakery$bakingresult, p_250646_, p_248945_); p_252136_.popPush("dispatch"); - Map map = p_248945_.getBakedTopLevelModels(); - BakedModel bakedmodel = map.get(MissingBlockModel.VARIANT); -@@ -289,6 +_,8 @@ - this.bakedRegistry = modelbakery.getBakedTopLevelModels(); + Map map = createBlockStateToModelDispatch(modelbakery$bakingresult.blockStateModels(), modelbakery$bakingresult.missingModel()); + CompletableFuture completablefuture = CompletableFuture.allOf( +@@ -304,6 +_,7 @@ this.modelGroups = p_248996_.modelGroups; - this.missingModel = p_248996_.missingModel; -+ this.modelBakery = modelbakery; -+ net.neoforged.neoforge.client.ClientHooks.onModelBake(this, this.bakedRegistry, modelbakery); + this.missingModel = modelbakery$bakingresult.missingModel(); + this.missingItemModel = modelbakery$bakingresult.missingItemModel(); ++ net.neoforged.neoforge.client.ClientHooks.onModelBake(this, modelbakery$bakingresult, this.modelBakery.get()); p_251960_.popPush("cache"); this.blockModelShaper.replaceCache(p_248996_.modelCache); - p_251960_.pop(); -@@ -313,6 +_,7 @@ + this.specialBlockModelRenderer = p_248996_.specialBlockModelRenderer; +@@ -330,6 +_,7 @@ } public TextureAtlas getAtlas(ResourceLocation p_119429_) { @@ -50,14 +77,13 @@ return this.atlases.getAtlas(p_119429_); } -@@ -323,6 +_,10 @@ - - public void updateMaxMipLevel(int p_119411_) { - this.maxMipmapLevels = p_119411_; +@@ -360,5 +_,9 @@ + SpecialBlockModelRenderer specialBlockModelRenderer, + CompletableFuture readyForUpload + ) { + } + + public ModelBakery getModelBakery() { -+ return com.google.common.base.Preconditions.checkNotNull(modelBakery, "Attempted to query model bakery before it has been initialized."); ++ return this.modelBakery.get(); } - - @OnlyIn(Dist.CLIENT) + } diff --git a/patches/net/minecraft/client/resources/model/ModelResourceLocation.java.patch b/patches/net/minecraft/client/resources/model/ModelResourceLocation.java.patch deleted file mode 100644 index 85d4fc00bc..0000000000 --- a/patches/net/minecraft/client/resources/model/ModelResourceLocation.java.patch +++ /dev/null @@ -1,25 +0,0 @@ ---- a/net/minecraft/client/resources/model/ModelResourceLocation.java -+++ b/net/minecraft/client/resources/model/ModelResourceLocation.java -@@ -8,6 +_,7 @@ - @OnlyIn(Dist.CLIENT) - public record ModelResourceLocation(ResourceLocation id, String variant) { - public static final String INVENTORY_VARIANT = "inventory"; -+ public static final String STANDALONE_VARIANT = "standalone"; - - public ModelResourceLocation(ResourceLocation id, String variant) { - variant = lowercaseVariant(variant); -@@ -17,6 +_,14 @@ - - public static ModelResourceLocation inventory(ResourceLocation p_352141_) { - return new ModelResourceLocation(p_352141_, "inventory"); -+ } -+ -+ /** -+ * Construct a {@code ModelResourceLocation} for use in the {@link net.neoforged.neoforge.client.event.ModelEvent.RegisterAdditional} -+ * to load a model at the given path directly instead of going through blockstates or item model auto-prefixing. -+ */ -+ public static ModelResourceLocation standalone(ResourceLocation id) { -+ return new ModelResourceLocation(id, STANDALONE_VARIANT); - } - - private static String lowercaseVariant(String p_248567_) { diff --git a/patches/net/minecraft/client/resources/model/SimpleBakedModel.java.patch b/patches/net/minecraft/client/resources/model/SimpleBakedModel.java.patch index 41044226e2..2e6c64d644 100644 --- a/patches/net/minecraft/client/resources/model/SimpleBakedModel.java.patch +++ b/patches/net/minecraft/client/resources/model/SimpleBakedModel.java.patch @@ -1,18 +1,18 @@ --- a/net/minecraft/client/resources/model/SimpleBakedModel.java +++ b/net/minecraft/client/resources/model/SimpleBakedModel.java -@@ -25,7 +_,11 @@ - protected final boolean usesBlockLight; - protected final TextureAtlasSprite particleIcon; - protected final ItemTransforms transforms; +@@ -29,7 +_,11 @@ + private final boolean usesBlockLight; + private final TextureAtlasSprite particleIcon; + private final ItemTransforms transforms; + @Nullable protected final net.neoforged.neoforge.client.ChunkRenderTypeSet blockRenderTypes; -+ @Nullable protected final List itemRenderTypes; ++ @Nullable protected final net.minecraft.client.renderer.RenderType itemRenderType; + /** @deprecated Forge: Use {@linkplain #SimpleBakedModel(List, Map, boolean, boolean, boolean, TextureAtlasSprite, ItemTransforms, net.neoforged.neoforge.client.RenderTypeGroup) variant with RenderTypeGroup} **/ + @Deprecated public SimpleBakedModel( List p_119489_, Map> p_119490_, -@@ -35,6 +_,19 @@ +@@ -39,6 +_,19 @@ TextureAtlasSprite p_119494_, ItemTransforms p_119495_ ) { @@ -32,16 +32,61 @@ this.unculledFaces = p_119489_; this.culledFaces = p_119490_; this.hasAmbientOcclusion = p_119491_; -@@ -42,6 +_,8 @@ +@@ -46,8 +_,14 @@ this.usesBlockLight = p_119492_; this.particleIcon = p_119494_; this.transforms = p_119495_; + this.blockRenderTypes = !renderTypes.isEmpty() ? net.neoforged.neoforge.client.ChunkRenderTypeSet.of(renderTypes.block()) : null; -+ this.itemRenderTypes = !renderTypes.isEmpty() ? List.of(renderTypes.entity()) : null; ++ this.itemRenderType = !renderTypes.isEmpty() ? renderTypes.entity() : null; } - @Override -@@ -79,6 +_,20 @@ ++ /** ++ * @deprecated Neo: Use {@linkplain #bakeElements(List, TextureSlots, SpriteGetter, ModelState, boolean, boolean, boolean, ItemTransforms, com.mojang.math.Transformation, net.neoforged.neoforge.client.RenderTypeGroup) variant with Transformation and RenderTypeGroup support} ++ */ ++ @Deprecated + public static BakedModel bakeElements( + List p_387963_, + TextureSlots p_388507_, +@@ -58,10 +_,29 @@ + boolean p_386706_, + ItemTransforms p_388032_ + ) { ++ return bakeElements(p_387963_, p_388507_, p_387357_, p_388846_, p_386975_, p_388143_, p_386706_, p_388032_, com.mojang.math.Transformation.identity(), net.neoforged.neoforge.client.RenderTypeGroup.EMPTY); ++ } ++ ++ public static BakedModel bakeElements( ++ List p_387963_, ++ TextureSlots p_388507_, ++ SpriteGetter p_387357_, ++ ModelState p_388846_, ++ boolean p_386975_, ++ boolean p_388143_, ++ boolean p_386706_, ++ ItemTransforms p_388032_, ++ com.mojang.math.Transformation rootTransform, ++ net.neoforged.neoforge.client.RenderTypeGroup renderTypes ++ ) { + TextureAtlasSprite textureatlassprite = findSprite(p_387357_, p_388507_, "particle"); + SimpleBakedModel.Builder simplebakedmodel$builder = new SimpleBakedModel.Builder(p_386975_, p_388143_, p_386706_, p_388032_) + .particle(textureatlassprite); + ++ if (!rootTransform.isIdentity()) { ++ p_388846_ = net.neoforged.neoforge.client.model.UnbakedElementsHelper.composeRootTransformIntoModelState(p_388846_, rootTransform); ++ } ++ + for (BlockElement blockelement : p_387963_) { + for (Direction direction : blockelement.faces.keySet()) { + BlockElementFace blockelementface = blockelement.faces.get(direction); +@@ -77,7 +_,7 @@ + } + } + +- return simplebakedmodel$builder.build(); ++ return simplebakedmodel$builder.build(renderTypes); + } + + public static BakedQuad bakeFace( +@@ -123,6 +_,20 @@ return this.transforms; } @@ -53,16 +98,16 @@ + } + + @Override -+ public List getRenderTypes(net.minecraft.world.item.ItemStack itemStack) { -+ if (itemRenderTypes != null) -+ return itemRenderTypes; -+ return BakedModel.super.getRenderTypes(itemStack); ++ public net.minecraft.client.renderer.RenderType getRenderType(net.minecraft.world.item.ItemStack itemStack) { ++ if (itemRenderType != null) ++ return itemRenderType; ++ return BakedModel.super.getRenderType(itemStack); + } + @OnlyIn(Dist.CLIENT) public static class Builder { private final ImmutableList.Builder unculledFaces = ImmutableList.builder(); -@@ -124,7 +_,13 @@ +@@ -164,7 +_,13 @@ return this; } @@ -76,7 +121,7 @@ if (this.particleIcon == null) { throw new RuntimeException("Missing particle!"); } else { -@@ -136,7 +_,8 @@ +@@ -176,7 +_,8 @@ this.usesBlockLight, this.isGui3d, this.particleIcon, diff --git a/patches/net/minecraft/client/resources/model/UnbakedModel.java.patch b/patches/net/minecraft/client/resources/model/UnbakedModel.java.patch new file mode 100644 index 0000000000..965a6a6304 --- /dev/null +++ b/patches/net/minecraft/client/resources/model/UnbakedModel.java.patch @@ -0,0 +1,57 @@ +--- a/net/minecraft/client/resources/model/UnbakedModel.java ++++ b/net/minecraft/client/resources/model/UnbakedModel.java +@@ -9,10 +_,14 @@ + import net.neoforged.api.distmarker.OnlyIn; + + @OnlyIn(Dist.CLIENT) +-public interface UnbakedModel extends ResolvableModel { ++public interface UnbakedModel extends ResolvableModel, net.neoforged.neoforge.client.extensions.IUnbakedModelExtension { + boolean DEFAULT_AMBIENT_OCCLUSION = true; + UnbakedModel.GuiLight DEFAULT_GUI_LIGHT = UnbakedModel.GuiLight.SIDE; + ++ /** ++ * @deprecated Neo: use {@link #bake(TextureSlots, ModelBaker, ModelState, boolean, boolean, ItemTransforms, net.minecraft.util.context.ContextMap)} instead ++ */ ++ @Deprecated + BakedModel bake(TextureSlots p_386641_, ModelBaker p_250133_, ModelState p_119536_, boolean p_387129_, boolean p_388638_, ItemTransforms p_386911_); + + @Nullable +@@ -44,7 +_,8 @@ + boolean flag = getTopAmbientOcclusion(p_388418_); + boolean flag1 = getTopGuiLight(p_388418_).lightLikeBlock(); + ItemTransforms itemtransforms = getTopTransforms(p_388418_); +- return p_388418_.bake(textureslots, p_388525_, p_386740_, flag, flag1, itemtransforms); ++ var additionalProperties = net.neoforged.neoforge.client.extensions.IUnbakedModelExtension.getTopAdditionalProperties(p_388418_); ++ return p_388418_.bake(textureslots, p_388525_, p_386740_, flag, flag1, itemtransforms, additionalProperties); + } + + static TextureSlots getTopTextureSlots(UnbakedModel p_387784_, ModelDebugName p_388419_) { +@@ -109,7 +_,16 @@ + ItemTransform itemtransform5 = getTopTransform(p_388720_, ItemDisplayContext.GUI); + ItemTransform itemtransform6 = getTopTransform(p_388720_, ItemDisplayContext.GROUND); + ItemTransform itemtransform7 = getTopTransform(p_388720_, ItemDisplayContext.FIXED); +- return new ItemTransforms(itemtransform, itemtransform1, itemtransform2, itemtransform3, itemtransform4, itemtransform5, itemtransform6, itemtransform7); ++ com.google.common.collect.ImmutableMap.Builder moddedTransforms = com.google.common.collect.ImmutableMap.builder(); ++ for (ItemDisplayContext context : ItemDisplayContext.values()) { ++ if (context.isModded()) { ++ ItemTransform transform = getTopTransform(p_388720_, context); ++ if (transform != ItemTransform.NO_TRANSFORM) { ++ moddedTransforms.put(context, transform); ++ } ++ } ++ } ++ return new ItemTransforms(itemtransform, itemtransform1, itemtransform2, itemtransform3, itemtransform4, itemtransform5, itemtransform6, itemtransform7, moddedTransforms.build()); + } + + @OnlyIn(Dist.CLIENT) +@@ -135,6 +_,10 @@ + + public boolean lightLikeBlock() { + return this == SIDE; ++ } ++ ++ public String getSerializedName() { ++ return name; + } + } + } diff --git a/patches/net/minecraft/client/sounds/MusicManager.java.patch b/patches/net/minecraft/client/sounds/MusicManager.java.patch index 578bcd60a4..3fb24324f8 100644 --- a/patches/net/minecraft/client/sounds/MusicManager.java.patch +++ b/patches/net/minecraft/client/sounds/MusicManager.java.patch @@ -1,12 +1,12 @@ --- a/net/minecraft/client/sounds/MusicManager.java +++ b/net/minecraft/client/sounds/MusicManager.java -@@ -24,7 +_,15 @@ +@@ -25,7 +_,15 @@ } public void tick() { -- Music music = this.minecraft.getSituationalMusic(); -+ Music music = net.neoforged.neoforge.client.ClientHooks.selectMusic(this.minecraft.getSituationalMusic(), this.currentMusic); -+ if (music == null) { +- MusicInfo musicinfo = this.minecraft.getSituationalMusic(); ++ MusicInfo musicinfo = net.neoforged.neoforge.client.ClientHooks.selectMusic(this.minecraft.getSituationalMusic(), this.currentMusic); ++ if (musicinfo == null) { + if (this.currentMusic != null) { + this.stopPlaying(); + } @@ -14,6 +14,6 @@ + return; + } + - if (this.currentMusic != null) { - if (!music.getEvent().value().location().equals(this.currentMusic.getLocation()) && music.replaceCurrentMusic()) { - this.minecraft.getSoundManager().stop(this.currentMusic); + float f = musicinfo.volume(); + if (this.currentMusic != null && this.currentGain != f) { + boolean flag = this.fadePlaying(f); diff --git a/patches/net/minecraft/client/sounds/SoundEngine.java.patch b/patches/net/minecraft/client/sounds/SoundEngine.java.patch index 29678b9899..9f7bec62bb 100644 --- a/patches/net/minecraft/client/sounds/SoundEngine.java.patch +++ b/patches/net/minecraft/client/sounds/SoundEngine.java.patch @@ -16,7 +16,7 @@ } private synchronized void loadLibrary() { -@@ -327,7 +_,8 @@ +@@ -336,7 +_,8 @@ public void play(SoundInstance p_120313_) { if (this.loaded) { @@ -26,7 +26,7 @@ WeighedSoundEvents weighedsoundevents = p_120313_.resolve(this.soundManager); ResourceLocation resourcelocation = p_120313_.getLocation(); if (weighedsoundevents == null) { -@@ -391,19 +_,22 @@ +@@ -400,19 +_,22 @@ p_194488_.setSelfPosition(vec3); p_194488_.setRelative(flag); }); @@ -34,8 +34,8 @@ if (!flag2) { this.soundBuffers .getCompleteBuffer(sound.getPath()) - .thenAccept(p_194501_ -> channelaccess$channelhandle.execute(p_194495_ -> { - p_194495_.attachStaticBuffer(p_194501_); + .thenAccept(p_383126_ -> channelaccess$channelhandle.execute(p_194495_ -> { + p_194495_.attachStaticBuffer(p_383126_); p_194495_.play(); + net.neoforged.neoforge.common.NeoForge.EVENT_BUS.post(new net.neoforged.neoforge.client.event.sound.PlaySoundSourceEvent(this, soundinstance, p_194495_)); })); @@ -44,8 +44,8 @@ - .getStream(sound.getPath(), flag1) + soundinstance + .getStream(this.soundBuffers, sound, flag1) - .thenAccept(p_194504_ -> channelaccess$channelhandle.execute(p_194498_ -> { - p_194498_.attachBufferStream(p_194504_); + .thenAccept(p_383033_ -> channelaccess$channelhandle.execute(p_194498_ -> { + p_194498_.attachBufferStream(p_383033_); p_194498_.play(); + net.neoforged.neoforge.common.NeoForge.EVENT_BUS.post(new net.neoforged.neoforge.client.event.sound.PlayStreamingSourceEvent(this, soundinstance, p_194498_)); })); diff --git a/patches/net/minecraft/core/MappedRegistry.java.patch b/patches/net/minecraft/core/MappedRegistry.java.patch index 08874566bd..678bcc5964 100644 --- a/patches/net/minecraft/core/MappedRegistry.java.patch +++ b/patches/net/minecraft/core/MappedRegistry.java.patch @@ -139,7 +139,7 @@ } else { List list1 = this.frozenTags .entrySet() -@@ -450,6 +_,54 @@ +@@ -450,6 +_,55 @@ } }; } @@ -157,6 +157,7 @@ + this.byKey.clear(); + this.byValue.clear(); + this.allTags = MappedRegistry.TagSet.unbound(); ++ this.frozenTags.entrySet().removeIf(entry -> !entry.getValue().isBound()); + if (unregisteredIntrusiveHolders != null) { + unregisteredIntrusiveHolders.clear(); + unregisteredIntrusiveHolders = null; diff --git a/patches/net/minecraft/core/component/PatchedDataComponentMap.java.patch b/patches/net/minecraft/core/component/PatchedDataComponentMap.java.patch index dfea9ede8a..5335c0034d 100644 --- a/patches/net/minecraft/core/component/PatchedDataComponentMap.java.patch +++ b/patches/net/minecraft/core/component/PatchedDataComponentMap.java.patch @@ -1,6 +1,6 @@ --- a/net/minecraft/core/component/PatchedDataComponentMap.java +++ b/net/minecraft/core/component/PatchedDataComponentMap.java -@@ -64,6 +_,7 @@ +@@ -68,6 +_,7 @@ @Nullable public T set(DataComponentType p_330791_, @Nullable T p_330369_) { @@ -8,7 +8,7 @@ this.ensureMapOwnership(); T t = this.prototype.get((DataComponentType)p_330791_); Optional optional; -@@ -197,6 +_,10 @@ +@@ -201,6 +_,10 @@ } return i; diff --git a/patches/net/minecraft/data/DataProvider.java.patch b/patches/net/minecraft/data/DataProvider.java.patch index 07f5c14860..6e4fbb1176 100644 --- a/patches/net/minecraft/data/DataProvider.java.patch +++ b/patches/net/minecraft/data/DataProvider.java.patch @@ -1,8 +1,14 @@ --- a/net/minecraft/data/DataProvider.java +++ b/net/minecraft/data/DataProvider.java -@@ -28,6 +_,9 @@ +@@ -28,7 +_,15 @@ + import org.slf4j.Logger; public interface DataProvider { ++ /** ++ * Neo: Allows changing the indentation width used by {@link #saveStable}. ++ */ ++ java.util.concurrent.atomic.AtomicInteger INDENT_WIDTH = new java.util.concurrent.atomic.AtomicInteger(2); ++ ToIntFunction FIXED_ORDER_FIELDS = Util.make(new Object2IntOpenHashMap<>(), p_236070_ -> { + // Neo: conditions go first + p_236070_.put("neoforge:conditions", -1); @@ -10,3 +16,12 @@ p_236070_.put("type", 0); p_236070_.put("parent", 1); p_236070_.defaultReturnValue(2); +@@ -80,7 +_,7 @@ + + try (JsonWriter jsonwriter = new JsonWriter(new OutputStreamWriter(hashingoutputstream, StandardCharsets.UTF_8))) { + jsonwriter.setSerializeNulls(false); +- jsonwriter.setIndent(" "); ++ jsonwriter.setIndent(" ".repeat(java.lang.Math.max(0, INDENT_WIDTH.get()))); // Neo: Allow changing the indent width without needing to mixin this lambda. + GsonHelper.writeValue(jsonwriter, p_254542_, KEY_COMPARATOR); + } + diff --git a/patches/net/minecraft/data/Main.java.patch b/patches/net/minecraft/data/Main.java.patch index 4e6c37a6e7..eed2934cc4 100644 --- a/patches/net/minecraft/data/Main.java.patch +++ b/patches/net/minecraft/data/Main.java.patch @@ -1,46 +1,41 @@ --- a/net/minecraft/data/Main.java +++ b/net/minecraft/data/Main.java -@@ -86,8 +_,15 @@ - OptionSpec optionspec6 = optionparser.accepts("all", "Include all generators"); - OptionSpec optionspec7 = optionparser.accepts("output", "Output folder").withRequiredArg().defaultsTo("generated"); - OptionSpec optionspec8 = optionparser.accepts("input", "Input folder").withRequiredArg(); +@@ -73,21 +_,34 @@ + OptionSpec optionspec1 = optionparser.accepts("server", "Include server generators"); + OptionSpec optionspec2 = optionparser.accepts("dev", "Include development tools"); + OptionSpec optionspec3 = optionparser.accepts("reports", "Include data reports"); +- optionparser.accepts("validate", "Validate inputs"); ++ OptionSpec validateSpec = optionparser.accepts("validate", "Validate inputs"); + OptionSpec optionspec4 = optionparser.accepts("all", "Include all generators"); + OptionSpec optionspec5 = optionparser.accepts("output", "Output folder").withRequiredArg().defaultsTo("generated"); + OptionSpec optionspec6 = optionparser.accepts("input", "Input folder").withRequiredArg(); + OptionSpec existing = optionparser.accepts("existing", "Existing resource packs that generated resources can reference").withRequiredArg(); + OptionSpec existingMod = optionparser.accepts("existing-mod", "Existing mods that generated resources can reference the resource packs of").withRequiredArg(); + OptionSpec gameDir = optionparser.accepts("gameDir").withRequiredArg().ofType(java.io.File.class).defaultsTo(new java.io.File(".")).required(); //Need by modlauncher, so lets just eat it + OptionSpec mod = optionparser.accepts("mod", "A modid to dump").withRequiredArg().withValuesSeparatedBy(","); + OptionSpec flat = optionparser.accepts("flat", "Do not append modid prefix to output directory when generating for multiple mods"); -+ OptionSpec assetIndex = optionparser.accepts("assetIndex").withRequiredArg(); -+ OptionSpec assetsDir = optionparser.accepts("assetsDir").withRequiredArg().ofType(java.io.File.class); OptionSet optionset = optionparser.parse(p_129669_); - if (!optionset.has(optionspec) && optionset.hasOptions()) { + if (!optionset.has(optionspec) && optionset.hasOptions() && !(optionset.specs().size() == 1 && optionset.has(gameDir))) { - Path path = Paths.get(optionspec7.value(optionset)); - boolean flag = optionset.has(optionspec6); - boolean flag1 = flag || optionset.has(optionspec2); -@@ -95,9 +_,16 @@ + Path path = Paths.get(optionspec5.value(optionset)); + boolean flag = optionset.has(optionspec4); + boolean flag1 = flag || optionset.has(optionspec1); + boolean flag2 = flag || optionset.has(optionspec2); boolean flag3 = flag || optionset.has(optionspec3); - boolean flag4 = flag || optionset.has(optionspec4); - boolean flag5 = flag || optionset.has(optionspec5); -+ Collection inputs = optionset.valuesOf(optionspec8).stream().map(Paths::get).collect(Collectors.toList()); -+ Collection existingPacks = optionset.valuesOf(existing).stream().map(Paths::get).collect(Collectors.toList()); + Collection collection = optionset.valuesOf(optionspec6).stream().map(p_129659_ -> Paths.get(p_129659_)).toList(); +- DataGenerator datagenerator = new DataGenerator(path, SharedConstants.getCurrentVersion(), true); ++ Collection existingPacks = optionset.valuesOf(existing).stream().map(Paths::get).toList(); + java.util.Set existingMods = new java.util.HashSet<>(optionset.valuesOf(existingMod)); + java.util.Set mods = new java.util.HashSet<>(optionset.valuesOf(mod)); + boolean isFlat = mods.isEmpty() || optionset.has(flat); -+ net.neoforged.neoforge.data.loading.DatagenModLoader.begin(mods, path, inputs, existingPacks, existingMods, flag2, flag1, flag3, flag4, flag5, isFlat, optionset.valueOf(assetIndex), optionset.valueOf(assetsDir)); ++ boolean validate = optionset.has(validateSpec); ++ DataGenerator datagenerator = new DataGenerator(isFlat ? path : path.resolve("minecraft"), SharedConstants.getCurrentVersion(), true); + if (mods.contains("minecraft") || mods.isEmpty()) { - DataGenerator datagenerator = createStandardGenerator( -- path, -- optionset.valuesOf(optionspec8).stream().map(p_129659_ -> Paths.get(p_129659_)).collect(Collectors.toList()), -+ isFlat ? path : path.resolve("minecraft"), -+ inputs, - flag1, - flag2, - flag3, -@@ -107,6 +_,7 @@ - true - ); - datagenerator.run(); + addServerProviders(datagenerator, collection, flag1, flag2, flag3); +- datagenerator.run(); + } ++ net.neoforged.neoforge.data.loading.DatagenModLoader.begin(mods, path, collection, existingPacks, existingMods, flag2, flag3, validate, isFlat, null, null, () -> {}, ++ net.neoforged.neoforge.data.event.GatherDataEvent.Server::new, datagenerator); } else { optionparser.printHelpOn(System.out); } diff --git a/patches/net/minecraft/data/loot/BlockLootSubProvider.java.patch b/patches/net/minecraft/data/loot/BlockLootSubProvider.java.patch index 2bd05fb51a..616ad56710 100644 --- a/patches/net/minecraft/data/loot/BlockLootSubProvider.java.patch +++ b/patches/net/minecraft/data/loot/BlockLootSubProvider.java.patch @@ -1,6 +1,6 @@ --- a/net/minecraft/data/loot/BlockLootSubProvider.java +++ b/net/minecraft/data/loot/BlockLootSubProvider.java -@@ -684,12 +_,16 @@ +@@ -709,12 +_,16 @@ protected abstract void generate(); diff --git a/patches/net/minecraft/network/protocol/game/ServerGamePacketListener.java.patch b/patches/net/minecraft/network/protocol/game/ServerGamePacketListener.java.patch index f24f3d2b43..141ddf719f 100644 --- a/patches/net/minecraft/network/protocol/game/ServerGamePacketListener.java.patch +++ b/patches/net/minecraft/network/protocol/game/ServerGamePacketListener.java.patch @@ -4,8 +4,8 @@ import net.minecraft.network.protocol.common.ServerCommonPacketListener; import net.minecraft.network.protocol.ping.ServerPingPacketListener; --public interface ServerGamePacketListener extends ServerCommonPacketListener, ServerPingPacketListener { -+public interface ServerGamePacketListener extends ServerCommonPacketListener, ServerPingPacketListener, net.neoforged.neoforge.common.extensions.IServerGamePacketListenerExtension { +-public interface ServerGamePacketListener extends ServerPingPacketListener, ServerCommonPacketListener { ++public interface ServerGamePacketListener extends ServerPingPacketListener, ServerCommonPacketListener, net.neoforged.neoforge.common.extensions.IServerGamePacketListenerExtension { @Override default ConnectionProtocol protocol() { return ConnectionProtocol.PLAY; diff --git a/patches/net/minecraft/resources/FileToIdConverter.java.patch b/patches/net/minecraft/resources/FileToIdConverter.java.patch index f908144107..43421d7973 100644 --- a/patches/net/minecraft/resources/FileToIdConverter.java.patch +++ b/patches/net/minecraft/resources/FileToIdConverter.java.patch @@ -1,6 +1,6 @@ --- a/net/minecraft/resources/FileToIdConverter.java +++ b/net/minecraft/resources/FileToIdConverter.java -@@ -34,4 +_,26 @@ +@@ -40,4 +_,26 @@ public Map> listMatchingResourceStacks(ResourceManager p_249881_) { return p_249881_.listResourceStacks(this.prefix, p_248700_ -> p_248700_.getPath().endsWith(this.extension)); } diff --git a/patches/net/minecraft/resources/HolderSetCodec.java.patch b/patches/net/minecraft/resources/HolderSetCodec.java.patch index 6c9fbdaec3..7d384fbff8 100644 --- a/patches/net/minecraft/resources/HolderSetCodec.java.patch +++ b/patches/net/minecraft/resources/HolderSetCodec.java.patch @@ -9,7 +9,7 @@ private static Codec>> homogenousList(Codec> p_206668_, boolean p_206669_) { Codec>> codec = p_206668_.listOf().validate(ExtraCodecs.ensureHomogenous(Holder::kind)); -@@ -42,6 +_,10 @@ +@@ -36,6 +_,10 @@ this.elementCodec = p_206661_; this.homogenousListCodec = homogenousList(p_206661_, p_206662_); this.registryAwareCodec = Codec.either(TagKey.hashedCodec(p_206660_), this.homogenousListCodec); @@ -20,7 +20,7 @@ } @Override -@@ -50,14 +_,17 @@ +@@ -44,14 +_,17 @@ Optional> optional = registryops.getter(this.registryKey); if (optional.isPresent()) { HolderGetter holdergetter = optional.get(); @@ -40,7 +40,7 @@ ); return dataresult.map(p_332563_ -> Pair.of((HolderSet)p_332563_, (T)p_337522_.getSecond())); } -@@ -82,6 +_,9 @@ +@@ -76,6 +_,9 @@ return DataResult.error(() -> "HolderSet " + p_206674_ + " is not valid in current registry set"); } diff --git a/patches/net/minecraft/resources/RegistryDataLoader.java.patch b/patches/net/minecraft/resources/RegistryDataLoader.java.patch index 4a24d6d0f3..9cfc3b7659 100644 --- a/patches/net/minecraft/resources/RegistryDataLoader.java.patch +++ b/patches/net/minecraft/resources/RegistryDataLoader.java.patch @@ -37,16 +37,16 @@ } } -@@ -264,7 +_,7 @@ +@@ -263,7 +_,7 @@ + Map, Exception> p_321649_ ) { - String s = Registries.elementsDirPath(p_321557_.key()); - FileToIdConverter filetoidconverter = FileToIdConverter.json(s); + FileToIdConverter filetoidconverter = FileToIdConverter.registry(p_321557_.key()); - RegistryOps registryops = RegistryOps.create(JsonOps.INSTANCE, p_321612_); + RegistryOps registryops = new net.neoforged.neoforge.common.conditions.ConditionalOps<>(RegistryOps.create(JsonOps.INSTANCE, p_321612_), net.neoforged.neoforge.common.conditions.ICondition.IContext.TAGS_INVALID); for (Entry entry : filetoidconverter.listMatchingResources(p_321535_).entrySet()) { ResourceLocation resourcelocation = entry.getKey(); -@@ -354,13 +_,20 @@ +@@ -352,13 +_,20 @@ public static record NetworkedRegistryData(List elements, TagNetworkSerialization.NetworkPayload tags) { } diff --git a/patches/net/minecraft/server/Bootstrap.java.patch b/patches/net/minecraft/server/Bootstrap.java.patch index e775eb5eea..cb98d9043b 100644 --- a/patches/net/minecraft/server/Bootstrap.java.patch +++ b/patches/net/minecraft/server/Bootstrap.java.patch @@ -1,6 +1,6 @@ --- a/net/minecraft/server/Bootstrap.java +++ b/net/minecraft/server/Bootstrap.java -@@ -53,6 +_,8 @@ +@@ -57,6 +_,8 @@ CauldronInteraction.bootStrap(); BuiltInRegistries.bootStrap(); CreativeModeTabs.validate(); @@ -9,7 +9,7 @@ wrapStreams(); bootstrapDuration.set(Duration.between(instant, Instant.now()).toMillis()); } -@@ -119,7 +_,6 @@ +@@ -123,7 +_,6 @@ Commands.validate(); } diff --git a/patches/net/minecraft/server/Main.java.patch b/patches/net/minecraft/server/Main.java.patch index e32cfe17d8..dfa781bb50 100644 --- a/patches/net/minecraft/server/Main.java.patch +++ b/patches/net/minecraft/server/Main.java.patch @@ -1,15 +1,6 @@ --- a/net/minecraft/server/Main.java +++ b/net/minecraft/server/Main.java -@@ -64,7 +_,7 @@ - private static final Logger LOGGER = LogUtils.getLogger(); - - @DontObfuscate -- public static void main(String[] p_129699_) { -+ public static void main(String[] p_129699_) throws Exception { - SharedConstants.tryDetectVersion(); - OptionParser optionparser = new OptionParser(); - OptionSpec optionspec = optionparser.accepts("nogui"); -@@ -83,6 +_,15 @@ +@@ -87,6 +_,15 @@ OptionSpec optionspec13 = optionparser.accepts("jfrProfile"); OptionSpec optionspec14 = optionparser.accepts("pidFile").withRequiredArg().withValuesConvertedBy(new PathConverter()); OptionSpec optionspec15 = optionparser.nonOptions(); @@ -25,7 +16,7 @@ try { OptionSet optionset = optionparser.parse(p_129699_); -@@ -90,6 +_,13 @@ +@@ -94,6 +_,13 @@ optionparser.printHelpOn(System.err); return; } @@ -39,7 +30,7 @@ Path path = optionset.valueOf(optionspec14); if (path != null) { -@@ -105,24 +_,22 @@ +@@ -109,24 +_,22 @@ Bootstrap.validate(); Util.startTimerHackThread(); Path path1 = Paths.get("server.properties"); @@ -69,7 +60,7 @@ LevelStorageSource levelstoragesource = LevelStorageSource.createDefault(file1.toPath()); LevelStorageSource.LevelStorageAccess levelstoragesource$levelstorageaccess = levelstoragesource.validateAndCreateAccess(s); Dynamic dynamic; -@@ -131,6 +_,7 @@ +@@ -135,6 +_,7 @@ try { dynamic = levelstoragesource$levelstorageaccess.getDataTag(); levelsummary = levelstoragesource$levelstorageaccess.getSummary(dynamic); @@ -77,7 +68,7 @@ } catch (NbtException | ReportedNbtException | IOException ioexception1) { LevelStorageSource.LevelDirectory levelstoragesource$leveldirectory = levelstoragesource$levelstorageaccess.getLevelDirectory(); LOGGER.warn("Failed to load world data from {}", levelstoragesource$leveldirectory.dataFile(), ioexception1); -@@ -139,6 +_,7 @@ +@@ -143,6 +_,7 @@ try { dynamic = levelstoragesource$levelstorageaccess.getDataTagFallback(); levelsummary = levelstoragesource$levelstorageaccess.getSummary(dynamic); @@ -85,7 +76,23 @@ } catch (NbtException | ReportedNbtException | IOException ioexception) { LOGGER.error("Failed to load world data from {}", levelstoragesource$leveldirectory.oldDataFile(), ioexception); LOGGER.error( -@@ -214,6 +_,9 @@ +@@ -177,6 +_,15 @@ + + PackRepository packrepository = ServerPacksSource.createPackRepository(levelstoragesource$levelstorageaccess); + ++ if (gametestEnabled) { ++ net.neoforged.neoforge.gametest.GameTestHooks.registerGametests(); ++ net.minecraft.core.BlockPos spawnPos = optionset.valueOf(spawnPosOpt); ++ MinecraftServer.spin(thread -> net.minecraft.gametest.framework.GameTestServer.create(thread, levelstoragesource$levelstorageaccess, packrepository, net.minecraft.gametest.framework.GameTestRegistry.getAllTestFunctions(), spawnPos)); ++ // If we're running a gametest server we don't need to load the resources normally (GameTestServer#create does it using a flat world) ++ // or create a shutdown thread as the gametest server will always exit itself ++ return; ++ } ++ + WorldStem worldstem; + try { + WorldLoader.InitConfig worldloader$initconfig = loadOrCreateConfig(dedicatedserversettings.getProperties(), dynamic1, flag, packrepository); +@@ -218,6 +_,9 @@ worlddimensions = dedicatedserverproperties.createDimensions(p_359487_.datapackWorldgen()); } @@ -95,48 +102,9 @@ WorldDimensions.Complete worlddimensions$complete = worlddimensions.bake(registry); Lifecycle lifecycle = worlddimensions$complete.lifecycle().add(p_359487_.datapackWorldgen().allRegistriesLifecycle()); return new WorldLoader.DataLoadOutput<>( -@@ -246,24 +_,22 @@ - - WorldData worlddata = worldstem.worldData(); - levelstoragesource$levelstorageaccess.saveDataTag(registryaccess$frozen, worlddata); -- final DedicatedServer dedicatedserver = MinecraftServer.spin( -+ final MinecraftServer dedicatedserver = MinecraftServer.spin( - p_293760_ -> { -- DedicatedServer dedicatedserver1 = new DedicatedServer( -- p_293760_, -- levelstoragesource$levelstorageaccess, -- packrepository, -- worldstem, -- dedicatedserversettings, -- DataFixers.getDataFixer(), -- services, -- LoggerChunkProgressListener::createFromGameruleRadius -- ); -+ MinecraftServer dedicatedserver1; -+ if (gametestEnabled) { -+ net.neoforged.neoforge.gametest.GameTestHooks.registerGametests(); -+ net.minecraft.core.BlockPos spawnPos = optionset.valueOf(spawnPosOpt); -+ dedicatedserver1 = net.minecraft.gametest.framework.GameTestServer.create(p_293760_, levelstoragesource$levelstorageaccess, packrepository, net.minecraft.gametest.framework.GameTestRegistry.getAllTestFunctions(), spawnPos); -+ } else { -+ dedicatedserver1 = new DedicatedServer(p_293760_, levelstoragesource$levelstorageaccess, packrepository, worldstem, dedicatedserversettings, DataFixers.getDataFixer(), services, LoggerChunkProgressListener::createFromGameruleRadius); -+ } - dedicatedserver1.setPort(optionset.valueOf(optionspec11)); - dedicatedserver1.setDemo(optionset.has(optionspec2)); - dedicatedserver1.setId(optionset.valueOf(optionspec12)); - boolean flag2 = !optionset.has(optionspec) && !optionset.valuesOf(optionspec15).contains("nogui"); -- if (flag2 && !GraphicsEnvironment.isHeadless()) { -- dedicatedserver1.showGui(); -+ if (dedicatedserver1 instanceof DedicatedServer dedicatedServer && flag2 && !GraphicsEnvironment.isHeadless()) { -+ dedicatedServer.showGui(); - } - - return dedicatedserver1; -@@ -272,7 +_,10 @@ - Thread thread = new Thread("Server Shutdown Thread") { +@@ -277,6 +_,7 @@ @Override public void run() { -+ // FORGE: Halting as GameTestServer will cause issues as it always calls System#exit on both crash and normal exit, so skip it -+ if (!(dedicatedserver instanceof net.minecraft.gametest.framework.GameTestServer)) dedicatedserver.halt(true); + org.apache.logging.log4j.LogManager.shutdown(); // we're manually managing the logging shutdown on the server. Make sure we do it here at the end. } diff --git a/patches/net/minecraft/server/ReloadableServerRegistries.java.patch b/patches/net/minecraft/server/ReloadableServerRegistries.java.patch index ff31d3b5cf..7fb8e44291 100644 --- a/patches/net/minecraft/server/ReloadableServerRegistries.java.patch +++ b/patches/net/minecraft/server/ReloadableServerRegistries.java.patch @@ -1,19 +1,18 @@ --- a/net/minecraft/server/ReloadableServerRegistries.java +++ b/net/minecraft/server/ReloadableServerRegistries.java -@@ -61,8 +_,16 @@ +@@ -61,7 +_,15 @@ () -> { WritableRegistry writableregistry = new MappedRegistry<>(p_335741_.registryKey(), Lifecycle.experimental()); Map map = new HashMap<>(); +- SimpleJsonResourceReloadListener.scanDirectory(p_335893_, p_335741_.registryKey(), p_336173_, p_335741_.codec(), map); + Map> optionalMap = new HashMap<>(); - String s = Registries.elementsDirPath(p_335741_.registryKey()); -- SimpleJsonResourceReloadListener.scanDirectory(p_335893_, s, p_336173_, p_335741_.codec(), map); -+ SimpleJsonResourceReloadListener.scanDirectory(p_335893_, s, p_336173_, p_335741_.conditionalCodec(), optionalMap); ++ SimpleJsonResourceReloadListener.scanDirectoryWithOptionalValues(p_335893_, p_335741_.registryKey(), p_336173_, p_335741_.conditionalCodec(), optionalMap); + optionalMap.forEach((rl, optionalEntry) -> { + optionalEntry.ifPresent(entry -> p_335741_.idSetter().accept(entry, rl)); + T value = optionalEntry.orElse(p_335741_.defaultValue()); + if (value instanceof LootTable lootTable) value = (T) net.neoforged.neoforge.event.EventHooks.loadLootTable(rl, lootTable); + if (value != null) -+ map.put(rl, value); ++ map.put(rl, value); + }); map.forEach( (p_335721_, p_335683_) -> writableregistry.register( diff --git a/patches/net/minecraft/server/ReloadableServerResources.java.patch b/patches/net/minecraft/server/ReloadableServerResources.java.patch index 4af3bf71c6..fae77e4d24 100644 --- a/patches/net/minecraft/server/ReloadableServerResources.java.patch +++ b/patches/net/minecraft/server/ReloadableServerResources.java.patch @@ -6,7 +6,7 @@ this.functionLibrary = new ServerFunctionLibrary(p_206859_, this.commands.getDispatcher()); + // Neo: Store registries and create context object + this.registryLookup = p_361583_; -+ this.context = new net.neoforged.neoforge.common.conditions.ConditionContext(this.postponedTags); ++ this.context = new net.neoforged.neoforge.common.conditions.ConditionContext(this.postponedTags, p_250695_); } public ServerFunctionLibrary getFunctionLibrary() { diff --git a/patches/net/minecraft/server/level/ServerEntity.java.patch b/patches/net/minecraft/server/level/ServerEntity.java.patch index 8df4830e9a..4f957b4d61 100644 --- a/patches/net/minecraft/server/level/ServerEntity.java.patch +++ b/patches/net/minecraft/server/level/ServerEntity.java.patch @@ -12,7 +12,7 @@ if (mapitemsaveddata != null) { for (ServerPlayer serverplayer : this.level.players()) { mapitemsaveddata.tickCarriedBy(serverplayer, itemstack); -@@ -274,22 +_,25 @@ +@@ -273,22 +_,25 @@ public void removePairing(ServerPlayer p_8535_) { this.entity.stopSeenByPlayer(p_8535_); p_8535_.connection.send(new ClientboundRemoveEntitiesPacket(this.entity.getId())); diff --git a/patches/net/minecraft/server/level/ServerLevel.java.patch b/patches/net/minecraft/server/level/ServerLevel.java.patch index 64e787851b..bce6a22dcc 100644 --- a/patches/net/minecraft/server/level/ServerLevel.java.patch +++ b/patches/net/minecraft/server/level/ServerLevel.java.patch @@ -1,6 +1,6 @@ --- a/net/minecraft/server/level/ServerLevel.java +++ b/net/minecraft/server/level/ServerLevel.java -@@ -202,7 +_,7 @@ +@@ -203,7 +_,7 @@ private final List customSpawners; @Nullable private EndDragonFight dragonFight; @@ -9,7 +9,7 @@ private final StructureManager structureManager; private final StructureCheck structureCheck; private final boolean tickTime; -@@ -225,7 +_,6 @@ +@@ -226,7 +_,6 @@ super(p_215002_, p_215003_, p_214999_.registryAccess(), p_215004_.type(), false, p_215006_, p_215007_, p_214999_.getMaxChainedNeighborUpdates()); this.tickTime = p_215009_; this.server = p_214999_; @@ -17,7 +17,7 @@ this.serverLevelData = p_215002_; ChunkGenerator chunkgenerator = p_215004_.generator(); boolean flag = p_214999_.forceSynchronousWrites(); -@@ -291,6 +_,11 @@ +@@ -292,6 +_,11 @@ this.randomSequences = Objects.requireNonNullElseGet( p_288977_, () -> this.getDataStorage().computeIfAbsent(RandomSequences.factory(i), "random_sequences") ); @@ -29,7 +29,7 @@ } @Deprecated -@@ -336,7 +_,7 @@ +@@ -337,7 +_,7 @@ if (this.sleepStatus.areEnoughSleeping(i) && this.sleepStatus.areEnoughDeepSleeping(i, this.players)) { if (this.getGameRules().getBoolean(GameRules.RULE_DAYLIGHT)) { long j = this.levelData.getDayTime() + 24000L; @@ -38,7 +38,7 @@ } this.wakeUpAllPlayers(); -@@ -374,7 +_,7 @@ +@@ -375,7 +_,7 @@ this.handlingTick = false; profilerfiller.pop(); @@ -47,7 +47,7 @@ if (flag1) { this.resetEmptyTime(); } -@@ -407,7 +_,9 @@ +@@ -408,7 +_,9 @@ } profilerfiller.push("tick"); @@ -58,7 +58,7 @@ profilerfiller.pop(); } } -@@ -436,7 +_,7 @@ +@@ -437,7 +_,7 @@ this.serverLevelData.getScheduledEvents().tick(this.server, i); Profiler.get().pop(); if (this.serverLevelData.getGameRules().getBoolean(GameRules.RULE_DAYLIGHT)) { @@ -67,7 +67,7 @@ } } } -@@ -534,6 +_,7 @@ +@@ -535,6 +_,7 @@ BlockPos blockpos = this.getHeightmapPos(Heightmap.Types.MOTION_BLOCKING, p_295060_); BlockPos blockpos1 = blockpos.below(); Biome biome = this.getBiome(blockpos).value(); @@ -75,7 +75,7 @@ if (biome.shouldFreeze(this, blockpos1)) { this.setBlockAndUpdate(blockpos1, Blocks.ICE.defaultBlockState()); } -@@ -706,15 +_,19 @@ +@@ -707,15 +_,19 @@ .broadcastAll(new ClientboundGameEventPacket(ClientboundGameEventPacket.THUNDER_LEVEL_CHANGE, this.thunderLevel), this.dimension()); } @@ -99,7 +99,7 @@ } } -@@ -751,7 +_,11 @@ +@@ -752,7 +_,11 @@ p_8648_.tickCount++; profilerfiller.push(() -> BuiltInRegistries.ENTITY_TYPE.getKey(p_8648_.getType()).toString()); profilerfiller.incrementCounter("tickNonPassenger"); @@ -112,7 +112,7 @@ profilerfiller.pop(); for (Entity entity : p_8648_.getPassengers()) { -@@ -800,6 +_,7 @@ +@@ -801,6 +_,7 @@ } else { this.entityManager.autoSave(); } @@ -120,7 +120,7 @@ } } -@@ -894,6 +_,7 @@ +@@ -895,6 +_,7 @@ } private void addPlayer(ServerPlayer p_8854_) { @@ -128,7 +128,7 @@ Entity entity = this.getEntities().get(p_8854_.getUUID()); if (entity != null) { LOGGER.warn("Force-added player with duplicate UUID {}", p_8854_.getUUID()); -@@ -901,7 +_,8 @@ +@@ -902,7 +_,8 @@ this.removePlayerImmediately((ServerPlayer)entity, Entity.RemovalReason.DISCARDED); } @@ -138,7 +138,7 @@ } private boolean addEntity(Entity p_8873_) { -@@ -909,7 +_,12 @@ +@@ -910,7 +_,12 @@ LOGGER.warn("Tried to add entity {} but it was marked as removed already", EntityType.getKey(p_8873_.getType())); return false; } else { @@ -152,7 +152,7 @@ } } -@@ -957,6 +_,12 @@ +@@ -958,6 +_,12 @@ float p_263390_, long p_263403_ ) { @@ -165,7 +165,7 @@ this.server .getPlayerList() .broadcast( -@@ -974,6 +_,12 @@ +@@ -975,6 +_,12 @@ public void playSeededSound( @Nullable Player p_263545_, Entity p_263544_, Holder p_263491_, SoundSource p_263542_, float p_263530_, float p_263520_, long p_263490_ ) { @@ -178,7 +178,7 @@ this.server .getPlayerList() .broadcast( -@@ -1032,6 +_,7 @@ +@@ -1033,6 +_,7 @@ @Override public void gameEvent(Holder p_316597_, Vec3 p_215042_, GameEvent.Context p_215043_) { @@ -186,7 +186,7 @@ this.gameEventDispatcher.post(p_316597_, p_215042_, p_215043_); } -@@ -1070,6 +_,7 @@ +@@ -1071,6 +_,7 @@ @Override public void updateNeighborsAt(BlockPos p_215045_, Block p_215046_) { @@ -194,7 +194,7 @@ this.updateNeighborsAt(p_215045_, p_215046_, ExperimentalRedstoneUtils.initialOrientation(this, null, null)); } -@@ -1080,6 +_,10 @@ +@@ -1081,6 +_,10 @@ @Override public void updateNeighborsAtExceptFromFacing(BlockPos p_215052_, Block p_215053_, Direction p_215054_, @Nullable Orientation p_365231_) { @@ -205,7 +205,7 @@ this.neighborUpdater.updateNeighborsAtExceptFromFacing(p_215052_, p_215053_, p_215054_, p_365231_); } -@@ -1125,7 +_,7 @@ +@@ -1126,7 +_,7 @@ Explosion.BlockInteraction explosion$blockinteraction = switch (p_255827_) { case NONE -> Explosion.BlockInteraction.KEEP; case BLOCK -> this.getDestroyType(GameRules.RULE_BLOCK_EXPLOSION_DROP_DECAY); @@ -214,7 +214,7 @@ ? this.getDestroyType(GameRules.RULE_MOB_EXPLOSION_DROP_DECAY) : Explosion.BlockInteraction.KEEP; case TNT -> this.getDestroyType(GameRules.RULE_TNT_EXPLOSION_DROP_DECAY); -@@ -1133,6 +_,7 @@ +@@ -1134,6 +_,7 @@ }; Vec3 vec3 = new Vec3(p_256067_, p_256370_, p_256153_); ServerExplosion serverexplosion = new ServerExplosion(this, p_256039_, p_255778_, p_256002_, vec3, p_256045_, p_255686_, explosion$blockinteraction); @@ -222,7 +222,16 @@ serverexplosion.explode(); ParticleOptions particleoptions = serverexplosion.isSmall() ? p_312436_ : p_312391_; -@@ -1740,8 +_,8 @@ +@@ -1295,7 +_,7 @@ + } + + @Override +- public Collection dragonParts() { ++ public Collection> dragonParts() { + return this.dragonParts.values(); + } + +@@ -1763,8 +_,8 @@ ServerLevel.this.navigatingMobs.add(mob); } @@ -233,7 +242,7 @@ ServerLevel.this.dragonParts.put(enderdragonpart.getId(), enderdragonpart); } } -@@ -1760,24 +_,106 @@ +@@ -1783,24 +_,101 @@ if (ServerLevel.this.isUpdatingNavigations) { String s = "onTrackingStart called during navigation iteration"; Util.logAndPauseIfInIde( @@ -264,11 +273,6 @@ } } + -+ @Override -+ public java.util.Collection> getPartEntities() { -+ return this.dragonParts.values(); -+ } -+ + private final net.neoforged.neoforge.capabilities.CapabilityListenerHolder capListenerHolder = new net.neoforged.neoforge.capabilities.CapabilityListenerHolder(); + + @Override diff --git a/patches/net/minecraft/server/level/ServerPlayer.java.patch b/patches/net/minecraft/server/level/ServerPlayer.java.patch index a0e3dd567a..11b750dab8 100644 --- a/patches/net/minecraft/server/level/ServerPlayer.java.patch +++ b/patches/net/minecraft/server/level/ServerPlayer.java.patch @@ -1,6 +1,6 @@ --- a/net/minecraft/server/level/ServerPlayer.java +++ b/net/minecraft/server/level/ServerPlayer.java -@@ -278,6 +_,10 @@ +@@ -276,6 +_,10 @@ } private void broadcastDataValue(AbstractContainerMenu p_143455_, int p_143456_, int p_143457_) { @@ -11,7 +11,7 @@ ServerPlayer.this.connection.send(new ClientboundContainerSetDataPacket(p_143455_.containerId, p_143456_, p_143457_)); } }; -@@ -768,6 +_,11 @@ +@@ -766,6 +_,11 @@ this.connection.send(new ClientboundSetExperiencePacket(this.experienceProgress, this.totalExperience, this.experienceLevel)); } @@ -23,7 +23,16 @@ if (this.tickCount % 20 == 0) { CriteriaTriggers.LOCATION.trigger(this); } -@@ -850,6 +_,7 @@ +@@ -779,7 +_,7 @@ + + private void synchronizeSpecialItemUpdates(ItemStack p_372884_) { + MapId mapid = p_372884_.get(DataComponents.MAP_ID); +- MapItemSavedData mapitemsaveddata = MapItem.getSavedData(mapid, this.level()); ++ MapItemSavedData mapitemsaveddata = MapItem.getSavedData(p_372884_, this.level()); + if (mapitemsaveddata != null) { + Packet packet = mapitemsaveddata.getUpdatePacket(mapid, this); + if (packet != null) { +@@ -848,6 +_,7 @@ @Override public void die(DamageSource p_9035_) { this.gameEvent(GameEvent.ENTITY_DIE); @@ -31,7 +40,7 @@ boolean flag = this.serverLevel().getGameRules().getBoolean(GameRules.RULE_SHOWDEATHMESSAGES); if (flag) { Component component = this.getCombatTracker().getDeathMessage(); -@@ -1019,7 +_,7 @@ +@@ -1012,7 +_,7 @@ return BedBlock.findStandUpPosition(EntityType.PLAYER, p_348505_, p_348607_, blockstate.getValue(BedBlock.FACING), p_348481_) .map(p_348148_ -> ServerPlayer.RespawnPosAngle.of(p_348148_, p_348607_)); } else if (!p_348513_) { @@ -40,7 +49,7 @@ } else { boolean flag = block.isPossibleToRespawnInThis(blockstate); BlockState blockstate1 = p_348505_.getBlockState(p_348607_.above()); -@@ -1046,6 +_,7 @@ +@@ -1039,6 +_,7 @@ @Nullable public ServerPlayer teleport(TeleportTransition p_379854_) { @@ -48,7 +57,7 @@ if (this.isRemoved()) { return null; } else { -@@ -1073,7 +_,7 @@ +@@ -1066,7 +_,7 @@ PlayerList playerlist = this.server.getPlayerList(); playerlist.sendPlayerPermissionLevel(this); serverlevel1.removePlayerImmediately(this, Entity.RemovalReason.CHANGED_DIMENSION); @@ -57,7 +66,7 @@ ProfilerFiller profilerfiller = Profiler.get(); profilerfiller.push("moving"); if (resourcekey == Level.OVERWORLD && serverlevel.dimension() == Level.NETHER) { -@@ -1093,10 +_,12 @@ +@@ -1086,10 +_,12 @@ playerlist.sendLevelInfo(this, serverlevel); playerlist.sendAllPlayerInfo(this); playerlist.sendActivePlayerEffects(this); @@ -70,7 +79,7 @@ return this; } } -@@ -1137,6 +_,15 @@ +@@ -1130,6 +_,15 @@ @Override public Either startSleepInBed(BlockPos p_9115_) { @@ -86,7 +95,7 @@ Direction direction = this.level().getBlockState(p_9115_).getValue(HorizontalDirectionalBlock.FACING); if (this.isSleeping() || !this.isAlive()) { return Either.left(Player.BedSleepingProblem.OTHER_PROBLEM); -@@ -1165,7 +_,21 @@ +@@ -1158,7 +_,21 @@ return Either.left(Player.BedSleepingProblem.NOT_SAFE); } } @@ -109,7 +118,7 @@ Either either = super.startSleepInBed(p_9115_).ifRight(p_9029_ -> { this.awardStat(Stats.SLEEP_IN_BED); CriteriaTriggers.SLEPT_IN_BED.trigger(this); -@@ -1187,6 +_,7 @@ +@@ -1180,6 +_,7 @@ } private boolean bedInRange(BlockPos p_9117_, Direction p_9118_) { @@ -117,7 +126,7 @@ return this.isReachableBedBlock(p_9117_) || this.isReachableBedBlock(p_9117_.relative(p_9118_.getOpposite())); } -@@ -1277,11 +_,19 @@ +@@ -1262,11 +_,19 @@ @Override public OptionalInt openMenu(@Nullable MenuProvider p_9033_) { @@ -137,7 +146,7 @@ } this.nextContainerCounter(); -@@ -1293,10 +_,16 @@ +@@ -1278,10 +_,16 @@ return OptionalInt.empty(); } else { @@ -154,7 +163,7 @@ return OptionalInt.of(this.containerCounter); } } -@@ -1318,6 +_,7 @@ +@@ -1303,6 +_,7 @@ this.connection.send(new ClientboundHorseScreenOpenPacket(this.containerCounter, i, p_9059_.getId())); this.containerMenu = new HorseInventoryMenu(this.containerCounter, this.getInventory(), p_9060_, p_9059_, i); this.initMenu(this.containerMenu); @@ -162,7 +171,7 @@ } @Override -@@ -1346,6 +_,7 @@ +@@ -1331,6 +_,7 @@ public void doCloseContainer() { this.containerMenu.removed(this); this.inventoryMenu.transferState(this.containerMenu); @@ -170,7 +179,7 @@ this.containerMenu = this.inventoryMenu; } -@@ -1569,6 +_,15 @@ +@@ -1554,6 +_,15 @@ this.setShoulderEntityLeft(p_9016_.getShoulderEntityLeft()); this.setShoulderEntityRight(p_9016_.getShoulderEntityRight()); this.setLastDeathLocation(p_9016_.getLastDeathLocation()); @@ -186,7 +195,7 @@ } @Override -@@ -1674,6 +_,8 @@ +@@ -1657,6 +_,8 @@ public boolean setGameMode(GameType p_143404_) { boolean flag = this.isSpectator(); @@ -195,7 +204,7 @@ if (!this.gameMode.changeGameModeForPlayer(p_143404_)) { return false; } else { -@@ -1848,6 +_,7 @@ +@@ -1831,6 +_,7 @@ public void setCamera(@Nullable Entity p_9214_) { Entity entity = this.getCamera(); this.camera = (Entity)(p_9214_ == null ? this : p_9214_); @@ -203,7 +212,7 @@ if (entity != this.camera) { if (this.camera.level() instanceof ServerLevel serverlevel) { this.teleportTo(serverlevel, this.camera.getX(), this.camera.getY(), this.camera.getZ(), Set.of(), this.getYRot(), this.getXRot(), false); -@@ -1884,7 +_,11 @@ +@@ -1867,7 +_,11 @@ @Nullable public Component getTabListDisplayName() { @@ -216,7 +225,7 @@ } public int getTabListOrder() { -@@ -1933,6 +_,7 @@ +@@ -1916,6 +_,7 @@ } public void setRespawnPosition(ResourceKey p_9159_, @Nullable BlockPos p_9160_, float p_9161_, boolean p_9162_, boolean p_9163_) { @@ -224,7 +233,7 @@ if (p_9160_ != null) { boolean flag = p_9160_.equals(this.respawnPosition) && p_9159_.equals(this.respawnDimension); if (p_9163_ && !flag) { -@@ -1990,6 +_,8 @@ +@@ -1973,6 +_,8 @@ if (itementity == null) { return null; } else { @@ -233,7 +242,7 @@ this.level().addFreshEntity(itementity); ItemStack itemstack = itementity.getItem(); if (p_9087_) { -@@ -2039,6 +_,75 @@ +@@ -2022,6 +_,75 @@ } } @@ -309,7 +318,7 @@ public TextFilter getTextFilter() { return this.textFilter; } -@@ -2099,9 +_,12 @@ +@@ -2082,9 +_,12 @@ public boolean drop(boolean p_182295_) { Inventory inventory = this.getInventory(); diff --git a/patches/net/minecraft/server/network/ServerGamePacketListenerImpl.java.patch b/patches/net/minecraft/server/network/ServerGamePacketListenerImpl.java.patch index 74247bc5af..8575501ec9 100644 --- a/patches/net/minecraft/server/network/ServerGamePacketListenerImpl.java.patch +++ b/patches/net/minecraft/server/network/ServerGamePacketListenerImpl.java.patch @@ -1,6 +1,6 @@ --- a/net/minecraft/server/network/ServerGamePacketListenerImpl.java +++ b/net/minecraft/server/network/ServerGamePacketListenerImpl.java -@@ -439,9 +_,11 @@ +@@ -441,9 +_,11 @@ } entity.absMoveTo(d3, d4, d5, f, f1); @@ -9,10 +9,10 @@ if (flag && (flag2 || !flag3)) { entity.absMoveTo(d0, d1, d2, f, f1); + resyncPlayerWithVehicle(entity); // Neo - Resync player position on vehicle moving - this.send(new ClientboundMoveVehiclePacket(entity)); + this.send(ClientboundMoveVehiclePacket.fromEntity(entity)); return; } -@@ -463,6 +_,23 @@ +@@ -467,6 +_,23 @@ } } @@ -36,7 +36,16 @@ private boolean noBlocksAround(Entity p_9794_) { return p_9794_.level() .getBlockStates(p_9794_.getBoundingBox().inflate(0.0625).expandTowards(0.0, -0.55, 0.0)) -@@ -930,7 +_,7 @@ +@@ -646,7 +_,7 @@ + if (serverlevel.isLoaded(blockpos)) { + BlockState blockstate = serverlevel.getBlockState(blockpos); + boolean flag = this.player.hasInfiniteMaterials() && p_382999_.includeData(); +- ItemStack itemstack = blockstate.getCloneItemStack(serverlevel, blockpos, flag); ++ ItemStack itemstack = blockstate.getCloneItemStack(blockpos, serverlevel, flag, player); + if (!itemstack.isEmpty()) { + if (flag) { + addBlockDataToItem(blockstate, serverlevel, blockpos, itemstack); +@@ -990,7 +_,7 @@ && !flag1 && this.player.gameMode.getGameModeForPlayer() != GameType.SPECTATOR && !this.server.isFlightAllowed() @@ -45,38 +54,39 @@ && !this.player.hasEffect(MobEffects.LEVITATION) && !flag && !flag3 -@@ -1041,8 +_,10 @@ - case SWAP_ITEM_WITH_OFFHAND: - if (!this.player.isSpectator()) { - ItemStack itemstack = this.player.getItemInHand(InteractionHand.OFF_HAND); -- this.player.setItemInHand(InteractionHand.OFF_HAND, this.player.getItemInHand(InteractionHand.MAIN_HAND)); -- this.player.setItemInHand(InteractionHand.MAIN_HAND, itemstack); -+ var event = net.neoforged.neoforge.common.CommonHooks.onLivingSwapHandItems(this.player); -+ if (event.isCanceled()) return; -+ this.player.setItemInHand(InteractionHand.OFF_HAND, event.getItemSwappedToOffHand()); -+ this.player.setItemInHand(InteractionHand.MAIN_HAND, event.getItemSwappedToMainHand()); - this.player.stopUsingItem(); - } +@@ -1101,9 +_,10 @@ + switch (serverboundplayeractionpacket$action) { + case SWAP_ITEM_WITH_OFFHAND: + if (!this.player.isSpectator()) { +- ItemStack itemstack = this.player.getItemInHand(InteractionHand.OFF_HAND); +- this.player.setItemInHand(InteractionHand.OFF_HAND, this.player.getItemInHand(InteractionHand.MAIN_HAND)); +- this.player.setItemInHand(InteractionHand.MAIN_HAND, itemstack); ++ var event = net.neoforged.neoforge.common.CommonHooks.onLivingSwapHandItems(this.player); ++ if (event.isCanceled()) return; ++ this.player.setItemInHand(InteractionHand.OFF_HAND, event.getItemSwappedToOffHand()); ++ this.player.setItemInHand(InteractionHand.MAIN_HAND, event.getItemSwappedToMainHand()); + this.player.stopUsingItem(); + } -@@ -1070,7 +_,7 @@ - .handleBlockBreakAction( - blockpos, serverboundplayeractionpacket$action, p_9889_.getDirection(), this.player.level().getMaxY(), p_9889_.getSequence() - ); -- this.player.connection.ackBlockChangesUpTo(p_9889_.getSequence()); -+ this.player.connection.ackBlockChangesUpTo =p_9889_.getSequence(); - return; - default: - throw new IllegalArgumentException("Invalid player action"); -@@ -1089,7 +_,7 @@ - @Override +@@ -1131,7 +_,7 @@ + .handleBlockBreakAction( + blockpos, serverboundplayeractionpacket$action, p_9889_.getDirection(), this.player.level().getMaxY(), p_9889_.getSequence() + ); +- this.player.connection.ackBlockChangesUpTo(p_9889_.getSequence()); ++ this.player.connection.ackBlockChangesUpTo = p_9889_.getSequence(); + return; + default: + throw new IllegalArgumentException("Invalid player action"); +@@ -1152,7 +_,7 @@ public void handleUseItemOn(ServerboundUseItemOnPacket p_9930_) { PacketUtils.ensureRunningOnSameThread(p_9930_, this, this.player.serverLevel()); -- this.player.connection.ackBlockChangesUpTo(p_9930_.getSequence()); -+ this.player.connection.ackBlockChangesUpTo = p_9930_.getSequence(); - ServerLevel serverlevel = this.player.serverLevel(); - InteractionHand interactionhand = p_9930_.getHand(); - ItemStack itemstack = this.player.getItemInHand(interactionhand); -@@ -1242,8 +_,9 @@ + if (this.player.hasClientLoaded()) { +- this.player.connection.ackBlockChangesUpTo(p_9930_.getSequence()); ++ this.player.connection.ackBlockChangesUpTo = p_9930_.getSequence(); + ServerLevel serverlevel = this.player.serverLevel(); + InteractionHand interactionhand = p_9930_.getHand(); + ItemStack itemstack = this.player.getItemInHand(interactionhand); +@@ -1308,8 +_,9 @@ } CompletableFuture completablefuture = this.filterTextPacket(playerchatmessage.signedContent()); @@ -87,20 +97,20 @@ PlayerChatMessage playerchatmessage1 = playerchatmessage.withUnsignedContent(component).filter(p_300785_.mask()); this.broadcastChatMessage(playerchatmessage1); }); -@@ -1569,7 +_,11 @@ +@@ -1638,7 +_,11 @@ - @Override - public void onInteraction(InteractionHand p_143682_, Vec3 p_143683_) { -- this.performInteraction(p_143682_, (p_143686_, p_143687_, p_143688_) -> p_143687_.interactAt(p_143686_, p_143683_, p_143688_)); -+ this.performInteraction(p_143682_, (p_143686_, p_143687_, p_143688_) -> { -+ InteractionResult onInteractEntityAtResult = net.neoforged.neoforge.common.CommonHooks.onInteractEntityAt(player, entity, p_143683_, p_143682_); -+ if (onInteractEntityAtResult != null) return onInteractEntityAtResult; -+ return p_143687_.interactAt(p_143686_, p_143683_, p_143688_); -+ }); - } + @Override + public void onInteraction(InteractionHand p_143682_, Vec3 p_143683_) { +- this.performInteraction(p_143682_, (p_143686_, p_143687_, p_143688_) -> p_143687_.interactAt(p_143686_, p_143683_, p_143688_)); ++ this.performInteraction(p_143682_, (p_143686_, p_143687_, p_143688_) -> { ++ InteractionResult onInteractEntityAtResult = net.neoforged.neoforge.common.CommonHooks.onInteractEntityAt(player, entity, p_143683_, p_143682_); ++ if (onInteractEntityAtResult != null) return onInteractEntityAtResult; ++ return p_143687_.interactAt(p_143686_, p_143683_, p_143688_); ++ }); + } - @Override -@@ -1785,13 +_,15 @@ + @Override +@@ -1845,14 +_,16 @@ @Override public void handlePlayerAbilities(ServerboundPlayerAbilitiesPacket p_9887_) { PacketUtils.ensureRunningOnSameThread(p_9887_, this, this.player.serverLevel()); @@ -111,13 +121,14 @@ @Override public void handleClientInformation(ServerboundClientInformationPacket p_301979_) { PacketUtils.ensureRunningOnSameThread(p_301979_, this, this.player.serverLevel()); + boolean flag = this.player.isModelPartShown(PlayerModelPart.HAT); + net.minecraft.server.level.ClientInformation oldInfo = this.player.clientInformation(); this.player.updateOptions(p_301979_.information()); + net.neoforged.neoforge.common.NeoForge.EVENT_BUS.post(new net.neoforged.neoforge.event.entity.player.ClientInformationUpdatedEvent(this.player, oldInfo, p_301979_.information())); - } - - @Override -@@ -1844,7 +_,7 @@ + if (this.player.isModelPartShown(PlayerModelPart.HAT) != flag) { + this.server.getPlayerList().broadcastAll(new ClientboundPlayerInfoUpdatePacket(ClientboundPlayerInfoUpdatePacket.Action.UPDATE_HAT, this.player)); + } +@@ -1908,7 +_,7 @@ this.connection .setupInboundProtocol( ConfigurationProtocols.SERVERBOUND, @@ -126,7 +137,7 @@ ); } } -@@ -1879,6 +_,7 @@ +@@ -1943,6 +_,7 @@ @Override public void handleCustomPayload(ServerboundCustomPayloadPacket p_333887_) { diff --git a/patches/net/minecraft/server/packs/AbstractPackResources.java.patch b/patches/net/minecraft/server/packs/AbstractPackResources.java.patch index 226fe4b923..f30c56201a 100644 --- a/patches/net/minecraft/server/packs/AbstractPackResources.java.patch +++ b/patches/net/minecraft/server/packs/AbstractPackResources.java.patch @@ -1,6 +1,6 @@ --- a/net/minecraft/server/packs/AbstractPackResources.java +++ b/net/minecraft/server/packs/AbstractPackResources.java -@@ -63,4 +_,9 @@ +@@ -62,4 +_,9 @@ public PackLocationInfo location() { return this.location; } diff --git a/patches/net/minecraft/server/packs/OverlayMetadataSection.java.patch b/patches/net/minecraft/server/packs/OverlayMetadataSection.java.patch index 4e52d4269f..e34b9d3ba2 100644 --- a/patches/net/minecraft/server/packs/OverlayMetadataSection.java.patch +++ b/patches/net/minecraft/server/packs/OverlayMetadataSection.java.patch @@ -8,10 +8,10 @@ + p_294898_ -> p_294898_.group(net.neoforged.neoforge.common.conditions.ConditionalOps.decodeListWithElementConditions(OverlayMetadataSection.OverlayEntry.CODEC).fieldOf("entries").forGetter(OverlayMetadataSection::overlays)) .apply(p_294898_, OverlayMetadataSection::new) ); - public static final MetadataSectionType TYPE = MetadataSectionType.fromCodec("overlays", CODEC); + public static final MetadataSectionType TYPE = new MetadataSectionType<>("overlays", CODEC); + // Neo: alternative metadata section that will only be loaded on neoforged. Useful for making datapacks with special + // logic on different modloaders, or when running on neo vs in vanilla, without having to invert the main pack and overlays -+ public static final MetadataSectionType NEOFORGE_TYPE = MetadataSectionType.fromCodec("neoforge:overlays", CODEC); ++ public static final MetadataSectionType NEOFORGE_TYPE = new MetadataSectionType<>("neoforge:overlays", CODEC); private static DataResult validateOverlayDir(String p_296447_) { return !DIR_VALIDATOR.matcher(p_296447_).matches() diff --git a/patches/net/minecraft/server/packs/metadata/pack/PackMetadataSection.java.patch b/patches/net/minecraft/server/packs/metadata/pack/PackMetadataSection.java.patch index e34e6e1a5a..cb6dd83a17 100644 --- a/patches/net/minecraft/server/packs/metadata/pack/PackMetadataSection.java.patch +++ b/patches/net/minecraft/server/packs/metadata/pack/PackMetadataSection.java.patch @@ -3,7 +3,7 @@ @@ -19,4 +_,8 @@ .apply(p_337567_, PackMetadataSection::new) ); - public static final MetadataSectionType TYPE = MetadataSectionType.fromCodec("pack", CODEC); + public static final MetadataSectionType TYPE = new MetadataSectionType<>("pack", CODEC); + + public PackMetadataSection(Component description, int packVersion) { + this(description, packVersion, Optional.empty()); diff --git a/patches/net/minecraft/server/packs/resources/SimpleJsonResourceReloadListener.java.patch b/patches/net/minecraft/server/packs/resources/SimpleJsonResourceReloadListener.java.patch index 3745422f2c..90886b7c44 100644 --- a/patches/net/minecraft/server/packs/resources/SimpleJsonResourceReloadListener.java.patch +++ b/patches/net/minecraft/server/packs/resources/SimpleJsonResourceReloadListener.java.patch @@ -1,24 +1,36 @@ --- a/net/minecraft/server/packs/resources/SimpleJsonResourceReloadListener.java +++ b/net/minecraft/server/packs/resources/SimpleJsonResourceReloadListener.java -@@ -41,13 +_,15 @@ +@@ -43,10 +_,21 @@ protected Map prepare(ResourceManager p_10771_, ProfilerFiller p_10772_) { Map map = new HashMap<>(); -- scanDirectory(p_10771_, this.directory, this.ops, this.codec, map); +- scanDirectory(p_10771_, this.lister, this.ops, this.codec, map); + // Neo: add condition context -+ scanDirectory(p_10771_, this.directory, this.makeConditionalOps(), this.codec, map); ++ scanDirectory(p_10771_, this.lister, this.makeConditionalOps(this.ops), this.codec, map); return map; } ++ public static void scanDirectoryWithOptionalValues( ++ ResourceManager p_386974_, ++ ResourceKey> p_388878_, ++ DynamicOps p_388402_, ++ Codec> p_387608_, ++ Map> p_386495_ ++ ) { ++ scanDirectory(p_386974_, FileToIdConverter.registry(p_388878_), p_388402_, p_387608_, p_386495_); ++ } ++ public static void scanDirectory( - ResourceManager p_279308_, String p_279131_, DynamicOps p_371830_, Codec p_371493_, Map p_279404_ + ResourceManager p_386974_, + ResourceKey> p_388878_, +@@ -60,13 +_,16 @@ + public static void scanDirectory( + ResourceManager p_279308_, FileToIdConverter p_387906_, DynamicOps p_371830_, Codec p_371493_, Map p_279404_ ) { + var conditionalCodec = net.neoforged.neoforge.common.conditions.ConditionalOps.createConditionalCodec(p_371493_); - FileToIdConverter filetoidconverter = FileToIdConverter.json(p_279131_); - - for (Entry entry : filetoidconverter.listMatchingResources(p_279308_).entrySet()) { -@@ -55,8 +_,10 @@ - ResourceLocation resourcelocation1 = filetoidconverter.fileToId(resourcelocation); + for (Entry entry : p_387906_.listMatchingResources(p_279308_).entrySet()) { + ResourceLocation resourcelocation = entry.getKey(); + ResourceLocation resourcelocation1 = p_387906_.fileToId(resourcelocation); try (Reader reader = entry.getValue().openAsReader()) { - p_371493_.parse(p_371830_, JsonParser.parseReader(reader)).ifSuccess(p_371454_ -> { @@ -30,13 +42,13 @@ throw new IllegalStateException("Duplicate data file ignored with ID " + resourcelocation1); } }).ifError(p_371566_ -> LOGGER.error("Couldn't parse data file '{}' from '{}': {}", resourcelocation1, resourcelocation, p_371566_)); -@@ -64,5 +_,9 @@ +@@ -74,5 +_,9 @@ LOGGER.error("Couldn't parse data file '{}' from '{}'", resourcelocation1, resourcelocation, jsonparseexception); } } + } + + protected ResourceLocation getPreparedPath(ResourceLocation rl) { -+ return rl.withPath(this.directory + "/" + rl.getPath() + ".json"); ++ return this.lister.idToFile(rl); } } diff --git a/patches/net/minecraft/tags/ItemTags.java.patch b/patches/net/minecraft/tags/ItemTags.java.patch index 9dd688d2d9..4b22abdb66 100644 --- a/patches/net/minecraft/tags/ItemTags.java.patch +++ b/patches/net/minecraft/tags/ItemTags.java.patch @@ -1,6 +1,6 @@ --- a/net/minecraft/tags/ItemTags.java +++ b/net/minecraft/tags/ItemTags.java -@@ -183,4 +_,8 @@ +@@ -185,4 +_,8 @@ private static TagKey bind(String p_203855_) { return TagKey.create(Registries.ITEM, ResourceLocation.withDefaultNamespace(p_203855_)); } diff --git a/patches/net/minecraft/util/SpawnUtil.java.patch b/patches/net/minecraft/util/SpawnUtil.java.patch index 558cc70a2a..e39af19e21 100644 --- a/patches/net/minecraft/util/SpawnUtil.java.patch +++ b/patches/net/minecraft/util/SpawnUtil.java.patch @@ -1,11 +1,11 @@ --- a/net/minecraft/util/SpawnUtil.java +++ b/net/minecraft/util/SpawnUtil.java -@@ -36,7 +_,7 @@ - && moveToPossibleSpawnPosition(p_216406_, p_216410_, blockpos$mutableblockpos, p_216411_)) { +@@ -47,7 +_,7 @@ + )) { T t = (T)p_216404_.create(p_216406_, null, blockpos$mutableblockpos, p_364255_, false, false); if (t != null) { - if (t.checkSpawnRules(p_216406_, p_364255_) && t.checkSpawnObstruction(p_216406_)) { + if (net.neoforged.neoforge.event.EventHooks.checkSpawnPosition(t, p_216406_, p_364255_)) { p_216406_.addFreshEntityWithPassengers(t); + t.playAmbientSound(); return Optional.of(t); - } diff --git a/patches/net/minecraft/util/context/ContextKeySet.java.patch b/patches/net/minecraft/util/context/ContextKeySet.java.patch new file mode 100644 index 0000000000..206d38b786 --- /dev/null +++ b/patches/net/minecraft/util/context/ContextKeySet.java.patch @@ -0,0 +1,12 @@ +--- a/net/minecraft/util/context/ContextKeySet.java ++++ b/net/minecraft/util/context/ContextKeySet.java +@@ -5,6 +_,9 @@ + import java.util.Set; + + public class ContextKeySet { ++ // Neo: Add EMPTY context set for convenience, since we disable the check for `required` keys in ContextMap.Builder. ++ public static final ContextKeySet EMPTY = new Builder().build(); ++ + private final Set> required; + private final Set> allowed; + diff --git a/patches/net/minecraft/util/context/ContextMap.java.patch b/patches/net/minecraft/util/context/ContextMap.java.patch index 86160aa256..2b800da6a3 100644 --- a/patches/net/minecraft/util/context/ContextMap.java.patch +++ b/patches/net/minecraft/util/context/ContextMap.java.patch @@ -1,5 +1,15 @@ --- a/net/minecraft/util/context/ContextMap.java +++ b/net/minecraft/util/context/ContextMap.java +@@ -9,6 +_,9 @@ + import org.jetbrains.annotations.Contract; + + public class ContextMap { ++ // Neo: Add EMPTY map for convenience. ++ public static final ContextMap EMPTY = new Builder().create(ContextKeySet.EMPTY); ++ + private final Map, Object> params; + + ContextMap(Map, Object> p_381157_) { @@ -73,7 +_,7 @@ public ContextMap create(ContextKeySet p_381168_) { diff --git a/patches/net/minecraft/util/datafix/DataFixers.java.patch b/patches/net/minecraft/util/datafix/DataFixers.java.patch index 5ab30c4c68..4baa96d57b 100644 --- a/patches/net/minecraft/util/datafix/DataFixers.java.patch +++ b/patches/net/minecraft/util/datafix/DataFixers.java.patch @@ -1,9 +1,9 @@ --- a/net/minecraft/util/datafix/DataFixers.java +++ b/net/minecraft/util/datafix/DataFixers.java -@@ -1308,10 +_,35 @@ - Schema schema199 = p_14514_.addSchema(3800, SAME_NAMESPACED); +@@ -1314,10 +_,35 @@ + Schema schema198 = p_14514_.addSchema(3800, SAME_NAMESPACED); UnaryOperator unaryoperator2 = createRenamer(Map.of("minecraft:scute", "minecraft:turtle_scute")); - p_14514_.addFixer(ItemRenameFix.create(schema199, "Rename scute item to turtle_scute", unaryoperator2)); + p_14514_.addFixer(ItemRenameFix.create(schema198, "Rename scute item to turtle_scute", unaryoperator2)); + + // Neo: Remove step height attribute, which cannot be sanely updated + // Happens in 23w51a @@ -16,9 +16,9 @@ + ) + )); + - Schema schema200 = p_14514_.addSchema(3803, SAME_NAMESPACED); + Schema schema199 = p_14514_.addSchema(3803, SAME_NAMESPACED); p_14514_.addFixer( - new RenameEnchantmentsFix(schema200, "Rename sweeping enchant to sweeping_edge", Map.of("minecraft:sweeping", "minecraft:sweeping_edge")) + new RenameEnchantmentsFix(schema199, "Rename sweeping enchant to sweeping_edge", Map.of("minecraft:sweeping", "minecraft:sweeping_edge")) ); + + // Neo: rename neo attributes to new MC attributes @@ -33,11 +33,11 @@ + )) + )); + - Schema schema201 = p_14514_.addSchema(3807, V3807::new); - p_14514_.addFixer(new AddNewChoices(schema201, "Added Vault", References.BLOCK_ENTITY)); - Schema schema202 = p_14514_.addSchema(3807, 1, SAME_NAMESPACED); -@@ -1334,6 +_,18 @@ - schema209, "Rename jump strength attribute", createRenamer("minecraft:horse.jump_strength", "minecraft:generic.jump_strength") + Schema schema200 = p_14514_.addSchema(3807, V3807::new); + p_14514_.addFixer(new AddNewChoices(schema200, "Added Vault", References.BLOCK_ENTITY)); + Schema schema201 = p_14514_.addSchema(3807, 1, SAME_NAMESPACED); +@@ -1340,6 +_,18 @@ + schema208, "Rename jump strength attribute", createRenamer("minecraft:horse.jump_strength", "minecraft:generic.jump_strength") ) ); + @@ -52,6 +52,6 @@ + ) + )); + - Schema schema210 = p_14514_.addSchema(3816, V3816::new); - p_14514_.addFixer(new AddNewChoices(schema210, "Added Bogged", References.ENTITY)); - Schema schema211 = p_14514_.addSchema(3818, V3818::new); + Schema schema209 = p_14514_.addSchema(3816, V3816::new); + p_14514_.addFixer(new AddNewChoices(schema209, "Added Bogged", References.ENTITY)); + Schema schema210 = p_14514_.addSchema(3818, V3818::new); diff --git a/patches/net/minecraft/world/effect/MobEffect.java.patch b/patches/net/minecraft/world/effect/MobEffect.java.patch index 1daf7ed3ab..ed25ba16ea 100644 --- a/patches/net/minecraft/world/effect/MobEffect.java.patch +++ b/patches/net/minecraft/world/effect/MobEffect.java.patch @@ -49,8 +49,8 @@ return this.requiredFeatures; } -- static record AttributeTemplate(ResourceLocation id, double amount, AttributeModifier.Operation operation) { -+ static record AttributeTemplate(ResourceLocation id, double amount, AttributeModifier.Operation operation, @Nullable it.unimi.dsi.fastutil.ints.Int2DoubleFunction curve) { +- public static record AttributeTemplate(ResourceLocation id, double amount, AttributeModifier.Operation operation) { ++ public static record AttributeTemplate(ResourceLocation id, double amount, AttributeModifier.Operation operation, @Nullable it.unimi.dsi.fastutil.ints.Int2DoubleFunction curve) { + + public AttributeTemplate(ResourceLocation id, double amount, AttributeModifier.Operation operation) { + this(id, amount, operation, null); diff --git a/patches/net/minecraft/world/effect/PoisonMobEffect.java.patch b/patches/net/minecraft/world/effect/PoisonMobEffect.java.patch index d2c1139e5e..b85aced5a1 100644 --- a/patches/net/minecraft/world/effect/PoisonMobEffect.java.patch +++ b/patches/net/minecraft/world/effect/PoisonMobEffect.java.patch @@ -1,6 +1,6 @@ --- a/net/minecraft/world/effect/PoisonMobEffect.java +++ b/net/minecraft/world/effect/PoisonMobEffect.java -@@ -11,7 +_,12 @@ +@@ -13,7 +_,12 @@ @Override public boolean applyEffectTick(ServerLevel p_376442_, LivingEntity p_296276_, int p_296233_) { if (p_296276_.getHealth() > 1.0F) { diff --git a/patches/net/minecraft/world/entity/Entity.java.patch b/patches/net/minecraft/world/entity/Entity.java.patch index c578716b29..f452fe7ba1 100644 --- a/patches/net/minecraft/world/entity/Entity.java.patch +++ b/patches/net/minecraft/world/entity/Entity.java.patch @@ -39,7 +39,7 @@ } public boolean isColliding(BlockPos p_20040_, BlockState p_20041_) { -@@ -478,7 +_,7 @@ +@@ -482,7 +_,7 @@ if (this.isInLava()) { this.lavaHurt(); @@ -48,7 +48,7 @@ } this.checkBelowWorld(); -@@ -760,12 +_,12 @@ +@@ -772,12 +_,12 @@ this.setRemainingFireTicks(-this.getFireImmuneTicks()); } @@ -63,7 +63,7 @@ this.setRemainingFireTicks(-this.getFireImmuneTicks()); } } -@@ -845,9 +_,7 @@ +@@ -857,9 +_,7 @@ return blockpos; } else { BlockState blockstate = this.level().getBlockState(blockpos); @@ -74,7 +74,7 @@ ? blockpos.atY(Mth.floor(this.position.y - (double)p_216987_)) : blockpos; } -@@ -1129,19 +_,19 @@ +@@ -1141,19 +_,19 @@ return !blockstate.is(BlockTags.INSIDE_STEP_SOUND_BLOCKS) && !blockstate.is(BlockTags.COMBINATION_STEP_SOUND_BLOCKS) ? p_278049_ : blockpos; } @@ -100,7 +100,7 @@ this.playSound(soundtype.getStepSound(), soundtype.getVolume() * 0.15F, soundtype.getPitch()); } -@@ -1294,20 +_,23 @@ +@@ -1315,20 +_,23 @@ public void updateSwimming() { if (this.isSwimming()) { @@ -129,7 +129,7 @@ } void updateInWaterStateAndDoWaterCurrentPushing() { -@@ -1332,6 +_,7 @@ +@@ -1353,6 +_,7 @@ private void updateFluidOnEyes() { this.wasEyeInWater = this.isEyeInFluid(FluidTags.WATER); this.fluidOnEyes.clear(); @@ -137,7 +137,7 @@ double d0 = this.getEyeY(); if (this.getVehicle() instanceof AbstractBoat abstractboat && !abstractboat.isUnderWater() -@@ -1344,7 +_,7 @@ +@@ -1365,7 +_,7 @@ FluidState fluidstate = this.level().getFluidState(blockpos); double d1 = (double)((float)blockpos.getY() + fluidstate.getHeight(this.level(), blockpos)); if (d1 > d0) { @@ -146,7 +146,7 @@ } } -@@ -1389,12 +_,13 @@ +@@ -1410,12 +_,13 @@ } public boolean canSpawnSprintParticle() { @@ -161,7 +161,7 @@ if (blockstate.getRenderShape() != RenderShape.INVISIBLE) { Vec3 vec3 = this.getDeltaMovement(); BlockPos blockpos1 = this.blockPosition(); -@@ -1408,16 +_,19 @@ +@@ -1429,16 +_,19 @@ d1 = Mth.clamp(d1, (double)blockpos.getZ(), (double)blockpos.getZ() + 1.0); } @@ -183,7 +183,7 @@ } public void moveRelative(float p_19921_, Vec3 p_19922_) { -@@ -1796,6 +_,10 @@ +@@ -1817,6 +_,10 @@ p_20241_.put("Tags", listtag); } @@ -194,7 +194,7 @@ this.addAdditionalSaveData(p_20241_); if (this.isVehicle()) { ListTag listtag1 = new ListTag(); -@@ -1877,6 +_,8 @@ +@@ -1900,6 +_,8 @@ this.setGlowingTag(p_20259_.getBoolean("Glowing")); this.setTicksFrozen(p_20259_.getInt("TicksFrozen")); this.hasVisualFire = p_20259_.getBoolean("HasVisualFire"); @@ -203,7 +203,7 @@ if (p_20259_.contains("Tags", 9)) { this.tags.clear(); ListTag listtag3 = p_20259_.getList("Tags", 8); -@@ -1959,6 +_,8 @@ +@@ -1982,6 +_,8 @@ } else { ItemEntity itementity = new ItemEntity(p_376141_, this.getX(), this.getY() + (double)p_376881_, this.getZ(), p_376472_); itementity.setDefaultPickUpDelay(); @@ -212,7 +212,7 @@ p_376141_.addFreshEntity(itementity); return itementity; } -@@ -2026,7 +_,11 @@ +@@ -2054,7 +_,11 @@ public void rideTick() { this.setDeltaMovement(Vec3.ZERO); @@ -225,7 +225,7 @@ if (this.isPassenger()) { this.getVehicle().positionRider(this); } -@@ -2086,6 +_,7 @@ +@@ -2114,6 +_,7 @@ } } @@ -233,7 +233,7 @@ if (p_19967_ || this.canRide(p_19966_) && p_19966_.canAddPassenger(this)) { if (this.isPassenger()) { this.stopRiding(); -@@ -2117,6 +_,7 @@ +@@ -2145,6 +_,7 @@ public void removeVehicle() { if (this.vehicle != null) { Entity entity = this.vehicle; @@ -241,7 +241,7 @@ this.vehicle = null; entity.removePassenger(this); } -@@ -2166,6 +_,8 @@ +@@ -2194,6 +_,8 @@ return this.passengers.isEmpty(); } @@ -250,7 +250,7 @@ protected boolean couldAcceptPassenger() { return true; } -@@ -2354,7 +_,7 @@ +@@ -2382,7 +_,7 @@ } public boolean isVisuallyCrawling() { @@ -259,7 +259,7 @@ } public void setSwimming(boolean p_20283_) { -@@ -2467,7 +_,7 @@ +@@ -2495,7 +_,7 @@ this.igniteForSeconds(8.0F); } @@ -268,7 +268,7 @@ } public void onAboveBubbleCol(boolean p_20313_) { -@@ -2562,7 +_,7 @@ +@@ -2590,7 +_,7 @@ } protected Component getTypeName() { @@ -277,7 +277,7 @@ } public boolean is(Entity p_20356_) { -@@ -2617,10 +_,11 @@ +@@ -2645,10 +_,11 @@ } protected final boolean isInvulnerableToBase(DamageSource p_20122_) { @@ -290,7 +290,7 @@ } public boolean isInvulnerable() { -@@ -2645,6 +_,7 @@ +@@ -2673,6 +_,7 @@ @Nullable public Entity teleport(TeleportTransition p_379899_) { @@ -298,7 +298,7 @@ if (this.level() instanceof ServerLevel serverlevel && !this.isRemoved()) { ServerLevel serverlevel1 = p_379899_.newLevel(); boolean flag = serverlevel1.dimension() != serverlevel.dimension(); -@@ -2852,6 +_,7 @@ +@@ -2880,6 +_,7 @@ return this.stringUUID; } @@ -306,7 +306,7 @@ public boolean isPushedByFluid() { return true; } -@@ -2960,6 +_,8 @@ +@@ -2988,6 +_,8 @@ EntityDimensions entitydimensions = this.dimensions; Pose pose = this.getPose(); EntityDimensions entitydimensions1 = this.getDimensions(pose); @@ -315,7 +315,7 @@ this.dimensions = entitydimensions1; this.eyeHeight = entitydimensions1.eyeHeight(); this.reapplyPosition(); -@@ -3265,9 +_,17 @@ +@@ -3289,9 +_,17 @@ return Mth.lerp(p_352259_, this.yRotO, this.yRot); } @@ -334,7 +334,7 @@ } else { AABB aabb = this.getBoundingBox().deflate(0.001); int i = Mth.floor(aabb.minX); -@@ -3282,25 +_,36 @@ +@@ -3306,25 +_,36 @@ Vec3 vec3 = Vec3.ZERO; int k1 = 0; BlockPos.MutableBlockPos blockpos$mutableblockpos = new BlockPos.MutableBlockPos(); @@ -378,7 +378,7 @@ } } } -@@ -3308,27 +_,30 @@ +@@ -3332,27 +_,30 @@ } } @@ -419,7 +419,7 @@ } } -@@ -3341,7 +_,10 @@ +@@ -3365,7 +_,10 @@ return !this.level().hasChunksAt(i, k, j, l); } @@ -430,7 +430,7 @@ return this.fluidHeight.getDouble(p_204037_); } -@@ -3478,6 +_,7 @@ +@@ -3502,6 +_,7 @@ this.levelCallback.onMove(); } @@ -438,7 +438,7 @@ } public void checkDespawn() { -@@ -3603,6 +_,128 @@ +@@ -3627,6 +_,128 @@ public boolean mayInteract(ServerLevel p_376870_, BlockPos p_146844_) { return true; diff --git a/patches/net/minecraft/world/entity/EntityType.java.patch b/patches/net/minecraft/world/entity/EntityType.java.patch index 4439873672..8f8b38efa5 100644 --- a/patches/net/minecraft/world/entity/EntityType.java.patch +++ b/patches/net/minecraft/world/entity/EntityType.java.patch @@ -1,6 +1,6 @@ --- a/net/minecraft/world/entity/EntityType.java +++ b/net/minecraft/world/entity/EntityType.java -@@ -1088,6 +_,10 @@ +@@ -1070,6 +_,10 @@ private final float spawnDimensionsScale; private final FeatureFlagSet requiredFeatures; @@ -11,7 +11,7 @@ private static EntityType register(ResourceKey> p_368669_, EntityType.Builder p_368714_) { return Registry.register(BuiltInRegistries.ENTITY_TYPE, p_368669_, p_368714_.build(p_368669_)); } -@@ -1124,6 +_,28 @@ +@@ -1106,6 +_,28 @@ Optional> p_368696_, FeatureFlagSet p_273518_ ) { @@ -40,7 +40,7 @@ this.factory = p_273268_; this.category = p_272918_; this.canSpawnFarFromPlayer = p_272654_; -@@ -1138,6 +_,9 @@ +@@ -1120,6 +_,9 @@ this.descriptionId = p_368582_; this.lootTable = p_368696_; this.requiredFeatures = p_273518_; @@ -50,11 +50,10 @@ } @Nullable -@@ -1228,6 +_,15 @@ +@@ -1213,6 +_,13 @@ mob.yHeadRot = mob.getYRot(); mob.yBodyRot = mob.getYRot(); mob.finalizeSpawn(p_262637_, p_262637_.getCurrentDifficultyAt(mob.blockPosition()), p_360546_, null); -+ + if (mob.isSpawnCancelled()) { + // Neo: Discard mob, spawn was cancelled + mob.discard(); @@ -62,11 +61,10 @@ + // fixes llamas for wandering trader spawning if wandering trader was cancelled + return null; + } -+ - mob.playAmbientSound(); } -@@ -1410,14 +_,23 @@ + if (p_262629_ != null) { +@@ -1399,14 +_,23 @@ } public int clientTrackingRange() { @@ -90,8 +88,8 @@ return this != PLAYER && this != LLAMA_SPIT && this != WITHER -@@ -1469,6 +_,8 @@ - return (p_375561_, p_375562_) -> new ChestRaft(p_375561_, p_375562_, p_376648_); +@@ -1462,6 +_,8 @@ + return OP_ONLY_CUSTOM_DATA.contains(this); } + public Stream>> getTags() {return this.builtInRegistryHolder().tags();} @@ -99,7 +97,7 @@ public static class Builder { private final EntityType.EntityFactory factory; private final MobCategory category; -@@ -1488,6 +_,10 @@ +@@ -1481,6 +_,10 @@ ); private DependantName, String> descriptionId = p_367918_ -> Util.makeDescriptionId("entity", p_367918_.location()); @@ -110,7 +108,7 @@ private Builder(EntityType.EntityFactory p_20696_, MobCategory p_20697_) { this.factory = p_20696_; this.category = p_20697_; -@@ -1600,6 +_,21 @@ +@@ -1593,6 +_,21 @@ return this; } @@ -132,7 +130,7 @@ public EntityType build(ResourceKey> p_368626_) { if (this.serialize) { Util.fetchChoiceType(References.ENTITY_TREE, p_368626_.location().toString()); -@@ -1619,7 +_,10 @@ +@@ -1612,7 +_,10 @@ this.updateInterval, this.descriptionId.get(p_368626_), this.lootTable.get(p_368626_), diff --git a/patches/net/minecraft/world/entity/ExperienceOrb.java.patch b/patches/net/minecraft/world/entity/ExperienceOrb.java.patch index 9a5df1bf49..673882035f 100644 --- a/patches/net/minecraft/world/entity/ExperienceOrb.java.patch +++ b/patches/net/minecraft/world/entity/ExperienceOrb.java.patch @@ -1,6 +1,6 @@ --- a/net/minecraft/world/entity/ExperienceOrb.java +++ b/net/minecraft/world/entity/ExperienceOrb.java -@@ -113,7 +_,8 @@ +@@ -114,7 +_,8 @@ this.applyEffectsFromBlocks(); float f = 0.98F; if (this.onGround()) { @@ -10,7 +10,7 @@ } this.setDeltaMovement(this.getDeltaMovement().multiply((double)f, 0.98, (double)f)); -@@ -134,7 +_,7 @@ +@@ -135,7 +_,7 @@ private void scanForEntities() { if (this.followingPlayer == null || this.followingPlayer.distanceToSqr(this) > 64.0) { @@ -19,7 +19,7 @@ } if (this.level() instanceof ServerLevel) { -@@ -232,6 +_,7 @@ +@@ -233,6 +_,7 @@ public void playerTouch(Player p_20792_) { if (p_20792_ instanceof ServerPlayer serverplayer) { if (p_20792_.takeXpDelay == 0) { @@ -27,7 +27,7 @@ p_20792_.takeXpDelay = 2; p_20792_.take(this, 1); int i = this.repairPlayerItems(serverplayer, this.value); -@@ -251,7 +_,7 @@ +@@ -252,7 +_,7 @@ Optional optional = EnchantmentHelper.getRandomItemWith(EnchantmentEffectComponents.REPAIR_WITH_XP, p_344821_, ItemStack::isDamaged); if (optional.isPresent()) { ItemStack itemstack = optional.get().itemStack(); diff --git a/patches/net/minecraft/world/entity/LivingEntity.java.patch b/patches/net/minecraft/world/entity/LivingEntity.java.patch index d9bcc4aaa2..d8a6e72d12 100644 --- a/patches/net/minecraft/world/entity/LivingEntity.java.patch +++ b/patches/net/minecraft/world/entity/LivingEntity.java.patch @@ -1,6 +1,6 @@ --- a/net/minecraft/world/entity/LivingEntity.java +++ b/net/minecraft/world/entity/LivingEntity.java -@@ -137,7 +_,7 @@ +@@ -136,7 +_,7 @@ import net.minecraft.world.scores.Scoreboard; import org.slf4j.Logger; @@ -9,7 +9,7 @@ private static final Logger LOGGER = LogUtils.getLogger(); private static final String TAG_ACTIVE_EFFECTS = "active_effects"; private static final ResourceLocation SPEED_MODIFIER_POWDER_SNOW_ID = ResourceLocation.withDefaultNamespace("powder_snow"); -@@ -179,13 +_,18 @@ +@@ -178,13 +_,18 @@ public static final float EXTRA_RENDER_CULLING_SIZE_WITH_BIG_HAT = 0.5F; public static final float DEFAULT_BABY_SCALE = 0.5F; public static final String ATTRIBUTES_FIELD = "attributes"; @@ -30,7 +30,7 @@ }; private final AttributeMap attributes; private final CombatTracker combatTracker = new CombatTracker(this); -@@ -268,6 +_,14 @@ +@@ -266,6 +_,14 @@ EquipmentSlot.class ); protected float appliedScale = 1.0F; @@ -45,7 +45,7 @@ protected LivingEntity(EntityType p_20966_, Level p_20967_) { super(p_20966_, p_20967_); -@@ -334,7 +_,9 @@ +@@ -332,7 +_,9 @@ .add(Attributes.EXPLOSION_KNOCKBACK_RESISTANCE) .add(Attributes.WATER_MOVEMENT_EFFICIENCY) .add(Attributes.MOVEMENT_EFFICIENCY) @@ -56,7 +56,7 @@ } @Override -@@ -362,7 +_,8 @@ +@@ -360,7 +_,8 @@ float f = (float)Mth.ceil((double)this.fallDistance - d7); double d4 = Math.min((double)(0.2F + f / 15.0F), 2.5); int i = (int)(150.0 * d4); @@ -66,7 +66,7 @@ } } -@@ -372,6 +_,7 @@ +@@ -370,6 +_,7 @@ } } @@ -74,7 +74,7 @@ public final boolean canBreatheUnderwater() { return this.getType().is(EntityTypeTags.CAN_BREATHE_UNDER_WATER); } -@@ -418,6 +_,9 @@ +@@ -416,6 +_,9 @@ } } @@ -258,22 +258,10 @@ } + p_376610_ = this.damageContainers.peek().getNewDamage(); //update local with container value - Entity entity = p_376460_.getEntity(); - if (entity != null) { - if (entity instanceof LivingEntity livingentity1 -@@ -1164,9 +_,9 @@ - if (entity instanceof Player player1) { - this.lastHurtByPlayerTime = 100; - this.lastHurtByPlayer = player1; -- } else if (entity instanceof Wolf wolf && wolf.isTame()) { -+ } else if (entity instanceof TamableAnimal tamableAnimal && tamableAnimal.isTame()) { - this.lastHurtByPlayerTime = 100; -- if (wolf.getOwner() instanceof Player player) { -+ if (tamableAnimal.getOwner() instanceof Player player) { - this.lastHurtByPlayer = player; - } else { - this.lastHurtByPlayer = null; -@@ -1237,6 +_,7 @@ + this.resolveMobResponsibleForDamage(p_376460_); + this.resolvePlayerResponsibleForDamage(p_376460_); + if (flag1) { +@@ -1218,6 +_,7 @@ CriteriaTriggers.PLAYER_HURT_ENTITY.trigger(serverplayer1, this, p_376460_, f, p_376610_, flag); } @@ -281,7 +269,19 @@ return flag2; } } -@@ -1259,7 +_,7 @@ +@@ -1238,9 +_,9 @@ + this.lastHurtByPlayer = player; + return player; + } else { +- if (entity instanceof Wolf wolf && wolf.isTame()) { ++ if (entity instanceof TamableAnimal tamableAnimal && tamableAnimal.isTame()) { + this.lastHurtByPlayerTime = 100; +- if (wolf.getOwner() instanceof Player player1) { ++ if (tamableAnimal.getOwner() instanceof Player player1) { + this.lastHurtByPlayer = player1; + } else { + this.lastHurtByPlayer = null; +@@ -1271,7 +_,7 @@ for (InteractionHand interactionhand : InteractionHand.values()) { ItemStack itemstack1 = this.getItemInHand(interactionhand); deathprotection = itemstack1.get(DataComponents.DEATH_PROTECTION); @@ -290,7 +290,16 @@ itemstack = itemstack1.copy(); itemstack1.shrink(1); break; -@@ -1343,6 +_,7 @@ +@@ -1321,7 +_,7 @@ + } + + ItemStack itemstack = this.getItemBlockingWith(); +- if (!p_21276_.is(DamageTypeTags.BYPASSES_SHIELD) && itemstack != null && itemstack.getItem() instanceof ShieldItem && !flag) { ++ if (!p_21276_.is(DamageTypeTags.BYPASSES_SHIELD) && itemstack != null && !flag) { + Vec3 vec3 = p_21276_.getSourcePosition(); + if (vec3 != null) { + Vec3 vec31 = this.calculateViewVector(0.0F, this.getYHeadRot()); +@@ -1355,6 +_,7 @@ } public void die(DamageSource p_21014_) { @@ -298,7 +307,7 @@ if (!this.isRemoved() && !this.dead) { Entity entity = p_21014_.getEntity(); LivingEntity livingentity = this.getKillCredit(); -@@ -1378,7 +_,7 @@ +@@ -1390,7 +_,7 @@ if (this.level() instanceof ServerLevel serverlevel) { boolean flag = false; if (p_21269_ instanceof WitherBoss) { @@ -307,7 +316,7 @@ BlockPos blockpos = this.blockPosition(); BlockState blockstate = Blocks.WITHER_ROSE.defaultBlockState(); if (this.level().getBlockState(blockpos).isAir() && blockstate.canSurvive(this.level(), blockpos)) { -@@ -1396,6 +_,7 @@ +@@ -1408,6 +_,7 @@ } protected void dropAllDeathLoot(ServerLevel p_348524_, DamageSource p_21192_) { @@ -315,7 +324,7 @@ boolean flag = this.lastHurtByPlayerTime > 0; if (this.shouldDropLoot() && p_348524_.getGameRules().getBoolean(GameRules.RULE_DOMOBLOOT)) { this.dropFromLootTable(p_348524_, p_21192_, flag); -@@ -1404,6 +_,10 @@ +@@ -1416,6 +_,10 @@ this.dropEquipment(p_348524_); this.dropExperience(p_348524_, p_21192_.getEntity()); @@ -326,7 +335,7 @@ } protected void dropEquipment(ServerLevel p_376330_) { -@@ -1415,7 +_,8 @@ +@@ -1427,7 +_,8 @@ this.isAlwaysExperienceDropper() || this.lastHurtByPlayerTime > 0 && this.shouldDropExperience() && p_376909_.getGameRules().getBoolean(GameRules.RULE_DOMOBLOOT) )) { @@ -336,7 +345,7 @@ } } -@@ -1496,6 +_,11 @@ +@@ -1508,6 +_,11 @@ } public void knockback(double p_147241_, double p_147242_, double p_147243_) { @@ -348,7 +357,7 @@ p_147241_ *= 1.0 - this.getAttributeValue(Attributes.KNOCKBACK_RESISTANCE); if (!(p_147241_ <= 0.0)) { this.hasImpulse = true; -@@ -1569,15 +_,9 @@ +@@ -1585,15 +_,9 @@ } else { BlockPos blockpos = this.blockPosition(); BlockState blockstate = this.getInBlockState(); @@ -367,27 +376,7 @@ } } -@@ -1595,10 +_,18 @@ - return !this.isRemoved() && this.getHealth() > 0.0F; - } - -+ /** @deprecated Neo: use {@link #isLookingAtMe(LivingEntity, double, boolean, boolean, java.util.function.BiPredicate, DoubleSupplier...)} instead */ -+ @Deprecated - public boolean isLookingAtMe( - LivingEntity p_379420_, double p_379671_, boolean p_380253_, boolean p_380067_, Predicate p_380351_, DoubleSupplier... p_380073_ - ) { -- if (!p_380351_.test(p_379420_)) { -+ return isLookingAtMe(p_379420_, p_379671_, p_380253_, p_380067_, (observer, target) -> p_380351_.test(observer), p_380073_); -+ } -+ -+ public boolean isLookingAtMe( -+ LivingEntity p_379420_, double p_379671_, boolean p_380253_, boolean p_380067_, java.util.function.BiPredicate p_380351_, DoubleSupplier... p_380073_ -+ ) { -+ if (!p_380351_.test(p_379420_, this)) { - return false; - } else { - Vec3 vec3 = p_379420_.getViewVector(1.0F).normalize(); -@@ -1630,6 +_,11 @@ +@@ -1639,6 +_,11 @@ @Override public boolean causeFallDamage(float p_147187_, float p_147188_, DamageSource p_147189_) { @@ -399,7 +388,7 @@ boolean flag = super.causeFallDamage(p_147187_, p_147188_, p_147189_); int i = this.calculateFallDamage(p_147187_, p_147188_); if (i > 0) { -@@ -1657,9 +_,10 @@ +@@ -1666,9 +_,10 @@ int i = Mth.floor(this.getX()); int j = Mth.floor(this.getY() - 0.2F); int k = Mth.floor(this.getZ()); @@ -412,7 +401,7 @@ this.playSound(soundtype.getFallSound(), soundtype.getVolume() * 0.5F, soundtype.getPitch() * 0.75F); } } -@@ -1688,6 +_,8 @@ +@@ -1697,6 +_,8 @@ if (!(p_330394_ <= 0.0F)) { int i = (int)Math.max(1.0F, p_330394_ / 4.0F); @@ -421,7 +410,7 @@ for (EquipmentSlot equipmentslot : p_331314_) { ItemStack itemstack = this.getItemBySlot(equipmentslot); Equippable equippable = itemstack.get(DataComponents.EQUIPPABLE); -@@ -1721,6 +_,7 @@ +@@ -1730,6 +_,7 @@ p_21194_ = Math.max(f / 25.0F, 0.0F); float f2 = f1 - p_21194_; if (f2 > 0.0F && f2 < 3.4028235E37F) { @@ -429,7 +418,7 @@ if (this instanceof ServerPlayer) { ((ServerPlayer)this).awardStat(Stats.DAMAGE_RESISTED, Math.round(f2 * 10.0F)); } else if (p_21193_.getEntity() instanceof ServerPlayer) { -@@ -1743,6 +_,7 @@ +@@ -1752,6 +_,7 @@ if (f3 > 0.0F) { p_21194_ = CombatRules.getDamageAfterMagicAbsorb(p_21194_, f3); @@ -437,7 +426,7 @@ } return p_21194_; -@@ -1752,11 +_,14 @@ +@@ -1761,11 +_,14 @@ protected void actuallyHurt(ServerLevel p_376745_, DamageSource p_21240_, float p_21241_) { if (!this.isInvulnerableTo(p_376745_, p_21240_)) { @@ -457,7 +446,7 @@ if (f > 0.0F && f < 3.4028235E37F && p_21240_.getEntity() instanceof ServerPlayer serverplayer) { serverplayer.awardStat(Stats.DAMAGE_DEALT_ABSORBED, Math.round(f * 10.0F)); } -@@ -1764,9 +_,10 @@ +@@ -1773,9 +_,10 @@ if (f1 != 0.0F) { this.getCombatTracker().recordDamage(p_21240_, f1); this.setHealth(this.getHealth() - f1); @@ -469,7 +458,7 @@ } } -@@ -1820,6 +_,8 @@ +@@ -1829,6 +_,8 @@ } public void swing(InteractionHand p_21012_, boolean p_21013_) { @@ -478,7 +467,7 @@ if (!this.swinging || this.swingTime >= this.getCurrentSwingDuration() / 2 || this.swingTime < 0) { this.swingTime = -1; this.swinging = true; -@@ -1933,8 +_,10 @@ +@@ -1942,8 +_,10 @@ private void swapHandItems() { ItemStack itemstack = this.getItemBySlot(EquipmentSlot.OFFHAND); @@ -491,7 +480,7 @@ } @Override -@@ -2152,15 +_,18 @@ +@@ -2161,15 +_,18 @@ } this.hasImpulse = true; @@ -512,7 +501,7 @@ } protected float getWaterSlowDown() { -@@ -2196,7 +_,7 @@ +@@ -2205,7 +_,7 @@ private void travelInAir(Vec3 p_362457_) { BlockPos blockpos = this.getBlockPosBelowThatAffectsMyMovement(); @@ -521,7 +510,7 @@ float f1 = f * 0.91F; Vec3 vec3 = this.handleRelativeFrictionAndCalculateMovement(p_362457_, f); double d0 = vec3.y; -@@ -2219,11 +_,19 @@ +@@ -2228,11 +_,19 @@ } } @@ -542,7 +531,7 @@ float f = this.isSprinting() ? 0.9F : this.getWaterSlowDown(); float f1 = 0.02F; float f2 = (float)this.getAttributeValue(Attributes.WATER_MOVEMENT_EFFICIENCY); -@@ -2240,6 +_,7 @@ +@@ -2249,6 +_,7 @@ f = 0.96F; } @@ -550,7 +539,7 @@ this.moveRelative(f1, p_365480_); this.move(MoverType.SELF, this.getDeltaMovement()); Vec3 vec3 = this.getDeltaMovement(); -@@ -2389,7 +_,7 @@ +@@ -2398,7 +_,7 @@ double d0 = Mth.clamp(p_21298_.x, -0.15F, 0.15F); double d1 = Mth.clamp(p_21298_.z, -0.15F, 0.15F); double d2 = Math.max(p_21298_.y, -0.15F); @@ -559,7 +548,7 @@ d2 = 0.0; } -@@ -2574,6 +_,7 @@ +@@ -2583,6 +_,7 @@ }; ItemStack itemstack1 = this.getItemBySlot(equipmentslot); if (this.equipmentHasChanged(itemstack, itemstack1)) { @@ -567,7 +556,7 @@ if (map == null) { map = Maps.newEnumMap(EquipmentSlot.class); } -@@ -2733,6 +_,9 @@ +@@ -2742,6 +_,9 @@ profilerfiller.push("jump"); if (this.jumping && this.isAffectedByFluids()) { double d3; @@ -577,7 +566,7 @@ if (this.isInLava()) { d3 = this.getFluidHeight(FluidTags.LAVA); } else { -@@ -2743,15 +_,17 @@ +@@ -2752,15 +_,17 @@ double d4 = this.getFluidJumpThreshold(); if (!flag || this.onGround() && !(d3 > d4)) { if (!this.isInLava() || this.onGround() && !(d3 > d4)) { @@ -597,7 +586,7 @@ } } else { this.noJumpDelay = 0; -@@ -3104,8 +_,11 @@ +@@ -3113,8 +_,11 @@ private void updatingUsingItem() { if (this.isUsingItem()) { @@ -611,7 +600,7 @@ this.updateUsingItem(this.useItem); } else { this.stopUsingItem(); -@@ -3114,8 +_,11 @@ +@@ -3123,8 +_,11 @@ } protected void updateUsingItem(ItemStack p_147201_) { @@ -624,7 +613,7 @@ this.completeUsingItem(); } } -@@ -3143,8 +_,10 @@ +@@ -3152,8 +_,10 @@ public void startUsingItem(InteractionHand p_21159_) { ItemStack itemstack = this.getItemInHand(p_21159_); if (!itemstack.isEmpty() && !this.isUsingItem()) { @@ -636,7 +625,7 @@ if (!this.level().isClientSide) { this.setLivingEntityFlag(1, true); this.setLivingEntityFlag(2, p_21159_ == InteractionHand.OFF_HAND); -@@ -3207,7 +_,8 @@ +@@ -3216,7 +_,8 @@ this.releaseUsingItem(); } else { if (!this.useItem.isEmpty() && this.isUsingItem()) { @@ -646,7 +635,7 @@ if (itemstack != this.useItem) { this.setItemInHand(interactionhand, itemstack); } -@@ -3235,7 +_,11 @@ +@@ -3244,7 +_,11 @@ public void releaseUsingItem() { if (!this.useItem.isEmpty()) { @@ -658,7 +647,7 @@ if (this.useItem.useOnRelease()) { this.updatingUsingItem(); } -@@ -3245,6 +_,7 @@ +@@ -3254,6 +_,7 @@ } public void stopUsingItem() { @@ -666,7 +655,7 @@ if (!this.level().isClientSide) { boolean flag = this.isUsingItem(); this.setLivingEntityFlag(1, false); -@@ -3265,7 +_,7 @@ +@@ -3274,7 +_,7 @@ public ItemStack getItemBlockingWith() { if (this.isUsingItem() && !this.useItem.isEmpty()) { Item item = this.useItem.getItem(); @@ -675,7 +664,7 @@ return null; } else { return item.getUseDuration(this.useItem, this) - this.useItemRemaining < 5 ? null : this.useItem; -@@ -3410,8 +_,8 @@ +@@ -3419,8 +_,8 @@ } BlockState blockstate = this.level().getBlockState(p_21141_); @@ -686,11 +675,11 @@ } this.setPose(Pose.SLEEPING); -@@ -3426,15 +_,17 @@ +@@ -3435,15 +_,17 @@ } private boolean checkBedExists() { -- return this.getSleepingPos().map(p_381383_ -> this.level().getBlockState(p_381383_).getBlock() instanceof BedBlock).orElse(false); +- return this.getSleepingPos().map(p_390116_ -> this.level().getBlockState(p_390116_).getBlock() instanceof BedBlock).orElse(false); + // Neo: Overwrite the vanilla instanceof BedBlock check with isBed and fire the CanContinueSleepingEvent. + boolean hasBed = this.getSleepingPos().map(pos -> this.level().getBlockState(pos).isBed(this.level(), pos, this)).orElse(false); + return net.neoforged.neoforge.event.EventHooks.canEntityContinueSleeping(this, hasBed ? null : Player.BedSleepingProblem.NOT_POSSIBLE_HERE); @@ -707,7 +696,7 @@ Vec3 vec31 = BedBlock.findStandUpPosition(this.getType(), this.level(), p_261435_, direction, this.getYRot()).orElseGet(() -> { BlockPos blockpos = p_261435_.above(); return new Vec3((double)blockpos.getX() + 0.5, (double)blockpos.getY() + 0.1, (double)blockpos.getZ() + 0.5); -@@ -3455,7 +_,9 @@ +@@ -3464,7 +_,9 @@ @Nullable public Direction getBedOrientation() { BlockPos blockpos = this.getSleepingPos().orElse(null); @@ -718,7 +707,7 @@ } @Override -@@ -3464,7 +_,7 @@ +@@ -3473,7 +_,7 @@ } public ItemStack getProjectile(ItemStack p_21272_) { @@ -727,7 +716,7 @@ } private static byte entityEventForEquipmentBreak(EquipmentSlot p_21267_) { -@@ -3519,6 +_,8 @@ +@@ -3528,6 +_,8 @@ } public final EquipmentSlot getEquipmentSlotForItem(ItemStack p_147234_) { @@ -736,7 +725,7 @@ Equippable equippable = p_147234_.get(DataComponents.EQUIPPABLE); return equippable != null && this.canUseSlot(equippable.slot()) ? equippable.slot() : EquipmentSlot.MAINHAND; } -@@ -3604,7 +_,7 @@ +@@ -3613,7 +_,7 @@ } public boolean canDisableShield() { diff --git a/patches/net/minecraft/world/entity/Mob.java.patch b/patches/net/minecraft/world/entity/Mob.java.patch index 715ee233ce..4fc5a918fc 100644 --- a/patches/net/minecraft/world/entity/Mob.java.patch +++ b/patches/net/minecraft/world/entity/Mob.java.patch @@ -1,6 +1,6 @@ --- a/net/minecraft/world/entity/Mob.java +++ b/net/minecraft/world/entity/Mob.java -@@ -135,6 +_,11 @@ +@@ -131,6 +_,11 @@ private Leashable.LeashData leashData; private BlockPos restrictCenter = BlockPos.ZERO; private float restrictRadius = -1.0F; @@ -12,7 +12,7 @@ protected Mob(EntityType p_21368_, Level p_21369_) { super(p_21368_, p_21369_); -@@ -241,7 +_,10 @@ +@@ -237,7 +_,10 @@ } public void setTarget(@Nullable LivingEntity p_21544_) { @@ -24,7 +24,7 @@ } @Override -@@ -344,6 +_,12 @@ +@@ -340,6 +_,12 @@ if (!this.level().isClientSide && this.tickCount % 5 == 0) { this.updateControlFlags(); } @@ -37,7 +37,7 @@ } protected void updateControlFlags() { -@@ -423,6 +_,9 @@ +@@ -420,6 +_,9 @@ if (this.isNoAi()) { p_21484_.putBoolean("NoAI", this.isNoAi()); } @@ -47,9 +47,9 @@ } @Override -@@ -482,6 +_,14 @@ - } +@@ -486,6 +_,14 @@ + this.lootTableSeed = p_21450_.getLong("DeathLootTableSeed"); this.setNoAi(p_21450_.getBoolean("NoAI")); + + if (p_21450_.contains("neoforge:spawn_type")) { @@ -62,7 +62,7 @@ } @Override -@@ -534,7 +_,7 @@ +@@ -538,7 +_,7 @@ && this.canPickUpLoot() && this.isAlive() && !this.dead @@ -71,7 +71,7 @@ Vec3i vec3i = this.getPickupReach(); for (ItemEntity itementity : this.level() -@@ -666,6 +_,8 @@ +@@ -661,6 +_,8 @@ private double getApproximateAttributeWith(ItemStack p_371461_, Holder p_371421_, EquipmentSlot p_371891_) { double d0 = this.getAttributes().hasAttribute(p_371421_) ? this.getAttributeBaseValue(p_371421_) : 0.0; ItemAttributeModifiers itemattributemodifiers = p_371461_.getOrDefault(DataComponents.ATTRIBUTE_MODIFIERS, ItemAttributeModifiers.EMPTY); @@ -80,7 +80,7 @@ return itemattributemodifiers.compute(d0, p_371891_); } -@@ -701,6 +_,7 @@ +@@ -703,6 +_,7 @@ @Override public void checkDespawn() { @@ -88,7 +88,7 @@ if (this.level().getDifficulty() == Difficulty.PEACEFUL && this.shouldDespawnInPeaceful()) { this.discard(); } else if (!this.isPersistenceRequired() && !this.requiresCustomPersistence()) { -@@ -1127,6 +_,11 @@ +@@ -1129,6 +_,11 @@ } } @@ -100,7 +100,7 @@ @Nullable public SpawnGroupData finalizeSpawn( ServerLevelAccessor p_21434_, DifficultyInstance p_21435_, EntitySpawnReason p_363352_, @Nullable SpawnGroupData p_21437_ -@@ -1140,6 +_,7 @@ +@@ -1142,6 +_,7 @@ } this.setLeftHanded(randomsource.nextFloat() < 0.05F); @@ -108,7 +108,7 @@ return p_21437_; } -@@ -1281,6 +_,7 @@ +@@ -1283,6 +_,7 @@ } else { p_371709_.type().convert(this, t, p_371709_); p_371266_.finalizeConversion(t); @@ -116,7 +116,7 @@ if (this.level() instanceof ServerLevel serverlevel) { serverlevel.addFreshEntity(t); } -@@ -1452,14 +_,24 @@ +@@ -1453,14 +_,24 @@ } @Override @@ -142,7 +142,7 @@ @VisibleForTesting public void removeFreeWill() { this.removeAllGoals(p_351790_ -> true); -@@ -1503,5 +_,40 @@ +@@ -1504,5 +_,40 @@ @VisibleForTesting public float[] getArmorDropChances() { return this.armorDropChances; diff --git a/patches/net/minecraft/world/entity/SpawnPlacements.java.patch b/patches/net/minecraft/world/entity/SpawnPlacements.java.patch index 4717f19fc6..97d3d28aa0 100644 --- a/patches/net/minecraft/world/entity/SpawnPlacements.java.patch +++ b/patches/net/minecraft/world/entity/SpawnPlacements.java.patch @@ -26,7 +26,7 @@ } static { -@@ -183,5 +_,16 @@ +@@ -182,5 +_,16 @@ @FunctionalInterface public interface SpawnPredicate { boolean test(EntityType p_217081_, ServerLevelAccessor p_217082_, EntitySpawnReason p_363558_, BlockPos p_217084_, RandomSource p_217085_); diff --git a/patches/net/minecraft/world/entity/TamableAnimal.java.patch b/patches/net/minecraft/world/entity/TamableAnimal.java.patch index f7af6e6974..a2b17b928a 100644 --- a/patches/net/minecraft/world/entity/TamableAnimal.java.patch +++ b/patches/net/minecraft/world/entity/TamableAnimal.java.patch @@ -1,6 +1,6 @@ --- a/net/minecraft/world/entity/TamableAnimal.java +++ b/net/minecraft/world/entity/TamableAnimal.java -@@ -221,13 +_,16 @@ +@@ -224,13 +_,16 @@ @Override public void die(DamageSource p_21809_) { diff --git a/patches/net/minecraft/world/entity/ai/attributes/DefaultAttributes.java.patch b/patches/net/minecraft/world/entity/ai/attributes/DefaultAttributes.java.patch index 26208739b5..2a3dd7d38f 100644 --- a/patches/net/minecraft/world/entity/ai/attributes/DefaultAttributes.java.patch +++ b/patches/net/minecraft/world/entity/ai/attributes/DefaultAttributes.java.patch @@ -1,6 +1,6 @@ --- a/net/minecraft/world/entity/ai/attributes/DefaultAttributes.java +++ b/net/minecraft/world/entity/ai/attributes/DefaultAttributes.java -@@ -177,11 +_,12 @@ +@@ -176,11 +_,12 @@ .build(); public static AttributeSupplier getSupplier(EntityType p_22298_) { diff --git a/patches/net/minecraft/world/entity/animal/Bee.java.patch b/patches/net/minecraft/world/entity/animal/Bee.java.patch index 3e5e904026..bb949d825f 100644 --- a/patches/net/minecraft/world/entity/animal/Bee.java.patch +++ b/patches/net/minecraft/world/entity/animal/Bee.java.patch @@ -1,6 +1,6 @@ --- a/net/minecraft/world/entity/animal/Bee.java +++ b/net/minecraft/world/entity/animal/Bee.java -@@ -498,7 +_,7 @@ +@@ -501,7 +_,7 @@ if (this.hivePos == null) { return null; } else { @@ -9,7 +9,7 @@ } } -@@ -644,8 +_,18 @@ +@@ -664,8 +_,18 @@ } @Override diff --git a/patches/net/minecraft/world/entity/animal/Cat.java.patch b/patches/net/minecraft/world/entity/animal/Cat.java.patch index 45b5cd9505..26022339ce 100644 --- a/patches/net/minecraft/world/entity/animal/Cat.java.patch +++ b/patches/net/minecraft/world/entity/animal/Cat.java.patch @@ -1,6 +1,6 @@ --- a/net/minecraft/world/entity/animal/Cat.java +++ b/net/minecraft/world/entity/animal/Cat.java -@@ -373,9 +_,9 @@ +@@ -371,9 +_,9 @@ } } else if (this.isFood(itemstack) && this.getHealth() < this.getMaxHealth()) { if (!this.level().isClientSide()) { @@ -11,7 +11,7 @@ this.playEatingSound(); } -@@ -437,7 +_,7 @@ +@@ -435,7 +_,7 @@ } private void tryToTame(Player p_333858_) { diff --git a/patches/net/minecraft/world/entity/animal/MushroomCow.java.patch b/patches/net/minecraft/world/entity/animal/MushroomCow.java.patch index 373b37a15a..2e26e763a7 100644 --- a/patches/net/minecraft/world/entity/animal/MushroomCow.java.patch +++ b/patches/net/minecraft/world/entity/animal/MushroomCow.java.patch @@ -1,19 +1,19 @@ --- a/net/minecraft/world/entity/animal/MushroomCow.java +++ b/net/minecraft/world/entity/animal/MushroomCow.java -@@ -166,11 +_,19 @@ +@@ -165,11 +_,19 @@ @Override public void shear(ServerLevel p_376375_, SoundSource p_28924_, ItemStack p_373092_) { p_376375_.playSound(null, this, SoundEvents.MOOSHROOM_SHEAR, p_28924_, 1.0F, 1.0F); + if (!net.neoforged.neoforge.event.EventHooks.canLivingConvert(this, EntityType.COW, (timer) -> {})) return; - this.convertTo(EntityType.COW, ConversionParams.single(this, false, false), p_381482_ -> { -+ net.neoforged.neoforge.event.EventHooks.onLivingConvert(this, p_381482_); + this.convertTo(EntityType.COW, ConversionParams.single(this, false, false), p_390217_ -> { ++ net.neoforged.neoforge.event.EventHooks.onLivingConvert(this, p_390217_); p_376375_.sendParticles(ParticleTypes.EXPLOSION, this.getX(), this.getY(0.5), this.getZ(), 1, 0.0, 0.0, 0.0, 0.0); - this.dropFromShearingLootTable(p_376375_, BuiltInLootTables.SHEAR_MOOSHROOM, p_373092_, (p_381483_, p_381484_) -> { - for (int i = 0; i < p_381484_.getCount(); i++) { -- p_381483_.addFreshEntity(new ItemEntity(this.level(), this.getX(), this.getY(1.0), this.getZ(), p_381484_.copyWithCount(1))); + this.dropFromShearingLootTable(p_376375_, BuiltInLootTables.SHEAR_MOOSHROOM, p_373092_, (p_390218_, p_390219_) -> { + for (int i = 0; i < p_390219_.getCount(); i++) { +- p_390218_.addFreshEntity(new ItemEntity(this.level(), this.getX(), this.getY(1.0), this.getZ(), p_390219_.copyWithCount(1))); + // Neo: Change from addFreshEntity to spawnAtLocation to ensure captureDrops can capture this, we also need to unset the default pickup delay from the item + // Vanilla uses this.getY(1.0) for the y-level, which is this.getY() + this.getBbHeight() * 1.0, so we pass the BB height as the Y-offset. -+ ItemEntity item = spawnAtLocation(p_381483_, p_381484_.copyWithCount(1), this.getBbHeight()); ++ ItemEntity item = spawnAtLocation(p_390218_, p_390219_.copyWithCount(1), this.getBbHeight()); + if (item != null) { + // addFreshEntity does not incur a pickup delay, while spawnAtLocation sets the default pickup delay. + item.setNoPickUpDelay(); diff --git a/patches/net/minecraft/world/entity/animal/Parrot.java.patch b/patches/net/minecraft/world/entity/animal/Parrot.java.patch index ba8eca84e8..ac16c7a64a 100644 --- a/patches/net/minecraft/world/entity/animal/Parrot.java.patch +++ b/patches/net/minecraft/world/entity/animal/Parrot.java.patch @@ -10,10 +10,10 @@ }; + /** @deprecated Neo: use the {@link net.neoforged.neoforge.registries.datamaps.builtin.NeoForgeDataMaps#PARROT_IMITATIONS data map} instead */ + @Deprecated - static final Map, SoundEvent> MOB_SOUND_MAP = Util.make(Maps.newHashMap(), p_379158_ -> { - p_379158_.put(EntityType.BLAZE, SoundEvents.PARROT_IMITATE_BLAZE); - p_379158_.put(EntityType.BOGGED, SoundEvents.PARROT_IMITATE_BOGGED); -@@ -261,7 +_,7 @@ + static final Map, SoundEvent> MOB_SOUND_MAP = Util.make(Maps.newHashMap(), p_389382_ -> { + p_389382_.put(EntityType.BLAZE, SoundEvents.PARROT_IMITATE_BLAZE); + p_389382_.put(EntityType.BOGGED, SoundEvents.PARROT_IMITATE_BOGGED); +@@ -259,7 +_,7 @@ } if (!this.level().isClientSide) { @@ -22,7 +22,7 @@ this.tame(p_29414_); this.level().broadcastEntityEvent(this, (byte)7); } else { -@@ -325,7 +_,10 @@ +@@ -323,7 +_,10 @@ public static SoundEvent getAmbient(Level p_218239_, RandomSource p_218240_) { if (p_218239_.getDifficulty() != Difficulty.PEACEFUL && p_218240_.nextInt(1000) == 0) { @@ -34,7 +34,7 @@ return getImitatedSound(list.get(p_218240_.nextInt(list.size()))); } else { return SoundEvents.PARROT_AMBIENT; -@@ -333,7 +_,9 @@ +@@ -331,7 +_,9 @@ } private static SoundEvent getImitatedSound(EntityType p_29409_) { diff --git a/patches/net/minecraft/world/entity/animal/Pig.java.patch b/patches/net/minecraft/world/entity/animal/Pig.java.patch index 35f07ef1d6..f1744c625a 100644 --- a/patches/net/minecraft/world/entity/animal/Pig.java.patch +++ b/patches/net/minecraft/world/entity/animal/Pig.java.patch @@ -6,13 +6,13 @@ public void thunderHit(ServerLevel p_29473_, LightningBolt p_29474_) { - if (p_29473_.getDifficulty() != Difficulty.PEACEFUL) { + if (p_29473_.getDifficulty() != Difficulty.PEACEFUL && net.neoforged.neoforge.event.EventHooks.canLivingConvert(this, EntityType.ZOMBIFIED_PIGLIN, (timer) -> {})) { - ZombifiedPiglin zombifiedpiglin = this.convertTo(EntityType.ZOMBIFIED_PIGLIN, ConversionParams.single(this, false, true), p_381485_ -> { + ZombifiedPiglin zombifiedpiglin = this.convertTo(EntityType.ZOMBIFIED_PIGLIN, ConversionParams.single(this, false, true), p_390220_ -> { if (this.getMainHandItem().isEmpty()) { - p_381485_.setItemSlot(EquipmentSlot.MAINHAND, new ItemStack(Items.GOLDEN_SWORD)); + p_390220_.setItemSlot(EquipmentSlot.MAINHAND, new ItemStack(Items.GOLDEN_SWORD)); } - p_381485_.setPersistenceRequired(); -+ net.neoforged.neoforge.event.EventHooks.onLivingConvert(this, p_381485_); + p_390220_.setPersistenceRequired(); ++ net.neoforged.neoforge.event.EventHooks.onLivingConvert(this, p_390220_); }); if (zombifiedpiglin == null) { super.thunderHit(p_29473_, p_29474_); diff --git a/patches/net/minecraft/world/entity/animal/Sheep.java.patch b/patches/net/minecraft/world/entity/animal/Sheep.java.patch index e22cf4c2d2..0105181c13 100644 --- a/patches/net/minecraft/world/entity/animal/Sheep.java.patch +++ b/patches/net/minecraft/world/entity/animal/Sheep.java.patch @@ -1,6 +1,6 @@ --- a/net/minecraft/world/entity/animal/Sheep.java +++ b/net/minecraft/world/entity/animal/Sheep.java -@@ -156,7 +_,7 @@ +@@ -151,7 +_,7 @@ @Override public InteractionResult mobInteract(Player p_29853_, InteractionHand p_29854_) { ItemStack itemstack = p_29853_.getItemInHand(p_29854_); diff --git a/patches/net/minecraft/world/entity/animal/allay/Allay.java.patch b/patches/net/minecraft/world/entity/animal/allay/Allay.java.patch index f0ede9df6e..bc64513b73 100644 --- a/patches/net/minecraft/world/entity/animal/allay/Allay.java.patch +++ b/patches/net/minecraft/world/entity/animal/allay/Allay.java.patch @@ -1,6 +1,6 @@ --- a/net/minecraft/world/entity/animal/allay/Allay.java +++ b/net/minecraft/world/entity/animal/allay/Allay.java -@@ -369,9 +_,9 @@ +@@ -375,9 +_,9 @@ public boolean wantsToPickUp(ServerLevel p_376111_, ItemStack p_218387_) { ItemStack itemstack = this.getItemInHand(InteractionHand.MAIN_HAND); return !itemstack.isEmpty() diff --git a/patches/net/minecraft/world/entity/animal/frog/Tadpole.java.patch b/patches/net/minecraft/world/entity/animal/frog/Tadpole.java.patch index 90a3bb4745..fd527c7159 100644 --- a/patches/net/minecraft/world/entity/animal/frog/Tadpole.java.patch +++ b/patches/net/minecraft/world/entity/animal/frog/Tadpole.java.patch @@ -6,8 +6,8 @@ private void ageUp() { + if (!net.neoforged.neoforge.event.EventHooks.canLivingConvert(this, EntityType.FROG, (timer) -> {})) return; if (this.level() instanceof ServerLevel serverlevel) { - this.convertTo(EntityType.FROG, ConversionParams.single(this, false, false), p_381493_ -> { -+ net.neoforged.neoforge.event.EventHooks.onLivingConvert(this, p_381493_); - p_381493_.finalizeSpawn(serverlevel, this.level().getCurrentDifficultyAt(p_381493_.blockPosition()), EntitySpawnReason.CONVERSION, null); - p_381493_.setPersistenceRequired(); - p_381493_.fudgePositionAfterSizeChange(this.getDimensions(this.getPose())); + this.convertTo(EntityType.FROG, ConversionParams.single(this, false, false), p_390228_ -> { ++ net.neoforged.neoforge.event.EventHooks.onLivingConvert(this, p_390228_); + p_390228_.finalizeSpawn(serverlevel, this.level().getCurrentDifficultyAt(p_390228_.blockPosition()), EntitySpawnReason.CONVERSION, null); + p_390228_.setPersistenceRequired(); + p_390228_.fudgePositionAfterSizeChange(this.getDimensions(this.getPose())); diff --git a/patches/net/minecraft/world/entity/boss/enderdragon/EnderDragon.java.patch b/patches/net/minecraft/world/entity/boss/enderdragon/EnderDragon.java.patch index b807a36c3f..7e30268049 100644 --- a/patches/net/minecraft/world/entity/boss/enderdragon/EnderDragon.java.patch +++ b/patches/net/minecraft/world/entity/boss/enderdragon/EnderDragon.java.patch @@ -50,8 +50,8 @@ } if (this.dragonDeathTime == 1 && !this.isSilent()) { -@@ -549,7 +_,8 @@ - this.move(MoverType.SELF, new Vec3(0.0, 0.1F, 0.0)); +@@ -556,7 +_,8 @@ + if (this.dragonDeathTime == 200 && this.level() instanceof ServerLevel serverlevel1) { if (serverlevel1.getGameRules().getBoolean(GameRules.RULE_DOMOBLOOT)) { - ExperienceOrb.award(serverlevel1, this.position(), Mth.floor((float)i * 0.2F)); @@ -60,7 +60,7 @@ } if (this.dragonFight != null) { -@@ -860,8 +_,19 @@ +@@ -867,8 +_,19 @@ } @Override diff --git a/patches/net/minecraft/world/entity/boss/wither/WitherBoss.java.patch b/patches/net/minecraft/world/entity/boss/wither/WitherBoss.java.patch index 5e036c641e..6b23c70528 100644 --- a/patches/net/minecraft/world/entity/boss/wither/WitherBoss.java.patch +++ b/patches/net/minecraft/world/entity/boss/wither/WitherBoss.java.patch @@ -1,6 +1,6 @@ --- a/net/minecraft/world/entity/boss/wither/WitherBoss.java +++ b/net/minecraft/world/entity/boss/wither/WitherBoss.java -@@ -320,7 +_,7 @@ +@@ -319,7 +_,7 @@ if (this.destroyBlocksTick > 0) { this.destroyBlocksTick--; @@ -9,7 +9,7 @@ boolean flag = false; int l = Mth.floor(this.getBbWidth() / 2.0F + 1.0F); int i1 = Mth.floor(this.getBbHeight()); -@@ -329,7 +_,7 @@ +@@ -328,7 +_,7 @@ this.getBlockX() - l, this.getBlockY(), this.getBlockZ() - l, this.getBlockX() + l, this.getBlockY() + i1, this.getBlockZ() + l )) { BlockState blockstate = p_376675_.getBlockState(blockpos); @@ -18,7 +18,7 @@ flag = p_376675_.destroyBlock(blockpos, true, this) || flag; } } -@@ -348,6 +_,10 @@ +@@ -347,6 +_,10 @@ } } @@ -29,7 +29,7 @@ public static boolean canDestroy(BlockState p_31492_) { return !p_31492_.isAir() && !p_31492_.is(BlockTags.WITHER_IMMUNE); } -@@ -493,6 +_,7 @@ +@@ -492,6 +_,7 @@ @Override public void checkDespawn() { diff --git a/patches/net/minecraft/world/entity/decoration/HangingEntity.java.patch b/patches/net/minecraft/world/entity/decoration/HangingEntity.java.patch index 830741b8a3..ef8a61ff2b 100644 --- a/patches/net/minecraft/world/entity/decoration/HangingEntity.java.patch +++ b/patches/net/minecraft/world/entity/decoration/HangingEntity.java.patch @@ -4,8 +4,8 @@ if (!this.level().noCollision(this)) { return false; } else { -- boolean flag = BlockPos.betweenClosedStream(this.calculateSupportBox()).allMatch(p_381503_ -> { -+ boolean flag = BlockPos.betweenClosedStream(this.calculateSupportBox()).filter(pos -> !net.minecraft.world.level.block.Block.canSupportCenter(this.level(), pos, this.direction)).allMatch(p_381503_ -> { - BlockState blockstate = this.level().getBlockState(p_381503_); +- boolean flag = BlockPos.betweenClosedStream(this.calculateSupportBox()).allMatch(p_390238_ -> { ++ boolean flag = BlockPos.betweenClosedStream(this.calculateSupportBox()).filter(pos -> !net.minecraft.world.level.block.Block.canSupportCenter(this.level(), pos, this.direction)).allMatch(p_390238_ -> { + BlockState blockstate = this.level().getBlockState(p_390238_); return blockstate.isSolid() || DiodeBlock.isDiode(blockstate); }); diff --git a/patches/net/minecraft/world/entity/item/FallingBlockEntity.java.patch b/patches/net/minecraft/world/entity/item/FallingBlockEntity.java.patch index a8174886eb..9c04db9fba 100644 --- a/patches/net/minecraft/world/entity/item/FallingBlockEntity.java.patch +++ b/patches/net/minecraft/world/entity/item/FallingBlockEntity.java.patch @@ -18,3 +18,13 @@ blockpos = blockhitresult.getBlockPos(); flag1 = true; } +@@ -236,6 +_,9 @@ + } + + this.setDeltaMovement(this.getDeltaMovement().scale(0.98)); ++ if (isAlive() && block instanceof net.neoforged.neoforge.common.extensions.IFallableExtension feblock) { ++ feblock.fallingTick(level(), blockPosition(), this); ++ } + } + } + diff --git a/patches/net/minecraft/world/entity/monster/AbstractSkeleton.java.patch b/patches/net/minecraft/world/entity/monster/AbstractSkeleton.java.patch index 02972fce45..2670ddf8d7 100644 --- a/patches/net/minecraft/world/entity/monster/AbstractSkeleton.java.patch +++ b/patches/net/minecraft/world/entity/monster/AbstractSkeleton.java.patch @@ -1,6 +1,6 @@ --- a/net/minecraft/world/entity/monster/AbstractSkeleton.java +++ b/net/minecraft/world/entity/monster/AbstractSkeleton.java -@@ -161,7 +_,7 @@ +@@ -163,7 +_,7 @@ if (this.level() != null && !this.level().isClientSide) { this.goalSelector.removeGoal(this.meleeGoal); this.goalSelector.removeGoal(this.bowGoal); @@ -9,7 +9,7 @@ if (itemstack.is(Items.BOW)) { int i = this.getHardAttackInterval(); if (this.level().getDifficulty() != Difficulty.HARD) { -@@ -186,9 +_,11 @@ +@@ -188,9 +_,11 @@ @Override public void performRangedAttack(LivingEntity p_32141_, float p_32142_) { diff --git a/patches/net/minecraft/world/entity/monster/EnderMan.java.patch b/patches/net/minecraft/world/entity/monster/EnderMan.java.patch index 7ef7212218..f5c5fc13eb 100644 --- a/patches/net/minecraft/world/entity/monster/EnderMan.java.patch +++ b/patches/net/minecraft/world/entity/monster/EnderMan.java.patch @@ -1,6 +1,6 @@ --- a/net/minecraft/world/entity/monster/EnderMan.java +++ b/net/minecraft/world/entity/monster/EnderMan.java -@@ -118,7 +_,6 @@ +@@ -117,7 +_,6 @@ @Override public void setTarget(@Nullable LivingEntity p_32537_) { @@ -8,7 +8,7 @@ AttributeInstance attributeinstance = this.getAttribute(Attributes.MOVEMENT_SPEED); if (p_32537_ == null) { this.targetChangeTime = 0; -@@ -132,6 +_,7 @@ +@@ -131,6 +_,7 @@ attributeinstance.addTransientModifier(SPEED_MODIFIER_ATTACKING); } } @@ -16,17 +16,20 @@ } @Override -@@ -213,7 +_,8 @@ +@@ -212,9 +_,10 @@ } boolean isBeingStaredBy(Player p_380267_) { -- return this.isLookingAtMe(p_380267_, 0.025, true, false, LivingEntity.PLAYER_NOT_WEARING_DISGUISE_ITEM, new DoubleSupplier[]{this::getEyeY}); -+ return this.isLookingAtMe(p_380267_, 0.025, true, false, LivingEntity.PLAYER_NOT_WEARING_DISGUISE_ITEM_FOR_TARGET, new DoubleSupplier[]{this::getEyeY}) && +- return !LivingEntity.PLAYER_NOT_WEARING_DISGUISE_ITEM.test(p_380267_) ++ return !LivingEntity.PLAYER_NOT_WEARING_DISGUISE_ITEM_FOR_TARGET.test(p_380267_, this) + ? false +- : this.isLookingAtMe(p_380267_, 0.025, true, false, new double[]{this.getEyeY()}); ++ : this.isLookingAtMe(p_380267_, 0.025, true, false, new double[]{this.getEyeY()}) && + !net.neoforged.neoforge.common.CommonHooks.shouldSuppressEnderManAnger(this, p_380267_); } @Override -@@ -291,8 +_,10 @@ +@@ -292,8 +_,10 @@ boolean flag = blockstate.blocksMotion(); boolean flag1 = blockstate.getFluidState().is(FluidTags.WATER); if (flag && !flag1) { @@ -38,7 +41,7 @@ if (flag2) { this.level().gameEvent(GameEvent.TELEPORT, vec3, GameEvent.Context.of(this)); if (!this.isSilent()) { -@@ -449,7 +_,7 @@ +@@ -450,7 +_,7 @@ if (this.enderman.getCarriedBlock() == null) { return false; } else { @@ -47,7 +50,7 @@ ? false : this.enderman.getRandom().nextInt(reducedTickDelay(2000)) == 0; } -@@ -469,7 +_,7 @@ +@@ -470,7 +_,7 @@ BlockState blockstate2 = this.enderman.getCarriedBlock(); if (blockstate2 != null) { blockstate2 = Block.updateFromNeighbourShapes(blockstate2, this.enderman.level(), blockpos); @@ -56,7 +59,7 @@ level.setBlock(blockpos, blockstate2, 3); level.gameEvent(GameEvent.BLOCK_PLACE, blockpos, GameEvent.Context.of(this.enderman, blockstate2)); this.enderman.setCarriedBlock(null); -@@ -481,6 +_,7 @@ +@@ -482,6 +_,7 @@ return p_32562_.isAir() && !p_32563_.isAir() && !p_32563_.is(Blocks.BEDROCK) @@ -64,7 +67,7 @@ && p_32563_.isCollisionShapeFullBlock(p_32559_, p_32564_) && p_32561_.canSurvive(p_32559_, p_32560_) && p_32559_.getEntities(this.enderman, AABB.unitCubeFromLowerCorner(Vec3.atLowerCornerOf(p_32560_))).isEmpty(); -@@ -592,7 +_,7 @@ +@@ -593,7 +_,7 @@ if (this.enderman.getCarriedBlock() != null) { return false; } else { diff --git a/patches/net/minecraft/world/entity/monster/Pillager.java.patch b/patches/net/minecraft/world/entity/monster/Pillager.java.patch index ea74e23dd8..28cf44850e 100644 --- a/patches/net/minecraft/world/entity/monster/Pillager.java.patch +++ b/patches/net/minecraft/world/entity/monster/Pillager.java.patch @@ -1,6 +1,6 @@ --- a/net/minecraft/world/entity/monster/Pillager.java +++ b/net/minecraft/world/entity/monster/Pillager.java -@@ -119,7 +_,7 @@ +@@ -127,7 +_,7 @@ public AbstractIllager.IllagerArmPose getArmPose() { if (this.isChargingCrossbow()) { return AbstractIllager.IllagerArmPose.CROSSBOW_CHARGE; diff --git a/patches/net/minecraft/world/entity/monster/Ravager.java.patch b/patches/net/minecraft/world/entity/monster/Ravager.java.patch index aabe0a1c02..5902929c19 100644 --- a/patches/net/minecraft/world/entity/monster/Ravager.java.patch +++ b/patches/net/minecraft/world/entity/monster/Ravager.java.patch @@ -1,6 +1,6 @@ --- a/net/minecraft/world/entity/monster/Ravager.java +++ b/net/minecraft/world/entity/monster/Ravager.java -@@ -144,7 +_,7 @@ +@@ -141,7 +_,7 @@ if (this.level() instanceof ServerLevel serverlevel && this.horizontalCollision @@ -9,7 +9,7 @@ boolean flag = false; AABB aabb = this.getBoundingBox().inflate(0.2); -@@ -227,7 +_,7 @@ +@@ -224,7 +_,7 @@ private void roar() { if (this.isAlive()) { if (this.level() instanceof ServerLevel serverlevel) { diff --git a/patches/net/minecraft/world/entity/monster/Skeleton.java.patch b/patches/net/minecraft/world/entity/monster/Skeleton.java.patch index 2b3815fc32..10e7c45cf4 100644 --- a/patches/net/minecraft/world/entity/monster/Skeleton.java.patch +++ b/patches/net/minecraft/world/entity/monster/Skeleton.java.patch @@ -5,8 +5,8 @@ protected void doFreezeConversion() { + if (!net.neoforged.neoforge.event.EventHooks.canLivingConvert(this, EntityType.STRAY, (timer) -> this.conversionTime = timer)) return; - this.convertTo(EntityType.STRAY, ConversionParams.single(this, true, true), p_381508_ -> { -+ net.neoforged.neoforge.event.EventHooks.onLivingConvert(this, p_381508_); + this.convertTo(EntityType.STRAY, ConversionParams.single(this, true, true), p_390244_ -> { ++ net.neoforged.neoforge.event.EventHooks.onLivingConvert(this, p_390244_); if (!this.isSilent()) { this.level().levelEvent(null, 1048, this.blockPosition(), 0); } diff --git a/patches/net/minecraft/world/entity/monster/Slime.java.patch b/patches/net/minecraft/world/entity/monster/Slime.java.patch index 7f975fbff1..de9d5d03d8 100644 --- a/patches/net/minecraft/world/entity/monster/Slime.java.patch +++ b/patches/net/minecraft/world/entity/monster/Slime.java.patch @@ -21,9 +21,9 @@ float f3 = ((float)(l / 2) - 0.5F) * f1; - this.convertTo( + Slime slime = this.convertTo( - this.getType(), new ConversionParams(ConversionType.SPLIT_ON_DEATH, false, false, playerteam), EntitySpawnReason.TRIGGERED, p_381514_ -> { - p_381514_.setSize(j, true); - p_381514_.moveTo(this.getX() + (double)f2, this.getY() + 0.5, this.getZ() + (double)f3, this.random.nextFloat() * 360.0F, 0.0F); + this.getType(), new ConversionParams(ConversionType.SPLIT_ON_DEATH, false, false, playerteam), EntitySpawnReason.TRIGGERED, p_390250_ -> { + p_390250_.setSize(j, true); + p_390250_.moveTo(this.getX() + (double)f2, this.getY() + 0.5, this.getZ() + (double)f3, this.random.nextFloat() * 360.0F, 0.0F); } ); + children.add(slime); // Neo: Record the slime until after event firing. diff --git a/patches/net/minecraft/world/entity/monster/Zombie.java.patch b/patches/net/minecraft/world/entity/monster/Zombie.java.patch index 08f332ee7f..f9f2b3d141 100644 --- a/patches/net/minecraft/world/entity/monster/Zombie.java.patch +++ b/patches/net/minecraft/world/entity/monster/Zombie.java.patch @@ -12,7 +12,7 @@ this.convertTo( p_34311_, ConversionParams.single(this, true, true), -- p_381517_ -> p_381517_.handleAttributes(p_381517_.level().getCurrentDifficultyAt(p_381517_.blockPosition()).getSpecialMultiplier()) +- p_390253_ -> p_390253_.handleAttributes(p_390253_.level().getCurrentDifficultyAt(p_390253_.blockPosition()).getSpecialMultiplier()) + p_381517_ -> { + p_381517_.handleAttributes(p_381517_.level().getCurrentDifficultyAt(p_381517_.blockPosition()).getSpecialMultiplier()); + net.neoforged.neoforge.event.EventHooks.onLivingConvert(this, p_381517_); diff --git a/patches/net/minecraft/world/entity/monster/creaking/Creaking.java.patch b/patches/net/minecraft/world/entity/monster/creaking/Creaking.java.patch index 8718946d79..c871e3d198 100644 --- a/patches/net/minecraft/world/entity/monster/creaking/Creaking.java.patch +++ b/patches/net/minecraft/world/entity/monster/creaking/Creaking.java.patch @@ -1,12 +1,12 @@ --- a/net/minecraft/world/entity/monster/creaking/Creaking.java +++ b/net/minecraft/world/entity/monster/creaking/Creaking.java -@@ -230,7 +_,8 @@ - - return true; - } else { -- Predicate predicate = this.isActive() ? LivingEntity.PLAYER_NOT_WEARING_DISGUISE_ITEM : p_380174_ -> true; -+ // Neo: provide entity being looked at to disguise check -+ java.util.function.BiPredicate predicate = this.isActive() ? LivingEntity.PLAYER_NOT_WEARING_DISGUISE_ITEM_FOR_TARGET : (p_380174_, target) -> true; - +@@ -503,7 +_,8 @@ for (Player player : list) { - if (!player.isCreative() + if (this.canAttack(player) && !this.isAlliedTo(player)) { + flag1 = true; +- if ((!flag || LivingEntity.PLAYER_NOT_WEARING_DISGUISE_ITEM.test(player)) ++ // Neo: provide entity being looked at to disguise check ++ if ((!flag || LivingEntity.PLAYER_NOT_WEARING_DISGUISE_ITEM_FOR_TARGET.test(player, this)) + && this.isLookingAtMe( + player, + 0.5, diff --git a/patches/net/minecraft/world/entity/monster/piglin/Piglin.java.patch b/patches/net/minecraft/world/entity/monster/piglin/Piglin.java.patch index fc04171127..94c8351221 100644 --- a/patches/net/minecraft/world/entity/monster/piglin/Piglin.java.patch +++ b/patches/net/minecraft/world/entity/monster/piglin/Piglin.java.patch @@ -1,6 +1,6 @@ --- a/net/minecraft/world/entity/monster/piglin/Piglin.java +++ b/net/minecraft/world/entity/monster/piglin/Piglin.java -@@ -355,7 +_,7 @@ +@@ -363,7 +_,7 @@ } else if (this.isChargingCrossbow()) { return PiglinArmPose.CROSSBOW_CHARGE; } else { @@ -9,7 +9,7 @@ } } -@@ -392,7 +_,7 @@ +@@ -400,7 +_,7 @@ } protected void holdInOffHand(ItemStack p_34786_) { @@ -18,7 +18,7 @@ this.setItemSlot(EquipmentSlot.OFFHAND, p_34786_); this.setGuaranteedDrop(EquipmentSlot.OFFHAND); } else { -@@ -402,7 +_,7 @@ +@@ -410,7 +_,7 @@ @Override public boolean wantsToPickUp(ServerLevel p_376232_, ItemStack p_34777_) { diff --git a/patches/net/minecraft/world/entity/npc/Villager.java.patch b/patches/net/minecraft/world/entity/npc/Villager.java.patch index 308d4a20d0..baae5ca954 100644 --- a/patches/net/minecraft/world/entity/npc/Villager.java.patch +++ b/patches/net/minecraft/world/entity/npc/Villager.java.patch @@ -27,10 +27,10 @@ - if (p_35409_.getDifficulty() != Difficulty.PEACEFUL) { + if (p_35409_.getDifficulty() != Difficulty.PEACEFUL && net.neoforged.neoforge.event.EventHooks.canLivingConvert(this, EntityType.WITCH, (timer) -> {})) { LOGGER.info("Villager {} was struck by lightning {}.", this, p_35410_); - Witch witch = this.convertTo(EntityType.WITCH, ConversionParams.single(this, false, false), p_381540_ -> { - p_381540_.finalizeSpawn(p_35409_, p_35409_.getCurrentDifficultyAt(p_381540_.blockPosition()), EntitySpawnReason.CONVERSION, null); -+ net.neoforged.neoforge.event.EventHooks.onLivingConvert(this, p_381540_); - p_381540_.setPersistenceRequired(); + Witch witch = this.convertTo(EntityType.WITCH, ConversionParams.single(this, false, false), p_390271_ -> { + p_390271_.finalizeSpawn(p_35409_, p_35409_.getCurrentDifficultyAt(p_390271_.blockPosition()), EntitySpawnReason.CONVERSION, null); ++ net.neoforged.neoforge.event.EventHooks.onLivingConvert(this, p_390271_); + p_390271_.setPersistenceRequired(); this.releaseAllPois(); }); @@ -804,7 +_,10 @@ diff --git a/patches/net/minecraft/world/entity/player/Inventory.java.patch b/patches/net/minecraft/world/entity/player/Inventory.java.patch index 17c929ae65..d9fee6ddad 100644 --- a/patches/net/minecraft/world/entity/player/Inventory.java.patch +++ b/patches/net/minecraft/world/entity/player/Inventory.java.patch @@ -1,6 +1,6 @@ --- a/net/minecraft/world/entity/player/Inventory.java +++ b/net/minecraft/world/entity/player/Inventory.java -@@ -132,7 +_,7 @@ +@@ -126,7 +_,7 @@ for (int k = 0; k < 9; k++) { int l = (this.selected + k) % 9; @@ -9,7 +9,7 @@ return l; } } -@@ -204,11 +_,15 @@ +@@ -198,11 +_,15 @@ } public void tick() { @@ -26,7 +26,7 @@ } } } -@@ -258,6 +_,8 @@ +@@ -252,6 +_,8 @@ } catch (Throwable throwable) { CrashReport crashreport = CrashReport.forThrowable(throwable, "Adding item to inventory"); CrashReportCategory crashreportcategory = crashreport.addCategory("Item being added"); diff --git a/patches/net/minecraft/world/entity/player/Player.java.patch b/patches/net/minecraft/world/entity/player/Player.java.patch index a4bdc8c832..52a2dd2d7c 100644 --- a/patches/net/minecraft/world/entity/player/Player.java.patch +++ b/patches/net/minecraft/world/entity/player/Player.java.patch @@ -10,7 +10,7 @@ private static final Logger LOGGER = LogUtils.getLogger(); public static final HumanoidArm DEFAULT_MAIN_HAND = HumanoidArm.RIGHT; public static final int DEFAULT_MODEL_CUSTOMIZATION = 0; -@@ -197,6 +_,10 @@ +@@ -200,6 +_,10 @@ public Entity currentExplosionCause; private boolean ignoreFallDamageFromCurrentImpulse; private int currentImpulseContextResetGraceTime; @@ -21,7 +21,7 @@ public Player(Level p_250508_, BlockPos p_250289_, float p_251702_, GameProfile p_252153_) { super(EntityType.PLAYER, p_250508_); -@@ -233,7 +_,8 @@ +@@ -236,7 +_,8 @@ .add(Attributes.SUBMERGED_MINING_SPEED) .add(Attributes.SNEAKING_SPEED) .add(Attributes.MINING_EFFICIENCY) @@ -31,15 +31,15 @@ } @Override -@@ -249,6 +_,7 @@ +@@ -252,6 +_,7 @@ @Override public void tick() { + net.neoforged.neoforge.event.EventHooks.firePlayerTickPre(this); this.noPhysics = this.isSpectator(); - if (this.isSpectator()) { + if (this.isSpectator() || this.isPassenger()) { this.setOnGround(false); -@@ -264,7 +_,7 @@ +@@ -267,7 +_,7 @@ this.sleepCounter = 100; } @@ -48,7 +48,7 @@ this.stopSleepInBed(false, true); } } else if (this.sleepCounter > 0) { -@@ -295,7 +_,11 @@ +@@ -298,7 +_,11 @@ } if (!this.isSleeping()) { @@ -61,7 +61,7 @@ } } -@@ -325,6 +_,7 @@ +@@ -328,6 +_,7 @@ if (this.currentImpulseContextResetGraceTime > 0) { this.currentImpulseContextResetGraceTime--; } @@ -69,7 +69,7 @@ } @Override -@@ -427,6 +_,10 @@ +@@ -430,6 +_,10 @@ } protected void updatePlayerPose() { @@ -80,7 +80,7 @@ if (this.canPlayerFitWithinBlocksAndEntitiesWhen(Pose.SWIMMING)) { Pose pose; if (this.isFallFlying()) { -@@ -654,6 +_,7 @@ +@@ -657,6 +_,7 @@ @Override public void die(DamageSource p_36152_) { @@ -88,7 +88,7 @@ super.die(p_36152_); this.reapplyPosition(); if (!this.isSpectator() && this.level() instanceof ServerLevel serverlevel) { -@@ -711,7 +_,7 @@ +@@ -714,7 +_,7 @@ @Nullable public ItemEntity drop(ItemStack p_36177_, boolean p_36178_) { @@ -97,7 +97,7 @@ } @Nullable -@@ -723,7 +_,15 @@ +@@ -726,7 +_,15 @@ return null; } @@ -113,7 +113,7 @@ float f = this.inventory.getDestroySpeed(p_36282_); if (f > 1.0F) { f += (float)this.getAttributeValue(Attributes.MINING_EFFICIENCY); -@@ -752,13 +_,19 @@ +@@ -755,13 +_,19 @@ f /= 5.0F; } @@ -133,7 +133,7 @@ @Override public void readAdditionalSaveData(CompoundTag p_36215_) { super.readAdditionalSaveData(p_36215_); -@@ -867,7 +_,8 @@ +@@ -870,7 +_,8 @@ return false; } else { this.removeEntitiesOnShoulder(); @@ -143,7 +143,7 @@ if (p_376451_.getDifficulty() == Difficulty.PEACEFUL) { p_376389_ = 0.0F; } -@@ -922,7 +_,7 @@ +@@ -925,7 +_,7 @@ @Override protected void hurtCurrentlyUsedShield(float p_36383_) { @@ -152,7 +152,7 @@ if (!this.level().isClientSide) { this.awardStat(Stats.ITEM_USED.get(this.useItem.getItem())); } -@@ -930,7 +_,13 @@ +@@ -933,7 +_,13 @@ if (p_36383_ >= 3.0F) { int i = 1 + Mth.floor(p_36383_); InteractionHand interactionhand = this.getUsedItemHand(); @@ -167,7 +167,7 @@ if (this.useItem.isEmpty()) { if (interactionhand == InteractionHand.MAIN_HAND) { this.setItemSlot(EquipmentSlot.MAINHAND, ItemStack.EMPTY); -@@ -948,11 +_,14 @@ +@@ -951,11 +_,14 @@ @Override protected void actuallyHurt(ServerLevel p_376500_, DamageSource p_36312_, float p_36313_) { if (!this.isInvulnerableTo(p_376500_, p_36312_)) { @@ -187,7 +187,7 @@ if (f > 0.0F && f < 3.4028235E37F) { this.awardStat(Stats.DAMAGE_ABSORBED, Math.round(f * 10.0F)); } -@@ -966,7 +_,9 @@ +@@ -969,7 +_,9 @@ } this.gameEvent(GameEvent.ENTITY_DAMAGE); @@ -197,7 +197,7 @@ } } -@@ -1010,6 +_,8 @@ +@@ -1013,6 +_,8 @@ return InteractionResult.PASS; } else { @@ -206,7 +206,7 @@ ItemStack itemstack = this.getItemInHand(p_36159_); ItemStack itemstack1 = itemstack.copy(); InteractionResult interactionresult = p_36158_.interact(this, p_36159_); -@@ -1018,6 +_,9 @@ +@@ -1021,6 +_,9 @@ itemstack.setCount(itemstack1.getCount()); } @@ -216,7 +216,7 @@ return interactionresult; } else { if (!itemstack.isEmpty() && p_36158_ instanceof LivingEntity) { -@@ -1029,6 +_,7 @@ +@@ -1032,6 +_,7 @@ if (interactionresult1.consumesAction()) { this.level().gameEvent(GameEvent.ENTITY_INTERACT, p_36158_.position(), GameEvent.Context.of(this)); if (itemstack.isEmpty() && !this.abilities.instabuild) { @@ -224,7 +224,7 @@ this.setItemInHand(p_36159_, ItemStack.EMPTY); } -@@ -1058,6 +_,7 @@ +@@ -1061,6 +_,7 @@ } @Override @@ -232,7 +232,7 @@ protected Vec3 maybeBackOffFromEdge(Vec3 p_36201_, MoverType p_36202_) { float f = this.maxUpStep(); if (!this.abilities.flying -@@ -1107,6 +_,7 @@ +@@ -1110,6 +_,7 @@ } } @@ -240,7 +240,7 @@ private boolean isAboveGround(float p_341626_) { return this.onGround() || this.fallDistance < p_341626_ && !this.canFallAtLeast(0.0, 0.0, p_341626_ - this.fallDistance); } -@@ -1128,6 +_,7 @@ +@@ -1131,6 +_,7 @@ } public void attack(Entity p_36347_) { @@ -248,7 +248,7 @@ if (p_36347_.isAttackable()) { if (!p_36347_.skipAttackInteraction(this)) { float f = this.isAutoSpinAttack() ? this.autoSpinAttackDmg : (float)this.getAttributeValue(Attributes.ATTACK_DAMAGE); -@@ -1137,7 +_,6 @@ +@@ -1140,7 +_,6 @@ float f2 = this.getAttackStrengthScale(0.5F); f *= 0.2F + f2 * f2 * 0.8F; f1 *= f2; @@ -256,7 +256,7 @@ if (p_36347_.getType().is(EntityTypeTags.REDIRECTABLE_PROJECTILE) && p_36347_ instanceof Projectile projectile && projectile.deflect(ProjectileDeflection.AIM_DEFLECT, this, this, true)) { -@@ -1166,20 +_,31 @@ +@@ -1169,20 +_,31 @@ && !this.isPassenger() && p_36347_ instanceof LivingEntity && !this.isSprinting(); @@ -291,7 +291,7 @@ float f6 = 0.0F; if (p_36347_ instanceof LivingEntity livingentity) { f6 = livingentity.getHealth(); -@@ -1213,11 +_,12 @@ +@@ -1216,11 +_,12 @@ for (LivingEntity livingentity2 : this.level() .getEntitiesOfClass(LivingEntity.class, p_36347_.getBoundingBox().inflate(1.0, 0.25, 1.0))) { @@ -305,7 +305,7 @@ float f5 = this.getEnchantedDamage(livingentity2, f7, damagesource) * f2; livingentity2.knockback( 0.4F, -@@ -1264,11 +_,12 @@ +@@ -1267,11 +_,12 @@ this.setLastHurtMob(p_36347_); Entity entity = p_36347_; @@ -320,7 +320,7 @@ if (this.level() instanceof ServerLevel serverlevel1) { if (entity instanceof LivingEntity livingentity3) { flag5 = itemstack.hurtEnemy(livingentity3, this); -@@ -1283,6 +_,7 @@ +@@ -1286,6 +_,7 @@ } if (itemstack.isEmpty()) { @@ -328,7 +328,7 @@ if (itemstack == this.getMainHandItem()) { this.setItemInHand(InteractionHand.MAIN_HAND, ItemStack.EMPTY); } else { -@@ -1307,6 +_,7 @@ +@@ -1310,6 +_,7 @@ .playSound(null, this.getX(), this.getY(), this.getZ(), SoundEvents.PLAYER_ATTACK_NODAMAGE, this.getSoundSource(), 1.0F, 1.0F); } } @@ -336,7 +336,7 @@ } } } -@@ -1391,6 +_,7 @@ +@@ -1394,6 +_,7 @@ } public void stopSleepInBed(boolean p_36226_, boolean p_36227_) { @@ -344,7 +344,7 @@ super.stopSleeping(); if (this.level() instanceof ServerLevel && p_36227_) { ((ServerLevel)this.level()).updateSleepingPlayerList(); -@@ -1496,7 +_,8 @@ +@@ -1499,7 +_,8 @@ @Override public boolean causeFallDamage(float p_150093_, float p_150094_, DamageSource p_150095_) { @@ -354,7 +354,7 @@ return false; } else { if (p_150093_ >= 2.0F) { -@@ -1555,13 +_,13 @@ +@@ -1558,13 +_,13 @@ protected void playStepSound(BlockPos p_282121_, BlockState p_282194_) { if (this.isInWater()) { this.waterSwimSound(); @@ -370,7 +370,7 @@ } else { super.playStepSound(blockpos, blockstate); } -@@ -1592,6 +_,10 @@ +@@ -1595,6 +_,10 @@ } public void giveExperiencePoints(int p_36291_) { @@ -381,7 +381,7 @@ this.increaseScore(p_36291_); this.experienceProgress = this.experienceProgress + (float)p_36291_ / (float)this.getXpNeededForNextLevel(); this.totalExperience = Mth.clamp(this.totalExperience + p_36291_, 0, Integer.MAX_VALUE); -@@ -1619,7 +_,7 @@ +@@ -1622,7 +_,7 @@ } public void onEnchantmentPerformed(ItemStack p_36172_, int p_36173_) { @@ -390,7 +390,7 @@ if (this.experienceLevel < 0) { this.experienceLevel = 0; this.experienceProgress = 0.0F; -@@ -1630,6 +_,10 @@ +@@ -1633,6 +_,10 @@ } public void giveExperienceLevels(int p_36276_) { @@ -401,7 +401,7 @@ this.experienceLevel = IntMath.saturatedAdd(this.experienceLevel, p_36276_); if (this.experienceLevel < 0) { this.experienceLevel = 0; -@@ -1833,7 +_,11 @@ +@@ -1836,7 +_,11 @@ @Override public Component getDisplayName() { @@ -414,7 +414,7 @@ return this.decorateDisplayNameComponent(mutablecomponent); } -@@ -1997,21 +_,21 @@ +@@ -2000,21 +_,21 @@ if (!(p_36349_.getItem() instanceof ProjectileWeaponItem)) { return ItemStack.EMPTY; } else { @@ -441,7 +441,7 @@ } } } -@@ -2172,5 +_,41 @@ +@@ -2197,5 +_,41 @@ public Component getMessage() { return this.message; } diff --git a/patches/net/minecraft/world/entity/projectile/AbstractArrow.java.patch b/patches/net/minecraft/world/entity/projectile/AbstractArrow.java.patch index f84027025e..808cd25f46 100644 --- a/patches/net/minecraft/world/entity/projectile/AbstractArrow.java.patch +++ b/patches/net/minecraft/world/entity/projectile/AbstractArrow.java.patch @@ -1,6 +1,6 @@ --- a/net/minecraft/world/entity/projectile/AbstractArrow.java +++ b/net/minecraft/world/entity/projectile/AbstractArrow.java -@@ -189,7 +_,7 @@ +@@ -191,7 +_,7 @@ this.shakeTime--; } @@ -9,7 +9,7 @@ this.clearFire(); } -@@ -269,11 +_,15 @@ +@@ -275,11 +_,15 @@ if (entityhitresult == null) { if (this.isAlive() && p_371761_.getType() != HitResult.Type.MISS) { diff --git a/patches/net/minecraft/world/entity/projectile/AbstractHurtingProjectile.java.patch b/patches/net/minecraft/world/entity/projectile/AbstractHurtingProjectile.java.patch index e90d9725f3..c6ff3c513e 100644 --- a/patches/net/minecraft/world/entity/projectile/AbstractHurtingProjectile.java.patch +++ b/patches/net/minecraft/world/entity/projectile/AbstractHurtingProjectile.java.patch @@ -1,6 +1,6 @@ --- a/net/minecraft/world/entity/projectile/AbstractHurtingProjectile.java +++ b/net/minecraft/world/entity/projectile/AbstractHurtingProjectile.java -@@ -76,7 +_,8 @@ +@@ -72,7 +_,8 @@ if (this.level().isClientSide || (entity == null || !entity.isRemoved()) && this.level().hasChunkAt(this.blockPosition())) { HitResult hitresult = ProjectileUtil.getHitResultOnMoveVector(this, this::canHitEntity, this.getClipType()); Vec3 vec3; @@ -10,7 +10,7 @@ vec3 = hitresult.getLocation(); } else { vec3 = this.position().add(this.getDeltaMovement()); -@@ -90,7 +_,7 @@ +@@ -86,7 +_,7 @@ this.igniteForSeconds(1.0F); } diff --git a/patches/net/minecraft/world/entity/projectile/Projectile.java.patch b/patches/net/minecraft/world/entity/projectile/Projectile.java.patch index b772c7043c..f381393b10 100644 --- a/patches/net/minecraft/world/entity/projectile/Projectile.java.patch +++ b/patches/net/minecraft/world/entity/projectile/Projectile.java.patch @@ -1,6 +1,6 @@ --- a/net/minecraft/world/entity/projectile/Projectile.java +++ b/net/minecraft/world/entity/projectile/Projectile.java -@@ -375,7 +_,7 @@ +@@ -364,7 +_,7 @@ Entity entity = this.getOwner(); return entity instanceof Player ? entity.mayInteract(p_376318_, p_150168_) diff --git a/patches/net/minecraft/world/flag/FeatureFlag.java.patch b/patches/net/minecraft/world/flag/FeatureFlag.java.patch new file mode 100644 index 0000000000..bfc3f2e4f8 --- /dev/null +++ b/patches/net/minecraft/world/flag/FeatureFlag.java.patch @@ -0,0 +1,28 @@ +--- a/net/minecraft/world/flag/FeatureFlag.java ++++ b/net/minecraft/world/flag/FeatureFlag.java +@@ -3,9 +_,25 @@ + public class FeatureFlag { + final FeatureFlagUniverse universe; + final long mask; ++ final int extMaskIndex; ++ final boolean modded; + ++ /** ++ * @deprecated Neo: use {@link #FeatureFlag(FeatureFlagUniverse, int, int, boolean)} instead ++ */ ++ @Deprecated + FeatureFlag(FeatureFlagUniverse p_249115_, int p_251067_) { ++ this(p_249115_, p_251067_, 0, false); ++ } ++ ++ FeatureFlag(FeatureFlagUniverse p_249115_, int p_251067_, int offset, boolean modded) { + this.universe = p_249115_; + this.mask = 1L << p_251067_; ++ this.extMaskIndex = offset - 1; ++ this.modded = modded; ++ } ++ ++ public boolean isModded() { ++ return modded; + } + } diff --git a/patches/net/minecraft/world/flag/FeatureFlagRegistry.java.patch b/patches/net/minecraft/world/flag/FeatureFlagRegistry.java.patch new file mode 100644 index 0000000000..8f3caa84e1 --- /dev/null +++ b/patches/net/minecraft/world/flag/FeatureFlagRegistry.java.patch @@ -0,0 +1,44 @@ +--- a/net/minecraft/world/flag/FeatureFlagRegistry.java ++++ b/net/minecraft/world/flag/FeatureFlagRegistry.java +@@ -75,6 +_,18 @@ + }, p_249796_ -> List.copyOf(this.toNames(p_249796_))); + } + ++ public FeatureFlag getFlag(ResourceLocation name) { ++ return com.google.common.base.Preconditions.checkNotNull(this.names.get(name), "Flag %s was not registered", name); ++ } ++ ++ public Map getAllFlags() { ++ return this.names; ++ } ++ ++ public boolean hasAnyModdedFlags() { ++ return this.names.values().stream().anyMatch(FeatureFlag::isModded); ++ } ++ + public static class Builder { + private final FeatureFlagUniverse universe; + private int id; +@@ -88,11 +_,20 @@ + return this.create(ResourceLocation.withDefaultNamespace(p_251782_)); + } + ++ /** ++ * @deprecated Neo: use {@link #create(ResourceLocation, boolean)} instead ++ */ ++ @Deprecated + public FeatureFlag create(ResourceLocation p_250098_) { +- if (this.id >= 64) { ++ return create(p_250098_, false); ++ } ++ ++ public FeatureFlag create(ResourceLocation p_250098_, boolean modded) { ++ if (this.id >= 64 && false) { + throw new IllegalStateException("Too many feature flags"); + } else { +- FeatureFlag featureflag = new FeatureFlag(this.universe, this.id++); ++ FeatureFlag featureflag = new FeatureFlag(this.universe, this.id % FeatureFlagSet.MAX_CONTAINER_SIZE, this.id / FeatureFlagSet.MAX_CONTAINER_SIZE, modded); ++ this.id++; + FeatureFlag featureflag1 = this.flags.put(p_250098_, featureflag); + if (featureflag1 != null) { + throw new IllegalStateException("Duplicate feature flag " + p_250098_); diff --git a/patches/net/minecraft/world/flag/FeatureFlagSet.java.patch b/patches/net/minecraft/world/flag/FeatureFlagSet.java.patch new file mode 100644 index 0000000000..6d9b96e496 --- /dev/null +++ b/patches/net/minecraft/world/flag/FeatureFlagSet.java.patch @@ -0,0 +1,188 @@ +--- a/net/minecraft/world/flag/FeatureFlagSet.java ++++ b/net/minecraft/world/flag/FeatureFlagSet.java +@@ -7,14 +_,21 @@ + + public final class FeatureFlagSet { + private static final FeatureFlagSet EMPTY = new FeatureFlagSet(null, 0L); ++ private static final long[] EMPTY_EXT_MASK = new long[0]; + public static final int MAX_CONTAINER_SIZE = 64; + @Nullable + private final FeatureFlagUniverse universe; + private final long mask; ++ private final long[] extendedMask; + + private FeatureFlagSet(@Nullable FeatureFlagUniverse p_250433_, long p_251523_) { ++ this(p_250433_, p_251523_, EMPTY_EXT_MASK); ++ } ++ ++ private FeatureFlagSet(@Nullable FeatureFlagUniverse p_250433_, long p_251523_, long[] extendedMask) { + this.universe = p_250433_; + this.mask = p_251523_; ++ this.extendedMask = extendedMask; + } + + static FeatureFlagSet create(FeatureFlagUniverse p_251573_, Collection p_251037_) { +@@ -22,7 +_,8 @@ + return EMPTY; + } else { + long i = computeMask(p_251573_, 0L, p_251037_); +- return new FeatureFlagSet(p_251573_, i); ++ long[] extMask = computeExtendedMask(p_251573_, 0, 0L, p_251037_); ++ return new FeatureFlagSet(p_251573_, i, extMask); + } + } + +@@ -31,16 +_,19 @@ + } + + public static FeatureFlagSet of(FeatureFlag p_252331_) { +- return new FeatureFlagSet(p_252331_.universe, p_252331_.mask); ++ long[] extMask = computeExtendedMask(p_252331_.universe, p_252331_.extMaskIndex, p_252331_.mask, java.util.List.of()); ++ return new FeatureFlagSet(p_252331_.universe, p_252331_.extMaskIndex >= 0 ? 0L : p_252331_.mask, extMask); + } + + public static FeatureFlagSet of(FeatureFlag p_251008_, FeatureFlag... p_249805_) { +- long i = p_249805_.length == 0 ? p_251008_.mask : computeMask(p_251008_.universe, p_251008_.mask, Arrays.asList(p_249805_)); +- return new FeatureFlagSet(p_251008_.universe, i); ++ long i = p_249805_.length == 0 ? (p_251008_.extMaskIndex >= 0 ? 0L : p_251008_.mask) : computeMask(p_251008_.universe, p_251008_.extMaskIndex >= 0 ? 0L : p_251008_.mask, Arrays.asList(p_249805_)); ++ long[] extMask = computeExtendedMask(p_251008_.universe, p_251008_.extMaskIndex, p_251008_.mask, p_249805_.length == 0 ? java.util.List.of() : Arrays.asList(p_249805_)); ++ return new FeatureFlagSet(p_251008_.universe, i, extMask); + } + + private static long computeMask(FeatureFlagUniverse p_249684_, long p_250982_, Iterable p_251734_) { + for (FeatureFlag featureflag : p_251734_) { ++ if (featureflag.extMaskIndex >= 0) continue; + if (p_249684_ != featureflag.universe) { + throw new IllegalStateException("Mismatched feature universe, expected '" + p_249684_ + "', but got '" + featureflag.universe + "'"); + } +@@ -51,8 +_,36 @@ + return p_250982_; + } + ++ private static long[] computeExtendedMask(FeatureFlagUniverse universe, int firstExtIndex, long firstMask, Iterable otherFlags) { ++ long[] extMask = EMPTY_EXT_MASK; ++ if (firstExtIndex >= 0) { ++ extMask = new long[firstExtIndex + 1]; ++ extMask[firstExtIndex] |= firstMask; ++ } ++ for (FeatureFlag flag : otherFlags) { ++ if (flag.extMaskIndex < 0) continue; ++ if (universe != flag.universe) { ++ throw new IllegalStateException("Mismatched feature universe, expected '" + universe + "', but got '" + flag.universe + "'"); ++ } ++ if (flag.extMaskIndex >= extMask.length) { ++ extMask = Arrays.copyOfRange(extMask, 0, flag.extMaskIndex + 1); ++ } ++ extMask[flag.extMaskIndex] |= flag.mask; ++ } ++ return extMask; ++ } ++ + public boolean contains(FeatureFlag p_249521_) { +- return this.universe != p_249521_.universe ? false : (this.mask & p_249521_.mask) != 0L; ++ if (this.universe != p_249521_.universe) { ++ return false; ++ } ++ if (p_249521_.extMaskIndex < 0) { ++ return (this.mask & p_249521_.mask) != 0L; ++ } ++ if (this.extendedMask.length > p_249521_.extMaskIndex) { ++ return (this.extendedMask[p_249521_.extMaskIndex] & p_249521_.mask) != 0L; ++ } ++ return false; + } + + public boolean isEmpty() { +@@ -62,13 +_,33 @@ + public boolean isSubsetOf(FeatureFlagSet p_249164_) { + if (this.universe == null) { + return true; +- } else { +- return this.universe != p_249164_.universe ? false : (this.mask & ~p_249164_.mask) == 0L; ++ } else if (this.universe == p_249164_.universe) { ++ int len = Math.max(this.extendedMask.length, p_249164_.extendedMask.length); ++ for (int i = 0; i < len; i++) { ++ long thisMask = i < this.extendedMask.length ? this.extendedMask[i] : 0L; ++ long otherMask = i < p_249164_.extendedMask.length ? p_249164_.extendedMask[i] : 0L; ++ if ((thisMask & ~otherMask) != 0L) { ++ return false; ++ } ++ } ++ return (this.mask & ~p_249164_.mask) == 0L; + } ++ return false; + } + + public boolean intersects(FeatureFlagSet p_341635_) { +- return this.universe != null && p_341635_.universe != null && this.universe == p_341635_.universe ? (this.mask & p_341635_.mask) != 0L : false; ++ if (this.universe == null || p_341635_.universe == null || this.universe != p_341635_.universe) { ++ return false; ++ } ++ int len = Math.min(this.extendedMask.length, p_341635_.extendedMask.length); ++ for (int i = 0; i < len; i++) { ++ long thisMask = this.extendedMask[i]; ++ long otherMask = p_341635_.extendedMask[i]; ++ if ((thisMask & otherMask) != 0L) { ++ return true; ++ } ++ } ++ return (this.mask & p_341635_.mask) != 0L; + } + + public FeatureFlagSet join(FeatureFlagSet p_251527_) { +@@ -79,7 +_,16 @@ + } else if (this.universe != p_251527_.universe) { + throw new IllegalArgumentException("Mismatched set elements: '" + this.universe + "' != '" + p_251527_.universe + "'"); + } else { +- return new FeatureFlagSet(this.universe, this.mask | p_251527_.mask); ++ long[] extMask = EMPTY_EXT_MASK; ++ if (this.extendedMask.length > 0 || p_251527_.extendedMask.length > 0) { ++ extMask = new long[Math.max(this.extendedMask.length, p_251527_.extendedMask.length)]; ++ for (int i = 0; i < extMask.length; i++) { ++ long thisMask = i < this.extendedMask.length ? this.extendedMask[i] : 0L; ++ long otherMask = i < p_251527_.extendedMask.length ? p_251527_.extendedMask[i] : 0L; ++ extMask[i] = thisMask | otherMask; ++ } ++ } ++ return new FeatureFlagSet(this.universe, this.mask | p_251527_.mask, extMask); + } + } + +@@ -90,7 +_,15 @@ + throw new IllegalArgumentException("Mismatched set elements: '" + this.universe + "' != '" + p_341688_.universe + "'"); + } else { + long i = this.mask & ~p_341688_.mask; +- return i == 0L ? EMPTY : new FeatureFlagSet(this.universe, i); ++ long[] extMask = EMPTY_EXT_MASK; ++ if (this.extendedMask.length > 0 || p_341688_.extendedMask.length > 0) { ++ extMask = new long[this.extendedMask.length]; ++ for (int idx = 0; idx < extMask.length; idx++) { ++ long otherMask = idx < p_341688_.extendedMask.length ? p_341688_.extendedMask[idx] : 0L; ++ extMask[idx] = this.extendedMask[idx] & ~otherMask; ++ } ++ } ++ return i == 0L && extMask.length == 0 ? EMPTY : new FeatureFlagSet(this.universe, i, extMask); + } + } + +@@ -99,7 +_,7 @@ + if (this == p_248691_) { + return true; + } else { +- if (p_248691_ instanceof FeatureFlagSet featureflagset && this.universe == featureflagset.universe && this.mask == featureflagset.mask) { ++ if (p_248691_ instanceof FeatureFlagSet featureflagset && this.universe == featureflagset.universe && this.mask == featureflagset.mask && Arrays.equals(this.extendedMask, featureflagset.extendedMask)) { + return true; + } + +@@ -109,6 +_,10 @@ + + @Override + public int hashCode() { +- return (int)HashCommon.mix(this.mask); ++ int hash = (int)HashCommon.mix(this.mask); ++ for (long extMask : this.extendedMask) { ++ hash = 13 * hash + (int) HashCommon.mix(extMask); ++ } ++ return hash; + } + } diff --git a/patches/net/minecraft/world/flag/FeatureFlags.java.patch b/patches/net/minecraft/world/flag/FeatureFlags.java.patch new file mode 100644 index 0000000000..0fb6d34232 --- /dev/null +++ b/patches/net/minecraft/world/flag/FeatureFlags.java.patch @@ -0,0 +1,10 @@ +--- a/net/minecraft/world/flag/FeatureFlags.java ++++ b/net/minecraft/world/flag/FeatureFlags.java +@@ -35,6 +_,7 @@ + TRADE_REBALANCE = featureflagregistry$builder.createVanilla("trade_rebalance"); + REDSTONE_EXPERIMENTS = featureflagregistry$builder.createVanilla("redstone_experiments"); + MINECART_IMPROVEMENTS = featureflagregistry$builder.createVanilla("minecart_improvements"); ++ net.neoforged.neoforge.common.util.flag.FeatureFlagLoader.loadModdedFlags(featureflagregistry$builder); + REGISTRY = featureflagregistry$builder.build(); + CODEC = REGISTRY.codec(); + VANILLA_SET = FeatureFlagSet.of(VANILLA); diff --git a/patches/net/minecraft/world/inventory/ArmorSlot.java.patch b/patches/net/minecraft/world/inventory/ArmorSlot.java.patch index 1922b22e68..7683484078 100644 --- a/patches/net/minecraft/world/inventory/ArmorSlot.java.patch +++ b/patches/net/minecraft/world/inventory/ArmorSlot.java.patch @@ -1,6 +1,6 @@ --- a/net/minecraft/world/inventory/ArmorSlot.java +++ b/net/minecraft/world/inventory/ArmorSlot.java -@@ -39,7 +_,7 @@ +@@ -38,7 +_,7 @@ @Override public boolean mayPlace(ItemStack p_345029_) { diff --git a/patches/net/minecraft/world/inventory/BrewingStandMenu.java.patch b/patches/net/minecraft/world/inventory/BrewingStandMenu.java.patch index f562eb3218..0a54b15446 100644 --- a/patches/net/minecraft/world/inventory/BrewingStandMenu.java.patch +++ b/patches/net/minecraft/world/inventory/BrewingStandMenu.java.patch @@ -1,6 +1,6 @@ --- a/net/minecraft/world/inventory/BrewingStandMenu.java +++ b/net/minecraft/world/inventory/BrewingStandMenu.java -@@ -42,9 +_,9 @@ +@@ -45,9 +_,9 @@ this.brewingStand = p_39095_; this.brewingStandData = p_39096_; PotionBrewing potionbrewing = p_39094_.player.level().potionBrewing(); @@ -13,7 +13,7 @@ this.ingredientSlot = this.addSlot(new BrewingStandMenu.IngredientsSlot(potionbrewing, p_39095_, 3, 79, 17)); this.addSlot(new BrewingStandMenu.FuelSlot(p_39095_, 4, 17, 17)); this.addDataSlots(p_39096_); -@@ -73,7 +_,7 @@ +@@ -76,7 +_,7 @@ if (!this.moveItemStackTo(itemstack1, 3, 4, false)) { return ItemStack.EMPTY; } @@ -22,7 +22,7 @@ if (!this.moveItemStackTo(itemstack1, 0, 3, false)) { return ItemStack.EMPTY; } -@@ -150,13 +_,20 @@ +@@ -158,13 +_,20 @@ } static class PotionSlot extends Slot { @@ -44,7 +44,7 @@ } @Override -@@ -168,14 +_,20 @@ +@@ -176,14 +_,20 @@ public void onTake(Player p_150499_, ItemStack p_150500_) { Optional> optional = p_150500_.getOrDefault(DataComponents.POTION_CONTENTS, PotionContents.EMPTY).potion(); if (optional.isPresent() && p_150499_ instanceof ServerPlayer serverplayer) { @@ -63,5 +63,5 @@ + public static boolean mayPlaceItem(PotionBrewing potionBrewing, ItemStack p_39134_) { + return potionBrewing.isInput(p_39134_) || p_39134_.is(Items.GLASS_BOTTLE); } - } - } + + @Override diff --git a/patches/net/minecraft/world/inventory/EnchantmentMenu.java.patch b/patches/net/minecraft/world/inventory/EnchantmentMenu.java.patch index a60216de04..8ee38753c8 100644 --- a/patches/net/minecraft/world/inventory/EnchantmentMenu.java.patch +++ b/patches/net/minecraft/world/inventory/EnchantmentMenu.java.patch @@ -1,6 +1,6 @@ --- a/net/minecraft/world/inventory/EnchantmentMenu.java +++ b/net/minecraft/world/inventory/EnchantmentMenu.java -@@ -63,7 +_,7 @@ +@@ -62,7 +_,7 @@ this.addSlot(new Slot(this.enchantSlots, 1, 35, 47) { @Override public boolean mayPlace(ItemStack p_39517_) { @@ -9,7 +9,7 @@ } @Override -@@ -91,23 +_,24 @@ +@@ -90,23 +_,24 @@ if (!itemstack.isEmpty() && itemstack.isEnchantable()) { this.access.execute((p_344366_, p_344367_) -> { IdMap> idmap = p_344366_.registryAccess().lookupOrThrow(Registries.ENCHANTMENT).asHolderIdMap(); @@ -37,7 +37,7 @@ } for (int l = 0; l < 3; l++) { -@@ -153,14 +_,10 @@ +@@ -152,14 +_,10 @@ List list = this.getEnchantmentList(p_347276_.registryAccess(), itemstack, p_39466_, this.costs[p_39466_]); if (!list.isEmpty()) { p_39465_.onEnchantmentPerformed(itemstack, i); @@ -56,7 +56,7 @@ itemstack1.consume(i, p_39465_); if (itemstack1.isEmpty()) { -@@ -239,7 +_,7 @@ +@@ -238,7 +_,7 @@ if (!this.moveItemStackTo(itemstack1, 2, 38, true)) { return ItemStack.EMPTY; } diff --git a/patches/net/minecraft/world/inventory/FurnaceResultSlot.java.patch b/patches/net/minecraft/world/inventory/FurnaceResultSlot.java.patch index b437d67b28..e7e60046f5 100644 --- a/patches/net/minecraft/world/inventory/FurnaceResultSlot.java.patch +++ b/patches/net/minecraft/world/inventory/FurnaceResultSlot.java.patch @@ -1,9 +1,12 @@ --- a/net/minecraft/world/inventory/FurnaceResultSlot.java +++ b/net/minecraft/world/inventory/FurnaceResultSlot.java -@@ -49,5 +_,6 @@ +@@ -48,6 +_,9 @@ + abstractfurnaceblockentity.awardUsedRecipesAndPopExperience(serverplayer); } ++ if (this.removeCount != 0) { ++ net.neoforged.neoforge.event.EventHooks.firePlayerSmeltedEvent(this.player, p_39558_, this.removeCount); ++ } this.removeCount = 0; -+ net.neoforged.neoforge.event.EventHooks.firePlayerSmeltedEvent(this.player, p_39558_); } } diff --git a/patches/net/minecraft/world/inventory/Slot.java.patch b/patches/net/minecraft/world/inventory/Slot.java.patch index 048dc6e960..7e6f116068 100644 --- a/patches/net/minecraft/world/inventory/Slot.java.patch +++ b/patches/net/minecraft/world/inventory/Slot.java.patch @@ -1,15 +1,15 @@ --- a/net/minecraft/world/inventory/Slot.java +++ b/net/minecraft/world/inventory/Slot.java -@@ -81,7 +_,7 @@ +@@ -80,7 +_,7 @@ @Nullable - public Pair getNoItemIcon() { + public ResourceLocation getNoItemIcon() { - return null; -+ return backgroundPair; ++ return backgroundSprite; } public ItemStack remove(int p_40227_) { -@@ -94,6 +_,38 @@ +@@ -93,6 +_,37 @@ public boolean isActive() { return true; @@ -34,16 +34,15 @@ + return this.container == other.container; + } + -+ private Pair backgroundPair; ++ private ResourceLocation backgroundSprite; + /** + * Sets the background atlas and sprite location. + * -+ * @param atlas The atlas name + * @param sprite The sprite located on that atlas. + * @return this, to allow chaining. + */ -+ public Slot setBackground(ResourceLocation atlas, ResourceLocation sprite) { -+ this.backgroundPair = Pair.of(atlas, sprite); ++ public Slot setBackground(ResourceLocation sprite) { ++ this.backgroundSprite = sprite; + return this; } diff --git a/patches/net/minecraft/world/item/BundleItem.java.patch b/patches/net/minecraft/world/item/BundleItem.java.patch index ff5bc70598..9763a4877c 100644 --- a/patches/net/minecraft/world/item/BundleItem.java.patch +++ b/patches/net/minecraft/world/item/BundleItem.java.patch @@ -1,6 +1,6 @@ --- a/net/minecraft/world/item/BundleItem.java +++ b/net/minecraft/world/item/BundleItem.java -@@ -61,7 +_,7 @@ +@@ -48,7 +_,7 @@ @Override public boolean overrideStackedOnOther(ItemStack p_150733_, Slot p_150734_, ClickAction p_150735_, Player p_150736_) { BundleContents bundlecontents = p_150733_.get(DataComponents.BUNDLE_CONTENTS); @@ -9,7 +9,7 @@ return false; } else { ItemStack itemstack = p_150734_.getItem(); -@@ -100,6 +_,7 @@ +@@ -87,6 +_,7 @@ public boolean overrideOtherStackedOnMe( ItemStack p_150742_, ItemStack p_150743_, Slot p_150744_, ClickAction p_150745_, Player p_150746_, SlotAccess p_150747_ ) { diff --git a/patches/net/minecraft/world/item/CrossbowItem.java.patch b/patches/net/minecraft/world/item/CrossbowItem.java.patch index 6cb5f7be7d..5ba2254704 100644 --- a/patches/net/minecraft/world/item/CrossbowItem.java.patch +++ b/patches/net/minecraft/world/item/CrossbowItem.java.patch @@ -1,6 +1,6 @@ --- a/net/minecraft/world/item/CrossbowItem.java +++ b/net/minecraft/world/item/CrossbowItem.java -@@ -184,6 +_,7 @@ +@@ -185,6 +_,7 @@ Level p_40888_, LivingEntity p_40889_, InteractionHand p_40890_, ItemStack p_40891_, float p_40892_, float p_40893_, @Nullable LivingEntity p_331602_ ) { if (p_40888_ instanceof ServerLevel serverlevel) { diff --git a/patches/net/minecraft/world/item/DyeColor.java.patch b/patches/net/minecraft/world/item/DyeColor.java.patch index ff5a295142..9e4d126ebd 100644 --- a/patches/net/minecraft/world/item/DyeColor.java.patch +++ b/patches/net/minecraft/world/item/DyeColor.java.patch @@ -1,6 +1,6 @@ --- a/net/minecraft/world/item/DyeColor.java +++ b/net/minecraft/world/item/DyeColor.java -@@ -43,6 +_,8 @@ +@@ -48,6 +_,8 @@ private final MapColor mapColor; private final int textureDiffuseColor; private final int fireworkColor; @@ -9,7 +9,7 @@ private final int textColor; private DyeColor(int p_41046_, String p_41047_, int p_41048_, MapColor p_285297_, int p_41050_, int p_41051_) { -@@ -50,6 +_,8 @@ +@@ -55,6 +_,8 @@ this.name = p_41047_; this.mapColor = p_285297_; this.textColor = p_41051_; @@ -18,7 +18,7 @@ this.textureDiffuseColor = ARGB.opaque(p_41048_); this.fireworkColor = p_41050_; } -@@ -102,5 +_,35 @@ +@@ -107,6 +_,36 @@ @Override public String getSerializedName() { return this.name; @@ -53,4 +53,5 @@ + + return null; } - } + + public static DyeColor getMixedColor(ServerLevel p_386897_, DyeColor p_388832_, DyeColor p_386710_) { diff --git a/patches/net/minecraft/world/item/Item.java.patch b/patches/net/minecraft/world/item/Item.java.patch index 85a8aab6d7..f4964bdec5 100644 --- a/patches/net/minecraft/world/item/Item.java.patch +++ b/patches/net/minecraft/world/item/Item.java.patch @@ -90,8 +90,8 @@ } public ItemStack getDefaultInstance() { -@@ -322,7 +_,7 @@ - return this.requiredFeatures; +@@ -326,7 +_,7 @@ + return false; } - public static class Properties { @@ -99,7 +99,7 @@ private static final DependantName BLOCK_DESCRIPTION_ID = p_371954_ -> Util.makeDescriptionId("block", p_371954_.location()); private static final DependantName ITEM_DESCRIPTION_ID = p_371511_ -> Util.makeDescriptionId("item", p_371511_.location()); private final DataComponentMap.Builder components = DataComponentMap.builder().addAll(DataComponents.COMMON_ITEM_COMPONENTS); -@@ -438,6 +_,7 @@ +@@ -437,6 +_,7 @@ } public Item.Properties component(DataComponentType p_330871_, T p_330323_) { @@ -107,7 +107,7 @@ this.components.set(p_330871_, p_330323_); return this; } -@@ -448,6 +_,10 @@ +@@ -447,6 +_,10 @@ DataComponentMap buildAndValidateComponents(Component p_371796_, ResourceLocation p_371450_) { DataComponentMap datacomponentmap = this.components.set(DataComponents.ITEM_NAME, p_371796_).set(DataComponents.ITEM_MODEL, p_371450_).build(); @@ -118,7 +118,7 @@ if (datacomponentmap.has(DataComponents.DAMAGE) && datacomponentmap.getOrDefault(DataComponents.MAX_STACK_SIZE, 1) > 1) { throw new IllegalStateException("Item cannot have both durability and be stackable"); } else { -@@ -484,6 +_,14 @@ +@@ -483,6 +_,14 @@ @Nullable MapItemSavedData mapData(MapId p_339670_); @@ -133,7 +133,7 @@ static Item.TooltipContext of(@Nullable final Level p_339599_) { return p_339599_ == null ? EMPTY : new Item.TooltipContext() { @Override -@@ -499,6 +_,11 @@ +@@ -498,6 +_,11 @@ @Override public MapItemSavedData mapData(MapId p_339628_) { return p_339599_.getMapData(p_339628_); diff --git a/patches/net/minecraft/world/item/ItemStack.java.patch b/patches/net/minecraft/world/item/ItemStack.java.patch index 6842e3538e..f826fb0dfc 100644 --- a/patches/net/minecraft/world/item/ItemStack.java.patch +++ b/patches/net/minecraft/world/item/ItemStack.java.patch @@ -6,21 +6,21 @@ -public final class ItemStack implements DataComponentHolder { +public final class ItemStack implements DataComponentHolder, net.neoforged.neoforge.common.extensions.IItemStackExtension, net.neoforged.neoforge.common.MutableDataComponentHolder { - public static final Codec CODEC = Codec.lazyInitialized( - () -> RecordCodecBuilder.create( - p_381569_ -> p_381569_.group( -@@ -233,6 +_,10 @@ - return !this.isEmpty() ? this.components.asPatch() : DataComponentPatch.EMPTY; + private static final List OP_NBT_WARNING = List.of( + Component.translatable("item.op_warning.line1").withStyle(ChatFormatting.RED, ChatFormatting.BOLD), + Component.translatable("item.op_warning.line2").withStyle(ChatFormatting.RED), +@@ -242,6 +_,10 @@ + return !this.isEmpty() && this.components.hasNonDefault(p_389426_); } + public boolean isComponentsPatchEmpty() { -+ return !this.isEmpty() ? this.components.isPatchEmpty() : true; ++ return this.isEmpty() || this.components.isPatchEmpty(); + } + public ItemStack(ItemLike p_41599_) { this(p_41599_, 1); } -@@ -338,7 +_,7 @@ +@@ -347,7 +_,7 @@ } public boolean is(Holder p_220166_) { @@ -29,7 +29,7 @@ } public boolean is(HolderSet p_298683_) { -@@ -350,13 +_,27 @@ +@@ -359,13 +_,27 @@ } public InteractionResult useOn(UseOnContext p_41662_) { @@ -58,7 +58,7 @@ if (player != null && interactionresult instanceof InteractionResult.Success interactionresult$success && interactionresult$success.wasItemInteraction()) { -@@ -410,7 +_,8 @@ +@@ -419,7 +_,8 @@ if (this.isEmpty()) { throw new IllegalStateException("Cannot encode empty ItemStack"); } else { @@ -68,7 +68,7 @@ } } -@@ -418,7 +_,8 @@ +@@ -427,7 +_,8 @@ if (this.isEmpty()) { throw new IllegalStateException("Cannot encode empty ItemStack"); } else { @@ -78,7 +78,7 @@ } } -@@ -427,7 +_,7 @@ +@@ -436,7 +_,7 @@ } public int getMaxStackSize() { @@ -87,7 +87,7 @@ } public boolean isStackable() { -@@ -439,19 +_,19 @@ +@@ -448,19 +_,19 @@ } public boolean isDamaged() { @@ -111,7 +111,7 @@ } public boolean isBroken() { -@@ -463,6 +_,11 @@ +@@ -472,6 +_,11 @@ } public void hurtAndBreak(int p_220158_, ServerLevel p_346256_, @Nullable ServerPlayer p_220160_, Consumer p_348596_) { @@ -123,7 +123,7 @@ int i = this.processDurabilityChange(p_220158_, p_346256_, p_220160_); if (i != 0) { this.applyDamage(this.getDamageValue() + i, p_220160_, p_348596_); -@@ -470,6 +_,10 @@ +@@ -479,6 +_,10 @@ } private int processDurabilityChange(int p_361290_, ServerLevel p_361409_, @Nullable ServerPlayer p_364940_) { @@ -134,7 +134,7 @@ if (!this.isDamageableItem()) { return 0; } else if (p_364940_ != null && p_364940_.hasInfiniteMaterials()) { -@@ -480,8 +_,12 @@ +@@ -489,8 +_,12 @@ } private void applyDamage(int p_361754_, @Nullable ServerPlayer p_364853_, Consumer p_360895_) { @@ -149,7 +149,7 @@ } this.setDamageValue(p_361754_); -@@ -510,7 +_,7 @@ +@@ -519,7 +_,7 @@ this.hurtAndBreak( p_41623_, serverlevel, @@ -158,7 +158,7 @@ p_348383_ -> p_41624_.onEquippedItemBroken(p_348383_, p_319898_) ); } -@@ -785,7 +_,7 @@ +@@ -800,7 +_,7 @@ } public Component getStyledHoverName() { @@ -167,7 +167,7 @@ if (this.has(DataComponents.CUSTOM_NAME)) { mutablecomponent.withStyle(ChatFormatting.ITALIC); } -@@ -826,7 +_,9 @@ +@@ -842,7 +_,9 @@ this.addToTooltip(DataComponents.ENCHANTMENTS, p_339637_, consumer, p_41653_); this.addToTooltip(DataComponents.DYED_COLOR, p_339637_, consumer, p_41653_); this.addToTooltip(DataComponents.LORE, p_339637_, consumer, p_41653_); @@ -178,8 +178,8 @@ this.addToTooltip(DataComponents.UNBREAKABLE, p_339637_, consumer, p_41653_); this.addToTooltip(DataComponents.OMINOUS_BOTTLE_AMPLIFIER, p_339637_, consumer, p_41653_); this.addToTooltip(DataComponents.SUSPICIOUS_STEW_EFFECTS, p_339637_, consumer, p_41653_); -@@ -860,10 +_,15 @@ - list.add(DISABLED_ITEM_TOOLTIP); +@@ -880,10 +_,15 @@ + list.addAll(OP_NBT_WARNING); } + net.neoforged.neoforge.event.EventHooks.onItemTooltip(this, p_41652_, list, p_41653_, p_339637_); @@ -194,7 +194,7 @@ private void addAttributeTooltips(Consumer p_330796_, @Nullable Player p_330530_) { ItemAttributeModifiers itemattributemodifiers = this.getOrDefault(DataComponents.ATTRIBUTE_MODIFIERS, ItemAttributeModifiers.EMPTY); if (itemattributemodifiers.showInTooltip()) { -@@ -973,6 +_,17 @@ +@@ -993,6 +_,17 @@ return !this.getOrDefault(DataComponents.ENCHANTMENTS, ItemEnchantments.EMPTY).isEmpty(); } @@ -212,7 +212,7 @@ public ItemEnchantments getEnchantments() { return this.getOrDefault(DataComponents.ENCHANTMENTS, ItemEnchantments.EMPTY); } -@@ -998,14 +_,26 @@ +@@ -1018,14 +_,26 @@ } public void forEachModifier(EquipmentSlotGroup p_348610_, BiConsumer, AttributeModifier> p_348516_) { @@ -239,7 +239,7 @@ EnchantmentHelper.forEachModifier(this, p_332001_, p_330882_); } -@@ -1017,7 +_,7 @@ +@@ -1037,7 +_,7 @@ MutableComponent mutablecomponent1 = ComponentUtils.wrapInSquareBrackets(mutablecomponent); if (!this.isEmpty()) { @@ -248,7 +248,7 @@ .withStyle(p_220170_ -> p_220170_.withHoverEvent(new HoverEvent(HoverEvent.Action.SHOW_ITEM, new HoverEvent.ItemStackInfo(this)))); } -@@ -1085,6 +_,8 @@ +@@ -1105,6 +_,8 @@ this.getItem().onUseTick(p_41732_, p_41733_, this, p_41734_); } @@ -257,7 +257,7 @@ public void onDestroyed(ItemEntity p_150925_) { this.getItem().onDestroyed(p_150925_); } -@@ -1094,6 +_,7 @@ +@@ -1114,6 +_,7 @@ } public boolean canBeHurtBy(DamageSource p_335431_) { diff --git a/patches/net/minecraft/world/item/ItemUseAnimation.java.patch b/patches/net/minecraft/world/item/ItemUseAnimation.java.patch index bae3e76080..9e2141275f 100644 --- a/patches/net/minecraft/world/item/ItemUseAnimation.java.patch +++ b/patches/net/minecraft/world/item/ItemUseAnimation.java.patch @@ -12,7 +12,7 @@ NONE(0, "none"), EAT(1, "eat"), DRINK(2, "drink"), -@@ -38,5 +_,9 @@ +@@ -39,5 +_,9 @@ @Override public String getSerializedName() { return this.name; diff --git a/patches/net/minecraft/world/item/Items.java.patch b/patches/net/minecraft/world/item/Items.java.patch index 98327eb50c..9e6b1ca654 100644 --- a/patches/net/minecraft/world/item/Items.java.patch +++ b/patches/net/minecraft/world/item/Items.java.patch @@ -1,6 +1,6 @@ --- a/net/minecraft/world/item/Items.java +++ b/net/minecraft/world/item/Items.java -@@ -2326,11 +_,19 @@ +@@ -2194,11 +_,19 @@ } public static Item registerBlock(Block p_252092_, Block... p_248886_) { diff --git a/patches/net/minecraft/world/item/SpawnEggItem.java.patch b/patches/net/minecraft/world/item/SpawnEggItem.java.patch index b3a50e656b..2a8f26d433 100644 --- a/patches/net/minecraft/world/item/SpawnEggItem.java.patch +++ b/patches/net/minecraft/world/item/SpawnEggItem.java.patch @@ -1,51 +1,48 @@ --- a/net/minecraft/world/item/SpawnEggItem.java +++ b/net/minecraft/world/item/SpawnEggItem.java -@@ -41,11 +_,14 @@ - private final int highlightColor; - private final EntityType defaultType; +@@ -190,4 +_,45 @@ -+ /** @deprecated Forge: Use {@link net.neoforged.neoforge.common.DeferredSpawnEggItem} instead for suppliers */ -+ @Deprecated - public SpawnEggItem(EntityType p_43207_, int p_43208_, int p_43209_, Item.Properties p_43210_) { - super(p_43210_); - this.defaultType = p_43207_; - this.backgroundColor = p_43208_; - this.highlightColor = p_43209_; -+ if (p_43207_ != null) - BY_ID.put(p_43207_, this); + return false; } - -@@ -133,6 +_,8 @@ - - @Nullable - public static SpawnEggItem byId(@Nullable EntityType p_43214_) { -+ var ret = net.neoforged.neoforge.common.DeferredSpawnEggItem.deferredOnlyById(p_43214_); -+ if (ret != null) return ret; - return BY_ID.get(p_43214_); - } - -@@ -142,12 +_,12 @@ - - public EntityType getType(ItemStack p_330335_) { - CustomData customdata = p_330335_.getOrDefault(DataComponents.ENTITY_DATA, CustomData.EMPTY); -- return !customdata.isEmpty() ? customdata.read(ENTITY_TYPE_FIELD_CODEC).result().orElse(this.defaultType) : this.defaultType; -+ return !customdata.isEmpty() ? customdata.read(ENTITY_TYPE_FIELD_CODEC).result().orElse(getDefaultType()) : getDefaultType(); - } - - @Override - public FeatureFlagSet requiredFeatures() { -- return this.defaultType.requiredFeatures(); -+ return this.getDefaultType().requiredFeatures(); - } - - public Optional spawnOffspringFromSpawnEgg( -@@ -178,5 +_,9 @@ - } - } - } ++ ++ public static final net.minecraft.core.dispenser.DispenseItemBehavior DEFAULT_DISPENSE_BEHAVIOR = new net.minecraft.core.dispenser.DefaultDispenseItemBehavior() { ++ @Override ++ protected ItemStack execute(net.minecraft.core.dispenser.BlockSource source, ItemStack egg) { ++ Direction direction = source.state().getValue(net.minecraft.world.level.block.DispenserBlock.FACING); ++ EntityType entitytype = ((SpawnEggItem)egg.getItem()).getType(source.level().registryAccess(), egg); ++ ++ try { ++ entitytype.spawn( ++ source.level(), egg, null, source.pos().relative(direction), EntitySpawnReason.DISPENSER, direction != Direction.UP, false ++ ); ++ } catch (Exception exception) { ++ LOGGER.error("Error while dispensing spawn egg from dispenser at {}", source.pos(), exception); ++ return ItemStack.EMPTY; ++ } ++ ++ egg.shrink(1); ++ source.level().gameEvent(null, GameEvent.ENTITY_PLACE, source.pos()); ++ return egg; ++ } ++ }; ++ ++ /** ++ * {@return the dispense behavior to register by default} ++ */ ++ @Nullable ++ protected net.minecraft.core.dispenser.DispenseItemBehavior createDispenseBehavior() { ++ return DEFAULT_DISPENSE_BEHAVIOR; + } + -+ protected EntityType getDefaultType() { -+ return defaultType; - } ++ @net.neoforged.bus.api.SubscribeEvent(priority = net.neoforged.bus.api.EventPriority.LOWEST) ++ private static void registerDispenseBehavior(final net.neoforged.fml.event.lifecycle.FMLCommonSetupEvent event) { ++ event.enqueueWork(() -> eggs().forEach(egg -> { ++ if (!net.minecraft.world.level.block.DispenserBlock.DISPENSER_REGISTRY.containsKey(egg)) { ++ var beh = egg.createDispenseBehavior(); ++ if (beh != null) { ++ net.minecraft.world.level.block.DispenserBlock.registerBehavior(egg, beh); ++ } ++ } ++ })); ++ } } diff --git a/patches/net/minecraft/world/item/alchemy/PotionContents.java.patch b/patches/net/minecraft/world/item/alchemy/PotionContents.java.patch index f5c53ac620..a3e7318da7 100644 --- a/patches/net/minecraft/world/item/alchemy/PotionContents.java.patch +++ b/patches/net/minecraft/world/item/alchemy/PotionContents.java.patch @@ -1,6 +1,6 @@ --- a/net/minecraft/world/item/alchemy/PotionContents.java +++ b/net/minecraft/world/item/alchemy/PotionContents.java -@@ -201,6 +_,10 @@ +@@ -197,6 +_,10 @@ p_331296_.accept(CommonComponents.EMPTY); p_331296_.accept(Component.translatable("potion.whenDrank").withStyle(ChatFormatting.DARK_PURPLE)); diff --git a/patches/net/minecraft/world/item/crafting/Ingredient.java.patch b/patches/net/minecraft/world/item/crafting/Ingredient.java.patch index f80d5c47b2..d38b0c8667 100644 --- a/patches/net/minecraft/world/item/crafting/Ingredient.java.patch +++ b/patches/net/minecraft/world/item/crafting/Ingredient.java.patch @@ -1,9 +1,9 @@ --- a/net/minecraft/world/item/crafting/Ingredient.java +++ b/net/minecraft/world/item/crafting/Ingredient.java -@@ -24,18 +_,20 @@ +@@ -23,16 +_,20 @@ import net.minecraft.world.level.ItemLike; - public final class Ingredient implements Predicate { + public final class Ingredient implements StackedContents.IngredientInfo>, Predicate { - public static final StreamCodec CONTENTS_STREAM_CODEC = ByteBufCodecs.holderSet(Registries.ITEM) - .map(Ingredient::new, p_360055_ -> p_360055_.values); - public static final StreamCodec> OPTIONAL_CONTENTS_STREAM_CODEC = ByteBufCodecs.holderSet(Registries.ITEM) @@ -20,14 +20,14 @@ - public static final Codec CODEC = ExtraCodecs.nonEmptyHolderSet(NON_AIR_HOLDER_SET_CODEC).xmap(Ingredient::new, p_360061_ -> p_360061_.values); + public static final Codec CODEC = net.neoforged.neoforge.common.crafting.IngredientCodecs.codec(ExtraCodecs.nonEmptyHolderSet(NON_AIR_HOLDER_SET_CODEC).xmap(Ingredient::new, p_360061_ -> p_360061_.values)); private final HolderSet values; - @Nullable - private List> items; -+ @Nullable ++ @org.jetbrains.annotations.Nullable + private net.neoforged.neoforge.common.crafting.ICustomIngredient customIngredient = null; ++ @org.jetbrains.annotations.Nullable ++ private List> customIngredientValues; private Ingredient(HolderSet p_365027_) { p_365027_.unwrap().ifRight(p_360057_ -> { -@@ -48,19 +_,31 @@ +@@ -45,12 +_,23 @@ this.values = p_365027_; } @@ -40,27 +40,36 @@ return p_362504_.map(p_360060_ -> p_360060_.test(p_363604_)).orElseGet(p_363604_::isEmpty); } - public List> items() { - if (this.items == null) { -- this.items = ImmutableList.copyOf(this.values); -+ if (this.customIngredient != null) { -+ this.items = this.customIngredient.items().toList(); -+ } else { -+ this.items = ImmutableList.copyOf(this.values); + @Deprecated + public Stream> items() { ++ if (this.customIngredient != null) { ++ if (this.customIngredientValues == null) { ++ this.customIngredientValues = this.customIngredient.items().toList(); + } - } ++ return this.customIngredientValues.stream(); ++ } + return this.values.stream(); + } - return this.items; +@@ -59,16 +_,67 @@ } public boolean test(ItemStack p_43914_) { + if (this.customIngredient != null) { + return this.customIngredient.test(p_43914_); + } - List> list = this.items(); + return p_43914_.is(this.values); + } - for (int i = 0; i < list.size(); i++) { -@@ -74,7 +_,49 @@ + public boolean acceptsItem(Holder p_389400_) { ++ if (this.customIngredient != null) { ++ if (this.customIngredientValues == null) { ++ this.customIngredientValues = this.customIngredient.items().toList(); ++ } ++ return this.customIngredientValues.contains(p_389400_); ++ } + return this.values.contains(p_389400_); + } @Override public boolean equals(Object p_301003_) { @@ -92,7 +101,7 @@ + */ + private HolderSet getValuesForSync() { + if (isCustom()) { -+ return HolderSet.direct(this.items()); ++ return HolderSet.direct(this.items().toList()); + } + return this.values; + } @@ -101,7 +110,7 @@ + return this.customIngredient == null || this.customIngredient.isSimple(); + } + -+ @Nullable ++ @org.jetbrains.annotations.Nullable + public net.neoforged.neoforge.common.crafting.ICustomIngredient getCustomIngredient() { + return this.customIngredient; + } @@ -111,7 +120,7 @@ } public static Ingredient of(ItemLike p_364285_) { -@@ -94,6 +_,9 @@ +@@ -88,6 +_,9 @@ } public SlotDisplay display() { diff --git a/patches/net/minecraft/world/item/crafting/RecipeManager.java.patch b/patches/net/minecraft/world/item/crafting/RecipeManager.java.patch index e1cbdae41d..af19f7f2a6 100644 --- a/patches/net/minecraft/world/item/crafting/RecipeManager.java.patch +++ b/patches/net/minecraft/world/item/crafting/RecipeManager.java.patch @@ -1,14 +1,23 @@ --- a/net/minecraft/world/item/crafting/RecipeManager.java +++ b/net/minecraft/world/item/crafting/RecipeManager.java +@@ -71,7 +_,7 @@ + protected RecipeMap prepare(ResourceManager p_379845_, ProfilerFiller p_380058_) { + SortedMap> sortedmap = new TreeMap<>(); + SimpleJsonResourceReloadListener.scanDirectory( +- p_379845_, RECIPE_LISTER, this.registries.createSerializationContext(JsonOps.INSTANCE), Recipe.CODEC, sortedmap ++ p_379845_, RECIPE_LISTER, new net.neoforged.neoforge.common.conditions.ConditionalOps<>(this.registries.createSerializationContext(JsonOps.INSTANCE), getContext()), Recipe.CODEC, sortedmap // Neo: add condition context + ); + List> list = new ArrayList<>(sortedmap.size()); + sortedmap.forEach((p_379232_, p_379233_) -> { @@ -260,6 +_,11 @@ + return p_380850_ -> p_380850_.getType() == p_381108_ && p_380850_ instanceof SingleItemRecipe singleitemrecipe + ? Optional.of(singleitemrecipe.input()) : Optional.empty(); - } - ++ } ++ + // Neo: expose recipe map + public RecipeMap recipeMap() { + return this.recipes; -+ } -+ - public interface CachedCheck> { - Optional> getRecipeFor(I p_344938_, ServerLevel p_379487_); } + + public interface CachedCheck> { diff --git a/patches/net/minecraft/world/level/Level.java.patch b/patches/net/minecraft/world/level/Level.java.patch index 00df1925bc..e76860b35b 100644 --- a/patches/net/minecraft/world/level/Level.java.patch +++ b/patches/net/minecraft/world/level/Level.java.patch @@ -1,6 +1,6 @@ --- a/net/minecraft/world/level/Level.java +++ b/net/minecraft/world/level/Level.java -@@ -78,7 +_,7 @@ +@@ -79,7 +_,7 @@ import net.minecraft.world.phys.Vec3; import net.minecraft.world.scores.Scoreboard; @@ -9,7 +9,7 @@ public static final Codec> RESOURCE_KEY_CODEC = ResourceKey.codec(Registries.DIMENSION); public static final ResourceKey OVERWORLD = ResourceKey.create(Registries.DIMENSION, ResourceLocation.withDefaultNamespace("overworld")); public static final ResourceKey NETHER = ResourceKey.create(Registries.DIMENSION, ResourceLocation.withDefaultNamespace("the_nether")); -@@ -115,6 +_,11 @@ +@@ -116,6 +_,11 @@ private final RegistryAccess registryAccess; private final DamageSources damageSources; private long subTickCount; @@ -21,7 +21,7 @@ protected Level( WritableLevelData p_270739_, -@@ -215,11 +_,36 @@ +@@ -216,11 +_,36 @@ } else { LevelChunk levelchunk = this.getChunkAt(p_46605_); Block block = p_46606_.getBlock(); @@ -58,7 +58,7 @@ if (blockstate1 == p_46606_) { if (blockstate != blockstate1) { this.setBlocksDirty(p_46605_, blockstate, blockstate1); -@@ -246,9 +_,8 @@ +@@ -247,9 +_,8 @@ } this.onBlockStateChange(p_46605_, blockstate, blockstate1); @@ -69,7 +69,7 @@ } } } -@@ -300,6 +_,7 @@ +@@ -301,6 +_,7 @@ } public void updateNeighborsAt(BlockPos p_46673_, Block p_46674_) { @@ -77,7 +77,7 @@ } public void updateNeighborsAt(BlockPos p_365514_, Block p_364886_, @Nullable Orientation p_363337_) { -@@ -491,10 +_,26 @@ +@@ -500,10 +_,26 @@ (this.tickingBlockEntities ? this.pendingBlockEntityTickers : this.blockEntityTickers).add(p_151526_); } @@ -104,7 +104,7 @@ if (!this.pendingBlockEntityTickers.isEmpty()) { this.blockEntityTickers.addAll(this.pendingBlockEntityTickers); this.pendingBlockEntityTickers.clear(); -@@ -518,12 +_,19 @@ +@@ -527,12 +_,19 @@ public void guardEntityTick(Consumer p_46654_, T p_46655_) { try { @@ -124,7 +124,7 @@ } } -@@ -673,6 +_,7 @@ +@@ -682,6 +_,7 @@ if (!this.isOutsideBuildHeight(p_46748_)) { this.getChunkAt(p_46748_).removeBlockEntity(p_46748_); } @@ -132,27 +132,19 @@ } public boolean isLoaded(BlockPos p_46750_) { -@@ -751,6 +_,7 @@ - list.add(p_151522_); - } - -+ if (false) - if (p_151522_ instanceof EnderDragon) { - for (EnderDragonPart enderdragonpart : ((EnderDragon)p_151522_).getSubEntities()) { - if (p_151522_ != p_46536_ && p_46538_.test(enderdragonpart)) { -@@ -759,6 +_,11 @@ - } +@@ -761,9 +_,9 @@ } }); -+ for (net.neoforged.neoforge.entity.PartEntity p : this.getPartEntities()) { -+ if (p != p_46536_ && p.getBoundingBox().intersects(p_46537_) && p_46538_.test(p)) { -+ list.add(p); -+ } -+ } - return list; - } -@@ -785,6 +_,8 @@ +- for (EnderDragonPart enderdragonpart : this.dragonParts()) { ++ for (net.neoforged.neoforge.entity.PartEntity enderdragonpart : this.dragonParts()) { + if (enderdragonpart != p_46536_ +- && enderdragonpart.parentMob != p_46536_ ++ && enderdragonpart.getParent() != p_46536_ + && p_46538_.test(enderdragonpart) + && p_46537_.intersects(enderdragonpart.getBoundingBox())) { + list.add(enderdragonpart); +@@ -796,6 +_,8 @@ } } @@ -161,11 +153,11 @@ if (p_261454_ instanceof EnderDragon enderdragon) { for (EnderDragonPart enderdragonpart : enderdragon.getSubEntities()) { T t = p_261885_.tryCast(enderdragonpart); -@@ -799,6 +_,15 @@ +@@ -810,12 +_,21 @@ return AbortableIterationConsumer.Continuation.CONTINUE; }); -+ for (net.neoforged.neoforge.entity.PartEntity p : this.getPartEntities()) { ++ for (net.neoforged.neoforge.entity.PartEntity p : this.dragonParts()) { + T t = p_261885_.tryCast(p); + if (t != null && t.getBoundingBox().intersects(p_262086_) && p_261688_.test(t)) { + p_262071_.add(t); @@ -177,7 +169,14 @@ } @Nullable -@@ -922,16 +_,15 @@ + public abstract Entity getEntity(int p_46492_); + +- public abstract Collection dragonParts(); ++ public abstract Collection> dragonParts(); + + public void blockEntityChanged(BlockPos p_151544_) { + if (this.hasChunkAt(p_151544_)) { +@@ -935,16 +_,15 @@ public abstract Scoreboard getScoreboard(); public void updateNeighbourForOutputSignal(BlockPos p_46718_, Block p_46719_) { @@ -198,7 +197,7 @@ this.neighborChanged(blockstate, blockpos, p_46719_, null, false); } } -@@ -1013,6 +_,18 @@ +@@ -1026,6 +_,18 @@ return this.biomeManager; } @@ -217,7 +216,7 @@ public final boolean isDebug() { return this.isDebug; } -@@ -1055,5 +_,38 @@ +@@ -1068,5 +_,38 @@ public String getSerializedName() { return this.id; } diff --git a/patches/net/minecraft/world/level/ServerExplosion.java.patch b/patches/net/minecraft/world/level/ServerExplosion.java.patch index 871697a696..d15e1e4a55 100644 --- a/patches/net/minecraft/world/level/ServerExplosion.java.patch +++ b/patches/net/minecraft/world/level/ServerExplosion.java.patch @@ -29,7 +29,7 @@ d3 *= d6; Vec3 vec3 = new Vec3(d1, d2, d3); + vec3 = net.neoforged.neoforge.event.EventHooks.getExplosionKnockback(this.level, this, entity, vec3, blocks); - entity.setDeltaMovement(entity.getDeltaMovement().add(vec3)); + entity.push(vec3); if (entity instanceof Player) { Player player = (Player)entity; @@ -246,7 +_,7 @@ diff --git a/patches/net/minecraft/world/level/biome/Biome.java.patch b/patches/net/minecraft/world/level/biome/Biome.java.patch index 54c553a09a..24e5ab6c1a 100644 --- a/patches/net/minecraft/world/level/biome/Biome.java.patch +++ b/patches/net/minecraft/world/level/biome/Biome.java.patch @@ -1,6 +1,6 @@ --- a/net/minecraft/world/level/biome/Biome.java +++ b/net/minecraft/world/level/biome/Biome.java -@@ -35,8 +_,8 @@ +@@ -36,8 +_,8 @@ public final class Biome { public static final Codec DIRECT_CODEC = RecordCodecBuilder.create( p_220544_ -> p_220544_.group( @@ -11,7 +11,7 @@ BiomeGenerationSettings.CODEC.forGetter(p_220548_ -> p_220548_.generationSettings), MobSpawnSettings.CODEC.forGetter(p_220546_ -> p_220546_.mobSettings) ) -@@ -60,9 +_,11 @@ +@@ -61,9 +_,11 @@ ) public static final PerlinSimplexNoise BIOME_INFO_NOISE = new PerlinSimplexNoise(new WorldgenRandom(new LegacyRandomSource(2345L)), ImmutableList.of(0)); private static final int TEMPERATURE_CACHE_SIZE = 1024; @@ -23,7 +23,7 @@ private final BiomeSpecialEffects specialEffects; private final ThreadLocal temperatureCache = ThreadLocal.withInitial(() -> Util.make(() -> { Long2FloatLinkedOpenHashMap long2floatlinkedopenhashmap = new Long2FloatLinkedOpenHashMap(1024, 0.25F) { -@@ -79,6 +_,7 @@ +@@ -80,6 +_,7 @@ this.generationSettings = p_220532_; this.mobSettings = p_220533_; this.specialEffects = p_220531_; @@ -31,7 +31,7 @@ } public int getSkyColor() { -@@ -86,7 +_,7 @@ +@@ -87,7 +_,7 @@ } public MobSpawnSettings getMobSettings() { @@ -40,7 +40,7 @@ } public boolean hasPrecipitation() { -@@ -188,7 +_,7 @@ +@@ -189,7 +_,7 @@ } public BiomeGenerationSettings getGenerationSettings() { @@ -49,7 +49,7 @@ } public int getFogColor() { -@@ -411,5 +_,32 @@ +@@ -416,5 +_,32 @@ public String getSerializedName() { return this.name; } diff --git a/patches/net/minecraft/world/level/biome/BiomeSpecialEffects.java.patch b/patches/net/minecraft/world/level/biome/BiomeSpecialEffects.java.patch index 212f97fe12..b0904a498a 100644 --- a/patches/net/minecraft/world/level/biome/BiomeSpecialEffects.java.patch +++ b/patches/net/minecraft/world/level/biome/BiomeSpecialEffects.java.patch @@ -1,6 +1,6 @@ --- a/net/minecraft/world/level/biome/BiomeSpecialEffects.java +++ b/net/minecraft/world/level/biome/BiomeSpecialEffects.java -@@ -212,7 +_,9 @@ +@@ -242,7 +_,9 @@ } } @@ -11,7 +11,7 @@ NONE("none") { @Override public int modifyColor(double p_48081_, double p_48082_, int p_48083_) { -@@ -234,12 +_,22 @@ +@@ -264,12 +_,22 @@ }; private final String name; @@ -35,7 +35,7 @@ } public String getName() { -@@ -249,6 +_,15 @@ +@@ -279,6 +_,15 @@ @Override public String getSerializedName() { return this.name; diff --git a/patches/net/minecraft/world/level/block/Block.java.patch b/patches/net/minecraft/world/level/block/Block.java.patch index 652a468069..651346f450 100644 --- a/patches/net/minecraft/world/level/block/Block.java.patch +++ b/patches/net/minecraft/world/level/block/Block.java.patch @@ -112,18 +112,7 @@ public float getExplosionResistance() { return this.explosionResistance; } -@@ -375,8 +_,10 @@ - - public void updateEntityMovementAfterFallOn(BlockGetter p_49821_, Entity p_49822_) { - p_49822_.setDeltaMovement(p_49822_.getDeltaMovement().multiply(1.0, 0.0, 1.0)); -+ p_49822_.setDeltaMovement(p_49822_.getDeltaMovement().multiply(1.0D, 0.0D, 1.0D)); - } - -+ @Deprecated //Forge: Use more sensitive version - public ItemStack getCloneItemStack(LevelReader p_304395_, BlockPos p_49824_, BlockState p_49825_) { - return new ItemStack(this); - } -@@ -410,6 +_,7 @@ +@@ -406,6 +_,7 @@ public void handlePrecipitation(BlockState p_152450_, Level p_152451_, BlockPos p_152452_, Biome.Precipitation p_152453_) { } @@ -131,7 +120,7 @@ public boolean dropFromExplosion(Explosion p_49826_) { return true; } -@@ -475,6 +_,35 @@ +@@ -471,6 +_,35 @@ return this.stateDefinition.getPossibleStates().stream().collect(ImmutableMap.toImmutableMap(Function.identity(), p_152459_)); } diff --git a/patches/net/minecraft/world/level/block/Blocks.java.patch b/patches/net/minecraft/world/level/block/Blocks.java.patch index c9befa089f..f16c2db321 100644 --- a/patches/net/minecraft/world/level/block/Blocks.java.patch +++ b/patches/net/minecraft/world/level/block/Blocks.java.patch @@ -1,6 +1,6 @@ --- a/net/minecraft/world/level/block/Blocks.java +++ b/net/minecraft/world/level/block/Blocks.java -@@ -690,7 +_,7 @@ +@@ -675,7 +_,7 @@ public static final Block RED_BED = registerBed("red_bed", DyeColor.RED); public static final Block BLACK_BED = registerBed("black_bed", DyeColor.BLACK); public static final Block POWERED_RAIL = register( @@ -9,7 +9,7 @@ ); public static final Block DETECTOR_RAIL = register( "detector_rail", DetectorRailBlock::new, BlockBehaviour.Properties.of().noCollission().strength(0.7F).sound(SoundType.METAL) -@@ -6919,7 +_,8 @@ +@@ -6935,7 +_,8 @@ static { for (Block block : BuiltInRegistries.BLOCK) { for (BlockState blockstate : block.getStateDefinition().getPossibleStates()) { diff --git a/patches/net/minecraft/world/level/block/CampfireBlock.java.patch b/patches/net/minecraft/world/level/block/CampfireBlock.java.patch index 927f77d8db..4db887cbad 100644 --- a/patches/net/minecraft/world/level/block/CampfireBlock.java.patch +++ b/patches/net/minecraft/world/level/block/CampfireBlock.java.patch @@ -1,6 +1,6 @@ --- a/net/minecraft/world/level/block/CampfireBlock.java +++ b/net/minecraft/world/level/block/CampfireBlock.java -@@ -292,7 +_,7 @@ +@@ -287,7 +_,7 @@ return true; } diff --git a/patches/net/minecraft/world/level/block/ChestBlock.java.patch b/patches/net/minecraft/world/level/block/ChestBlock.java.patch index 47e4ad187d..05a8241fdc 100644 --- a/patches/net/minecraft/world/level/block/ChestBlock.java.patch +++ b/patches/net/minecraft/world/level/block/ChestBlock.java.patch @@ -1,6 +1,6 @@ --- a/net/minecraft/world/level/block/ChestBlock.java +++ b/net/minecraft/world/level/block/ChestBlock.java -@@ -365,7 +_,8 @@ +@@ -360,7 +_,8 @@ @Override protected BlockState mirror(BlockState p_51549_, Mirror p_51550_) { diff --git a/patches/net/minecraft/world/level/block/ComposterBlock.java.patch b/patches/net/minecraft/world/level/block/ComposterBlock.java.patch index 5d1d96dda8..7824b8e764 100644 --- a/patches/net/minecraft/world/level/block/ComposterBlock.java.patch +++ b/patches/net/minecraft/world/level/block/ComposterBlock.java.patch @@ -9,7 +9,7 @@ public static final Object2FloatMap COMPOSTABLES = new Object2FloatOpenHashMap<>(); private static final int AABB_SIDE_THICKNESS = 2; private static final VoxelShape OUTER_SHAPE = Shapes.block(); -@@ -231,6 +_,15 @@ +@@ -233,6 +_,15 @@ if (p_51978_.getValue(LEVEL) == 7) { p_51979_.scheduleTick(p_51980_, p_51978_.getBlock(), 20); } @@ -25,7 +25,7 @@ } @Override -@@ -238,7 +_,7 @@ +@@ -240,7 +_,7 @@ ItemStack p_316332_, BlockState p_316118_, Level p_316624_, BlockPos p_316660_, Player p_316715_, InteractionHand p_316472_, BlockHitResult p_316606_ ) { int i = p_316118_.getValue(LEVEL); @@ -34,7 +34,7 @@ if (i < 7 && !p_316624_.isClientSide) { BlockState blockstate = addItem(p_316715_, p_316118_, p_316624_, p_316660_, p_316332_); p_316624_.levelEvent(1500, p_316660_, p_316118_ != blockstate ? 1 : 0); -@@ -265,7 +_,7 @@ +@@ -267,7 +_,7 @@ public static BlockState insertItem(Entity p_270919_, BlockState p_270087_, ServerLevel p_270284_, ItemStack p_270253_, BlockPos p_270678_) { int i = p_270087_.getValue(LEVEL); @@ -43,7 +43,7 @@ BlockState blockstate = addItem(p_270919_, p_270087_, p_270284_, p_270678_, p_270253_); p_270253_.shrink(1); return blockstate; -@@ -296,7 +_,7 @@ +@@ -298,7 +_,7 @@ static BlockState addItem(@Nullable Entity p_270464_, BlockState p_270603_, LevelAccessor p_270151_, BlockPos p_270547_, ItemStack p_270354_) { int i = p_270603_.getValue(LEVEL); @@ -52,7 +52,7 @@ if ((i != 0 || !(f > 0.0F)) && !(p_270151_.getRandom().nextDouble() < (double)f)) { return p_270603_; } else { -@@ -396,7 +_,7 @@ +@@ -398,7 +_,7 @@ @Override public boolean canPlaceItemThroughFace(int p_52028_, ItemStack p_52029_, @Nullable Direction p_52030_) { @@ -61,7 +61,7 @@ } @Override -@@ -454,5 +_,11 @@ +@@ -456,5 +_,11 @@ ComposterBlock.empty(null, this.state, this.level, this.pos); this.changed = true; } diff --git a/patches/net/minecraft/world/level/block/Fallable.java.patch b/patches/net/minecraft/world/level/block/Fallable.java.patch new file mode 100644 index 0000000000..4f0be6f7e3 --- /dev/null +++ b/patches/net/minecraft/world/level/block/Fallable.java.patch @@ -0,0 +1,11 @@ +--- a/net/minecraft/world/level/block/Fallable.java ++++ b/net/minecraft/world/level/block/Fallable.java +@@ -7,7 +_,7 @@ + import net.minecraft.world.level.Level; + import net.minecraft.world.level.block.state.BlockState; + +-public interface Fallable { ++public interface Fallable extends net.neoforged.neoforge.common.extensions.IFallableExtension { + default void onLand(Level p_153220_, BlockPos p_153221_, BlockState p_153222_, BlockState p_153223_, FallingBlockEntity p_153224_) { + } + diff --git a/patches/net/minecraft/world/level/block/FlowerPotBlock.java.patch b/patches/net/minecraft/world/level/block/FlowerPotBlock.java.patch index ce68c441bd..0dd734152f 100644 --- a/patches/net/minecraft/world/level/block/FlowerPotBlock.java.patch +++ b/patches/net/minecraft/world/level/block/FlowerPotBlock.java.patch @@ -1,6 +1,6 @@ --- a/net/minecraft/world/level/block/FlowerPotBlock.java +++ b/net/minecraft/world/level/block/FlowerPotBlock.java -@@ -35,6 +_,7 @@ +@@ -37,6 +_,7 @@ private static final Map POTTED_BY_CONTENT = Maps.newHashMap(); public static final float AABB_SIZE = 3.0F; protected static final VoxelShape SHAPE = Block.box(5.0, 0.0, 5.0, 11.0, 6.0, 11.0); @@ -8,7 +8,7 @@ private final Block potted; @Override -@@ -42,10 +_,31 @@ +@@ -44,10 +_,31 @@ return CODEC; } @@ -43,7 +43,7 @@ } @Override -@@ -58,7 +_,7 @@ +@@ -60,7 +_,7 @@ ItemStack p_316610_, BlockState p_316240_, Level p_316456_, BlockPos p_316502_, Player p_316491_, InteractionHand p_316444_, BlockHitResult p_316826_ ) { BlockState blockstate = (p_316610_.getItem() instanceof BlockItem blockitem @@ -52,7 +52,7 @@ : Blocks.AIR) .defaultBlockState(); if (blockstate.isAir()) { -@@ -84,7 +_,7 @@ +@@ -86,7 +_,7 @@ p_316338_.drop(itemstack, false); } @@ -61,7 +61,7 @@ p_316655_.gameEvent(p_316338_, GameEvent.BLOCK_CHANGE, p_316654_); return InteractionResult.SUCCESS; } -@@ -116,11 +_,44 @@ +@@ -118,12 +_,45 @@ } public Block getPotted() { @@ -106,4 +106,5 @@ + public Map> getFullPotsView() { + return java.util.Collections.unmodifiableMap(fullPots); } - } + + @Override diff --git a/patches/net/minecraft/world/level/block/SculkCatalystBlock.java.patch b/patches/net/minecraft/world/level/block/SculkCatalystBlock.java.patch index 6fb3c95fbd..a3a2a23c13 100644 --- a/patches/net/minecraft/world/level/block/SculkCatalystBlock.java.patch +++ b/patches/net/minecraft/world/level/block/SculkCatalystBlock.java.patch @@ -1,6 +1,6 @@ --- a/net/minecraft/world/level/block/SculkCatalystBlock.java +++ b/net/minecraft/world/level/block/SculkCatalystBlock.java -@@ -66,8 +_,14 @@ +@@ -61,8 +_,14 @@ @Override protected void spawnAfterBreak(BlockState p_222109_, ServerLevel p_222110_, BlockPos p_222111_, ItemStack p_222112_, boolean p_222113_) { super.spawnAfterBreak(p_222109_, p_222110_, p_222111_, p_222112_, p_222113_); diff --git a/patches/net/minecraft/world/level/block/SculkSensorBlock.java.patch b/patches/net/minecraft/world/level/block/SculkSensorBlock.java.patch index 5aee48d600..3a4f746903 100644 --- a/patches/net/minecraft/world/level/block/SculkSensorBlock.java.patch +++ b/patches/net/minecraft/world/level/block/SculkSensorBlock.java.patch @@ -1,6 +1,6 @@ --- a/net/minecraft/world/level/block/SculkSensorBlock.java +++ b/net/minecraft/world/level/block/SculkSensorBlock.java -@@ -299,8 +_,13 @@ +@@ -294,8 +_,13 @@ @Override protected void spawnAfterBreak(BlockState p_222142_, ServerLevel p_222143_, BlockPos p_222144_, ItemStack p_222145_, boolean p_222146_) { super.spawnAfterBreak(p_222142_, p_222143_, p_222144_, p_222145_, p_222146_); diff --git a/patches/net/minecraft/world/level/block/SculkShriekerBlock.java.patch b/patches/net/minecraft/world/level/block/SculkShriekerBlock.java.patch index 646554aa30..39d5e849d8 100644 --- a/patches/net/minecraft/world/level/block/SculkShriekerBlock.java.patch +++ b/patches/net/minecraft/world/level/block/SculkShriekerBlock.java.patch @@ -1,6 +1,6 @@ --- a/net/minecraft/world/level/block/SculkShriekerBlock.java +++ b/net/minecraft/world/level/block/SculkShriekerBlock.java -@@ -149,9 +_,14 @@ +@@ -144,9 +_,14 @@ @Override protected void spawnAfterBreak(BlockState p_222192_, ServerLevel p_222193_, BlockPos p_222194_, ItemStack p_222195_, boolean p_222196_) { super.spawnAfterBreak(p_222192_, p_222193_, p_222194_, p_222195_, p_222196_); diff --git a/patches/net/minecraft/world/level/block/SoundType.java.patch b/patches/net/minecraft/world/level/block/SoundType.java.patch index 92c1ab63a3..f17836597f 100644 --- a/patches/net/minecraft/world/level/block/SoundType.java.patch +++ b/patches/net/minecraft/world/level/block/SoundType.java.patch @@ -1,6 +1,6 @@ --- a/net/minecraft/world/level/block/SoundType.java +++ b/net/minecraft/world/level/block/SoundType.java -@@ -789,6 +_,7 @@ +@@ -801,6 +_,7 @@ private final SoundEvent hitSound; private final SoundEvent fallSound; diff --git a/patches/net/minecraft/world/level/block/SpongeBlock.java.patch b/patches/net/minecraft/world/level/block/SpongeBlock.java.patch index ac00ddee76..1d850fcce7 100644 --- a/patches/net/minecraft/world/level/block/SpongeBlock.java.patch +++ b/patches/net/minecraft/world/level/block/SpongeBlock.java.patch @@ -14,6 +14,6 @@ FluidState fluidstate = p_56808_.getFluidState(p_294069_); - if (!fluidstate.is(FluidTags.WATER)) { + if (!spongeState.canBeHydrated(p_56808_, p_56809_, fluidstate, p_294069_)) { - return false; + return BlockPos.TraversalNodeStatus.SKIP; } else { if (blockstate.getBlock() instanceof BucketPickup bucketpickup diff --git a/patches/net/minecraft/world/level/block/entity/AbstractFurnaceBlockEntity.java.patch b/patches/net/minecraft/world/level/block/entity/AbstractFurnaceBlockEntity.java.patch index 03bd036ae7..0d5b214c7a 100644 --- a/patches/net/minecraft/world/level/block/entity/AbstractFurnaceBlockEntity.java.patch +++ b/patches/net/minecraft/world/level/block/entity/AbstractFurnaceBlockEntity.java.patch @@ -1,30 +1,30 @@ --- a/net/minecraft/world/level/block/entity/AbstractFurnaceBlockEntity.java +++ b/net/minecraft/world/level/block/entity/AbstractFurnaceBlockEntity.java -@@ -54,6 +_,7 @@ +@@ -53,6 +_,7 @@ + public static final int NUM_DATA_VALUES = 4; public static final int BURN_TIME_STANDARD = 200; public static final int BURN_COOL_SPEED = 2; - public static final int UNKNOWN_LIT_DURATION = 0; + private final RecipeType recipeType; protected NonNullList items = NonNullList.withSize(3, ItemStack.EMPTY); - int litTime; - int litDuration = 0; -@@ -64,9 +_,14 @@ + int litTimeRemaining; + int litTotalTime; +@@ -63,9 +_,14 @@ public int get(int p_58431_) { switch (p_58431_) { case 0: -+ if (litDuration > Short.MAX_VALUE) { ++ if (litTotalTime > Short.MAX_VALUE) { + // Neo: preserve litTime / litDuration ratio on the client as data slots are synced as shorts. -+ return net.minecraft.util.Mth.floor(((double) litTime / litDuration) * Short.MAX_VALUE); ++ return net.minecraft.util.Mth.floor(((double) litTimeRemaining / litTotalTime) * Short.MAX_VALUE); + } + - return AbstractFurnaceBlockEntity.this.litTime; + return AbstractFurnaceBlockEntity.this.litTimeRemaining; case 1: -- return AbstractFurnaceBlockEntity.this.litDuration; -+ return Math.min(AbstractFurnaceBlockEntity.this.litDuration, Short.MAX_VALUE); +- return AbstractFurnaceBlockEntity.this.litTotalTime; ++ return Math.min(AbstractFurnaceBlockEntity.this.litTotalTime, Short.MAX_VALUE); case 2: - return AbstractFurnaceBlockEntity.this.cookingProgress; + return AbstractFurnaceBlockEntity.this.cookingTimer; case 3: -@@ -106,6 +_,7 @@ +@@ -105,6 +_,7 @@ ) { super(p_154991_, p_154992_, p_154993_); this.quickCheck = RecipeManager.createCheck((RecipeType)p_154994_); @@ -32,34 +32,38 @@ } private boolean isLit() { -@@ -117,9 +_,9 @@ +@@ -116,10 +_,10 @@ super.loadAdditional(p_155025_, p_323468_); this.items = NonNullList.withSize(this.getContainerSize(), ItemStack.EMPTY); ContainerHelper.loadAllItems(p_155025_, this.items, p_323468_); -- this.litTime = p_155025_.getShort("BurnTime"); -- this.cookingProgress = p_155025_.getShort("CookTime"); -- this.cookingTotalTime = p_155025_.getShort("CookTimeTotal"); -+ this.litTime = p_155025_.getInt("BurnTime"); -+ this.cookingProgress = p_155025_.getInt("CookTime"); -+ this.cookingTotalTime = p_155025_.getInt("CookTimeTotal"); - this.litDuration = 0; +- this.cookingTimer = p_155025_.getShort("cooking_time_spent"); +- this.cookingTotalTime = p_155025_.getShort("cooking_total_time"); +- this.litTimeRemaining = p_155025_.getShort("lit_time_remaining"); +- this.litTotalTime = p_155025_.getShort("lit_total_time"); ++ this.cookingTimer = p_155025_.getInt("cooking_time_spent"); ++ this.cookingTotalTime = p_155025_.getInt("cooking_total_time"); ++ this.litTimeRemaining = p_155025_.getInt("lit_time_remaining"); ++ this.litTotalTime = p_155025_.getInt("lit_total_time"); CompoundTag compoundtag = p_155025_.getCompound("RecipesUsed"); -@@ -131,9 +_,9 @@ + for (String s : compoundtag.getAllKeys()) { +@@ -130,10 +_,10 @@ @Override protected void saveAdditional(CompoundTag p_187452_, HolderLookup.Provider p_323656_) { super.saveAdditional(p_187452_, p_323656_); -- p_187452_.putShort("BurnTime", (short)this.litTime); -- p_187452_.putShort("CookTime", (short)this.cookingProgress); -- p_187452_.putShort("CookTimeTotal", (short)this.cookingTotalTime); -+ p_187452_.putInt("BurnTime", this.litTime); -+ p_187452_.putInt("CookTime", this.cookingProgress); -+ p_187452_.putInt("CookTimeTotal", this.cookingTotalTime); +- p_187452_.putShort("cooking_time_spent", (short)this.cookingTimer); +- p_187452_.putShort("cooking_total_time", (short)this.cookingTotalTime); +- p_187452_.putShort("lit_time_remaining", (short)this.litTimeRemaining); +- p_187452_.putShort("lit_total_time", (short)this.litTotalTime); ++ p_187452_.putInt("cooking_time_spent", this.cookingTimer); ++ p_187452_.putInt("cooking_total_time", this.cookingTotalTime); ++ p_187452_.putInt("lit_time_remaining", this.litTimeRemaining); ++ p_187452_.putInt("lit_total_time", this.litTotalTime); ContainerHelper.saveAllItems(p_187452_, this.items, p_323656_); CompoundTag compoundtag = new CompoundTag(); this.recipesUsed.forEach((p_380898_, p_380899_) -> compoundtag.putInt(p_380898_.location().toString(), p_380899_)); -@@ -170,11 +_,15 @@ - p_155017_.litDuration = p_155017_.litTime; +@@ -166,11 +_,15 @@ + p_155017_.litTotalTime = p_155017_.litTimeRemaining; if (p_155017_.isLit()) { flag1 = true; + var remainder = itemstack.getCraftingRemainder(); @@ -75,7 +79,7 @@ } } } -@@ -227,9 +_,9 @@ +@@ -223,9 +_,9 @@ } else if (!ItemStack.isSameItemSameComponents(itemstack1, itemstack)) { return false; } else { @@ -87,7 +91,7 @@ } } } else { -@@ -251,7 +_,7 @@ +@@ -247,7 +_,7 @@ if (itemstack2.isEmpty()) { p_267073_.set(2, itemstack1.copy()); } else if (ItemStack.isSameItemSameComponents(itemstack2, itemstack1)) { @@ -96,7 +100,7 @@ } if (itemstack.is(Blocks.WET_SPONGE.asItem()) && !p_267073_.get(1).isEmpty() && p_267073_.get(1).is(Items.BUCKET)) { -@@ -266,7 +_,7 @@ +@@ -262,7 +_,7 @@ } protected int getBurnDuration(FuelValues p_363501_, ItemStack p_58343_) { @@ -105,7 +109,7 @@ } private static int getTotalCookTime(ServerLevel p_380169_, AbstractFurnaceBlockEntity p_222694_) { -@@ -329,7 +_,7 @@ +@@ -325,7 +_,7 @@ return true; } else { ItemStack itemstack = this.items.get(1); diff --git a/patches/net/minecraft/world/level/block/entity/BlockEntity.java.patch b/patches/net/minecraft/world/level/block/entity/BlockEntity.java.patch index 16fb21f9f0..c2a70f7e4a 100644 --- a/patches/net/minecraft/world/level/block/entity/BlockEntity.java.patch +++ b/patches/net/minecraft/world/level/block/entity/BlockEntity.java.patch @@ -1,6 +1,6 @@ --- a/net/minecraft/world/level/block/entity/BlockEntity.java +++ b/net/minecraft/world/level/block/entity/BlockEntity.java -@@ -27,8 +_,9 @@ +@@ -26,8 +_,9 @@ import net.minecraft.world.level.block.state.BlockState; import org.slf4j.Logger; @@ -11,7 +11,7 @@ private final BlockEntityType type; @Nullable protected Level level; -@@ -36,6 +_,8 @@ +@@ -35,6 +_,8 @@ protected boolean remove; private BlockState blockState; private DataComponentMap components = DataComponentMap.EMPTY; @@ -20,7 +20,7 @@ public BlockEntity(BlockEntityType p_155228_, BlockPos p_155229_, BlockState p_155230_) { this.type = p_155228_; -@@ -51,7 +_,7 @@ +@@ -50,7 +_,7 @@ } public boolean isValidBlockState(BlockState p_353131_) { @@ -29,7 +29,7 @@ } public static BlockPos getPosFromTag(CompoundTag p_187473_) { -@@ -72,6 +_,8 @@ +@@ -71,6 +_,8 @@ } protected void loadAdditional(CompoundTag p_338466_, HolderLookup.Provider p_338445_) { @@ -38,7 +38,7 @@ } public final void loadWithComponents(CompoundTag p_338356_, HolderLookup.Provider p_338558_) { -@@ -87,6 +_,9 @@ +@@ -86,6 +_,9 @@ } protected void saveAdditional(CompoundTag p_187471_, HolderLookup.Provider p_323635_) { @@ -48,7 +48,7 @@ } public final CompoundTag saveWithFullMetadata(HolderLookup.Provider p_323767_) { -@@ -217,10 +_,14 @@ +@@ -208,10 +_,14 @@ public void setRemoved() { this.remove = true; @@ -63,7 +63,7 @@ } public boolean triggerEvent(int p_58889_, int p_58890_) { -@@ -247,6 +_,27 @@ +@@ -234,6 +_,27 @@ return this.type; } @@ -91,7 +91,7 @@ @Deprecated public void setBlockState(BlockState p_155251_) { this.validateBlockState(p_155251_); -@@ -326,5 +_,15 @@ +@@ -314,5 +_,15 @@ T get(DataComponentType p_338658_); T getOrDefault(DataComponentType p_338573_, T p_338734_); diff --git a/patches/net/minecraft/world/level/block/entity/BlockEntityType.java.patch b/patches/net/minecraft/world/level/block/entity/BlockEntityType.java.patch index 77f3f48d29..87245aa63a 100644 --- a/patches/net/minecraft/world/level/block/entity/BlockEntityType.java.patch +++ b/patches/net/minecraft/world/level/block/entity/BlockEntityType.java.patch @@ -1,14 +1,14 @@ --- a/net/minecraft/world/level/block/entity/BlockEntityType.java +++ b/net/minecraft/world/level/block/entity/BlockEntityType.java -@@ -229,6 +_,7 @@ - public static final BlockEntityType TRIAL_SPAWNER = register("trial_spawner", TrialSpawnerBlockEntity::new, Blocks.TRIAL_SPAWNER); +@@ -230,6 +_,7 @@ public static final BlockEntityType VAULT = register("vault", VaultBlockEntity::new, Blocks.VAULT); + private static final Set> OP_ONLY_CUSTOM_DATA = Set.of(COMMAND_BLOCK, LECTERN, SIGN, HANGING_SIGN, MOB_SPAWNER, TRIAL_SPAWNER); private final BlockEntityType.BlockEntitySupplier factory; + // Neo: This field will be modified by BlockEntityTypeAddBlocksEvent event. Please use the event to add to this field for vanilla or other mod's BlockEntityTypes. private final Set validBlocks; private final Holder.Reference> builtInRegistryHolder = BuiltInRegistries.BLOCK_ENTITY_TYPE.createIntrusiveHolder(this); -@@ -253,9 +_,24 @@ +@@ -254,9 +_,24 @@ this.validBlocks = p_155260_; } diff --git a/patches/net/minecraft/world/level/block/entity/ShulkerBoxBlockEntity.java.patch b/patches/net/minecraft/world/level/block/entity/ShulkerBoxBlockEntity.java.patch index d884906785..16cb1518aa 100644 --- a/patches/net/minecraft/world/level/block/entity/ShulkerBoxBlockEntity.java.patch +++ b/patches/net/minecraft/world/level/block/entity/ShulkerBoxBlockEntity.java.patch @@ -1,6 +1,6 @@ --- a/net/minecraft/world/level/block/entity/ShulkerBoxBlockEntity.java +++ b/net/minecraft/world/level/block/entity/ShulkerBoxBlockEntity.java -@@ -231,7 +_,7 @@ +@@ -232,7 +_,7 @@ @Override public boolean canPlaceItemThroughFace(int p_59663_, ItemStack p_59664_, @Nullable Direction p_59665_) { diff --git a/patches/net/minecraft/world/level/block/entity/trialspawner/TrialSpawner.java.patch b/patches/net/minecraft/world/level/block/entity/trialspawner/TrialSpawner.java.patch index 54f0c94786..7e157d8a3b 100644 --- a/patches/net/minecraft/world/level/block/entity/trialspawner/TrialSpawner.java.patch +++ b/patches/net/minecraft/world/level/block/entity/trialspawner/TrialSpawner.java.patch @@ -22,7 +22,7 @@ mob.setPersistenceRequired(); spawndata.getEquipment().ifPresent(mob::equip); -@@ -413,5 +_,14 @@ +@@ -420,5 +_,14 @@ TrialSpawnerState getState(); void markUpdated(); diff --git a/patches/net/minecraft/world/level/block/state/BlockBehaviour.java.patch b/patches/net/minecraft/world/level/block/state/BlockBehaviour.java.patch index 9fdd0acefe..d1ee4eefd2 100644 --- a/patches/net/minecraft/world/level/block/state/BlockBehaviour.java.patch +++ b/patches/net/minecraft/world/level/block/state/BlockBehaviour.java.patch @@ -30,7 +30,7 @@ } } -@@ -389,6 +_,7 @@ +@@ -389,10 +_,13 @@ return this.isRandomlyTicking; } @@ -38,7 +38,13 @@ protected SoundType getSoundType(BlockState p_320941_) { return this.soundType; } -@@ -405,6 +_,10 @@ + ++ @Deprecated ++ /** @deprecated NeoForge: use {@link net.neoforged.neoforge.common.extensions.IBlockExtension#getCloneItemStack(BlockState, LevelReader, BlockPos, Player, boolean) player-sensitive version} */ + protected ItemStack getCloneItemStack(LevelReader p_382795_, BlockPos p_383120_, BlockState p_382830_, boolean p_388788_) { + return new ItemStack(this.asItem()); + } +@@ -409,6 +_,10 @@ return this.properties.destroyTime; } @@ -49,7 +55,7 @@ public abstract static class BlockStateBase extends StateHolder { private static final Direction[] DIRECTIONS = Direction.values(); private static final VoxelShape[] EMPTY_OCCLUSION_SHAPES = Util.make( -@@ -561,12 +_,14 @@ +@@ -565,12 +_,14 @@ return this.useShapeForLightOcclusion; } @@ -65,7 +71,7 @@ } public boolean ignitedByLava() { -@@ -579,9 +_,11 @@ +@@ -583,9 +_,11 @@ } public MapColor getMapColor(BlockGetter p_285002_, BlockPos p_285293_) { @@ -78,7 +84,7 @@ public BlockState rotate(Rotation p_60718_) { return this.getBlock().rotate(this.asState(), p_60718_); } -@@ -635,6 +_,8 @@ +@@ -639,6 +_,8 @@ } public PushReaction getPistonPushReaction() { @@ -87,7 +93,7 @@ return this.pushReaction; } -@@ -762,6 +_,9 @@ +@@ -766,6 +_,9 @@ } public InteractionResult useItemOn(ItemStack p_316374_, Level p_316651_, Player p_316623_, InteractionHand p_316469_, BlockHitResult p_316877_) { @@ -97,7 +103,7 @@ return this.getBlock().useItemOn(p_316374_, this.asState(), p_316651_, p_316877_.getBlockPos(), p_316623_, p_316469_, p_316877_); } -@@ -871,6 +_,7 @@ +@@ -875,6 +_,7 @@ return this.getBlock().getSeed(this.asState(), p_60727_); } @@ -105,7 +111,7 @@ public SoundType getSoundType() { return this.getBlock().getSoundType(this.asState()); } -@@ -996,7 +_,7 @@ +@@ -1004,7 +_,7 @@ BlockBehaviour.StateArgumentPredicate> isValidSpawn = (p_360193_, p_360194_, p_360195_, p_360196_) -> p_360193_.isFaceSturdy( p_360194_, p_360195_, Direction.UP ) diff --git a/patches/net/minecraft/world/level/chunk/ChunkAccess.java.patch b/patches/net/minecraft/world/level/chunk/ChunkAccess.java.patch index 7de81bd23b..303f56095a 100644 --- a/patches/net/minecraft/world/level/chunk/ChunkAccess.java.patch +++ b/patches/net/minecraft/world/level/chunk/ChunkAccess.java.patch @@ -9,7 +9,7 @@ public static final int NO_FILLED_SECTION = -1; private static final Logger LOGGER = LogUtils.getLogger(); private static final LongSet EMPTY_REFERENCE_SET = new LongOpenHashSet(); -@@ -324,10 +_,19 @@ +@@ -327,10 +_,19 @@ @Override public final void findBlockLightSources(BiConsumer p_285269_) { @@ -30,7 +30,7 @@ BlockPos.MutableBlockPos blockpos$mutableblockpos = new BlockPos.MutableBlockPos(); for (int i = this.getMinSectionY(); i <= this.getMaxSectionY(); i++) { -@@ -339,8 +_,9 @@ +@@ -342,8 +_,9 @@ for (int k = 0; k < 16; k++) { for (int l = 0; l < 16; l++) { BlockState blockstate = levelchunksection.getBlockState(l, j, k); @@ -42,7 +42,7 @@ } } } -@@ -482,6 +_,74 @@ +@@ -485,6 +_,74 @@ public ChunkSkyLightSources getSkyLightSources() { return this.skyLightSources; } diff --git a/patches/net/minecraft/world/level/chunk/ChunkGenerator.java.patch b/patches/net/minecraft/world/level/chunk/ChunkGenerator.java.patch index 37936df20a..6aca891ac4 100644 --- a/patches/net/minecraft/world/level/chunk/ChunkGenerator.java.patch +++ b/patches/net/minecraft/world/level/chunk/ChunkGenerator.java.patch @@ -1,6 +1,6 @@ --- a/net/minecraft/world/level/chunk/ChunkGenerator.java +++ b/net/minecraft/world/level/chunk/ChunkGenerator.java -@@ -92,9 +_,14 @@ +@@ -93,9 +_,14 @@ public ChunkGenerator(BiomeSource p_255838_, Function, BiomeGenerationSettings> p_256216_) { this.biomeSource = p_255838_; this.generationSettingsGetter = p_256216_; diff --git a/patches/net/minecraft/world/level/chunk/LevelChunk.java.patch b/patches/net/minecraft/world/level/chunk/LevelChunk.java.patch index dfa6a02fdf..4a9a08cd9f 100644 --- a/patches/net/minecraft/world/level/chunk/LevelChunk.java.patch +++ b/patches/net/minecraft/world/level/chunk/LevelChunk.java.patch @@ -1,6 +1,6 @@ --- a/net/minecraft/world/level/chunk/LevelChunk.java +++ b/net/minecraft/world/level/chunk/LevelChunk.java -@@ -51,7 +_,7 @@ +@@ -52,7 +_,7 @@ import net.minecraft.world.ticks.TickContainerAccess; import org.slf4j.Logger; @@ -9,7 +9,7 @@ static final Logger LOGGER = LogUtils.getLogger(); private static final TickingBlockEntity NULL_TICKER = new TickingBlockEntity() { @Override -@@ -142,6 +_,7 @@ +@@ -146,6 +_,7 @@ this.setAllStarts(p_196851_.getAllStarts()); this.setAllReferences(p_196851_.getAllReferences()); @@ -17,7 +17,7 @@ for (Entry entry : p_196851_.getHeightmaps()) { if (ChunkStatus.FULL.heightmapsAfter().contains(entry.getKey())) { this.setHeightmap(entry.getKey(), entry.getValue().getRawData()); -@@ -279,7 +_,7 @@ +@@ -283,7 +_,7 @@ this.level.getChunkSource().onSectionEmptinessChanged(this.chunkPos.x, SectionPos.blockToSectionCoord(i), this.chunkPos.z, flag1); } @@ -26,7 +26,7 @@ ProfilerFiller profilerfiller = Profiler.get(); profilerfiller.push("updateSkyLightSources"); this.skyLightSources.update(this, j, i, l); -@@ -298,7 +_,7 @@ +@@ -302,7 +_,7 @@ if (!levelchunksection.getBlockState(j, k, l).is(block)) { return null; } else { @@ -35,7 +35,7 @@ p_62866_.onPlace(this.level, p_62865_, blockstate, p_62867_); } -@@ -353,6 +_,10 @@ +@@ -357,6 +_,10 @@ @Nullable public BlockEntity getBlockEntity(BlockPos p_62868_, LevelChunk.EntityCreationType p_62869_) { BlockEntity blockentity = this.blockEntities.get(p_62868_); @@ -46,7 +46,7 @@ if (blockentity == null) { CompoundTag compoundtag = this.pendingBlockEntities.remove(p_62868_); if (compoundtag != null) { -@@ -370,9 +_,6 @@ +@@ -374,9 +_,6 @@ this.addAndRegisterBlockEntity(blockentity); } } @@ -56,7 +56,7 @@ } return blockentity; -@@ -386,6 +_,7 @@ +@@ -390,6 +_,7 @@ } this.updateBlockEntityTicker(p_156391_); @@ -64,7 +64,7 @@ } } -@@ -429,6 +_,7 @@ +@@ -433,6 +_,7 @@ BlockEntity blockentity = this.blockEntities.put(blockpos.immutable(), p_156374_); if (blockentity != null && blockentity != p_156374_) { blockentity.setRemoved(); @@ -72,7 +72,7 @@ } } } -@@ -438,9 +_,14 @@ +@@ -442,9 +_,14 @@ public CompoundTag getBlockEntityNbtForSaving(BlockPos p_62932_, HolderLookup.Provider p_323699_) { BlockEntity blockentity = this.getBlockEntity(p_62932_); if (blockentity != null && !blockentity.isRemoved()) { @@ -87,7 +87,7 @@ } else { CompoundTag compoundtag = this.pendingBlockEntities.get(p_62932_); if (compoundtag != null) { -@@ -462,6 +_,7 @@ +@@ -466,6 +_,7 @@ } blockentity.setRemoved(); @@ -95,7 +95,7 @@ } } -@@ -522,7 +_,7 @@ +@@ -526,7 +_,7 @@ p_187974_.accept((p_338077_, p_338078_, p_338079_) -> { BlockEntity blockentity = this.getBlockEntity(p_338077_, LevelChunk.EntityCreationType.IMMEDIATE); if (blockentity != null && p_338079_ != null && blockentity.getType() == p_338078_) { @@ -104,7 +104,7 @@ } }); } -@@ -632,6 +_,7 @@ +@@ -636,6 +_,7 @@ } public void clearAllBlockEntities() { @@ -112,7 +112,7 @@ this.blockEntities.values().forEach(BlockEntity::setRemoved); this.blockEntities.clear(); this.tickersInLevel.values().forEach(p_187966_ -> p_187966_.rebind(NULL_TICKER)); -@@ -639,6 +_,7 @@ +@@ -643,6 +_,7 @@ } public void registerAllBlockEntitiesAfterLevelLoad() { @@ -120,7 +120,7 @@ this.blockEntities.values().forEach(p_187988_ -> { if (this.level instanceof ServerLevel serverlevel) { this.addGameEventListener(p_187988_, serverlevel); -@@ -690,6 +_,14 @@ +@@ -694,6 +_,14 @@ return new LevelChunk.BoundTickingBlockEntity<>(p_156376_, p_156377_); } @@ -135,7 +135,7 @@ class BoundTickingBlockEntity implements TickingBlockEntity { private final T blockEntity; private final BlockEntityTicker ticker; -@@ -707,6 +_,7 @@ +@@ -711,6 +_,7 @@ if (LevelChunk.this.isTicking(blockpos)) { try { ProfilerFiller profilerfiller = Profiler.get(); @@ -143,7 +143,7 @@ profilerfiller.push(this::getType); BlockState blockstate = LevelChunk.this.getBlockState(blockpos); if (this.blockEntity.getType().isValid(blockstate)) { -@@ -728,7 +_,15 @@ +@@ -732,7 +_,15 @@ CrashReport crashreport = CrashReport.forThrowable(throwable, "Ticking block entity"); CrashReportCategory crashreportcategory = crashreport.addCategory("Block entity being ticked"); this.blockEntity.fillCrashReportCategory(crashreportcategory); diff --git a/patches/net/minecraft/world/level/chunk/status/ChunkStatusTasks.java.patch b/patches/net/minecraft/world/level/chunk/status/ChunkStatusTasks.java.patch index a16cba61c5..79373d9e75 100644 --- a/patches/net/minecraft/world/level/chunk/status/ChunkStatusTasks.java.patch +++ b/patches/net/minecraft/world/level/chunk/status/ChunkStatusTasks.java.patch @@ -1,6 +1,6 @@ --- a/net/minecraft/world/level/chunk/status/ChunkStatusTasks.java +++ b/net/minecraft/world/level/chunk/status/ChunkStatusTasks.java -@@ -198,11 +_,22 @@ +@@ -199,11 +_,22 @@ } levelchunk.setFullStatus(generationchunkholder::getFullStatus); diff --git a/patches/net/minecraft/world/level/chunk/storage/SectionStorage.java.patch b/patches/net/minecraft/world/level/chunk/storage/SectionStorage.java.patch index 5eef3aff03..c8542d92f5 100644 --- a/patches/net/minecraft/world/level/chunk/storage/SectionStorage.java.patch +++ b/patches/net/minecraft/world/level/chunk/storage/SectionStorage.java.patch @@ -1,6 +1,6 @@ --- a/net/minecraft/world/level/chunk/storage/SectionStorage.java +++ b/net/minecraft/world/level/chunk/storage/SectionStorage.java -@@ -302,6 +_,19 @@ +@@ -306,6 +_,19 @@ this.simpleRegionStorage.close(); } diff --git a/patches/net/minecraft/world/level/levelgen/structure/Structure.java.patch b/patches/net/minecraft/world/level/levelgen/structure/Structure.java.patch index 62c1626156..a658f2049a 100644 --- a/patches/net/minecraft/world/level/levelgen/structure/Structure.java.patch +++ b/patches/net/minecraft/world/level/levelgen/structure/Structure.java.patch @@ -1,6 +1,6 @@ --- a/net/minecraft/world/level/levelgen/structure/Structure.java +++ b/net/minecraft/world/level/levelgen/structure/Structure.java -@@ -42,10 +_,11 @@ +@@ -46,10 +_,11 @@ public abstract class Structure { public static final Codec DIRECT_CODEC = BuiltInRegistries.STRUCTURE_TYPE.byNameCodec().dispatch(Structure::type, StructureType::codec); public static final Codec> CODEC = RegistryFileCodec.create(Registries.STRUCTURE, DIRECT_CODEC); @@ -14,7 +14,7 @@ } public static MapCodec simpleCodec(Function p_226608_) { -@@ -54,6 +_,7 @@ +@@ -58,6 +_,7 @@ protected Structure(Structure.StructureSettings p_226558_) { this.settings = p_226558_; @@ -22,7 +22,7 @@ } public HolderSet biomes() { -@@ -196,6 +_,25 @@ +@@ -211,6 +_,25 @@ } public abstract StructureType type(); diff --git a/patches/net/minecraft/world/level/storage/DimensionDataStorage.java.patch b/patches/net/minecraft/world/level/storage/DimensionDataStorage.java.patch index 8dd492afb3..0158a9c570 100644 --- a/patches/net/minecraft/world/level/storage/DimensionDataStorage.java.patch +++ b/patches/net/minecraft/world/level/storage/DimensionDataStorage.java.patch @@ -1,6 +1,6 @@ --- a/net/minecraft/world/level/storage/DimensionDataStorage.java +++ b/net/minecraft/world/level/storage/DimensionDataStorage.java -@@ -69,7 +_,7 @@ +@@ -73,7 +_,7 @@ } @Nullable @@ -9,7 +9,7 @@ try { Path path = this.getDataFile(p_164870_); if (Files.exists(path)) { -@@ -88,7 +_,7 @@ +@@ -92,7 +_,7 @@ p_164857_.setDirty(); } @@ -18,7 +18,7 @@ CompoundTag compoundtag1; try ( InputStream inputstream = Files.newInputStream(this.getDataFile(p_78159_)); -@@ -103,9 +_,16 @@ +@@ -107,9 +_,16 @@ } } @@ -37,16 +37,16 @@ return compoundtag1; } -@@ -156,7 +_,11 @@ - private static CompletableFuture tryWriteAsync(Path p_364583_, CompoundTag p_363128_) { - return CompletableFuture.runAsync(() -> { - try { -- NbtIo.writeCompressed(p_363128_, p_364583_); -+ // Neo: ensure parent directories exist if the SavedData's path contains slashes -+ if (!Files.exists(p_364583_)) { -+ Files.createDirectories(p_364583_.getParent()); -+ } -+ net.neoforged.neoforge.common.IOUtilities.writeNbtCompressed(p_363128_, p_364583_); - } catch (IOException ioexception) { - LOGGER.error("Could not save data to {}", p_364583_.getFileName(), ioexception); - } +@@ -182,7 +_,11 @@ + + private static void tryWrite(Path p_390658_, CompoundTag p_390657_) { + try { +- NbtIo.writeCompressed(p_390657_, p_390658_); ++ // Neo: ensure parent directories exist if the SavedData's path contains slashes ++ if (!Files.exists(p_390658_)) { ++ Files.createDirectories(p_390658_.getParent()); ++ } ++ net.neoforged.neoforge.common.IOUtilities.writeNbtCompressed(p_390657_, p_390658_); + } catch (IOException ioexception) { + LOGGER.error("Could not save data to {}", p_390658_.getFileName(), ioexception); + } diff --git a/patches/net/minecraft/world/level/storage/PrimaryLevelData.java.patch b/patches/net/minecraft/world/level/storage/PrimaryLevelData.java.patch index b9f9edb375..1ff81563a8 100644 --- a/patches/net/minecraft/world/level/storage/PrimaryLevelData.java.patch +++ b/patches/net/minecraft/world/level/storage/PrimaryLevelData.java.patch @@ -17,6 +17,16 @@ p_78531_.get("Player").flatMap(CompoundTag.CODEC::parse).result().orElse(null), p_78531_.get("WasModded").asBoolean(false), new BlockPos(p_78531_.get("SpawnX").asInt(0), p_78531_.get("SpawnY").asInt(0), p_78531_.get("SpawnZ").asInt(0)), +@@ -192,7 +_,8 @@ + .asStream() + .flatMap(p_338118_ -> p_338118_.asString().result().stream()) + .collect(Collectors.toCollection(Sets::newLinkedHashSet)), +- p_78531_.get("removed_features").asStream().flatMap(p_338117_ -> p_338117_.asString().result().stream()).collect(Collectors.toSet()), ++ // Neo: Append removed modded feature flags ++ updateRemovedFeatureFlags(p_78531_.get("removed_features").asStream().flatMap(p_338117_ -> p_338117_.asString().result().stream()), p_78531_.get("enabled_features").asStream().flatMap(features -> features.asString().result().stream())).collect(Collectors.toSet()), + new TimerQueue<>(TimerCallbacks.SERVER_CALLBACKS, p_78531_.get("ScheduledEvents").asStream()), + (CompoundTag)p_78531_.get("CustomBossEvents").orElseEmptyMap().getValue(), + p_78531_.get("DragonFight").read(EndDragonFight.Data.CODEC).resultOrPartial(LOGGER::error).orElse(EndDragonFight.Data.DEFAULT), @@ -200,7 +_,11 @@ p_251864_, p_250651_, @@ -42,7 +52,7 @@ } private static ListTag stringCollectionToTag(Set p_277880_) { -@@ -572,10 +_,44 @@ +@@ -572,10 +_,58 @@ return this.settings.copy(); } @@ -85,5 +95,19 @@ + @Override + public void setDayTimePerTick(float dayTimePerTick) { + this.dayTimePerTick = dayTimePerTick; ++ } ++ ++ private static java.util.stream.Stream updateRemovedFeatureFlags(java.util.stream.Stream removedFeatures, java.util.stream.Stream enabledFeatures) { ++ var unknownFeatureFlags = new HashSet(); ++ // parses the incoming Stream and spits out unknown flag names (ResourceLocation) ++ // we do not care about the returned FeatureFlagSet, only the flags which do not exist ++ net.minecraft.world.flag.FeatureFlags.REGISTRY.fromNames(enabledFeatures.map(net.minecraft.resources.ResourceLocation::parse).collect(Collectors.toSet()), unknownFeatureFlags::add); ++ // concat the received removed flags with our new additions ++ return java.util.stream.Stream.concat(removedFeatures, unknownFeatureFlags.stream() ++ // we only want modded flags, mojang has datafixers for vanilla flags ++ .filter(java.util.function.Predicate.not(name -> name.getNamespace().equals(net.minecraft.resources.ResourceLocation.DEFAULT_NAMESPACE))) ++ .map(net.minecraft.resources.ResourceLocation::toString)) ++ // no duplicates should exist in this stream ++ .distinct(); } } diff --git a/projects/base/.gitignore b/projects/base/.gitignore index 1b10a46d0a..cb56563da3 100644 --- a/projects/base/.gitignore +++ b/projects/base/.gitignore @@ -1,3 +1,4 @@ src build .gradle +run diff --git a/projects/base/build.gradle b/projects/base/build.gradle index 7a78b9aa79..d0b0c18084 100644 --- a/projects/base/build.gradle +++ b/projects/base/build.gradle @@ -1,3 +1,39 @@ -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() + } +} + +neoDev { + runs { + configureEach { + gameDirectory = layout.projectDir.dir("run/$name") + } + client { + client() + } + server { + server() + } + // Generated files are in run/clientData/generated + clientData { + clientData() + programArgument "--all" + } + // Generated files are in run/serverData/generated + serverData { + serverData() + programArgument "--all" + } + } } diff --git a/projects/neoforge/build.gradle b/projects/neoforge/build.gradle index bc642bd0c9..bc5638d3ed 100644 --- a/projects/neoforge/build.gradle +++ b/projects/neoforge/build.gradle @@ -10,17 +10,27 @@ 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 { - from '21.0' + from '21.3' disableAutomaticPublicationRegistration() } -dynamicProject { - runtime("${project.minecraft_version}-${project.neoform_version}", - rootProject.layout.projectDirectory.dir('patches'), - rootProject.layout.projectDirectory.dir('rejects')) +sourceSets { + main { + java { + srcDirs rootProject.file('src/main/java') + } + resources { + srcDirs rootProject.file('src/main/resources'), rootProject.file('src/generated/resources') + } + } } final checkVersion = JCCPlugin.providePreviousVersion( @@ -29,7 +39,7 @@ final checkVersion = JCCPlugin.providePreviousVersion( ) final createCompatJar = tasks.register('createCompatibilityCheckJar', ProvideNeoForgeJarTask) { // Use the same jar that the patches were generated against - cleanJar.set(tasks.generateClientBinaryPatches.clean) + cleanJar.set(tasks.generateClientBinPatches.cleanJar) maven.set('https://maven.neoforged.net/releases') artifact.set('net.neoforged:neoforge') version.set(checkVersion) @@ -42,47 +52,37 @@ checkJarCompatibility { 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') +neoDev { + mods { + minecraft { + sourceSet sourceSets.main + } + "neoforge-coremods" { + sourceSet project(":neoforge-coremods").sourceSets.main } } } -sourceSets { - main { - java { - srcDirs rootProject.file('src/main/java') +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' } - resources { - srcDirs rootProject.file('src/main/resources'), rootProject.file('src/generated/resources') + endorseStrictVersions() + } + neoFormDependencies("net.neoforged:neoform:${project.minecraft_version}-${project.neoform_version}") { + capabilities { + requireCapability 'net.neoforged:neoform-dependencies' } + endorseStrictVersions() } -} - -dependencies { - runtimeOnly "cpw.mods:bootstraplauncher:${project.bootstraplauncher_version}" - moduleOnly "cpw.mods:securejarhandler:${project.securejarhandler_version}" + 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,77 @@ 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},)") - } - - userdevTestImplementation("net.neoforged.fancymodloader:junit-fml:${project.fancy_mod_loader_version}") - compileOnly(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 + userdevCompileOnly jarJar("io.github.llamalad7:mixinextras-neoforge:${project.mixin_extras_version}") - 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 + userdevTestFixtures("net.neoforged.fancymodloader:junit-fml:${project.fancy_mod_loader_version}") { + endorseStrictVersions() } - 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 - } + // 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"))) } -runs { - client { } - server { } - gameTestServer { } - gameTestClient { } - data { - arguments.addAll '--mod', 'neoforge' - - modSources.add project.sourceSets.main - - idea { - primarySourceSet project.sourceSets.main +neoDev { + runs { + configureEach { + gameDirectory = layout.projectDir.dir("run/$name") + } + client { + client() + } + server { + server() + } + gameTestServer { + type = "gameTestServer" + } + data { + // We perform client and server datagen in a single clientData run to avoid + // having to juggle two generated resources folders and two runs for no additional benefit. + clientData() + 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:API Note:', @@ -259,27 +170,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 +180,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 +196,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 +208,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 +224,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 +237,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 +249,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 +263,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 +277,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 +290,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 +305,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") } } diff --git a/settings.gradle b/settings.gradle index 74b177945e..aff93fdd4c 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1,25 +1,40 @@ pluginManagement { repositories { gradlePluginPortal() - mavenLocal() maven { url = 'https://maven.neoforged.net/releases' } + mavenLocal() } } plugins { - id 'net.neoforged.gradle.platform' version '7.0.163' + id 'net.neoforged.moddev.repositories' version "${moddevgradle_plugin_version}" + id 'org.gradle.toolchains.foojay-resolver-convention' version '0.8.0' } -rootProject.name = rootDir.name +// 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 -dynamicProjects { - include ':base' - include ':neoforge' +dependencyResolutionManagement { + repositoriesMode = RepositoriesMode.FAIL_ON_PROJECT_REPOS + rulesMode = RulesMode.FAIL_ON_PROJECT_RULES + repositories { + mavenCentral() + mavenLocal() + } +} - project(":base").projectDir = file("projects/base") - project(":neoforge").projectDir = file("projects/neoforge") +if (rootProject.name.toLowerCase() == "neoforge") { + // Solve name clashes between projects/neoforge and the rootProject for Eclipse. + rootProject.name = "NeoForge-Root" } +include ':base' +project(':base').projectDir = file('projects/base') +include ':neoforge' +project(':neoforge').projectDir = file('projects/neoforge') + include ':tests' project(":tests").projectDir = file("tests") diff --git a/src/generated/resources/assets/c/lang/en_us.json b/src/generated/resources/assets/c/lang/en_us.json index 3c27e29b49..ddba91b3e2 100644 --- a/src/generated/resources/assets/c/lang/en_us.json +++ b/src/generated/resources/assets/c/lang/en_us.json @@ -330,7 +330,6 @@ "tag.item.c.seeds.wheat": "Wheat Seeds", "tag.item.c.shulker_boxes": "Shulker Boxes", "tag.item.c.slime_balls": "Slimeballs", - "tag.item.c.slimeballs": "Slimeballs", "tag.item.c.stones": "Stones", "tag.item.c.storage_blocks": "Storage Blocks", "tag.item.c.storage_blocks.bone_meal": "Bone Meal Storage Blocks", diff --git a/src/generated/resources/data/c/tags/item/slime_balls.json b/src/generated/resources/data/c/tags/item/slime_balls.json index c5d2c7d82d..168a8c2a45 100644 --- a/src/generated/resources/data/c/tags/item/slime_balls.json +++ b/src/generated/resources/data/c/tags/item/slime_balls.json @@ -1,10 +1,6 @@ { "values": [ "minecraft:slime_ball", - { - "id": "#c:slimeballs", - "required": false - }, { "id": "#forge:slime_balls", "required": false diff --git a/src/generated/resources/data/c/tags/item/slimeballs.json b/src/generated/resources/data/c/tags/item/slimeballs.json deleted file mode 100644 index 533c25d916..0000000000 --- a/src/generated/resources/data/c/tags/item/slimeballs.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "values": [ - "minecraft:slime_ball" - ] -} \ No newline at end of file diff --git a/src/generated/resources/data/minecraft/loot_table/blocks/pale_oak_leaves.json b/src/generated/resources/data/minecraft/loot_table/blocks/pale_oak_leaves.json new file mode 100644 index 0000000000..ff55c72291 --- /dev/null +++ b/src/generated/resources/data/minecraft/loot_table/blocks/pale_oak_leaves.json @@ -0,0 +1,132 @@ +{ + "type": "minecraft:block", + "pools": [ + { + "bonus_rolls": 0.0, + "entries": [ + { + "type": "minecraft:alternatives", + "children": [ + { + "type": "minecraft:item", + "conditions": [ + { + "condition": "minecraft:any_of", + "terms": [ + { + "ability": "shears_dig", + "condition": "neoforge:can_item_perform_ability" + }, + { + "condition": "minecraft:match_tool", + "predicate": { + "predicates": { + "minecraft:enchantments": [ + { + "enchantments": "minecraft:silk_touch", + "levels": { + "min": 1 + } + } + ] + } + } + } + ] + } + ], + "name": "minecraft:pale_oak_leaves" + }, + { + "type": "minecraft:item", + "conditions": [ + { + "condition": "minecraft:survives_explosion" + }, + { + "chances": [ + 0.05, + 0.0625, + 0.083333336, + 0.1 + ], + "condition": "minecraft:table_bonus", + "enchantment": "minecraft:fortune" + } + ], + "name": "minecraft:pale_oak_sapling" + } + ] + } + ], + "rolls": 1.0 + }, + { + "bonus_rolls": 0.0, + "conditions": [ + { + "condition": "minecraft:inverted", + "term": { + "condition": "minecraft:any_of", + "terms": [ + { + "ability": "shears_dig", + "condition": "neoforge:can_item_perform_ability" + }, + { + "condition": "minecraft:match_tool", + "predicate": { + "predicates": { + "minecraft:enchantments": [ + { + "enchantments": "minecraft:silk_touch", + "levels": { + "min": 1 + } + } + ] + } + } + } + ] + } + } + ], + "entries": [ + { + "type": "minecraft:item", + "conditions": [ + { + "chances": [ + 0.02, + 0.022222223, + 0.025, + 0.033333335, + 0.1 + ], + "condition": "minecraft:table_bonus", + "enchantment": "minecraft:fortune" + } + ], + "functions": [ + { + "add": false, + "count": { + "type": "minecraft:uniform", + "max": 2.0, + "min": 1.0 + }, + "function": "minecraft:set_count" + }, + { + "function": "minecraft:explosion_decay" + } + ], + "name": "minecraft:stick" + } + ], + "rolls": 1.0 + } + ], + "random_sequence": "minecraft:blocks/pale_oak_leaves" +} \ No newline at end of file diff --git a/src/generated/resources/data/minecraft/recipe/pale_oak_chest_boat.json b/src/generated/resources/data/minecraft/recipe/pale_oak_chest_boat.json new file mode 100644 index 0000000000..8339b115b1 --- /dev/null +++ b/src/generated/resources/data/minecraft/recipe/pale_oak_chest_boat.json @@ -0,0 +1,17 @@ +{ + "type": "minecraft:crafting_shapeless", + "category": "misc", + "group": "chest_boat", + "ingredients": [ + { + "neoforge:ingredient_type": "neoforge:difference", + "base": "#c:chests/wooden", + "subtracted": "#c:chests/trapped" + }, + "minecraft:pale_oak_boat" + ], + "result": { + "count": 1, + "id": "minecraft:pale_oak_chest_boat" + } +} \ No newline at end of file diff --git a/src/generated/resources/data/minecraft/recipe/pale_oak_fence.json b/src/generated/resources/data/minecraft/recipe/pale_oak_fence.json new file mode 100644 index 0000000000..c4a34ad537 --- /dev/null +++ b/src/generated/resources/data/minecraft/recipe/pale_oak_fence.json @@ -0,0 +1,17 @@ +{ + "type": "minecraft:crafting_shaped", + "category": "misc", + "group": "wooden_fence", + "key": { + "#": "#c:rods/wooden", + "W": "minecraft:pale_oak_planks" + }, + "pattern": [ + "W#W", + "W#W" + ], + "result": { + "count": 3, + "id": "minecraft:pale_oak_fence" + } +} \ No newline at end of file diff --git a/src/generated/resources/data/minecraft/recipe/pale_oak_fence_gate.json b/src/generated/resources/data/minecraft/recipe/pale_oak_fence_gate.json new file mode 100644 index 0000000000..b09f87fa2b --- /dev/null +++ b/src/generated/resources/data/minecraft/recipe/pale_oak_fence_gate.json @@ -0,0 +1,17 @@ +{ + "type": "minecraft:crafting_shaped", + "category": "redstone", + "group": "wooden_fence_gate", + "key": { + "#": "#c:rods/wooden", + "W": "minecraft:pale_oak_planks" + }, + "pattern": [ + "#W#", + "#W#" + ], + "result": { + "count": 1, + "id": "minecraft:pale_oak_fence_gate" + } +} \ No newline at end of file diff --git a/src/generated/resources/data/minecraft/recipe/pale_oak_sign.json b/src/generated/resources/data/minecraft/recipe/pale_oak_sign.json new file mode 100644 index 0000000000..ac471218f3 --- /dev/null +++ b/src/generated/resources/data/minecraft/recipe/pale_oak_sign.json @@ -0,0 +1,18 @@ +{ + "type": "minecraft:crafting_shaped", + "category": "misc", + "group": "wooden_sign", + "key": { + "#": "minecraft:pale_oak_planks", + "X": "#c:rods/wooden" + }, + "pattern": [ + "###", + "###", + " X " + ], + "result": { + "count": 3, + "id": "minecraft:pale_oak_sign" + } +} \ No newline at end of file diff --git a/src/generated/resources/data/minecraft/tags/block/incorrect_for_diamond_tool.json b/src/generated/resources/data/minecraft/tags/block/incorrect_for_diamond_tool.json new file mode 100644 index 0000000000..1486484827 --- /dev/null +++ b/src/generated/resources/data/minecraft/tags/block/incorrect_for_diamond_tool.json @@ -0,0 +1,5 @@ +{ + "values": [ + "#neoforge:needs_netherite_tool" + ] +} \ No newline at end of file diff --git a/src/generated/resources/data/minecraft/tags/block/incorrect_for_gold_tool.json b/src/generated/resources/data/minecraft/tags/block/incorrect_for_gold_tool.json new file mode 100644 index 0000000000..1486484827 --- /dev/null +++ b/src/generated/resources/data/minecraft/tags/block/incorrect_for_gold_tool.json @@ -0,0 +1,5 @@ +{ + "values": [ + "#neoforge:needs_netherite_tool" + ] +} \ No newline at end of file diff --git a/src/generated/resources/data/minecraft/tags/block/incorrect_for_iron_tool.json b/src/generated/resources/data/minecraft/tags/block/incorrect_for_iron_tool.json new file mode 100644 index 0000000000..1486484827 --- /dev/null +++ b/src/generated/resources/data/minecraft/tags/block/incorrect_for_iron_tool.json @@ -0,0 +1,5 @@ +{ + "values": [ + "#neoforge:needs_netherite_tool" + ] +} \ No newline at end of file diff --git a/src/generated/resources/data/minecraft/tags/block/incorrect_for_stone_tool.json b/src/generated/resources/data/minecraft/tags/block/incorrect_for_stone_tool.json new file mode 100644 index 0000000000..1486484827 --- /dev/null +++ b/src/generated/resources/data/minecraft/tags/block/incorrect_for_stone_tool.json @@ -0,0 +1,5 @@ +{ + "values": [ + "#neoforge:needs_netherite_tool" + ] +} \ No newline at end of file diff --git a/src/generated/resources/data/minecraft/tags/block/incorrect_for_wooden_tool.json b/src/generated/resources/data/minecraft/tags/block/incorrect_for_wooden_tool.json new file mode 100644 index 0000000000..1486484827 --- /dev/null +++ b/src/generated/resources/data/minecraft/tags/block/incorrect_for_wooden_tool.json @@ -0,0 +1,5 @@ +{ + "values": [ + "#neoforge:needs_netherite_tool" + ] +} \ No newline at end of file diff --git a/src/generated/resources/data/neoforge/data_maps/entity_type/parrot_imitations.json b/src/generated/resources/data/neoforge/data_maps/entity_type/parrot_imitations.json index 513f03410d..8f1d1c0056 100644 --- a/src/generated/resources/data/neoforge/data_maps/entity_type/parrot_imitations.json +++ b/src/generated/resources/data/neoforge/data_maps/entity_type/parrot_imitations.json @@ -15,9 +15,6 @@ "minecraft:creaking": { "sound": "minecraft:entity.parrot.imitate.creaking" }, - "minecraft:creaking_transient": { - "sound": "minecraft:entity.parrot.imitate.creaking" - }, "minecraft:creeper": { "sound": "minecraft:entity.parrot.imitate.creeper" }, diff --git a/src/generated/resources/data/neoforge/data_maps/item/compostables.json b/src/generated/resources/data/neoforge/data_maps/item/compostables.json index 2b3bdff6a6..c0b3789290 100644 --- a/src/generated/resources/data/neoforge/data_maps/item/compostables.json +++ b/src/generated/resources/data/neoforge/data_maps/item/compostables.json @@ -70,6 +70,9 @@ "minecraft:cherry_sapling": { "chance": 0.3 }, + "minecraft:closed_eyeblossom": { + "chance": 0.65 + }, "minecraft:cocoa_beans": { "chance": 0.65 }, @@ -184,6 +187,9 @@ "minecraft:oak_sapling": { "chance": 0.3 }, + "minecraft:open_eyeblossom": { + "chance": 0.65 + }, "minecraft:orange_tulip": { "chance": 0.65 }, diff --git a/src/generated/resources/data/neoforge/tags/block/needs_netherite_tool.json b/src/generated/resources/data/neoforge/tags/block/needs_netherite_tool.json new file mode 100644 index 0000000000..f72d209df7 --- /dev/null +++ b/src/generated/resources/data/neoforge/tags/block/needs_netherite_tool.json @@ -0,0 +1,3 @@ +{ + "values": [] +} \ No newline at end of file diff --git a/src/generated/resources/pack.mcmeta b/src/generated/resources/pack.mcmeta index f49d678e8d..607b7ff420 100644 --- a/src/generated/resources/pack.mcmeta +++ b/src/generated/resources/pack.mcmeta @@ -3,7 +3,7 @@ "description": { "translate": "pack.neoforge.description" }, - "pack_format": 57, + "pack_format": 61, "supported_formats": [ 0, 2147483647 diff --git a/src/main/java/net/neoforged/neoforge/client/ClientHooks.java b/src/main/java/net/neoforged/neoforge/client/ClientHooks.java index 0132b8a863..50750c45cb 100644 --- a/src/main/java/net/neoforged/neoforge/client/ClientHooks.java +++ b/src/main/java/net/neoforged/neoforge/client/ClientHooks.java @@ -17,16 +17,15 @@ import java.io.File; import java.util.ArrayList; import java.util.Arrays; -import java.util.Collections; import java.util.Comparator; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Optional; -import java.util.Set; import java.util.Stack; import java.util.UUID; +import java.util.function.Consumer; import java.util.function.Function; import java.util.function.Supplier; import java.util.stream.Collectors; @@ -38,7 +37,6 @@ import net.minecraft.client.MouseHandler; import net.minecraft.client.Options; import net.minecraft.client.color.block.BlockColors; -import net.minecraft.client.color.item.ItemColors; import net.minecraft.client.gui.Font; import net.minecraft.client.gui.GuiGraphics; import net.minecraft.client.gui.components.LerpingBossEvent; @@ -85,11 +83,12 @@ import net.minecraft.client.resources.language.I18n; import net.minecraft.client.resources.model.AtlasSet; import net.minecraft.client.resources.model.BakedModel; +import net.minecraft.client.resources.model.EquipmentClientInfo; import net.minecraft.client.resources.model.Material; import net.minecraft.client.resources.model.ModelBakery; import net.minecraft.client.resources.model.ModelManager; -import net.minecraft.client.resources.model.ModelResourceLocation; import net.minecraft.client.resources.sounds.SoundInstance; +import net.minecraft.client.sounds.MusicInfo; import net.minecraft.client.sounds.SoundEngine; import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; @@ -104,7 +103,6 @@ import net.minecraft.resources.ResourceKey; import net.minecraft.resources.ResourceLocation; import net.minecraft.server.packs.resources.ReloadableResourceManager; -import net.minecraft.sounds.Music; import net.minecraft.util.Mth; import net.minecraft.util.RandomSource; import net.minecraft.util.profiling.Profiler; @@ -118,7 +116,6 @@ import net.minecraft.world.inventory.tooltip.TooltipComponent; import net.minecraft.world.item.ItemDisplayContext; import net.minecraft.world.item.ItemStack; -import net.minecraft.world.item.equipment.EquipmentModel; import net.minecraft.world.level.BlockAndTintGetter; import net.minecraft.world.level.GameType; import net.minecraft.world.level.Level; @@ -154,6 +151,7 @@ import net.neoforged.neoforge.client.event.RegisterClientReloadListenersEvent; import net.neoforged.neoforge.client.event.RegisterColorHandlersEvent; import net.neoforged.neoforge.client.event.RegisterKeyMappingsEvent; +import net.neoforged.neoforge.client.event.RegisterMaterialAtlasesEvent; import net.neoforged.neoforge.client.event.RegisterParticleProvidersEvent; import net.neoforged.neoforge.client.event.RegisterShadersEvent; import net.neoforged.neoforge.client.event.RegisterSpriteSourceTypesEvent; @@ -179,6 +177,7 @@ import net.neoforged.neoforge.client.gui.GuiLayerManager; import net.neoforged.neoforge.client.gui.map.MapDecorationRendererManager; import net.neoforged.neoforge.client.model.data.ModelData; +import net.neoforged.neoforge.client.renderstate.RegisterRenderStateModifiersEvent; import net.neoforged.neoforge.common.NeoForge; import net.neoforged.neoforge.common.NeoForgeMod; import net.neoforged.neoforge.forge.snapshots.ForgeSnapshotsModClient; @@ -253,8 +252,8 @@ public static float getGuiFarPlane() { return 11_000F + depth; } - public static ResourceLocation getArmorTexture(ItemStack armor, EquipmentModel.LayerType type, EquipmentModel.Layer layer, ResourceLocation _default) { - ResourceLocation result = armor.getItem().getArmorTexture(armor, type, layer, _default); + public static ResourceLocation getArmorTexture(ItemStack armor, EquipmentClientInfo.LayerType type, EquipmentClientInfo.Layer layer, ResourceLocation _default) { + ResourceLocation result = IClientItemExtensions.of(armor).getArmorTexture(armor, type, layer, _default); return result != null ? result : _default; } @@ -301,11 +300,7 @@ public static void onBlockColorsInit(BlockColors blockColors) { ModLoader.postEvent(new RegisterColorHandlersEvent.Block(blockColors)); } - public static void onItemColorsInit(ItemColors itemColors, BlockColors blockColors) { - ModLoader.postEvent(new RegisterColorHandlersEvent.Item(itemColors, blockColors)); - } - - public static Model getArmorModel(ItemStack itemStack, EquipmentModel.LayerType layerType, Model _default) { + public static Model getArmorModel(ItemStack itemStack, EquipmentClientInfo.LayerType layerType, Model _default) { return IClientItemExtensions.of(itemStack).getGenericArmorModel(itemStack, layerType, _default); } @@ -393,7 +388,7 @@ public static SoundInstance playSound(SoundEngine manager, SoundInstance sound) } @Nullable - public static Music selectMusic(Music situational, @Nullable SoundInstance playing) { + public static MusicInfo selectMusic(MusicInfo situational, @Nullable SoundInstance playing) { SelectMusicEvent e = new SelectMusicEvent(situational, playing); NeoForge.EVENT_BUS.post(e); return e.getMusic(); @@ -450,7 +445,7 @@ public static FogParameters onFogRender(FogRenderer.FogMode mode, FogType type, return fogParameters; } - public static void onModifyBakingResult(Map models, Map stitchResults, ModelBakery modelBakery) { + public static void onModifyBakingResult(ModelBakery.BakingResult bakingResult, Map stitchResults, ModelBakery modelBakery) { Function textureGetter = material -> { AtlasSet.StitchResult stitchResult = stitchResults.get(material.atlasLocation()); TextureAtlasSprite sprite = stitchResult.getSprite(material.texture()); @@ -460,14 +455,14 @@ public static void onModifyBakingResult(Map m LOGGER.warn("Failed to retrieve texture '{}' from atlas '{}'", material.texture(), material.atlasLocation(), new Throwable()); return stitchResult.missing(); }; - ModLoader.postEvent(new ModelEvent.ModifyBakingResult(models, textureGetter, modelBakery)); + ModLoader.postEvent(new ModelEvent.ModifyBakingResult(bakingResult, textureGetter, modelBakery)); } - public static void onModelBake(ModelManager modelManager, Map models, ModelBakery modelBakery) { - ModLoader.postEvent(new ModelEvent.BakingCompleted(modelManager, Collections.unmodifiableMap(models), modelBakery)); + public static void onModelBake(ModelManager modelManager, ModelBakery.BakingResult bakingResult, ModelBakery modelBakery) { + ModLoader.postEvent(new ModelEvent.BakingCompleted(modelManager, bakingResult, modelBakery)); } - public static BakedModel handleCameraTransforms(PoseStack poseStack, BakedModel model, ItemDisplayContext cameraTransformType, boolean applyLeftHandTransform) { + public static BakedModel handleCameraTransforms(PoseStack poseStack, @Nullable BakedModel model, ItemDisplayContext cameraTransformType, boolean applyLeftHandTransform) { model = model.applyTransform(cameraTransformType, poseStack, applyLeftHandTransform); return model; } @@ -723,8 +718,8 @@ public static void onRegisterKeyMappings(Options options) { ModLoader.postEvent(new RegisterKeyMappingsEvent(options)); } - public static void onRegisterAdditionalModels(Set additionalModels) { - ModLoader.postEvent(new ModelEvent.RegisterAdditional(additionalModels)); + public static void onRegisterAdditionalModels(Consumer registrar) { + ModLoader.postEvent(new ModelEvent.RegisterAdditional(registrar)); } @Nullable @@ -989,6 +984,7 @@ public static void initClientHooks(Minecraft mc, ReloadableResourceManager resou ModLoader.postEvent(new RegisterClientReloadListenersEvent(resourceManager)); ModLoader.postEvent(new EntityRenderersEvent.RegisterLayerDefinitions()); ModLoader.postEvent(new EntityRenderersEvent.RegisterRenderers()); + ModLoader.postEvent(new RegisterRenderStateModifiersEvent()); ClientTooltipComponentManager.init(); EntitySpectatorShaderManager.init(); ClientHooks.onRegisterKeyMappings(mc.options); @@ -1094,4 +1090,14 @@ public static boolean isInTranslucentBlockOutlinePass(Level level, BlockPos pos, ChunkRenderTypeSet renderTypes = model.getRenderTypes(state, OUTLINE_PASS_RANDOM, level.getModelData(pos)); return renderTypes.contains(RenderType.TRANSLUCENT) || renderTypes.contains(RenderType.TRIPWIRE); } + + public static void reloadRenderer() { + Minecraft.getInstance().levelRenderer.allChanged(); + } + + public static Map gatherMaterialAtlases(Map vanillaAtlases) { + vanillaAtlases = new HashMap<>(vanillaAtlases); + ModLoader.postEvent(new RegisterMaterialAtlasesEvent(vanillaAtlases)); + return Map.copyOf(vanillaAtlases); + } } diff --git a/src/main/java/net/neoforged/neoforge/client/ClientNeoForgeMod.java b/src/main/java/net/neoforged/neoforge/client/ClientNeoForgeMod.java index c378194405..f783a342f5 100644 --- a/src/main/java/net/neoforged/neoforge/client/ClientNeoForgeMod.java +++ b/src/main/java/net/neoforged/neoforge/client/ClientNeoForgeMod.java @@ -5,11 +5,18 @@ package net.neoforged.neoforge.client; +import java.util.Optional; +import net.minecraft.DetectedVersion; import net.minecraft.client.Minecraft; import net.minecraft.client.renderer.BiomeColors; import net.minecraft.client.renderer.RenderType; import net.minecraft.core.BlockPos; +import net.minecraft.data.metadata.PackMetadataGenerator; +import net.minecraft.network.chat.Component; import net.minecraft.resources.ResourceLocation; +import net.minecraft.server.packs.PackType; +import net.minecraft.server.packs.metadata.pack.PackMetadataSection; +import net.minecraft.util.InclusiveRange; import net.minecraft.world.level.BlockAndTintGetter; import net.minecraft.world.level.material.FluidState; import net.neoforged.api.distmarker.Dist; @@ -18,33 +25,58 @@ import net.neoforged.fml.ModContainer; import net.neoforged.fml.common.Mod; import net.neoforged.fml.config.ModConfigs; +import net.neoforged.neoforge.client.color.item.FluidContentsTint; import net.neoforged.neoforge.client.entity.animation.json.AnimationLoader; import net.neoforged.neoforge.client.event.ClientPlayerNetworkEvent; import net.neoforged.neoforge.client.event.ModelEvent; import net.neoforged.neoforge.client.event.RegisterClientReloadListenersEvent; +import net.neoforged.neoforge.client.event.RegisterColorHandlersEvent; +import net.neoforged.neoforge.client.event.RegisterItemModelsEvent; import net.neoforged.neoforge.client.event.RegisterNamedRenderTypesEvent; import net.neoforged.neoforge.client.event.RegisterSpriteSourceTypesEvent; import net.neoforged.neoforge.client.extensions.common.IClientFluidTypeExtensions; import net.neoforged.neoforge.client.extensions.common.RegisterClientExtensionsEvent; import net.neoforged.neoforge.client.gui.ConfigurationScreen; import net.neoforged.neoforge.client.gui.IConfigScreenFactory; -import net.neoforged.neoforge.client.model.CompositeModel; -import net.neoforged.neoforge.client.model.DynamicFluidContainerModel; import net.neoforged.neoforge.client.model.EmptyModel; -import net.neoforged.neoforge.client.model.ItemLayerModel; -import net.neoforged.neoforge.client.model.SeparateTransformsModel; +import net.neoforged.neoforge.client.model.UnbakedCompositeModel; +import net.neoforged.neoforge.client.model.item.DynamicFluidContainerModel; import net.neoforged.neoforge.client.model.obj.ObjLoader; import net.neoforged.neoforge.client.textures.NamespacedDirectoryLister; import net.neoforged.neoforge.common.ModConfigSpec; import net.neoforged.neoforge.common.NeoForge; import net.neoforged.neoforge.common.NeoForgeMod; +import net.neoforged.neoforge.common.data.internal.NeoForgeAdvancementProvider; +import net.neoforged.neoforge.common.data.internal.NeoForgeBiomeTagsProvider; +import net.neoforged.neoforge.common.data.internal.NeoForgeBlockTagsProvider; +import net.neoforged.neoforge.common.data.internal.NeoForgeDamageTypeTagsProvider; +import net.neoforged.neoforge.common.data.internal.NeoForgeDataMapsProvider; +import net.neoforged.neoforge.common.data.internal.NeoForgeEnchantmentTagsProvider; +import net.neoforged.neoforge.common.data.internal.NeoForgeEntityTypeTagsProvider; +import net.neoforged.neoforge.common.data.internal.NeoForgeFluidTagsProvider; +import net.neoforged.neoforge.common.data.internal.NeoForgeItemTagsProvider; +import net.neoforged.neoforge.common.data.internal.NeoForgeLanguageProvider; +import net.neoforged.neoforge.common.data.internal.NeoForgeLootTableProvider; +import net.neoforged.neoforge.common.data.internal.NeoForgeRecipeProvider; +import net.neoforged.neoforge.common.data.internal.NeoForgeRegistryOrderReportProvider; +import net.neoforged.neoforge.common.data.internal.NeoForgeSpriteSourceProvider; +import net.neoforged.neoforge.common.data.internal.NeoForgeStructureTagsProvider; +import net.neoforged.neoforge.common.data.internal.VanillaSoundDefinitionsProvider; +import net.neoforged.neoforge.common.util.SelfTest; +import net.neoforged.neoforge.data.event.GatherDataEvent; import net.neoforged.neoforge.internal.versions.neoforge.NeoForgeVersion; import org.jetbrains.annotations.ApiStatus; @ApiStatus.Internal @Mod(value = "neoforge", dist = Dist.CLIENT) public class ClientNeoForgeMod { + private static ResourceLocation neoForgeId(String path) { + return ResourceLocation.fromNamespaceAndPath("neoforge", path); + } + public ClientNeoForgeMod(IEventBus modEventBus, ModContainer container) { + SelfTest.initClient(); + ClientCommandHandler.init(); TagConventionLogWarningClient.init(); @@ -63,13 +95,39 @@ public ClientNeoForgeMod(IEventBus modEventBus, ModContainer container) { } @SubscribeEvent - static void onRegisterGeometryLoaders(ModelEvent.RegisterGeometryLoaders event) { - event.register(ResourceLocation.fromNamespaceAndPath("neoforge", "empty"), EmptyModel.LOADER); - event.register(ResourceLocation.fromNamespaceAndPath("neoforge", "obj"), ObjLoader.INSTANCE); - event.register(ResourceLocation.fromNamespaceAndPath("neoforge", "fluid_container"), DynamicFluidContainerModel.Loader.INSTANCE); - event.register(ResourceLocation.fromNamespaceAndPath("neoforge", "composite"), CompositeModel.Loader.INSTANCE); - event.register(ResourceLocation.fromNamespaceAndPath("neoforge", "item_layers"), ItemLayerModel.Loader.INSTANCE); - event.register(ResourceLocation.fromNamespaceAndPath("neoforge", "separate_transforms"), SeparateTransformsModel.Loader.INSTANCE); + static void onGatherData(GatherDataEvent.Client event) { + // We perform client and server datagen in a single clientData run to avoid + // having to juggle two generated resources folders and two runs for no additional benefit. + + event.createProvider(output -> new PackMetadataGenerator(output) + .add(PackMetadataSection.TYPE, new PackMetadataSection( + Component.translatable("pack.neoforge.description"), + DetectedVersion.BUILT_IN.getPackVersion(PackType.SERVER_DATA), + Optional.of(new InclusiveRange<>(0, Integer.MAX_VALUE))))); + + event.createProvider(NeoForgeAdvancementProvider::new); + event.createBlockAndItemTags(NeoForgeBlockTagsProvider::new, NeoForgeItemTagsProvider::new); + event.createProvider(NeoForgeEntityTypeTagsProvider::new); + event.createProvider(NeoForgeFluidTagsProvider::new); + event.createProvider(NeoForgeEnchantmentTagsProvider::new); + event.createProvider(NeoForgeRecipeProvider.Runner::new); + event.createProvider(NeoForgeLootTableProvider::new); + event.createProvider(NeoForgeBiomeTagsProvider::new); + event.createProvider(NeoForgeStructureTagsProvider::new); + event.createProvider(NeoForgeDamageTypeTagsProvider::new); + event.createProvider(NeoForgeRegistryOrderReportProvider::new); + event.createProvider(NeoForgeDataMapsProvider::new); + + event.createProvider(NeoForgeSpriteSourceProvider::new); + event.createProvider(VanillaSoundDefinitionsProvider::new); + event.createProvider(NeoForgeLanguageProvider::new); + } + + @SubscribeEvent + static void onRegisterModelLoaders(ModelEvent.RegisterLoaders event) { + event.register(neoForgeId("empty"), EmptyModel.LOADER); + event.register(neoForgeId("obj"), ObjLoader.INSTANCE); + event.register(neoForgeId("composite"), UnbakedCompositeModel.Loader.INSTANCE); } @SubscribeEvent @@ -157,4 +215,24 @@ public ResourceLocation getFlowingTexture() { } }, milkType)); } + + @SubscribeEvent + static void registerItemTintSources(RegisterColorHandlersEvent.ItemTintSources event) { + event.register(neoForgeId("fluid_contents_tint"), FluidContentsTint.MAP_CODEC); + } + + @SubscribeEvent + static void registerItemModels(RegisterItemModelsEvent event) { + event.register(neoForgeId("fluid_container"), DynamicFluidContainerModel.Unbaked.MAP_CODEC); + } + + // TODO 1.21.4 +// @SubscribeEvent(priority = EventPriority.LOWEST) +// static void registerSpawnEggColors(RegisterColorHandlersEvent.Item event) { +// SpawnEggItem.eggs().forEach(egg -> { +// if (event.getItemColors().get(egg) == null) { +// event.register((stack, layer) -> ARGB.opaque(egg.getColor(layer)), egg); +// } +// }); +// } } diff --git a/src/main/java/net/neoforged/neoforge/client/NeoForgeRenderTypes.java b/src/main/java/net/neoforged/neoforge/client/NeoForgeRenderTypes.java index 98988e55c7..bc467405ce 100644 --- a/src/main/java/net/neoforged/neoforge/client/NeoForgeRenderTypes.java +++ b/src/main/java/net/neoforged/neoforge/client/NeoForgeRenderTypes.java @@ -7,7 +7,6 @@ import com.mojang.blaze3d.vertex.DefaultVertexFormat; import com.mojang.blaze3d.vertex.VertexFormat; -import java.util.function.BooleanSupplier; import java.util.function.Function; import java.util.function.Supplier; import net.minecraft.Util; @@ -32,8 +31,6 @@ public enum NeoForgeRenderTypes { ITEM_UNSORTED_UNLIT_TRANSLUCENT(() -> getUnlitTranslucent(TextureAtlas.LOCATION_BLOCKS, false)), TRANSLUCENT_ON_PARTICLES_TARGET(() -> getTranslucentParticlesTarget(TextureAtlas.LOCATION_BLOCKS)); - public static boolean enableTextTextureLinearFiltering = false; - /** * @return A RenderType fit for multi-layer solid item rendering. */ @@ -94,45 +91,45 @@ public static RenderType getEntityCutoutMipped(ResourceLocation textureLocation) } /** - * @return Replacement of {@link RenderType#text(ResourceLocation)}, but with optional linear texture filtering. + * @return Replacement of {@link RenderType#text(ResourceLocation)}, but with linear texture filtering. */ - public static RenderType getText(ResourceLocation locationIn) { - return Internal.TEXT.apply(locationIn); + public static RenderType getTextFiltered(ResourceLocation locationIn) { + return Internal.TEXT_FILTERED.apply(locationIn); } /** - * @return Replacement of {@link RenderType#textIntensity(ResourceLocation)}, but with optional linear texture filtering. + * @return Replacement of {@link RenderType#textIntensity(ResourceLocation)}, but with linear texture filtering. */ - public static RenderType getTextIntensity(ResourceLocation locationIn) { - return Internal.TEXT_INTENSITY.apply(locationIn); + public static RenderType getTextIntensityFiltered(ResourceLocation locationIn) { + return Internal.TEXT_INTENSITY_FILTERED.apply(locationIn); } /** - * @return Replacement of {@link RenderType#textPolygonOffset(ResourceLocation)}, but with optional linear texture filtering. + * @return Replacement of {@link RenderType#textPolygonOffset(ResourceLocation)}, but with linear texture filtering. */ - public static RenderType getTextPolygonOffset(ResourceLocation locationIn) { - return Internal.TEXT_POLYGON_OFFSET.apply(locationIn); + public static RenderType getTextPolygonOffsetFiltered(ResourceLocation locationIn) { + return Internal.TEXT_POLYGON_OFFSET_FILTERED.apply(locationIn); } /** - * @return Replacement of {@link RenderType#textIntensityPolygonOffset(ResourceLocation)}, but with optional linear texture filtering. + * @return Replacement of {@link RenderType#textIntensityPolygonOffset(ResourceLocation)}, but with linear texture filtering. */ - public static RenderType getTextIntensityPolygonOffset(ResourceLocation locationIn) { - return Internal.TEXT_INTENSITY_POLYGON_OFFSET.apply(locationIn); + public static RenderType getTextIntensityPolygonOffsetFiltered(ResourceLocation locationIn) { + return Internal.TEXT_INTENSITY_POLYGON_OFFSET_FILTERED.apply(locationIn); } /** - * @return Replacement of {@link RenderType#textSeeThrough(ResourceLocation)}, but with optional linear texture filtering. + * @return Replacement of {@link RenderType#textSeeThrough(ResourceLocation)}, but with linear texture filtering. */ - public static RenderType getTextSeeThrough(ResourceLocation locationIn) { - return Internal.TEXT_SEETHROUGH.apply(locationIn); + public static RenderType getTextSeeThroughFiltered(ResourceLocation locationIn) { + return Internal.TEXT_SEETHROUGH_FILTERED.apply(locationIn); } /** - * @return Replacement of {@link RenderType#textIntensitySeeThrough(ResourceLocation)}, but with optional linear texture filtering. + * @return Replacement of {@link RenderType#textIntensitySeeThrough(ResourceLocation)}, but with linear texture filtering. */ - public static RenderType getTextIntensitySeeThrough(ResourceLocation locationIn) { - return Internal.TEXT_INTENSITY_SEETHROUGH.apply(locationIn); + public static RenderType getTextIntensitySeeThroughFiltered(ResourceLocation locationIn) { + return Internal.TEXT_INTENSITY_SEETHROUGH_FILTERED.apply(locationIn); } /** @@ -242,82 +239,82 @@ private static RenderType layeredItemTranslucent(ResourceLocation locationIn) { return RenderType.create("neoforge_item_entity_translucent_cull", DefaultVertexFormat.NEW_ENTITY, VertexFormat.Mode.QUADS, 256, true, true, rendertype$state); } - public static Function TEXT = Util.memoize(Internal::getText); + public static Function TEXT_FILTERED = Util.memoize(Internal::getTextFiltered); - private static RenderType getText(ResourceLocation locationIn) { + private static RenderType getTextFiltered(ResourceLocation locationIn) { var rendertype$state = RenderType.CompositeState.builder() .setShaderState(RenderType.RENDERTYPE_TEXT_SHADER) - .setTextureState(new CustomizableTextureState(locationIn, () -> NeoForgeRenderTypes.enableTextTextureLinearFiltering, () -> false)) + .setTextureState(new RenderStateShard.TextureStateShard(locationIn, TriState.TRUE, false)) .setTransparencyState(RenderType.TRANSLUCENT_TRANSPARENCY) .setLightmapState(RenderType.LIGHTMAP) .createCompositeState(false); - return RenderType.create("neoforge_text", DefaultVertexFormat.POSITION_COLOR_TEX_LIGHTMAP, VertexFormat.Mode.QUADS, 256, false, true, rendertype$state); + return RenderType.create("neoforge_text", DefaultVertexFormat.POSITION_COLOR_TEX_LIGHTMAP, VertexFormat.Mode.QUADS, 256, false, false, rendertype$state); } - public static Function TEXT_INTENSITY = Util.memoize(Internal::getTextIntensity); + public static Function TEXT_INTENSITY_FILTERED = Util.memoize(Internal::getTextIntensityFiltered); - private static RenderType getTextIntensity(ResourceLocation locationIn) { + private static RenderType getTextIntensityFiltered(ResourceLocation locationIn) { var rendertype$state = RenderType.CompositeState.builder() .setShaderState(RenderType.RENDERTYPE_TEXT_INTENSITY_SHADER) - .setTextureState(new CustomizableTextureState(locationIn, () -> NeoForgeRenderTypes.enableTextTextureLinearFiltering, () -> false)) + .setTextureState(new RenderStateShard.TextureStateShard(locationIn, TriState.TRUE, false)) .setTransparencyState(RenderType.TRANSLUCENT_TRANSPARENCY) .setLightmapState(RenderType.LIGHTMAP) .createCompositeState(false); - return RenderType.create("neoforge_text_intensity", DefaultVertexFormat.POSITION_COLOR_TEX_LIGHTMAP, VertexFormat.Mode.QUADS, 256, false, true, rendertype$state); + return RenderType.create("neoforge_text_intensity", DefaultVertexFormat.POSITION_COLOR_TEX_LIGHTMAP, VertexFormat.Mode.QUADS, 256, false, false, rendertype$state); } - public static Function TEXT_POLYGON_OFFSET = Util.memoize(Internal::getTextPolygonOffset); + public static Function TEXT_POLYGON_OFFSET_FILTERED = Util.memoize(Internal::getTextPolygonOffsetFiltered); - private static RenderType getTextPolygonOffset(ResourceLocation locationIn) { + private static RenderType getTextPolygonOffsetFiltered(ResourceLocation locationIn) { var rendertype$state = RenderType.CompositeState.builder() .setShaderState(RenderType.RENDERTYPE_TEXT_SHADER) - .setTextureState(new CustomizableTextureState(locationIn, () -> NeoForgeRenderTypes.enableTextTextureLinearFiltering, () -> false)) + .setTextureState(new RenderStateShard.TextureStateShard(locationIn, TriState.TRUE, false)) .setTransparencyState(RenderType.TRANSLUCENT_TRANSPARENCY) .setLightmapState(RenderType.LIGHTMAP) .setLayeringState(RenderType.POLYGON_OFFSET_LAYERING) .createCompositeState(false); - return RenderType.create("neoforge_text_polygon_offset", DefaultVertexFormat.POSITION_COLOR_TEX_LIGHTMAP, VertexFormat.Mode.QUADS, 256, false, true, rendertype$state); + return RenderType.create("neoforge_text_polygon_offset", DefaultVertexFormat.POSITION_COLOR_TEX_LIGHTMAP, VertexFormat.Mode.QUADS, 256, false, false, rendertype$state); } - public static Function TEXT_INTENSITY_POLYGON_OFFSET = Util.memoize(Internal::getTextIntensityPolygonOffset); + public static Function TEXT_INTENSITY_POLYGON_OFFSET_FILTERED = Util.memoize(Internal::getTextIntensityPolygonOffsetFiltered); - private static RenderType getTextIntensityPolygonOffset(ResourceLocation locationIn) { + private static RenderType getTextIntensityPolygonOffsetFiltered(ResourceLocation locationIn) { var rendertype$state = RenderType.CompositeState.builder() .setShaderState(RenderType.RENDERTYPE_TEXT_INTENSITY_SHADER) - .setTextureState(new CustomizableTextureState(locationIn, () -> NeoForgeRenderTypes.enableTextTextureLinearFiltering, () -> false)) + .setTextureState(new RenderStateShard.TextureStateShard(locationIn, TriState.TRUE, false)) .setTransparencyState(RenderType.TRANSLUCENT_TRANSPARENCY) .setLightmapState(RenderType.LIGHTMAP) .setLayeringState(RenderType.POLYGON_OFFSET_LAYERING) .createCompositeState(false); - return RenderType.create("neoforge_text_intensity_polygon_offset", DefaultVertexFormat.POSITION_COLOR_TEX_LIGHTMAP, VertexFormat.Mode.QUADS, 256, false, true, rendertype$state); + return RenderType.create("neoforge_text_intensity_polygon_offset", DefaultVertexFormat.POSITION_COLOR_TEX_LIGHTMAP, VertexFormat.Mode.QUADS, 256, false, false, rendertype$state); } - public static Function TEXT_SEETHROUGH = Util.memoize(Internal::getTextSeeThrough); + public static Function TEXT_SEETHROUGH_FILTERED = Util.memoize(Internal::getTextSeeThroughFiltered); - private static RenderType getTextSeeThrough(ResourceLocation locationIn) { + private static RenderType getTextSeeThroughFiltered(ResourceLocation locationIn) { var rendertype$state = RenderType.CompositeState.builder() .setShaderState(RenderType.RENDERTYPE_TEXT_SEE_THROUGH_SHADER) - .setTextureState(new CustomizableTextureState(locationIn, () -> NeoForgeRenderTypes.enableTextTextureLinearFiltering, () -> false)) + .setTextureState(new RenderStateShard.TextureStateShard(locationIn, TriState.TRUE, false)) .setTransparencyState(RenderType.TRANSLUCENT_TRANSPARENCY) .setLightmapState(RenderType.LIGHTMAP) .setDepthTestState(RenderType.NO_DEPTH_TEST) .setWriteMaskState(RenderType.COLOR_WRITE) .createCompositeState(false); - return RenderType.create("neoforge_text_see_through", DefaultVertexFormat.POSITION_COLOR_TEX_LIGHTMAP, VertexFormat.Mode.QUADS, 256, false, true, rendertype$state); + return RenderType.create("neoforge_text_see_through", DefaultVertexFormat.POSITION_COLOR_TEX_LIGHTMAP, VertexFormat.Mode.QUADS, 256, false, false, rendertype$state); } - public static Function TEXT_INTENSITY_SEETHROUGH = Util.memoize(Internal::getTextIntensitySeeThrough); + public static Function TEXT_INTENSITY_SEETHROUGH_FILTERED = Util.memoize(Internal::getTextIntensitySeeThroughFiltered); - private static RenderType getTextIntensitySeeThrough(ResourceLocation locationIn) { + private static RenderType getTextIntensitySeeThroughFiltered(ResourceLocation locationIn) { var rendertype$state = RenderType.CompositeState.builder() .setShaderState(RenderType.RENDERTYPE_TEXT_INTENSITY_SEE_THROUGH_SHADER) - .setTextureState(new CustomizableTextureState(locationIn, () -> NeoForgeRenderTypes.enableTextTextureLinearFiltering, () -> false)) + .setTextureState(new RenderStateShard.TextureStateShard(locationIn, TriState.TRUE, false)) .setTransparencyState(RenderType.TRANSLUCENT_TRANSPARENCY) .setLightmapState(RenderType.LIGHTMAP) .setDepthTestState(RenderType.NO_DEPTH_TEST) .setWriteMaskState(RenderType.COLOR_WRITE) .createCompositeState(false); - return RenderType.create("neoforge_text_see_through", DefaultVertexFormat.POSITION_COLOR_TEX_LIGHTMAP, VertexFormat.Mode.QUADS, 256, false, true, rendertype$state); + return RenderType.create("neoforge_text_see_through", DefaultVertexFormat.POSITION_COLOR_TEX_LIGHTMAP, VertexFormat.Mode.QUADS, 256, false, false, rendertype$state); } public static Function TRANSLUCENT_PARTICLES_TARGET = Util.memoize(Internal::getTranslucentParticlesTarget); @@ -333,23 +330,4 @@ private static RenderType getTranslucentParticlesTarget(ResourceLocation locatio return RenderType.create("neoforge_translucent_particles_target", DefaultVertexFormat.BLOCK, VertexFormat.Mode.QUADS, 2097152, true, true, rendertype$state); } } - - private static class CustomizableTextureState extends TextureStateShard { - private final BooleanSupplier blurSupplier; - private final BooleanSupplier mipmapSupplier; - - private CustomizableTextureState(ResourceLocation resLoc, BooleanSupplier blur, BooleanSupplier mipmap) { - super(resLoc, blur.getAsBoolean() ? TriState.TRUE : TriState.DEFAULT, mipmap.getAsBoolean()); - blurSupplier = blur; - mipmapSupplier = mipmap; - } - - @Override - public void setupRenderState() { - // must be done before super call as super uses the `blur` and `mipmap` fields within the `setupState` runnable | See super constructor - blur = blurSupplier.getAsBoolean() ? TriState.TRUE : TriState.DEFAULT; - mipmap = mipmapSupplier.getAsBoolean(); - super.setupRenderState(); - } - } } diff --git a/src/main/java/net/neoforged/neoforge/client/color/item/FluidContentsTint.java b/src/main/java/net/neoforged/neoforge/client/color/item/FluidContentsTint.java new file mode 100644 index 0000000000..db4842d04a --- /dev/null +++ b/src/main/java/net/neoforged/neoforge/client/color/item/FluidContentsTint.java @@ -0,0 +1,39 @@ +/* + * Copyright (c) NeoForged and contributors + * SPDX-License-Identifier: LGPL-2.1-only + */ + +package net.neoforged.neoforge.client.color.item; + +import com.mojang.serialization.MapCodec; +import net.minecraft.client.color.item.ItemTintSource; +import net.minecraft.client.multiplayer.ClientLevel; +import net.minecraft.world.entity.LivingEntity; +import net.minecraft.world.item.ItemStack; +import net.neoforged.neoforge.client.extensions.common.IClientFluidTypeExtensions; +import net.neoforged.neoforge.client.model.item.DynamicFluidContainerModel; +import net.neoforged.neoforge.fluids.FluidUtil; +import org.jetbrains.annotations.Nullable; + +/** + * Returns the tint color of the fluid contained in the item stack. + * Notably, this is used internally by {@link DynamicFluidContainerModel}. + */ +public final class FluidContentsTint implements ItemTintSource { + public static final FluidContentsTint INSTANCE = new FluidContentsTint(); + public static final MapCodec MAP_CODEC = MapCodec.unit(INSTANCE); + + private FluidContentsTint() {} + + @Override + public int calculate(ItemStack stack, @Nullable ClientLevel level, @Nullable LivingEntity entity) { + return FluidUtil.getFluidContained(stack) + .map(fluidStack -> IClientFluidTypeExtensions.of(fluidStack.getFluid()).getTintColor(fluidStack)) + .orElse(0xFFFFFFFF); + } + + @Override + public MapCodec type() { + return MAP_CODEC; + } +} diff --git a/src/main/java/net/neoforged/neoforge/client/model/geometry/package-info.java b/src/main/java/net/neoforged/neoforge/client/color/item/package-info.java similarity index 86% rename from src/main/java/net/neoforged/neoforge/client/model/geometry/package-info.java rename to src/main/java/net/neoforged/neoforge/client/color/item/package-info.java index a553db4599..bceffd3ecd 100644 --- a/src/main/java/net/neoforged/neoforge/client/model/geometry/package-info.java +++ b/src/main/java/net/neoforged/neoforge/client/color/item/package-info.java @@ -6,7 +6,7 @@ @FieldsAreNonnullByDefault @MethodsReturnNonnullByDefault @ParametersAreNonnullByDefault -package net.neoforged.neoforge.client.model.geometry; +package net.neoforged.neoforge.client.color.item; import javax.annotation.ParametersAreNonnullByDefault; import net.minecraft.FieldsAreNonnullByDefault; diff --git a/src/main/java/net/neoforged/neoforge/client/entity/animation/json/AnimationLoader.java b/src/main/java/net/neoforged/neoforge/client/entity/animation/json/AnimationLoader.java index 7d788419d2..4e959af379 100644 --- a/src/main/java/net/neoforged/neoforge/client/entity/animation/json/AnimationLoader.java +++ b/src/main/java/net/neoforged/neoforge/client/entity/animation/json/AnimationLoader.java @@ -11,6 +11,7 @@ import java.util.List; import java.util.Map; import net.minecraft.client.animation.AnimationDefinition; +import net.minecraft.resources.FileToIdConverter; import net.minecraft.resources.ResourceLocation; import net.minecraft.server.packs.resources.ResourceManager; import net.minecraft.server.packs.resources.SimpleJsonResourceReloadListener; @@ -31,7 +32,7 @@ public final class AnimationLoader extends SimpleJsonResourceReloadListener strongHolderReferences = new ArrayList<>(); private AnimationLoader() { - super(AnimationParser.CODEC, "neoforge/animations/entity"); + super(AnimationParser.CODEC, FileToIdConverter.json("neoforge/animations/entity")); } /** diff --git a/src/main/java/net/neoforged/neoforge/client/event/ModelEvent.java b/src/main/java/net/neoforged/neoforge/client/event/ModelEvent.java index 650217ec9b..a3a234a883 100644 --- a/src/main/java/net/neoforged/neoforge/client/event/ModelEvent.java +++ b/src/main/java/net/neoforged/neoforge/client/event/ModelEvent.java @@ -6,21 +6,20 @@ package net.neoforged.neoforge.client.event; import com.google.common.base.Preconditions; +import java.util.Collections; import java.util.Map; -import java.util.Set; +import java.util.function.Consumer; import java.util.function.Function; import net.minecraft.client.renderer.texture.TextureAtlasSprite; -import net.minecraft.client.resources.model.BakedModel; import net.minecraft.client.resources.model.Material; import net.minecraft.client.resources.model.ModelBakery; import net.minecraft.client.resources.model.ModelManager; -import net.minecraft.client.resources.model.ModelResourceLocation; import net.minecraft.resources.ResourceLocation; import net.neoforged.bus.api.Event; import net.neoforged.bus.api.ICancellableEvent; import net.neoforged.fml.LogicalSide; import net.neoforged.fml.event.IModBusEvent; -import net.neoforged.neoforge.client.model.geometry.IGeometryLoader; +import net.neoforged.neoforge.client.model.UnbakedModelLoader; import org.jetbrains.annotations.ApiStatus; /** @@ -41,27 +40,27 @@ protected ModelEvent() {} * must therefore not be accessed in this event. *

* - *

This event is not {@linkplain ICancellableEvent cancellable}, and does not {@linkplain HasResult have a result}.

+ *

This event is not {@linkplain ICancellableEvent cancellable}.

* *

This event is fired on the mod-specific event bus, only on the {@linkplain LogicalSide#CLIENT logical client}.

*/ public static class ModifyBakingResult extends ModelEvent implements IModBusEvent { - private final Map models; + private final ModelBakery.BakingResult bakingResult; private final Function textureGetter; private final ModelBakery modelBakery; @ApiStatus.Internal - public ModifyBakingResult(Map models, Function textureGetter, ModelBakery modelBakery) { - this.models = models; + public ModifyBakingResult(ModelBakery.BakingResult bakingResult, Function textureGetter, ModelBakery modelBakery) { + this.bakingResult = bakingResult; this.textureGetter = textureGetter; this.modelBakery = modelBakery; } /** - * @return the modifiable registry map of models and their model names + * @return The result of the model baking */ - public Map getModels() { - return models; + public ModelBakery.BakingResult getBakingResult() { + return bakingResult; } /** @@ -89,19 +88,25 @@ public ModelBakery getModelBakery() { * The model registry given by this event is unmodifiable. To modify the model registry, use * {@link ModelEvent.ModifyBakingResult} instead. * - *

This event is not {@linkplain ICancellableEvent cancellable}, and does not {@linkplain HasResult have a result}.

+ *

This event is not {@linkplain ICancellableEvent cancellable}.

* *

This event is fired on the mod-specific event bus, only on the {@linkplain LogicalSide#CLIENT logical client}.

*/ public static class BakingCompleted extends ModelEvent implements IModBusEvent { private final ModelManager modelManager; - private final Map models; + private final ModelBakery.BakingResult bakingResult; private final ModelBakery modelBakery; @ApiStatus.Internal - public BakingCompleted(ModelManager modelManager, Map models, ModelBakery modelBakery) { + public BakingCompleted(ModelManager modelManager, ModelBakery.BakingResult bakingResult, ModelBakery modelBakery) { this.modelManager = modelManager; - this.models = models; + this.bakingResult = new ModelBakery.BakingResult( + bakingResult.missingModel(), + Collections.unmodifiableMap(bakingResult.blockStateModels()), + bakingResult.missingItemModel(), + Collections.unmodifiableMap(bakingResult.itemStackModels()), + Collections.unmodifiableMap(bakingResult.itemProperties()), + Collections.unmodifiableMap(bakingResult.standaloneModels())); this.modelBakery = modelBakery; } @@ -113,10 +118,10 @@ public ModelManager getModelManager() { } /** - * @return an unmodifiable view of the registry map of models and their model names + * @return The result of the model baking */ - public Map getModels() { - return models; + public ModelBakery.BakingResult getBakingResult() { + return bakingResult; } /** @@ -128,58 +133,52 @@ public ModelBakery getModelBakery() { } /** - * Fired when the {@link net.minecraft.client.resources.model.ModelBakery} is notified of the resource manager reloading. - * Allows developers to register models to be loaded, along with their dependencies. Models registered through this - * event must use the {@link ModelResourceLocation#STANDALONE_VARIANT} variant. + * Fired when the {@link net.minecraft.client.resources.model.ModelDiscovery} is notified of dependency discovery of its top models. + * Allows developers to register models to be loaded, along with their dependencies. * - *

This event is not {@linkplain ICancellableEvent cancellable}, and does not {@linkplain HasResult have a result}.

+ *

This event is not {@linkplain ICancellableEvent cancellable}.

* *

This event is fired on the mod-specific event bus, only on the {@linkplain LogicalSide#CLIENT logical client}.

*/ public static class RegisterAdditional extends ModelEvent implements IModBusEvent { - private final Set models; + private final Consumer registrar; @ApiStatus.Internal - public RegisterAdditional(Set models) { - this.models = models; + public RegisterAdditional(Consumer registrar) { + this.registrar = registrar; } /** * Registers a model to be loaded, along with its dependencies. - *

- * The {@link ModelResourceLocation} passed to this method must later be used to recover the loaded model. */ - public void register(ModelResourceLocation model) { - Preconditions.checkArgument( - model.getVariant().equals(ModelResourceLocation.STANDALONE_VARIANT), - "Side-loaded models must use the '" + ModelResourceLocation.STANDALONE_VARIANT + "' variant"); - models.add(model); + public void register(ResourceLocation model) { + registrar.accept(model); } } /** - * Allows users to register their own {@link IGeometryLoader geometry loaders} for use in block/item models. + * Allows users to register their own {@link UnbakedModelLoader unbaked model loaders} for use in block/item models. * - *

This event is not {@linkplain ICancellableEvent cancellable}, and does not {@linkplain HasResult have a result}.

+ *

This event is not {@linkplain ICancellableEvent cancellable}.

* *

This event is fired on the mod-specific event bus, only on the {@linkplain LogicalSide#CLIENT logical client}.

*/ - public static class RegisterGeometryLoaders extends ModelEvent implements IModBusEvent { - private final Map> loaders; + public static class RegisterLoaders extends ModelEvent implements IModBusEvent { + private final Map> loaders; @ApiStatus.Internal - public RegisterGeometryLoaders(Map> loaders) { + public RegisterLoaders(Map> loaders) { this.loaders = loaders; } /** - * Registers a new geometry loader. - * + * Registers a new unbaked model loader. + * * @param key the ID of the loader - * @param loader the geometry loader to register + * @param loader the loader to register */ - public void register(ResourceLocation key, IGeometryLoader loader) { - Preconditions.checkArgument(!loaders.containsKey(key), "Geometry loader already registered: " + key); + public void register(ResourceLocation key, UnbakedModelLoader loader) { + Preconditions.checkArgument(!loaders.containsKey(key), "Unbaked model loader already registered: " + key); loaders.put(key, loader); } } diff --git a/src/main/java/net/neoforged/neoforge/client/event/RegisterColorHandlersEvent.java b/src/main/java/net/neoforged/neoforge/client/event/RegisterColorHandlersEvent.java index b4210540d6..afa2873449 100644 --- a/src/main/java/net/neoforged/neoforge/client/event/RegisterColorHandlersEvent.java +++ b/src/main/java/net/neoforged/neoforge/client/event/RegisterColorHandlersEvent.java @@ -6,13 +6,14 @@ package net.neoforged.neoforge.client.event; import com.google.common.collect.ImmutableList; +import com.mojang.serialization.MapCodec; import net.minecraft.client.color.block.BlockColor; import net.minecraft.client.color.block.BlockColors; -import net.minecraft.client.color.item.ItemColor; -import net.minecraft.client.color.item.ItemColors; +import net.minecraft.client.color.item.ItemTintSource; import net.minecraft.core.BlockPos; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.util.ExtraCodecs; import net.minecraft.world.level.ColorResolver; -import net.minecraft.world.level.ItemLike; import net.neoforged.bus.api.Event; import net.neoforged.bus.api.ICancellableEvent; import net.neoforged.fml.LogicalSide; @@ -26,7 +27,8 @@ *

These events are fired on the mod-specific event bus, only on the {@linkplain LogicalSide#CLIENT logical client}.

* * @see RegisterColorHandlersEvent.Block - * @see RegisterColorHandlersEvent.Item + * @see RegisterColorHandlersEvent.ItemTintSources + * @see RegisterColorHandlersEvent.ColorResolvers */ public abstract class RegisterColorHandlersEvent extends Event implements IModBusEvent { @ApiStatus.Internal @@ -35,7 +37,7 @@ protected RegisterColorHandlersEvent() {} /** * Fired for registering block color handlers. * - *

This event is not {@linkplain ICancellableEvent cancellable}, and does not {@linkplain HasResult have a result}.

+ *

This event is not {@linkplain ICancellableEvent cancellable}.

* *

This event is fired on the mod-specific event bus, only on the {@linkplain LogicalSide#CLIENT logical client}.

*/ @@ -68,67 +70,40 @@ public void register(BlockColor blockColor, net.minecraft.world.level.block.Bloc } /** - * Fired for registering item color handlers. - * - *

The block colors should only be used for referencing or delegating item colors to their respective block - * colors. Use {@link RegisterColorHandlersEvent.Block} for registering your block color handlers.

- * - *

This event is not {@linkplain ICancellableEvent cancellable}, and does not {@linkplain HasResult have a result}.

- * - *

This event is fired on the mod-specific event bus, only on the {@linkplain LogicalSide#CLIENT logical client}.

+ * Allows registration of custom {@link ColorResolver} implementations to be used with + * {@link net.minecraft.world.level.BlockAndTintGetter#getBlockTint(BlockPos, ColorResolver)}. */ - public static class Item extends RegisterColorHandlersEvent { - private final ItemColors itemColors; - private final BlockColors blockColors; + public static class ColorResolvers extends RegisterColorHandlersEvent { + private final ImmutableList.Builder builder; @ApiStatus.Internal - public Item(ItemColors itemColors, BlockColors blockColors) { - this.itemColors = itemColors; - this.blockColors = blockColors; - } - - /** - * {@return the item colors registry} - * - * @see ItemColors#register(ItemColor, ItemLike...) - */ - public ItemColors getItemColors() { - return itemColors; - } - - /** - * {@return the block colors registry} - * This should only be used for referencing or delegating item colors to their respective block colors. - */ - public BlockColors getBlockColors() { - return blockColors; + public ColorResolvers(ImmutableList.Builder builder) { + this.builder = builder; } - /** - * Registers a {@link ItemColor} instance for a set of blocks. - * - * @param itemColor The color provider - * @param items The items - */ - public void register(ItemColor itemColor, ItemLike... items) { - itemColors.register(itemColor, items); + public void register(ColorResolver resolver) { + this.builder.add(resolver); } } /** - * Allows registration of custom {@link ColorResolver} implementations to be used with - * {@link net.minecraft.world.level.BlockAndTintGetter#getBlockTint(BlockPos, ColorResolver)}. + * Fired for registering item color handlers. + *

+ * This event is fired on the mod-specific event bus, only on the {@linkplain LogicalSide#CLIENT logical client} + * + * @see ItemTintSource + * @see ItemTintSources */ - public static class ColorResolvers extends RegisterColorHandlersEvent { - private final ImmutableList.Builder builder; + public static class ItemTintSources extends RegisterColorHandlersEvent { + private final ExtraCodecs.LateBoundIdMapper> idMapper; @ApiStatus.Internal - public ColorResolvers(ImmutableList.Builder builder) { - this.builder = builder; + public ItemTintSources(ExtraCodecs.LateBoundIdMapper> idMapper) { + this.idMapper = idMapper; } - public void register(ColorResolver resolver) { - this.builder.add(resolver); + public void register(ResourceLocation location, MapCodec source) { + this.idMapper.put(location, source); } } } diff --git a/src/main/java/net/neoforged/neoforge/client/event/RegisterConditionalItemModelPropertyEvent.java b/src/main/java/net/neoforged/neoforge/client/event/RegisterConditionalItemModelPropertyEvent.java new file mode 100644 index 0000000000..8b76afe1e9 --- /dev/null +++ b/src/main/java/net/neoforged/neoforge/client/event/RegisterConditionalItemModelPropertyEvent.java @@ -0,0 +1,35 @@ +/* + * Copyright (c) NeoForged and contributors + * SPDX-License-Identifier: LGPL-2.1-only + */ + +package net.neoforged.neoforge.client.event; + +import com.mojang.serialization.MapCodec; +import net.minecraft.client.renderer.item.properties.conditional.ConditionalItemModelProperty; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.util.ExtraCodecs; +import net.neoforged.bus.api.Event; +import net.neoforged.fml.event.IModBusEvent; +import org.jetbrains.annotations.ApiStatus; + +/** + * Event fired when special model renderers are registered. + *

+ * This event is fired during the model registration process for conditional item model properties. + * It is used to register property codecs which can be used to create custom conditional item model properties. + *

+ * This event is fired on the mod event bus. + */ +public class RegisterConditionalItemModelPropertyEvent extends Event implements IModBusEvent { + private final ExtraCodecs.LateBoundIdMapper> idMapper; + + @ApiStatus.Internal + public RegisterConditionalItemModelPropertyEvent(ExtraCodecs.LateBoundIdMapper> idMapper) { + this.idMapper = idMapper; + } + + public void register(ResourceLocation location, MapCodec source) { + this.idMapper.put(location, source); + } +} diff --git a/src/main/java/net/neoforged/neoforge/client/event/RegisterItemModelsEvent.java b/src/main/java/net/neoforged/neoforge/client/event/RegisterItemModelsEvent.java new file mode 100644 index 0000000000..0887e4b1c5 --- /dev/null +++ b/src/main/java/net/neoforged/neoforge/client/event/RegisterItemModelsEvent.java @@ -0,0 +1,35 @@ +/* + * Copyright (c) NeoForged and contributors + * SPDX-License-Identifier: LGPL-2.1-only + */ + +package net.neoforged.neoforge.client.event; + +import com.mojang.serialization.MapCodec; +import net.minecraft.client.renderer.item.ItemModel; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.util.ExtraCodecs; +import net.neoforged.bus.api.Event; +import net.neoforged.fml.event.IModBusEvent; +import org.jetbrains.annotations.ApiStatus; + +/** + * Event fired when item models are registered. + *

+ * This event is fired during the model registration process for items. + * It is used to register custom item model codecs which can be used to create custom item models. + *

+ * This event is fired on the mod event bus. + */ +public class RegisterItemModelsEvent extends Event implements IModBusEvent { + private final ExtraCodecs.LateBoundIdMapper> idMapper; + + @ApiStatus.Internal + public RegisterItemModelsEvent(ExtraCodecs.LateBoundIdMapper> idMapper) { + this.idMapper = idMapper; + } + + public void register(ResourceLocation location, MapCodec source) { + this.idMapper.put(location, source); + } +} diff --git a/src/main/java/net/neoforged/neoforge/client/event/RegisterMaterialAtlasesEvent.java b/src/main/java/net/neoforged/neoforge/client/event/RegisterMaterialAtlasesEvent.java new file mode 100644 index 0000000000..9d6f6c68ce --- /dev/null +++ b/src/main/java/net/neoforged/neoforge/client/event/RegisterMaterialAtlasesEvent.java @@ -0,0 +1,58 @@ +/* + * Copyright (c) NeoForged and contributors + * SPDX-License-Identifier: LGPL-2.1-only + */ + +package net.neoforged.neoforge.client.event; + +import java.util.Map; +import net.minecraft.client.Minecraft; +import net.minecraft.client.renderer.texture.TextureAtlas; +import net.minecraft.client.resources.TextureAtlasHolder; +import net.minecraft.client.resources.model.Material; +import net.minecraft.client.resources.model.ModelManager; +import net.minecraft.resources.ResourceLocation; +import net.neoforged.bus.api.Event; +import net.neoforged.bus.api.ICancellableEvent; +import net.neoforged.fml.LogicalSide; +import net.neoforged.fml.event.IModBusEvent; +import org.jetbrains.annotations.ApiStatus; + +/** + * Fired for registering {@linkplain TextureAtlas texture atlases} that will be used with {@link Material} or + * other systems which retrieve the atlas via {@link Minecraft#getTextureAtlas(ResourceLocation)} or + * {@link ModelManager#getAtlas(ResourceLocation)}. + *

+ * If an atlas is registered via this event, then it must NOT be used through a {@link TextureAtlasHolder}. + *

+ * This event fires during startup when the {@link ModelManager} is constructed. + *

+ * This event is not {@linkplain ICancellableEvent cancellable}. + *

+ * This event is fired on the mod-specific event bus, only on the {@linkplain LogicalSide#CLIENT logical client}. + */ +public class RegisterMaterialAtlasesEvent extends Event implements IModBusEvent { + private final Map atlases; + + @ApiStatus.Internal + public RegisterMaterialAtlasesEvent(Map atlases) { + this.atlases = atlases; + } + + /** + * Register a texture atlas with the given name and info location + * + * @param atlasLocation The name of the texture atlas + * @param atlasInfoLocation The location of the atlas info JSON relative to the {@code atlases} directory + */ + public void register(ResourceLocation atlasLocation, ResourceLocation atlasInfoLocation) { + ResourceLocation oldAtlasInfoLoc = this.atlases.putIfAbsent(atlasLocation, atlasInfoLocation); + if (oldAtlasInfoLoc != null) { + throw new IllegalStateException(String.format( + "Duplicate registration of atlas: %s (old info: %s, new info: %s)", + atlasLocation, + oldAtlasInfoLoc, + atlasInfoLocation)); + } + } +} diff --git a/src/main/java/net/neoforged/neoforge/client/event/RegisterRangeSelectItemModelPropertyEvent.java b/src/main/java/net/neoforged/neoforge/client/event/RegisterRangeSelectItemModelPropertyEvent.java new file mode 100644 index 0000000000..28a7192685 --- /dev/null +++ b/src/main/java/net/neoforged/neoforge/client/event/RegisterRangeSelectItemModelPropertyEvent.java @@ -0,0 +1,35 @@ +/* + * Copyright (c) NeoForged and contributors + * SPDX-License-Identifier: LGPL-2.1-only + */ + +package net.neoforged.neoforge.client.event; + +import com.mojang.serialization.MapCodec; +import net.minecraft.client.renderer.item.properties.numeric.RangeSelectItemModelProperty; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.util.ExtraCodecs; +import net.neoforged.bus.api.Event; +import net.neoforged.fml.event.IModBusEvent; +import org.jetbrains.annotations.ApiStatus; + +/** + * Event fired when special model renderers are registered. + *

+ * This event is fired during the model registration process for range select item model properties. + * It is used to register property codecs which can be used to create custom range select item model properties. + *

+ * This event is fired on the mod event bus. + */ +public class RegisterRangeSelectItemModelPropertyEvent extends Event implements IModBusEvent { + private final ExtraCodecs.LateBoundIdMapper> idMapper; + + @ApiStatus.Internal + public RegisterRangeSelectItemModelPropertyEvent(ExtraCodecs.LateBoundIdMapper> idMapper) { + this.idMapper = idMapper; + } + + public void register(ResourceLocation location, MapCodec source) { + this.idMapper.put(location, source); + } +} diff --git a/src/main/java/net/neoforged/neoforge/client/event/RegisterSelectItemModelPropertyEvent.java b/src/main/java/net/neoforged/neoforge/client/event/RegisterSelectItemModelPropertyEvent.java new file mode 100644 index 0000000000..2db147ea4a --- /dev/null +++ b/src/main/java/net/neoforged/neoforge/client/event/RegisterSelectItemModelPropertyEvent.java @@ -0,0 +1,34 @@ +/* + * Copyright (c) NeoForged and contributors + * SPDX-License-Identifier: LGPL-2.1-only + */ + +package net.neoforged.neoforge.client.event; + +import net.minecraft.client.renderer.item.properties.select.SelectItemModelProperty; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.util.ExtraCodecs; +import net.neoforged.bus.api.Event; +import net.neoforged.fml.event.IModBusEvent; +import org.jetbrains.annotations.ApiStatus; + +/** + * Event fired when item model property selectors are registered. + *

+ * This event is fired during the model registration process for item model property selectors. + * It is used to register custom selector types which can be used to create custom item model property selectors. + *

+ * This event is fired on the mod event bus. + */ +public class RegisterSelectItemModelPropertyEvent extends Event implements IModBusEvent { + private final ExtraCodecs.LateBoundIdMapper> idMapper; + + @ApiStatus.Internal + public RegisterSelectItemModelPropertyEvent(ExtraCodecs.LateBoundIdMapper> idMapper) { + this.idMapper = idMapper; + } + + public void register(ResourceLocation location, SelectItemModelProperty.Type source) { + this.idMapper.put(location, source); + } +} diff --git a/src/main/java/net/neoforged/neoforge/client/event/RegisterSpecialModelRendererEvent.java b/src/main/java/net/neoforged/neoforge/client/event/RegisterSpecialModelRendererEvent.java new file mode 100644 index 0000000000..b521657b64 --- /dev/null +++ b/src/main/java/net/neoforged/neoforge/client/event/RegisterSpecialModelRendererEvent.java @@ -0,0 +1,35 @@ +/* + * Copyright (c) NeoForged and contributors + * SPDX-License-Identifier: LGPL-2.1-only + */ + +package net.neoforged.neoforge.client.event; + +import com.mojang.serialization.MapCodec; +import net.minecraft.client.renderer.special.SpecialModelRenderer; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.util.ExtraCodecs; +import net.neoforged.bus.api.Event; +import net.neoforged.fml.event.IModBusEvent; +import org.jetbrains.annotations.ApiStatus; + +/** + * Event fired when special model renderers are registered. + *

+ * This event is fired during the model registration process for special item model renderers. + * It is used to register custom special item model renderer codecs which can be used to create custom special item model renderers. + *

+ * This event is fired on the mod event bus. + */ +public class RegisterSpecialModelRendererEvent extends Event implements IModBusEvent { + private final ExtraCodecs.LateBoundIdMapper> idMapper; + + @ApiStatus.Internal + public RegisterSpecialModelRendererEvent(ExtraCodecs.LateBoundIdMapper> idMapper) { + this.idMapper = idMapper; + } + + public void register(ResourceLocation location, MapCodec source) { + this.idMapper.put(location, source); + } +} diff --git a/src/main/java/net/neoforged/neoforge/client/event/RenderItemInFrameEvent.java b/src/main/java/net/neoforged/neoforge/client/event/RenderItemInFrameEvent.java index 479758d4c0..5d1fdb416c 100644 --- a/src/main/java/net/neoforged/neoforge/client/event/RenderItemInFrameEvent.java +++ b/src/main/java/net/neoforged/neoforge/client/event/RenderItemInFrameEvent.java @@ -10,7 +10,7 @@ import net.minecraft.client.renderer.MultiBufferSource; import net.minecraft.client.renderer.entity.ItemFrameRenderer; import net.minecraft.client.renderer.entity.state.ItemFrameRenderState; -import net.minecraft.world.item.ItemStack; +import net.minecraft.client.renderer.item.ItemStackRenderState; import net.neoforged.bus.api.Event; import net.neoforged.bus.api.ICancellableEvent; import net.neoforged.fml.LogicalSide; @@ -30,7 +30,7 @@ * @see ItemFrameRenderer */ public class RenderItemInFrameEvent extends Event implements ICancellableEvent { - private final ItemStack itemStack; + private final ItemStackRenderState itemStack; private final ItemFrameRenderState frameRenderState; private final ItemFrameRenderer renderer; private final PoseStack poseStack; @@ -40,7 +40,7 @@ public class RenderItemInFrameEvent extends Event implements ICancellableEvent { @ApiStatus.Internal public RenderItemInFrameEvent(ItemFrameRenderState frameRenderState, ItemFrameRenderer renderItemFrame, PoseStack poseStack, MultiBufferSource multiBufferSource, int packedLight) { - this.itemStack = frameRenderState.itemStack; + this.itemStack = frameRenderState.item; this.frameRenderState = frameRenderState; this.renderer = renderItemFrame; this.poseStack = poseStack; @@ -51,7 +51,7 @@ public RenderItemInFrameEvent(ItemFrameRenderState frameRenderState, ItemFrameRe /** * {@return the item stack being rendered} */ - public ItemStack getItemStack() { + public ItemStackRenderState getItemStackRenderState() { return itemStack; } diff --git a/src/main/java/net/neoforged/neoforge/client/event/RenderLivingEvent.java b/src/main/java/net/neoforged/neoforge/client/event/RenderLivingEvent.java index 5ecbefe370..d705fb1522 100644 --- a/src/main/java/net/neoforged/neoforge/client/event/RenderLivingEvent.java +++ b/src/main/java/net/neoforged/neoforge/client/event/RenderLivingEvent.java @@ -22,9 +22,6 @@ * Fired when a {@link LivingEntity} is rendered. * See the two subclasses to listen for before and after rendering. * - *

Despite this event's use of generic type parameters, this is not a {@link net.neoforged.bus.api.GenericEvent}, - * and should not be treated as such (such as using generic-specific listeners, which may cause a {@link ClassCastException}).

- * * @param the living entity that is being rendered * @param the model for the living entity * @see RenderLivingEvent.Pre @@ -99,11 +96,11 @@ public int getPackedLight() { * Fired before an entity is rendered. * This can be used to render additional effects or suppress rendering. * - *

This event is {@linkplain ICancellableEvent cancelable}, and does not {@linkplain HasResult have a result}. + *

This event is {@linkplain ICancellableEvent cancelable}. * If this event is cancelled, then the entity will not be rendered and the corresponding * {@link RenderLivingEvent.Post} will not be fired.

* - *

This event is fired on the {@linkplain NeoForge#EVENT_BUS main Forge event bus}, + *

This event is fired on the {@linkplain NeoForge#EVENT_BUS main game event bus}, * only on the {@linkplain LogicalSide#CLIENT logical client}.

* * @param the living entity that is being rendered @@ -119,9 +116,9 @@ public Pre(S renderState, LivingEntityRenderer renderer, float partialT /** * Fired after an entity is rendered, if the corresponding {@link RenderLivingEvent.Post} is not cancelled. * - *

This event is not {@linkplain ICancellableEvent cancelable}, and does not {@linkplain HasResult have a result}.

+ *

This event is not {@linkplain ICancellableEvent cancelable}.

* - *

This event is fired on the {@linkplain NeoForge#EVENT_BUS main Forge event bus}, + *

This event is fired on the {@linkplain NeoForge#EVENT_BUS main game event bus}, * only on the {@linkplain LogicalSide#CLIENT logical client}.

* * @param the living entity that was rendered diff --git a/src/main/java/net/neoforged/neoforge/client/event/RenderNameTagEvent.java b/src/main/java/net/neoforged/neoforge/client/event/RenderNameTagEvent.java index 6e6c501f2f..a33fc96ffe 100644 --- a/src/main/java/net/neoforged/neoforge/client/event/RenderNameTagEvent.java +++ b/src/main/java/net/neoforged/neoforge/client/event/RenderNameTagEvent.java @@ -16,6 +16,7 @@ import net.neoforged.neoforge.common.NeoForge; import net.neoforged.neoforge.common.util.TriState; import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.Nullable; /** * This event is fired before an entity renderer renders the nameplate of an entity. @@ -26,14 +27,12 @@ */ public abstract class RenderNameTagEvent extends Event { private final EntityRenderState renderState; - protected final Component originalContent; private final EntityRenderer entityRenderer; private final float partialTick; @ApiStatus.Internal - public RenderNameTagEvent(EntityRenderState renderState, Component content, EntityRenderer entityRenderer, float partialTick) { + public RenderNameTagEvent(EntityRenderState renderState, EntityRenderer entityRenderer, float partialTick) { this.renderState = renderState; - this.originalContent = content; this.entityRenderer = entityRenderer; this.partialTick = partialTick; } @@ -69,12 +68,16 @@ public float getPartialTick() { */ public static class CanRender extends RenderNameTagEvent { private final Entity entity; + @Nullable + private final Component originalContent; + @Nullable private Component content; private TriState canRender = TriState.DEFAULT; - public CanRender(Entity entity, EntityRenderState renderState, Component content, EntityRenderer entityRenderer, float partialTick) { - super(renderState, content, entityRenderer, partialTick); + public CanRender(Entity entity, EntityRenderState renderState, @Nullable Component content, EntityRenderer entityRenderer, float partialTick) { + super(renderState, entityRenderer, partialTick); this.entity = entity; + this.originalContent = content; this.content = content; } @@ -88,6 +91,7 @@ public Entity getEntity() { /** * {@return the original text on the nameplate} */ + @Nullable public Component getOriginalContent() { return this.originalContent; } @@ -121,6 +125,7 @@ public void setContent(Component contents) { /** * {@return the text on the nameplate that will be rendered} */ + @Nullable public Component getContent() { return this.content; } @@ -137,12 +142,14 @@ public Component getContent() { * @see EntityRenderer */ public static class DoRender extends RenderNameTagEvent implements ICancellableEvent { + private final Component content; private final PoseStack poseStack; private final MultiBufferSource multiBufferSource; private final int packedLight; public DoRender(EntityRenderState renderState, Component content, EntityRenderer entityRenderer, PoseStack poseStack, MultiBufferSource multiBufferSource, int packedLight, float partialTick) { - super(renderState, content, entityRenderer, partialTick); + super(renderState, entityRenderer, partialTick); + this.content = content; this.poseStack = poseStack; this.multiBufferSource = multiBufferSource; this.packedLight = packedLight; @@ -152,7 +159,7 @@ public DoRender(EntityRenderState renderState, Component content, EntityRenderer * {@return the text on the nameplate} */ public Component getContent() { - return this.originalContent; + return this.content; } /** diff --git a/src/main/java/net/neoforged/neoforge/client/event/RenderPlayerEvent.java b/src/main/java/net/neoforged/neoforge/client/event/RenderPlayerEvent.java index b2fd3652e7..ff8113951f 100644 --- a/src/main/java/net/neoforged/neoforge/client/event/RenderPlayerEvent.java +++ b/src/main/java/net/neoforged/neoforge/client/event/RenderPlayerEvent.java @@ -11,7 +11,6 @@ import net.minecraft.client.renderer.MultiBufferSource; import net.minecraft.client.renderer.entity.player.PlayerRenderer; import net.minecraft.client.renderer.entity.state.PlayerRenderState; -import net.neoforged.bus.api.Event; import net.neoforged.bus.api.ICancellableEvent; import net.neoforged.fml.LogicalSide; import net.neoforged.neoforge.common.NeoForge; @@ -31,15 +30,20 @@ protected RenderPlayerEvent(PlayerRenderState renderState, PlayerRenderer render super(renderState, renderer, partialTick, poseStack, multiBufferSource, packedLight); } + @Override + public PlayerRenderer getRenderer() { + return (PlayerRenderer) super.getRenderer(); + } + /** * Fired before the player is rendered. * This can be used for rendering additional effects or suppressing rendering. * - *

This event is {@linkplain ICancellableEvent cancellable}, and does not {@linkplain Event.HasResult have a result}. + *

This event is {@linkplain ICancellableEvent cancellable}. * If this event is cancelled, then the player will not be rendered and the corresponding * {@link RenderPlayerEvent.Post} will not be fired.

* - *

This event is fired on the {@linkplain NeoForge#EVENT_BUS main Forge event bus}, + *

This event is fired on the {@linkplain NeoForge#EVENT_BUS main game event bus}, * only on the {@linkplain LogicalSide#CLIENT logical client}.

*/ public static class Pre extends RenderPlayerEvent implements ICancellableEvent { @@ -52,9 +56,9 @@ public Pre(PlayerRenderState renderState, PlayerRenderer renderer, float partial /** * Fired after the player is rendered, if the corresponding {@link RenderPlayerEvent.Pre} is not cancelled. * - *

This event is not {@linkplain ICancellableEvent cancellable}, and does not {@linkplain Event.HasResult have a result}.

+ *

This event is not {@linkplain ICancellableEvent cancellable}.

* - *

This event is fired on the {@linkplain NeoForge#EVENT_BUS main Forge event bus}, + *

This event is fired on the {@linkplain NeoForge#EVENT_BUS main game event bus}, * only on the {@linkplain LogicalSide#CLIENT logical client}.

*/ public static class Post extends RenderPlayerEvent { diff --git a/src/main/java/net/neoforged/neoforge/client/event/SelectMusicEvent.java b/src/main/java/net/neoforged/neoforge/client/event/SelectMusicEvent.java index e0d20a712b..5a4004a76b 100644 --- a/src/main/java/net/neoforged/neoforge/client/event/SelectMusicEvent.java +++ b/src/main/java/net/neoforged/neoforge/client/event/SelectMusicEvent.java @@ -6,6 +6,7 @@ package net.neoforged.neoforge.client.event; import net.minecraft.client.resources.sounds.SoundInstance; +import net.minecraft.client.sounds.MusicInfo; import net.minecraft.sounds.Music; import net.neoforged.bus.api.Event; import net.neoforged.bus.api.ICancellableEvent; @@ -25,7 +26,7 @@ *
* Higher priorities would likely be better suited for biome-based or dimension-based musics, whereas lower priority is likely good for specific structures or situations.
*
- * This event is {@linkplain ICancellableEvent cancellable}, and does not {@linkplain HasResult have a result}.
+ * This event is {@linkplain ICancellableEvent cancellable}.
* If the event is canceled, then whatever the latest music set was will be used as the music. *
* This event is fired on the {@linkplain NeoForge#EVENT_BUS main Forge event bus},
@@ -33,11 +34,11 @@ * */ public class SelectMusicEvent extends Event implements ICancellableEvent { - private @Nullable Music music; - private final Music originalMusic; + private @Nullable MusicInfo music; + private final MusicInfo originalMusic; private final @Nullable SoundInstance playingMusic; - public SelectMusicEvent(Music music, @Nullable SoundInstance playingMusic) { + public SelectMusicEvent(MusicInfo music, @Nullable SoundInstance playingMusic) { this.music = music; this.originalMusic = music; this.playingMusic = playingMusic; @@ -46,7 +47,7 @@ public SelectMusicEvent(Music music, @Nullable SoundInstance playingMusic) { /** * {@return the original situational music that was selected} */ - public Music getOriginalMusic() { + public MusicInfo getOriginalMusic() { return originalMusic; } @@ -62,7 +63,7 @@ public SoundInstance getPlayingMusic() { * {@return the Music to be played, or {@code null} if any playing music should be cancelled} */ @Nullable - public Music getMusic() { + public MusicInfo getMusic() { return music; } @@ -71,7 +72,7 @@ public Music getMusic() { * If this was {@code null} but on the next tick isn't, the music given will be immediately played.
*
*/ - public void setMusic(@Nullable Music newMusic) { + public void setMusic(@Nullable MusicInfo newMusic) { this.music = newMusic; } @@ -79,7 +80,7 @@ public void setMusic(@Nullable Music newMusic) { * Sets the music and then cancels the event so that other listeners will not be invoked.
* Note that listeners using {@link SubscribeEvent#receiveCanceled()} will still be able to override this, but by default they will not */ - public void overrideMusic(@Nullable Music newMusic) { + public void overrideMusic(@Nullable MusicInfo newMusic) { this.music = newMusic; this.setCanceled(true); } diff --git a/src/main/java/net/neoforged/neoforge/client/extensions/IBakedModelExtension.java b/src/main/java/net/neoforged/neoforge/client/extensions/IBakedModelExtension.java index eee572c5e7..2567494587 100644 --- a/src/main/java/net/neoforged/neoforge/client/extensions/IBakedModelExtension.java +++ b/src/main/java/net/neoforged/neoforge/client/extensions/IBakedModelExtension.java @@ -11,6 +11,7 @@ import net.minecraft.client.renderer.RenderType; import net.minecraft.client.renderer.block.model.BakedQuad; import net.minecraft.client.renderer.block.model.ItemTransforms; +import net.minecraft.client.renderer.item.ItemModel; import net.minecraft.client.renderer.texture.TextureAtlasSprite; import net.minecraft.client.resources.model.BakedModel; import net.minecraft.core.BlockPos; @@ -86,7 +87,7 @@ default ChunkRenderTypeSet getRenderTypes(BlockState state, RandomSource rand, M } /** - * Gets an ordered list of {@link RenderType render types} to use when drawing this item. + * Gets the {@link RenderType render type} to use when drawing this item. * All render types using the {@link com.mojang.blaze3d.vertex.DefaultVertexFormat#NEW_ENTITY} format are supported. *

* This method will only be called on the models returned by {@link #getRenderPasses(ItemStack)}. @@ -95,18 +96,20 @@ default ChunkRenderTypeSet getRenderTypes(BlockState state, RandomSource rand, M * * @see #getRenderPasses(ItemStack) */ - default List getRenderTypes(ItemStack itemStack) { - return List.of(RenderTypeHelper.getFallbackItemRenderType(itemStack, self())); + default RenderType getRenderType(ItemStack itemStack) { + return RenderTypeHelper.getFallbackItemRenderType(itemStack, self()); } /** * Gets an ordered list of baked models used to render this model as an item. - * Each of those models' render types will be queried via {@link #getRenderTypes(ItemStack)}. + * Each of those models' render type will be queried via {@link #getRenderType(ItemStack)}. *

* By default, returns the model itself. * - * @see #getRenderTypes(ItemStack) + * @see #getRenderType(ItemStack) + * @deprecated Please migrate to {@link ItemModel}s, or if this is not possible contact us at NeoForge. */ + @Deprecated(forRemoval = true, since = "1.21.4") default List getRenderPasses(ItemStack itemStack) { return List.of(self()); } diff --git a/src/main/java/net/neoforged/neoforge/client/extensions/IDimensionSpecialEffectsExtension.java b/src/main/java/net/neoforged/neoforge/client/extensions/IDimensionSpecialEffectsExtension.java index 9268c20007..f6391f9357 100644 --- a/src/main/java/net/neoforged/neoforge/client/extensions/IDimensionSpecialEffectsExtension.java +++ b/src/main/java/net/neoforged/neoforge/client/extensions/IDimensionSpecialEffectsExtension.java @@ -8,7 +8,6 @@ import net.minecraft.client.Camera; import net.minecraft.client.multiplayer.ClientLevel; import net.minecraft.client.renderer.DimensionSpecialEffects; -import net.minecraft.client.renderer.LightTexture; import org.joml.Matrix4f; /** @@ -42,7 +41,7 @@ default boolean renderSky(ClientLevel level, int ticks, float partialTick, Matri * * @return true to prevent vanilla snow and rain rendering */ - default boolean renderSnowAndRain(ClientLevel level, int ticks, float partialTick, LightTexture lightTexture, double camX, double camY, double camZ) { + default boolean renderSnowAndRain(ClientLevel level, int ticks, float partialTick, double camX, double camY, double camZ) { return false; } diff --git a/src/main/java/net/neoforged/neoforge/client/extensions/IModelBakerExtension.java b/src/main/java/net/neoforged/neoforge/client/extensions/IModelBakerExtension.java index 90d84c3928..c0ed814b70 100644 --- a/src/main/java/net/neoforged/neoforge/client/extensions/IModelBakerExtension.java +++ b/src/main/java/net/neoforged/neoforge/client/extensions/IModelBakerExtension.java @@ -5,23 +5,40 @@ package net.neoforged.neoforge.client.extensions; -import java.util.function.Function; +import net.minecraft.client.renderer.block.model.TextureSlots; import net.minecraft.client.renderer.texture.TextureAtlasSprite; -import net.minecraft.client.resources.model.BakedModel; import net.minecraft.client.resources.model.Material; -import net.minecraft.client.resources.model.ModelResourceLocation; -import net.minecraft.client.resources.model.ModelState; +import net.minecraft.client.resources.model.ModelBaker; import net.minecraft.client.resources.model.UnbakedModel; import net.minecraft.resources.ResourceLocation; import org.jetbrains.annotations.Nullable; +/** + * An extension to {@link ModelBaker} that allows for custom model baking. + */ public interface IModelBakerExtension { - @Nullable - UnbakedModel getTopLevelModel(ModelResourceLocation location); + default ModelBaker self() { + return (ModelBaker) this; + } - BakedModel bake(ResourceLocation location, ModelState state, Function sprites); - - BakedModel bakeUncached(UnbakedModel model, ModelState state, Function sprites); + /** + * Gets the unbaked model for the given location. + * + * @param location The location of the model + * @return The unbaked model, or null if not found + */ + @Nullable + UnbakedModel getModel(ResourceLocation location); - Function getModelTextureGetter(); + /** + * Finds a sprite for the given slot name. + * + * @param slots The texture slots + * @param slotName The name of the slot + * @return The sprite, or a missing reference sprite if not found + */ + default TextureAtlasSprite findSprite(TextureSlots slots, String slotName) { + Material material = slots.getMaterial(slotName); + return material != null ? self().sprites().get(material) : self().sprites().reportMissingReference(slotName); + } } diff --git a/src/main/java/net/neoforged/neoforge/client/extensions/IRenderStateExtension.java b/src/main/java/net/neoforged/neoforge/client/extensions/IRenderStateExtension.java new file mode 100644 index 0000000000..1170a443ac --- /dev/null +++ b/src/main/java/net/neoforged/neoforge/client/extensions/IRenderStateExtension.java @@ -0,0 +1,66 @@ +/* + * Copyright (c) NeoForged and contributors + * SPDX-License-Identifier: LGPL-2.1-only + */ + +package net.neoforged.neoforge.client.extensions; + +import net.minecraft.util.context.ContextKey; +import net.neoforged.neoforge.client.renderstate.BaseRenderState; +import org.jetbrains.annotations.Nullable; + +/** + * Extension class for render state objects. Implemented by {@link BaseRenderState} for + * simple class extension. + */ +public interface IRenderStateExtension { + /** + * Gets the object associated with the given key. + * + * @param key Static key reference object + * @return The object associated with the key or null if the key is not present. + * @param Type of render data + */ + @Nullable + T getRenderData(ContextKey key); + + /** + * Sets the object associated with the given key. Key should be stored statically for later retrieval of the object. + * + * @param key Static key reference object + * @param data Object to store for custom rendering + * @param Type of render data + */ + void setRenderData(ContextKey key, @Nullable T data); + + /** + * Gets the value or throws an exception. Should be used in cases where the data must be present. + * + * @param key Static key reference object + * @return The data associate with the key + * @param Type of render data + */ + default T getRenderDataOrThrow(ContextKey key) { + T data = getRenderData(key); + if (data == null) { + throw new IllegalStateException("No value associated for key " + key); + } + return data; + } + + /** + * Gets the value or returns the default object if an object is not present + * + * @param key Static key reference object + * @param defaultVal Default value if an object is not present + * @return Value from the render data or the given default value if value is not present + * @param Type of render data + */ + default T getRenderDataOrDefault(ContextKey key, T defaultVal) { + T data = getRenderData(key); + if (data == null) { + return defaultVal; + } + return data; + } +} diff --git a/src/main/java/net/neoforged/neoforge/client/extensions/IUnbakedModelExtension.java b/src/main/java/net/neoforged/neoforge/client/extensions/IUnbakedModelExtension.java new file mode 100644 index 0000000000..0d528dd671 --- /dev/null +++ b/src/main/java/net/neoforged/neoforge/client/extensions/IUnbakedModelExtension.java @@ -0,0 +1,69 @@ +/* + * Copyright (c) NeoForged and contributors + * SPDX-License-Identifier: LGPL-2.1-only + */ + +package net.neoforged.neoforge.client.extensions; + +import net.minecraft.client.renderer.block.model.ItemTransforms; +import net.minecraft.client.renderer.block.model.TextureSlots; +import net.minecraft.client.resources.model.BakedModel; +import net.minecraft.client.resources.model.ModelBaker; +import net.minecraft.client.resources.model.ModelState; +import net.minecraft.client.resources.model.UnbakedModel; +import net.minecraft.util.context.ContextKeySet; +import net.minecraft.util.context.ContextMap; +import net.neoforged.neoforge.client.model.ExtendedUnbakedModel; +import net.neoforged.neoforge.client.model.NeoForgeModelProperties; +import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.Nullable; + +/** + * Extension type for the {@link UnbakedModel} interface. + */ +public interface IUnbakedModelExtension { + private UnbakedModel self() { + return (UnbakedModel) this; + } + + /** + * {@code bake} override with additional context. + * Consider inheriting from {@link ExtendedUnbakedModel} which overrides the vanilla {@code bake} method. + * + * @param additionalProperties additional properties provided by NeoForge or mods + */ + default BakedModel bake(TextureSlots textures, ModelBaker baker, ModelState modelState, boolean useAmbientOcclusion, boolean usesBlockLight, ItemTransforms itemTransforms, ContextMap additionalProperties) { + return self().bake(textures, baker, modelState, useAmbientOcclusion, usesBlockLight, itemTransforms); + } + + /** + * Appends additional properties for this model to the builder. + * + *

This method will already have been called on the parent models. + * It can modify the properties added by a parent model and/or add its own. + * This ensures that the properties are merged across the model parent-child chain. + * + *

The context map containing all the properties will be passed as the last parameter to + * {@link #bake(TextureSlots, ModelBaker, ModelState, boolean, boolean, ItemTransforms, ContextMap)}. + * + * @see NeoForgeModelProperties + */ + @ApiStatus.OverrideOnly + default void fillAdditionalProperties(ContextMap.Builder propertiesBuilder) {} + + /** + * Resolves additional properties by walking the model child-parent chain and calling {@link #fillAdditionalProperties(ContextMap.Builder)}. + */ + static ContextMap getTopAdditionalProperties(UnbakedModel topModel) { + var builder = new ContextMap.Builder(); + fillAdditionalProperties(topModel, builder); + return builder.create(ContextKeySet.EMPTY); + } + + private static void fillAdditionalProperties(@Nullable UnbakedModel model, ContextMap.Builder propertiesBuilder) { + if (model != null) { + fillAdditionalProperties(model.getParent(), propertiesBuilder); + model.fillAdditionalProperties(propertiesBuilder); + } + } +} diff --git a/src/main/java/net/neoforged/neoforge/client/extensions/common/IClientFluidTypeExtensions.java b/src/main/java/net/neoforged/neoforge/client/extensions/common/IClientFluidTypeExtensions.java index d9df521cab..f53cfe257d 100644 --- a/src/main/java/net/neoforged/neoforge/client/extensions/common/IClientFluidTypeExtensions.java +++ b/src/main/java/net/neoforged/neoforge/client/extensions/common/IClientFluidTypeExtensions.java @@ -13,6 +13,7 @@ import net.minecraft.client.multiplayer.ClientLevel; import net.minecraft.client.renderer.FogParameters; import net.minecraft.client.renderer.FogRenderer; +import net.minecraft.client.renderer.MultiBufferSource; import net.minecraft.client.renderer.ScreenEffectRenderer; import net.minecraft.core.BlockPos; import net.minecraft.resources.ResourceLocation; @@ -150,10 +151,10 @@ default ResourceLocation getRenderOverlayTexture(Minecraft mc) { * @param mc the client instance * @param poseStack the transformations representing the current rendering position */ - default void renderOverlay(Minecraft mc, PoseStack poseStack) { + default void renderOverlay(Minecraft mc, PoseStack poseStack, MultiBufferSource buffers) { ResourceLocation texture = this.getRenderOverlayTexture(mc); if (texture != null) - ScreenEffectRenderer.renderFluid(mc, poseStack, texture); + ScreenEffectRenderer.renderFluid(mc, poseStack, buffers, texture); } /** diff --git a/src/main/java/net/neoforged/neoforge/client/extensions/common/IClientItemExtensions.java b/src/main/java/net/neoforged/neoforge/client/extensions/common/IClientItemExtensions.java index 34018888a6..71032b91b4 100644 --- a/src/main/java/net/neoforged/neoforge/client/extensions/common/IClientItemExtensions.java +++ b/src/main/java/net/neoforged/neoforge/client/extensions/common/IClientItemExtensions.java @@ -6,14 +6,14 @@ package net.neoforged.neoforge.client.extensions.common; import com.mojang.blaze3d.vertex.PoseStack; -import net.minecraft.client.Minecraft; import net.minecraft.client.gui.Font; import net.minecraft.client.model.HumanoidModel; import net.minecraft.client.model.Model; import net.minecraft.client.player.LocalPlayer; -import net.minecraft.client.renderer.BlockEntityWithoutLevelRenderer; import net.minecraft.client.renderer.entity.layers.EquipmentLayerRenderer; -import net.minecraft.client.resources.model.BakedModel; +import net.minecraft.client.resources.model.EquipmentClientInfo; +import net.minecraft.core.component.DataComponents; +import net.minecraft.resources.ResourceLocation; import net.minecraft.tags.ItemTags; import net.minecraft.util.ARGB; import net.minecraft.world.InteractionHand; @@ -24,8 +24,6 @@ import net.minecraft.world.item.Item; import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.component.DyedItemColor; -import net.minecraft.world.item.equipment.EquipmentModel; -import net.minecraft.world.level.block.state.BlockState; import net.neoforged.fml.LogicalSide; import net.neoforged.neoforge.client.IArmPoseTransformer; import org.jetbrains.annotations.Nullable; @@ -96,15 +94,15 @@ default boolean applyForgeHandTransform(PoseStack poseStack, LocalPlayer player, * @param layerType The slot the item is in * @param original The original armor model. Will have attributes set. * @return A HumanoidModel to be rendered. Relevant properties are to be copied over by the caller. - * @see #getGenericArmorModel(ItemStack, EquipmentModel.LayerType, Model) + * @see #getGenericArmorModel(ItemStack, EquipmentClientInfo.LayerType, Model) */ - default Model getHumanoidArmorModel(ItemStack itemStack, EquipmentModel.LayerType layerType, Model original) { + default Model getHumanoidArmorModel(ItemStack itemStack, EquipmentClientInfo.LayerType layerType, Model original) { return original; } /** * Queries the armor model for this item when it's equipped. Useful in place of - * {@link #getHumanoidArmorModel(ItemStack, EquipmentModel.LayerType, Model)} for wrapping the original + * {@link #getHumanoidArmorModel(ItemStack, EquipmentClientInfo.LayerType, Model)} for wrapping the original * model or returning anything non-standard. *

* If you override this method you are responsible for copying any properties you care about from the original model. @@ -113,9 +111,9 @@ default Model getHumanoidArmorModel(ItemStack itemStack, EquipmentModel.LayerTyp * @param layerType The slot the item is in * @param original The original armor model. Will have attributes set. * @return A Model to be rendered. Relevant properties must be copied over manually. - * @see #getHumanoidArmorModel(ItemStack, EquipmentModel.LayerType, Model) + * @see #getHumanoidArmorModel(ItemStack, EquipmentClientInfo.LayerType, Model) */ - default Model getGenericArmorModel(ItemStack itemStack, EquipmentModel.LayerType layerType, Model original) { + default Model getGenericArmorModel(ItemStack itemStack, EquipmentClientInfo.LayerType layerType, Model original) { Model replacement = getHumanoidArmorModel(itemStack, layerType, original); if (replacement != original) { // FIXME: equipment rendering deals with a plain Model now @@ -155,18 +153,6 @@ default void setupModelAnimations(LivingEntity livingEntity, ItemStack itemStack */ default void renderHelmetOverlay(ItemStack stack, Player player, int width, int height, float partialTick) {} - /** - * Queries this item's renderer. - *

- * Only used if {@link BakedModel#isCustomRenderer()} returns {@code true} or {@link BlockState#getRenderShape()} - * returns {@link net.minecraft.world.level.block.RenderShape#ENTITYBLOCK_ANIMATED}. - *

- * By default, returns vanilla's block entity renderer. - */ - default BlockEntityWithoutLevelRenderer getCustomRenderer() { - return Minecraft.getInstance().getItemRenderer().getBlockEntityRenderer(); - } - /** * {@return Whether the item should bob when rendered in the world as an entity} * @@ -201,13 +187,13 @@ default boolean shouldSpreadAsEntity(ItemStack stack) { * performance * @return a custom color for the layer, in ARGB format, or 0 to skip rendering */ - default int getArmorLayerTintColor(ItemStack stack, EquipmentModel.Layer layer, int layerIdx, int fallbackColor) { + default int getArmorLayerTintColor(ItemStack stack, EquipmentClientInfo.Layer layer, int layerIdx, int fallbackColor) { return EquipmentLayerRenderer.getColorForLayer(layer, fallbackColor); } /** * Called once per render pass of equipped armor items, regardless of the number of layers; the return value of this - * method is passed to {@link #getArmorLayerTintColor(ItemStack, EquipmentModel.Layer, int, int)} as + * method is passed to {@link #getArmorLayerTintColor(ItemStack, EquipmentClientInfo.Layer, int, int)} as * the {@code fallbackColor} parameter. *

* You can override this method for your custom armor item to provide an alternative default color for the item when @@ -220,6 +206,24 @@ default int getDefaultDyeColor(ItemStack stack) { return stack.is(ItemTags.DYEABLE) ? ARGB.opaque(DyedItemColor.getOrDefault(stack, 0)) : 0; } + /** + * Called by RenderBiped and RenderPlayer to determine the armor texture that + * should be used for the currently equipped item. This will be called on + * stacks with the {@link DataComponents#EQUIPPABLE} component. + * + * Returning null from this function will use the default value. + * + * @param stack ItemStack for the equipped armor + * @param type The layer type of the armor + * @param layer The armor layer + * @param _default The default texture determined by the equipment renderer + * @return Path of texture to bind, or null to use default + */ + @Nullable + default ResourceLocation getArmorTexture(ItemStack stack, EquipmentClientInfo.LayerType type, EquipmentClientInfo.Layer layer, ResourceLocation _default) { + return null; + } + enum FontContext { /** * Used to display the amount of items in the {@link ItemStack}. diff --git a/src/main/java/net/neoforged/neoforge/client/gui/ConfigurationScreen.java b/src/main/java/net/neoforged/neoforge/client/gui/ConfigurationScreen.java index becd2d4ca8..3ad5d25f70 100644 --- a/src/main/java/net/neoforged/neoforge/client/gui/ConfigurationScreen.java +++ b/src/main/java/net/neoforged/neoforge/client/gui/ConfigurationScreen.java @@ -11,6 +11,7 @@ import com.electronwill.nightconfig.core.UnmodifiableConfig; import com.electronwill.nightconfig.core.UnmodifiableConfig.Entry; import com.google.common.collect.ImmutableList; +import com.mojang.datafixers.util.Function4; import com.mojang.realmsclient.RealmsMainScreen; import com.mojang.serialization.Codec; import it.unimi.dsi.fastutil.booleans.BooleanConsumer; @@ -53,8 +54,6 @@ import net.minecraft.client.gui.screens.options.OptionsSubScreen; import net.minecraft.client.multiplayer.ServerData; import net.minecraft.client.resources.language.I18n; -import net.minecraft.data.models.blockstates.PropertyDispatch.QuadFunction; -import net.minecraft.data.models.blockstates.PropertyDispatch.TriFunction; import net.minecraft.network.chat.CommonComponents; import net.minecraft.network.chat.Component; import net.minecraft.network.chat.MutableComponent; @@ -87,9 +86,9 @@ *

  • As an entry point for your custom configuration screen that handles fetching your configs, matching {@link Type} to the current game, enforcing level and game restarts, etc. *
  • As a ready-made system but extensible that works out of the box with all configs that use the {@link ModConfigSpec} system and don't do anything overly weird with it. * - * For the former one, use the 3-argument constructor {@link #ConfigurationScreen(ModContainer, Screen, TriFunction)} and return your own screen from the TriFunction. For the latter, + * For the former one, use the 3-argument constructor {@link #ConfigurationScreen(ModContainer, Screen, Function4)} and return your own screen from the Function4. For the latter, * use either the 2-argument constructor {@link #ConfigurationScreen(ModContainer, Screen)} if you don't need to extend the system, or the 3-argument one and return a subclass of - * {@link ConfigurationSectionScreen} from the TriFunction.

    + * {@link ConfigurationSectionScreen} from the Function4.

    * * In any case, register your configuration screen in your client mod class like this: * @@ -260,7 +259,7 @@ public void finish() { protected static final TranslationChecker translationChecker = new TranslationChecker(); protected final ModContainer mod; - private final QuadFunction sectionScreen; + private final Function4 sectionScreen; public RestartType needsRestart = RestartType.NONE; // If there is only one config type (and it can be edited, we show that instantly on the way "down" and want to close on the way "up". @@ -275,8 +274,7 @@ public ConfigurationScreen(final ModContainer mod, final Screen parent, Configur this(mod, parent, (a, b, c, d) -> new ConfigurationSectionScreen(a, b, c, d, filter)); } - @SuppressWarnings("resource") - public ConfigurationScreen(final ModContainer mod, final Screen parent, QuadFunction sectionScreen) { + public ConfigurationScreen(final ModContainer mod, final Screen parent, Function4 sectionScreen) { super(parent, Minecraft.getInstance().options, Component.translatable(translationChecker.check(mod.getModId() + ".configuration.title", LANG_PREFIX + "title"), mod.getModInfo().getDisplayName())); this.mod = mod; this.sectionScreen = sectionScreen; @@ -1389,6 +1387,16 @@ protected void checkButtons() { protected void updateWidgetNarration(final NarrationElementOutput pNarrationElementOutput) { // TODO I have no idea. Help? } + + @Override + protected int contentHeight() { + return 0; // TODO 1.21.4 no idea + } + + @Override + protected double scrollRate() { + return 4.0; // TODO 1.21.4 no idea + } } } diff --git a/src/main/java/net/neoforged/neoforge/client/gui/LoadingErrorScreen.java b/src/main/java/net/neoforged/neoforge/client/gui/LoadingErrorScreen.java index 77cf132865..741c30259d 100644 --- a/src/main/java/net/neoforged/neoforge/client/gui/LoadingErrorScreen.java +++ b/src/main/java/net/neoforged/neoforge/client/gui/LoadingErrorScreen.java @@ -115,7 +115,7 @@ public static class LoadingEntryList extends ObjectSelectionList logoData = selectedMod.getLogoFile().map(logoFile -> { - TextureManager tm = this.minecraft.getTextureManager(); - final Pack.ResourcesSupplier resourcePack = ResourcePackLoader.getPackFor(selectedMod.getModId()) - .orElse(ResourcePackLoader.getPackFor("neoforge").orElseThrow(() -> new RuntimeException("Can't find neoforge, WHAT!"))); - try (PackResources packResources = resourcePack.openPrimary(new PackLocationInfo("mod/" + selectedMod.getModId(), Component.empty(), PackSource.BUILT_IN, Optional.empty()))) { - NativeImage logo = null; - IoSupplier logoResource = packResources.getRootResource(logoFile.split("[/\\\\]")); - if (logoResource != null) - logo = NativeImage.read(logoResource.get()); - if (logo != null) { - - return Pair.of(tm.register("modlogo", new DynamicTexture(logo) { - @Override - public void upload() { - this.bind(); - NativeImage td = this.getPixels(); - // Use custom "blur" value which controls texture filtering (nearest-neighbor vs linear) - this.getPixels().upload(0, 0, 0, 0, 0, td.getWidth(), td.getHeight(), selectedMod.getLogoBlur(), false, false, false); - } - }), new Size2i(logo.getWidth(), logo.getHeight())); - } - } catch (IOException | IllegalArgumentException e) {} - return Pair.of(null, new Size2i(0, 0)); - }).orElse(Pair.of(null, new Size2i(0, 0))); + Pair logoData; + + if (selectedMod.getModId().equals(ResourceLocation.DEFAULT_NAMESPACE)) { + logoData = Pair.of(LogoRenderer.MINECRAFT_LOGO, new Size2i(LogoRenderer.LOGO_TEXTURE_WIDTH, LogoRenderer.LOGO_TEXTURE_HEIGHT)); + } else { + logoData = selectedMod.getLogoFile().map(logoFile -> { + TextureManager tm = this.minecraft.getTextureManager(); + final Pack.ResourcesSupplier resourcePack = ResourcePackLoader.getPackFor(selectedMod.getModId()) + .orElse(ResourcePackLoader.getPackFor("neoforge").orElseThrow(() -> new RuntimeException("Can't find neoforge, WHAT!"))); + try (PackResources packResources = resourcePack.openPrimary(new PackLocationInfo("mod/" + selectedMod.getModId(), Component.empty(), PackSource.BUILT_IN, Optional.empty()))) { + NativeImage logo = null; + IoSupplier logoResource = packResources.getRootResource(logoFile.split("[/\\\\]")); + if (logoResource != null) + logo = NativeImage.read(logoResource.get()); + if (logo != null) { + var textureId = ResourceLocation.fromNamespaceAndPath("neoforge", "modlogo"); + tm.register(textureId, new DynamicTexture(logo) { + @Override + public void upload() { + this.bind(); + NativeImage td = this.getPixels(); + // Use custom "blur" value which controls texture filtering (nearest-neighbor vs linear) + // TODO 1.21.4 restore selectedMod.getLogoBlur() check + this.getPixels().upload(0, 0, 0, 0, 0, td.getWidth(), td.getHeight(), false); + } + }); + + return Pair.of(textureId, new Size2i(logo.getWidth(), logo.getHeight())); + } + } catch (IOException | IllegalArgumentException e) {} + return Pair.of(null, new Size2i(0, 0)); + }).orElse(Pair.of(null, new Size2i(0, 0))); + } lines.add(selectedMod.getDisplayName()); lines.add(FMLTranslations.parseMessage("fml.menu.mods.info.version", MavenVersionTranslator.artifactVersionToString(selectedMod.getVersion()))); diff --git a/src/main/java/net/neoforged/neoforge/client/gui/ScrollableExperimentsScreen.java b/src/main/java/net/neoforged/neoforge/client/gui/ScrollableExperimentsScreen.java new file mode 100644 index 0000000000..fb3cbe2905 --- /dev/null +++ b/src/main/java/net/neoforged/neoforge/client/gui/ScrollableExperimentsScreen.java @@ -0,0 +1,147 @@ +/* + * Copyright (c) NeoForged and contributors + * SPDX-License-Identifier: LGPL-2.1-only + */ + +package net.neoforged.neoforge.client.gui; + +import java.util.List; +import java.util.function.BooleanSupplier; +import java.util.function.Consumer; +import net.minecraft.ChatFormatting; +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.GuiGraphics; +import net.minecraft.client.gui.components.AbstractWidget; +import net.minecraft.client.gui.components.Button; +import net.minecraft.client.gui.components.ContainerObjectSelectionList; +import net.minecraft.client.gui.components.CycleButton; +import net.minecraft.client.gui.components.MultiLineTextWidget; +import net.minecraft.client.gui.components.StringWidget; +import net.minecraft.client.gui.components.events.GuiEventListener; +import net.minecraft.client.gui.layouts.LayoutSettings; +import net.minecraft.client.gui.layouts.LinearLayout; +import net.minecraft.client.gui.narration.NarratableEntry; +import net.minecraft.client.gui.screens.Screen; +import net.minecraft.client.gui.screens.worldselection.ExperimentsScreen; +import net.minecraft.network.chat.CommonComponents; +import net.minecraft.network.chat.Component; +import net.minecraft.server.packs.repository.PackRepository; +import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.Nullable; + +@ApiStatus.Internal +public class ScrollableExperimentsScreen extends ExperimentsScreen { + private static final int DEFAULT_LIST_HEIGHT = 121; + private static final int ROW_PADDING = 4; + private static final int LIST_PADDING = ROW_PADDING + 34; + private static final int ENTRY_HEIGHT = 42; + + @Nullable + private ExperimentSelectionList selectionList; + @Nullable + private LinearLayout listLayout; + + public ScrollableExperimentsScreen(Screen parent, PackRepository packRepository, Consumer output) { + super(parent, packRepository, output); + } + + @Override + protected void init() { + this.layout.addTitleHeader(TITLE, this.font); + + LinearLayout contentLayout = this.layout.addToContents(LinearLayout.vertical(), LayoutSettings::alignVerticallyTop); + contentLayout.addChild( + new MultiLineTextWidget(INFO, this.font).setMaxWidth(MAIN_CONTENT_WIDTH), + settings -> settings.paddingBottom(15).alignHorizontallyCenter()); + + this.listLayout = contentLayout.addChild(LinearLayout.vertical()); + this.selectionList = new ExperimentSelectionList(this.minecraft); + this.packs.forEach((pack, selected) -> selectionList.addEntry(new ExperimentSelectionList.ExperimentEntry( + getHumanReadableTitle(pack), + () -> this.packs.getBoolean(pack), + flag -> this.packs.put(pack, flag.booleanValue()), + pack.getDescription()))); + this.listLayout.addChild(selectionList); + + LinearLayout footerLayout = this.layout.addToFooter(LinearLayout.horizontal().spacing(8)); + footerLayout.addChild(Button.builder(CommonComponents.GUI_DONE, btn -> this.onDone()).build()); + footerLayout.addChild(Button.builder(CommonComponents.GUI_CANCEL, btn -> this.onClose()).build()); + + this.layout.visitWidgets(this::addRenderableWidget); + this.repositionElements(); + } + + @Override + protected void repositionElements() { + if (this.selectionList != null) { + // Reset list height to empirical default because layouts can't squish elements to fit... + this.selectionList.setHeight(DEFAULT_LIST_HEIGHT); + } + super.repositionElements(); + if (this.selectionList != null && this.listLayout != null) { + this.selectionList.setHeight(this.layout.getContentHeight() - this.listLayout.getY()); + this.selectionList.setPosition(this.listLayout.getX(), this.listLayout.getY()); + this.selectionList.refreshScrollAmount(); + } + } + + private static class ExperimentSelectionList extends ContainerObjectSelectionList { + public ExperimentSelectionList(Minecraft mc) { + super(mc, ExperimentsScreen.MAIN_CONTENT_WIDTH + LIST_PADDING, DEFAULT_LIST_HEIGHT, 0, ENTRY_HEIGHT); + } + + @Override + public int getRowWidth() { + return ExperimentsScreen.MAIN_CONTENT_WIDTH + ROW_PADDING; + } + + @Override + protected int addEntry(ExperimentEntry entry) { + return super.addEntry(entry); + } + + private static class ExperimentEntry extends ContainerObjectSelectionList.Entry { + private static final int BUTTON_WIDTH = 44; + private static final int TITLE_Y = 6; + private static final int DESCRIPTION_Y = 20; + + private final StringWidget titleWidget; + private final MultiLineTextWidget descriptionWidget; + private final CycleButton button; + private final List children; + + public ExperimentEntry(Component title, BooleanSupplier selectedSupplier, Consumer selectedSetter, Component description) { + this.titleWidget = new StringWidget(title, Minecraft.getInstance().font).alignLeft(); + this.descriptionWidget = new MultiLineTextWidget(description.copy().withStyle(ChatFormatting.GRAY), Minecraft.getInstance().font) + .setMaxRows(2); + this.button = CycleButton.onOffBuilder(selectedSupplier.getAsBoolean()) + .displayOnlyValue() + .withCustomNarration(btn -> CommonComponents.joinForNarration(title, btn.createDefaultNarrationMessage(), description)) + .create(0, 0, BUTTON_WIDTH, Button.DEFAULT_HEIGHT, Component.empty(), (btn, val) -> selectedSetter.accept(val)); + this.children = List.of(titleWidget, descriptionWidget, this.button); + } + + @Override + public void render(GuiGraphics graphics, int entryIdx, int top, int left, int entryWidth, int entryHeight, int mouseX, int mouseY, boolean hovered, float partialTick) { + this.titleWidget.setPosition(left, top + TITLE_Y); + this.descriptionWidget.setPosition(left, top + DESCRIPTION_Y); + this.descriptionWidget.setMaxWidth(entryWidth - this.button.getWidth()); + this.button.setPosition(left + entryWidth - this.button.getWidth() - ROW_PADDING, top); + + this.titleWidget.render(graphics, mouseX, mouseY, partialTick); + this.descriptionWidget.render(graphics, mouseX, mouseY, partialTick); + this.button.render(graphics, mouseX, mouseY, partialTick); + } + + @Override + public List children() { + return this.children; + } + + @Override + public List narratables() { + return this.children; + } + } + } +} diff --git a/src/main/java/net/neoforged/neoforge/client/gui/widget/ModListWidget.java b/src/main/java/net/neoforged/neoforge/client/gui/widget/ModListWidget.java index cd6626c322..831ac723f6 100644 --- a/src/main/java/net/neoforged/neoforge/client/gui/widget/ModListWidget.java +++ b/src/main/java/net/neoforged/neoforge/client/gui/widget/ModListWidget.java @@ -40,7 +40,7 @@ public ModListWidget(ModListScreen parent, int listWidth, int top, int bottom) { } @Override - protected int getScrollbarPosition() { + protected int scrollBarX() { return this.listWidth; } diff --git a/src/main/java/net/neoforged/neoforge/client/model/AbstractSimpleUnbakedModel.java b/src/main/java/net/neoforged/neoforge/client/model/AbstractSimpleUnbakedModel.java new file mode 100644 index 0000000000..a00f3502cc --- /dev/null +++ b/src/main/java/net/neoforged/neoforge/client/model/AbstractSimpleUnbakedModel.java @@ -0,0 +1,60 @@ +/* + * Copyright (c) NeoForged and contributors + * SPDX-License-Identifier: LGPL-2.1-only + */ + +package net.neoforged.neoforge.client.model; + +import net.minecraft.client.data.models.model.TextureSlot; +import net.minecraft.client.renderer.block.model.ItemTransforms; +import net.minecraft.client.renderer.block.model.TextureSlots; +import net.minecraft.client.renderer.texture.TextureAtlasSprite; +import net.minecraft.client.resources.model.BakedModel; +import net.minecraft.client.resources.model.ModelBaker; +import net.minecraft.client.resources.model.ModelState; +import net.minecraft.client.resources.model.SimpleBakedModel; +import net.minecraft.util.context.ContextMap; +import net.neoforged.neoforge.client.RenderTypeGroup; + +/** + * @deprecated Extend {@link ExtendedUnbakedModel} directly instead, and use {@link SimpleBakedModel.Builder} if appropriate. + */ +@Deprecated(forRemoval = true, since = "1.21.4") +public abstract class AbstractSimpleUnbakedModel implements ExtendedUnbakedModel { + @Override + public BakedModel bake(TextureSlots slots, + ModelBaker baker, + ModelState state, + boolean useAmbientOcclusion, + boolean usesBlockLight, + ItemTransforms transforms, + ContextMap additionalProperties) { + TextureAtlasSprite particle = baker.findSprite(slots, TextureSlot.PARTICLE.getId()); + var renderTypes = additionalProperties.getOrDefault(NeoForgeModelProperties.RENDER_TYPE, RenderTypeGroup.EMPTY); + + IModelBuilder builder = IModelBuilder.of(useAmbientOcclusion, usesBlockLight, isGui3d(), + transforms, particle, renderTypes); + + addQuads(builder, slots, baker, state, useAmbientOcclusion, usesBlockLight, transforms); + + return builder.build(); + } + + @Override + public void resolveDependencies(Resolver p_387087_) { + //Has no dependencies + } + + public abstract void addQuads( + IModelBuilder builder, + TextureSlots slots, + ModelBaker baker, + ModelState state, + boolean useAmbientOcclusion, + boolean usesBlockLight, + ItemTransforms transforms); + + protected boolean isGui3d() { + return true; + } +} diff --git a/src/main/java/net/neoforged/neoforge/client/model/BakedModelWrapper.java b/src/main/java/net/neoforged/neoforge/client/model/BakedModelWrapper.java deleted file mode 100644 index 01417638b2..0000000000 --- a/src/main/java/net/neoforged/neoforge/client/model/BakedModelWrapper.java +++ /dev/null @@ -1,119 +0,0 @@ -/* - * Copyright (c) Forge Development LLC and contributors - * SPDX-License-Identifier: LGPL-2.1-only - */ - -package net.neoforged.neoforge.client.model; - -import com.mojang.blaze3d.vertex.PoseStack; -import java.util.List; -import net.minecraft.client.renderer.RenderType; -import net.minecraft.client.renderer.block.model.BakedOverrides; -import net.minecraft.client.renderer.block.model.BakedQuad; -import net.minecraft.client.renderer.block.model.ItemTransforms; -import net.minecraft.client.renderer.texture.TextureAtlasSprite; -import net.minecraft.client.resources.model.BakedModel; -import net.minecraft.core.BlockPos; -import net.minecraft.core.Direction; -import net.minecraft.util.RandomSource; -import net.minecraft.world.item.ItemDisplayContext; -import net.minecraft.world.item.ItemStack; -import net.minecraft.world.level.BlockAndTintGetter; -import net.minecraft.world.level.block.state.BlockState; -import net.neoforged.neoforge.client.ChunkRenderTypeSet; -import net.neoforged.neoforge.client.model.data.ModelData; -import net.neoforged.neoforge.common.util.TriState; -import org.jetbrains.annotations.Nullable; - -/** - * Wrapper for {@link BakedModel} which delegates all operations to its parent. - *

    - * Useful for creating wrapper baked models which only override certain properties. - */ -public abstract class BakedModelWrapper implements BakedModel { - protected final T originalModel; - - public BakedModelWrapper(T originalModel) { - this.originalModel = originalModel; - } - - @Override - public List getQuads(@Nullable BlockState state, @Nullable Direction side, RandomSource rand) { - return originalModel.getQuads(state, side, rand); - } - - @Override - public boolean useAmbientOcclusion() { - return originalModel.useAmbientOcclusion(); - } - - @Override - public TriState useAmbientOcclusion(BlockState state, ModelData data, RenderType renderType) { - return originalModel.useAmbientOcclusion(state, data, renderType); - } - - @Override - public boolean isGui3d() { - return originalModel.isGui3d(); - } - - @Override - public boolean usesBlockLight() { - return originalModel.usesBlockLight(); - } - - @Override - public boolean isCustomRenderer() { - return originalModel.isCustomRenderer(); - } - - @Override - public TextureAtlasSprite getParticleIcon() { - return originalModel.getParticleIcon(); - } - - @Override - public ItemTransforms getTransforms() { - return originalModel.getTransforms(); - } - - @Override - public BakedOverrides overrides() { - return originalModel.overrides(); - } - - @Override - public BakedModel applyTransform(ItemDisplayContext cameraTransformType, PoseStack poseStack, boolean applyLeftHandTransform) { - return originalModel.applyTransform(cameraTransformType, poseStack, applyLeftHandTransform); - } - - @Override - public TextureAtlasSprite getParticleIcon(ModelData data) { - return originalModel.getParticleIcon(data); - } - - @Override - public List getQuads(@Nullable BlockState state, @Nullable Direction side, RandomSource rand, ModelData extraData, @Nullable RenderType renderType) { - return originalModel.getQuads(state, side, rand, extraData, renderType); - } - - @Override - public ModelData getModelData(BlockAndTintGetter level, BlockPos pos, BlockState state, ModelData modelData) { - return originalModel.getModelData(level, pos, state, modelData); - } - - @Override - public ChunkRenderTypeSet getRenderTypes(BlockState state, RandomSource rand, ModelData data) { - return originalModel.getRenderTypes(state, rand, data); - } - - @Override - public List getRenderTypes(ItemStack itemStack) { - return originalModel.getRenderTypes(itemStack); - } - - @Override - public List getRenderPasses(ItemStack itemStack) { - return originalModel.getRenderPasses(itemStack); - } -} diff --git a/src/main/java/net/neoforged/neoforge/client/model/DelegateUnbakedModel.java b/src/main/java/net/neoforged/neoforge/client/model/DelegateUnbakedModel.java new file mode 100644 index 0000000000..1d61e20c54 --- /dev/null +++ b/src/main/java/net/neoforged/neoforge/client/model/DelegateUnbakedModel.java @@ -0,0 +1,67 @@ +/* + * Copyright (c) NeoForged and contributors + * SPDX-License-Identifier: LGPL-2.1-only + */ + +package net.neoforged.neoforge.client.model; + +import net.minecraft.client.renderer.block.model.ItemTransforms; +import net.minecraft.client.renderer.block.model.TextureSlots; +import net.minecraft.client.resources.model.BakedModel; +import net.minecraft.client.resources.model.ModelBaker; +import net.minecraft.client.resources.model.ModelState; +import net.minecraft.client.resources.model.UnbakedModel; +import net.minecraft.util.context.ContextMap; +import org.jetbrains.annotations.Nullable; + +public abstract class DelegateUnbakedModel implements ExtendedUnbakedModel { + protected final UnbakedModel wrapped; + + protected DelegateUnbakedModel(UnbakedModel wrapped) { + this.wrapped = wrapped; + } + + @Override + public BakedModel bake(TextureSlots textures, ModelBaker baker, ModelState modelState, boolean useAmbientOcclusion, boolean usesBlockLight, ItemTransforms itemTransforms, ContextMap additionalProperties) { + return this.wrapped.bake(textures, baker, modelState, useAmbientOcclusion, usesBlockLight, itemTransforms, additionalProperties); + } + + @Override + public void resolveDependencies(Resolver resolver) { + this.wrapped.resolveDependencies(resolver); + } + + @Nullable + @Override + public Boolean getAmbientOcclusion() { + return this.wrapped.getAmbientOcclusion(); + } + + @Nullable + @Override + public GuiLight getGuiLight() { + return this.wrapped.getGuiLight(); + } + + @Nullable + @Override + public ItemTransforms getTransforms() { + return this.wrapped.getTransforms(); + } + + @Override + public TextureSlots.Data getTextureSlots() { + return this.wrapped.getTextureSlots(); + } + + @Nullable + @Override + public UnbakedModel getParent() { + return this.wrapped.getParent(); + } + + @Override + public void fillAdditionalProperties(ContextMap.Builder propertiesBuilder) { + this.wrapped.fillAdditionalProperties(propertiesBuilder); + } +} diff --git a/src/main/java/net/neoforged/neoforge/client/model/DynamicFluidContainerModel.java b/src/main/java/net/neoforged/neoforge/client/model/DynamicFluidContainerModel.java deleted file mode 100644 index b15c779a95..0000000000 --- a/src/main/java/net/neoforged/neoforge/client/model/DynamicFluidContainerModel.java +++ /dev/null @@ -1,231 +0,0 @@ -/* - * Copyright (c) Forge Development LLC and contributors - * SPDX-License-Identifier: LGPL-2.1-only - */ - -package net.neoforged.neoforge.client.model; - -import com.google.common.collect.Maps; -import com.google.gson.JsonDeserializationContext; -import com.google.gson.JsonObject; -import com.mojang.math.Transformation; -import java.util.List; -import java.util.Map; -import java.util.function.Function; -import net.minecraft.client.color.item.ItemColor; -import net.minecraft.client.multiplayer.ClientLevel; -import net.minecraft.client.renderer.RenderType; -import net.minecraft.client.renderer.block.model.BakedOverrides; -import net.minecraft.client.renderer.block.model.ItemOverride; -import net.minecraft.client.renderer.texture.TextureAtlasSprite; -import net.minecraft.client.resources.model.BakedModel; -import net.minecraft.client.resources.model.BlockModelRotation; -import net.minecraft.client.resources.model.ItemModel; -import net.minecraft.client.resources.model.Material; -import net.minecraft.client.resources.model.ModelBaker; -import net.minecraft.client.resources.model.ModelState; -import net.minecraft.core.registries.BuiltInRegistries; -import net.minecraft.resources.ResourceLocation; -import net.minecraft.util.GsonHelper; -import net.minecraft.world.entity.LivingEntity; -import net.minecraft.world.item.ItemStack; -import net.minecraft.world.level.material.Fluid; -import net.minecraft.world.level.material.Fluids; -import net.neoforged.neoforge.client.ClientHooks; -import net.neoforged.neoforge.client.NeoForgeRenderTypes; -import net.neoforged.neoforge.client.RenderTypeGroup; -import net.neoforged.neoforge.client.extensions.common.IClientFluidTypeExtensions; -import net.neoforged.neoforge.client.model.geometry.IGeometryBakingContext; -import net.neoforged.neoforge.client.model.geometry.IGeometryLoader; -import net.neoforged.neoforge.client.model.geometry.IUnbakedGeometry; -import net.neoforged.neoforge.client.model.geometry.StandaloneGeometryBakingContext; -import net.neoforged.neoforge.client.model.geometry.UnbakedGeometryHelper; -import net.neoforged.neoforge.fluids.FluidUtil; -import org.jetbrains.annotations.Nullable; -import org.joml.Quaternionf; -import org.joml.Vector3f; - -/** - * A dynamic fluid container model, capable of re-texturing itself at runtime to match the contained fluid. - *

    - * Composed of a base layer, a fluid layer (applied with a mask) and a cover layer (optionally applied with a mask). - * The entire model may optionally be flipped if the fluid is gaseous, and the fluid layer may glow if light-emitting. - *

    - * Fluid tinting requires registering a separate {@link ItemColor}. An implementation is provided in {@link Colors}. - * - * @see Colors - */ -public class DynamicFluidContainerModel implements IUnbakedGeometry { - // Depth offsets to prevent Z-fighting - private static final Transformation FLUID_TRANSFORM = new Transformation(new Vector3f(), new Quaternionf(), new Vector3f(1, 1, 1.002f), new Quaternionf()); - private static final Transformation COVER_TRANSFORM = new Transformation(new Vector3f(), new Quaternionf(), new Vector3f(1, 1, 1.004f), new Quaternionf()); - - private final Fluid fluid; - private final boolean flipGas; - private final boolean coverIsMask; - private final boolean applyFluidLuminosity; - - private DynamicFluidContainerModel(Fluid fluid, boolean flipGas, boolean coverIsMask, boolean applyFluidLuminosity) { - this.fluid = fluid; - this.flipGas = flipGas; - this.coverIsMask = coverIsMask; - this.applyFluidLuminosity = applyFluidLuminosity; - } - - public static RenderTypeGroup getLayerRenderTypes(boolean unlit) { - return new RenderTypeGroup(RenderType.translucent(), unlit ? NeoForgeRenderTypes.ITEM_UNSORTED_UNLIT_TRANSLUCENT.get() : NeoForgeRenderTypes.ITEM_UNSORTED_TRANSLUCENT.get()); - } - - /** - * Returns a new ModelDynBucket representing the given fluid, but with the same - * other properties (flipGas, tint, coverIsMask). - */ - public DynamicFluidContainerModel withFluid(Fluid newFluid) { - return new DynamicFluidContainerModel(newFluid, flipGas, coverIsMask, applyFluidLuminosity); - } - - @Override - public BakedModel bake(IGeometryBakingContext context, ModelBaker baker, Function spriteGetter, ModelState modelState, List overrides) { - Material particleLocation = context.hasMaterial("particle") ? context.getMaterial("particle") : null; - Material baseLocation = context.hasMaterial("base") ? context.getMaterial("base") : null; - Material fluidMaskLocation = context.hasMaterial("fluid") ? context.getMaterial("fluid") : null; - Material coverLocation = context.hasMaterial("cover") ? context.getMaterial("cover") : null; - - TextureAtlasSprite baseSprite = baseLocation != null ? spriteGetter.apply(baseLocation) : null; - TextureAtlasSprite fluidSprite = fluid != Fluids.EMPTY ? spriteGetter.apply(ClientHooks.getBlockMaterial(IClientFluidTypeExtensions.of(fluid).getStillTexture())) : null; - TextureAtlasSprite coverSprite = (coverLocation != null && (!coverIsMask || baseLocation != null)) ? spriteGetter.apply(coverLocation) : null; - - TextureAtlasSprite particleSprite = particleLocation != null ? spriteGetter.apply(particleLocation) : null; - - if (particleSprite == null) particleSprite = fluidSprite; - if (particleSprite == null) particleSprite = baseSprite; - if (particleSprite == null && !coverIsMask) particleSprite = coverSprite; - - // If the fluid is lighter than air, rotate 180deg to turn it upside down - if (flipGas && fluid != Fluids.EMPTY && fluid.getFluidType().isLighterThanAir()) { - modelState = new SimpleModelState( - modelState.getRotation().compose( - new Transformation(null, new Quaternionf(0, 0, 1, 0), null, null))); - } - - // We need to disable GUI 3D and block lighting for this to render properly - var itemContext = StandaloneGeometryBakingContext.builder(context).withGui3d(false).withUseBlockLight(false).build(ResourceLocation.fromNamespaceAndPath("neoforge", "dynamic_fluid_container")); - var modelBuilder = CompositeModel.Baked.builder(itemContext, particleSprite, context.getTransforms()); - - var normalRenderTypes = getLayerRenderTypes(false); - - if (baseLocation != null && baseSprite != null) { - // Base texture - var unbaked = UnbakedGeometryHelper.createUnbakedItemElements(0, baseSprite); - var quads = UnbakedGeometryHelper.bakeElements(unbaked, $ -> baseSprite, modelState); - modelBuilder.addQuads(normalRenderTypes, quads); - } - - if (fluidMaskLocation != null && fluidSprite != null) { - TextureAtlasSprite templateSprite = spriteGetter.apply(fluidMaskLocation); - if (templateSprite != null) { - // Fluid layer - var transformedState = new SimpleModelState(modelState.getRotation().compose(FLUID_TRANSFORM), modelState.isUvLocked()); - var unbaked = UnbakedGeometryHelper.createUnbakedItemMaskElements(1, templateSprite); // Use template as mask - var quads = UnbakedGeometryHelper.bakeElements(unbaked, $ -> fluidSprite, transformedState); // Bake with fluid texture - - var emissive = applyFluidLuminosity && fluid.getFluidType().getLightLevel() > 0; - var renderTypes = getLayerRenderTypes(emissive); - if (emissive) QuadTransformers.settingMaxEmissivity().processInPlace(quads); - - modelBuilder.addQuads(renderTypes, quads); - } - } - - if (coverSprite != null) { - var sprite = coverIsMask ? baseSprite : coverSprite; - if (sprite != null) { - // Cover/overlay - var transformedState = new SimpleModelState(modelState.getRotation().compose(COVER_TRANSFORM), modelState.isUvLocked()); - var unbaked = UnbakedGeometryHelper.createUnbakedItemMaskElements(2, coverSprite); // Use cover as mask - var quads = UnbakedGeometryHelper.bakeElements(unbaked, $ -> sprite, transformedState); // Bake with selected texture - modelBuilder.addQuads(normalRenderTypes, quads); - } - } - - modelBuilder.setParticle(particleSprite); - - BakedModel bakedModel = modelBuilder.build(); - var bakedOverrides = new ContainedFluidOverrideHandler(new BakedOverrides(baker, overrides, spriteGetter), bakedModel, baker, itemContext, this); - return new ItemModel.BakedModelWithOverrides(bakedModel, bakedOverrides); - } - - public static final class Loader implements IGeometryLoader { - public static final Loader INSTANCE = new Loader(); - - private Loader() {} - - @Override - public DynamicFluidContainerModel read(JsonObject jsonObject, JsonDeserializationContext deserializationContext) { - if (!jsonObject.has("fluid")) - throw new RuntimeException("Bucket model requires 'fluid' value."); - - ResourceLocation fluidName = ResourceLocation.parse(jsonObject.get("fluid").getAsString()); - - Fluid fluid = BuiltInRegistries.FLUID.getValue(fluidName); - - boolean flip = GsonHelper.getAsBoolean(jsonObject, "flip_gas", false); - boolean coverIsMask = GsonHelper.getAsBoolean(jsonObject, "cover_is_mask", true); - boolean applyFluidLuminosity = GsonHelper.getAsBoolean(jsonObject, "apply_fluid_luminosity", true); - - // create new model with correct liquid - return new DynamicFluidContainerModel(fluid, flip, coverIsMask, applyFluidLuminosity); - } - } - - private static final class ContainedFluidOverrideHandler extends BakedOverrides { - private final Map cache = Maps.newHashMap(); // contains all the baked models since they'll never change - private final BakedOverrides nested; - private final BakedModel baseModel; - private final ModelBaker baker; - private final IGeometryBakingContext owner; - private final DynamicFluidContainerModel parent; - - private ContainedFluidOverrideHandler(BakedOverrides nested, BakedModel baseModel, ModelBaker baker, IGeometryBakingContext owner, DynamicFluidContainerModel parent) { - this.nested = nested; - this.baseModel = baseModel; - this.baker = baker; - this.owner = owner; - this.parent = parent; - } - - @Override - @Nullable - public BakedModel findOverride(ItemStack stack, @Nullable ClientLevel level, @Nullable LivingEntity entity, int seed) { - BakedModel overridden = nested.findOverride(stack, level, entity, seed); - if (overridden != null) return overridden; - - return FluidUtil.getFluidContained(stack) - .map(fluidStack -> { - Fluid fluid = fluidStack.getFluid(); - String name = BuiltInRegistries.FLUID.getKey(fluid).toString(); - - if (!cache.containsKey(name)) { - DynamicFluidContainerModel unbaked = this.parent.withFluid(fluid); - BakedModel bakedModel = unbaked.bake(owner, baker, Material::sprite, BlockModelRotation.X0_Y0, List.of()); - cache.put(name, bakedModel); - return bakedModel; - } - - return cache.get(name); - }) - // not a fluid item apparently - .orElse(baseModel); // empty bucket - } - } - - public static class Colors implements ItemColor { - @Override - public int getColor(ItemStack stack, int tintIndex) { - if (tintIndex != 1) return 0xFFFFFFFF; - return FluidUtil.getFluidContained(stack) - .map(fluidStack -> IClientFluidTypeExtensions.of(fluidStack.getFluid()).getTintColor(fluidStack)) - .orElse(0xFFFFFFFF); - } - } -} diff --git a/src/main/java/net/neoforged/neoforge/client/model/EmptyModel.java b/src/main/java/net/neoforged/neoforge/client/model/EmptyModel.java index f3e1084ed1..b512835250 100644 --- a/src/main/java/net/neoforged/neoforge/client/model/EmptyModel.java +++ b/src/main/java/net/neoforged/neoforge/client/model/EmptyModel.java @@ -5,13 +5,17 @@ package net.neoforged.neoforge.client.model; +import com.google.gson.JsonDeserializationContext; +import com.google.gson.JsonDeserializer; +import com.google.gson.JsonElement; +import com.google.gson.JsonParseException; +import java.lang.reflect.Type; import java.util.EnumMap; import java.util.List; import java.util.Map; -import java.util.function.Function; import net.minecraft.client.renderer.block.model.BakedQuad; -import net.minecraft.client.renderer.block.model.ItemOverride; import net.minecraft.client.renderer.block.model.ItemTransforms; +import net.minecraft.client.renderer.block.model.TextureSlots; import net.minecraft.client.renderer.texture.MissingTextureAtlasSprite; import net.minecraft.client.renderer.texture.TextureAtlas; import net.minecraft.client.renderer.texture.TextureAtlasSprite; @@ -20,34 +24,34 @@ import net.minecraft.client.resources.model.ModelBaker; import net.minecraft.client.resources.model.ModelState; import net.minecraft.client.resources.model.SimpleBakedModel; +import net.minecraft.client.resources.model.UnbakedModel; import net.minecraft.core.Direction; import net.neoforged.neoforge.client.RenderTypeGroup; -import net.neoforged.neoforge.client.model.geometry.IGeometryBakingContext; -import net.neoforged.neoforge.client.model.geometry.IGeometryLoader; -import net.neoforged.neoforge.client.model.geometry.IUnbakedGeometry; -import net.neoforged.neoforge.client.model.geometry.SimpleUnbakedGeometry; import net.neoforged.neoforge.client.textures.UnitTextureAtlasSprite; /** * A completely empty model with no quads or texture dependencies. *

    - * You can access it as a {@link BakedModel}, an {@link IUnbakedGeometry} or an {@link IGeometryLoader}. + * You can access it as a {@link BakedModel}. */ -public class EmptyModel extends SimpleUnbakedGeometry { +public class EmptyModel implements UnbakedModel, JsonDeserializer { public static final BakedModel BAKED = new Baked(); public static final EmptyModel INSTANCE = new EmptyModel(); - public static final IGeometryLoader LOADER = (json, ctx) -> INSTANCE; + public static final UnbakedModelLoader LOADER = (object, context) -> INSTANCE; private EmptyModel() {} @Override - protected void addQuads(IGeometryBakingContext owner, IModelBuilder modelBuilder, ModelBaker baker, Function spriteGetter, ModelState modelTransform) { - // NO-OP + public BakedModel bake(TextureSlots p_386641_, ModelBaker p_250133_, ModelState p_119536_, boolean p_387129_, boolean p_388638_, ItemTransforms p_386911_) { + return BAKED; } @Override - public BakedModel bake(IGeometryBakingContext context, ModelBaker baker, Function spriteGetter, ModelState modelState, List overrides) { - return BAKED; + public void resolveDependencies(Resolver p_387087_) {} + + @Override + public EmptyModel deserialize(JsonElement jsonElement, Type type, JsonDeserializationContext jsonDeserializationContext) throws JsonParseException { + return INSTANCE; } private static class Baked extends SimpleBakedModel { diff --git a/src/main/java/net/neoforged/neoforge/client/model/ExtendedBlockModelDeserializer.java b/src/main/java/net/neoforged/neoforge/client/model/ExtendedBlockModelDeserializer.java deleted file mode 100644 index db0e19574d..0000000000 --- a/src/main/java/net/neoforged/neoforge/client/model/ExtendedBlockModelDeserializer.java +++ /dev/null @@ -1,107 +0,0 @@ -/* - * Copyright (c) Forge Development LLC and contributors - * SPDX-License-Identifier: LGPL-2.1-only - */ - -package net.neoforged.neoforge.client.model; - -import com.google.gson.Gson; -import com.google.gson.GsonBuilder; -import com.google.gson.JsonDeserializationContext; -import com.google.gson.JsonElement; -import com.google.gson.JsonObject; -import com.google.gson.JsonParseException; -import com.mojang.math.Transformation; -import java.lang.reflect.Type; -import java.util.List; -import java.util.Locale; -import java.util.Map; -import net.minecraft.client.renderer.block.model.BlockElement; -import net.minecraft.client.renderer.block.model.BlockElementFace; -import net.minecraft.client.renderer.block.model.BlockFaceUV; -import net.minecraft.client.renderer.block.model.BlockModel; -import net.minecraft.client.renderer.block.model.ItemOverride; -import net.minecraft.client.renderer.block.model.ItemTransform; -import net.minecraft.client.renderer.block.model.ItemTransforms; -import net.minecraft.resources.ResourceLocation; -import net.minecraft.util.GsonHelper; -import net.neoforged.neoforge.client.model.geometry.GeometryLoaderManager; -import net.neoforged.neoforge.client.model.geometry.IUnbakedGeometry; -import net.neoforged.neoforge.common.util.TransformationHelper; -import org.jetbrains.annotations.Nullable; - -/** - * A version of {@link BlockModel.Deserializer} capable of deserializing models with custom loaders, as well as other - * changes introduced to the spec by Forge. - */ -public class ExtendedBlockModelDeserializer extends BlockModel.Deserializer { - public static final Gson INSTANCE = (new GsonBuilder()) - .registerTypeAdapter(BlockModel.class, new ExtendedBlockModelDeserializer()) - .registerTypeAdapter(BlockElement.class, new BlockElement.Deserializer()) - .registerTypeAdapter(BlockElementFace.class, new BlockElementFace.Deserializer()) - .registerTypeAdapter(BlockFaceUV.class, new BlockFaceUV.Deserializer()) - .registerTypeAdapter(ItemTransform.class, new ItemTransform.Deserializer()) - .registerTypeAdapter(ItemTransforms.class, new ItemTransforms.Deserializer()) - .registerTypeAdapter(ItemOverride.class, new ItemOverride.Deserializer()) - .registerTypeAdapter(Transformation.class, new TransformationHelper.Deserializer()) - .create(); - - @Override - public BlockModel deserialize(JsonElement element, Type targetType, JsonDeserializationContext deserializationContext) throws JsonParseException { - BlockModel model = super.deserialize(element, targetType, deserializationContext); - JsonObject jsonobject = element.getAsJsonObject(); - IUnbakedGeometry geometry = deserializeGeometry(deserializationContext, jsonobject); - - List elements = model.getElements(); - if (geometry != null) { - elements.clear(); - model.customData.setCustomGeometry(geometry); - } - - if (jsonobject.has("transform")) { - JsonElement transform = jsonobject.get("transform"); - model.customData.setRootTransform(deserializationContext.deserialize(transform, Transformation.class)); - } - - if (jsonobject.has("render_type")) { - var renderTypeHintName = GsonHelper.getAsString(jsonobject, "render_type"); - model.customData.setRenderTypeHint(ResourceLocation.parse(renderTypeHintName)); - } - - if (jsonobject.has("visibility")) { - JsonObject visibility = GsonHelper.getAsJsonObject(jsonobject, "visibility"); - for (Map.Entry part : visibility.entrySet()) { - model.customData.visibilityData.setVisibilityState(part.getKey(), part.getValue().getAsBoolean()); - } - } - - return model; - } - - @Nullable - public static IUnbakedGeometry deserializeGeometry(JsonDeserializationContext deserializationContext, JsonObject object) throws JsonParseException { - if (!object.has("loader")) - return null; - - ResourceLocation name; - boolean optional; - if (object.get("loader").isJsonObject()) { - JsonObject loaderObj = object.getAsJsonObject("loader"); - name = ResourceLocation.parse(GsonHelper.getAsString(loaderObj, "id")); - optional = GsonHelper.getAsBoolean(loaderObj, "optional", false); - } else { - name = ResourceLocation.parse(GsonHelper.getAsString(object, "loader")); - optional = false; - } - - var loader = GeometryLoaderManager.get(name); - if (loader == null) { - if (optional) { - return null; - } - throw new JsonParseException(String.format(Locale.ENGLISH, "Model loader '%s' not found. Registered loaders: %s", name, GeometryLoaderManager.getLoaderList())); - } - - return loader.read(object, deserializationContext); - } -} diff --git a/src/main/java/net/neoforged/neoforge/client/model/ExtendedUnbakedModel.java b/src/main/java/net/neoforged/neoforge/client/model/ExtendedUnbakedModel.java new file mode 100644 index 0000000000..7c981e8f94 --- /dev/null +++ b/src/main/java/net/neoforged/neoforge/client/model/ExtendedUnbakedModel.java @@ -0,0 +1,30 @@ +/* + * Copyright (c) NeoForged and contributors + * SPDX-License-Identifier: LGPL-2.1-only + */ + +package net.neoforged.neoforge.client.model; + +import net.minecraft.client.renderer.block.model.ItemTransforms; +import net.minecraft.client.renderer.block.model.TextureSlots; +import net.minecraft.client.resources.model.BakedModel; +import net.minecraft.client.resources.model.ModelBaker; +import net.minecraft.client.resources.model.ModelState; +import net.minecraft.client.resources.model.UnbakedModel; +import net.minecraft.util.context.ContextMap; + +/** + * Base interface for unbaked models that wish to support the NeoForge-added {@code bake} method + * that receives {@linkplain #fillAdditionalProperties(ContextMap.Builder) additional properties}. + */ +public interface ExtendedUnbakedModel extends UnbakedModel { + @Deprecated + @Override + default BakedModel bake(TextureSlots textures, ModelBaker baker, ModelState modelState, boolean useAmbientOcclusion, boolean usesBlockLight, ItemTransforms itemTransforms) { + return bake(textures, baker, modelState, useAmbientOcclusion, usesBlockLight, itemTransforms, ContextMap.EMPTY); + } + + // Re-abstract the extended version + @Override + BakedModel bake(TextureSlots textures, ModelBaker baker, ModelState modelState, boolean useAmbientOcclusion, boolean usesBlockLight, ItemTransforms itemTransforms, ContextMap additionalProperties); +} diff --git a/src/main/java/net/neoforged/neoforge/client/model/IModelBuilder.java b/src/main/java/net/neoforged/neoforge/client/model/IModelBuilder.java index 51111b4d0b..a75207f330 100644 --- a/src/main/java/net/neoforged/neoforge/client/model/IModelBuilder.java +++ b/src/main/java/net/neoforged/neoforge/client/model/IModelBuilder.java @@ -19,7 +19,10 @@ *

    * Provides a generic base implementation via {@link #of(boolean, boolean, boolean, ItemTransforms, TextureAtlasSprite, RenderTypeGroup)} * and a quad-collecting alternative via {@link #collecting(List)}. + * + * @deprecated Use {@link SimpleBakedModel.Builder} instead. */ +@Deprecated(forRemoval = true, since = "1.21.4") public interface IModelBuilder> { /** * Creates a new model builder that uses the provided attributes in the final baked model. @@ -38,10 +41,28 @@ static IModelBuilder collecting(List quads) { return new Collecting(quads); } + /** + * Adds a face to the model that will be culled based on the provided facing. + * + * @param facing The facing + * @param quad The quad + * @return This builder + */ T addCulledFace(Direction facing, BakedQuad quad); + /** + * Adds a face to the model that will not be culled. + * + * @param quad The quad + * @return This builder + */ T addUnculledFace(BakedQuad quad); + /** + * Builds the model from the collected faces. + * + * @return The baked model + */ BakedModel build(); class Simple implements IModelBuilder { diff --git a/src/main/java/net/neoforged/neoforge/client/model/ItemLayerModel.java b/src/main/java/net/neoforged/neoforge/client/model/ItemLayerModel.java deleted file mode 100644 index 005c98f626..0000000000 --- a/src/main/java/net/neoforged/neoforge/client/model/ItemLayerModel.java +++ /dev/null @@ -1,126 +0,0 @@ -/* - * Copyright (c) Forge Development LLC and contributors - * SPDX-License-Identifier: LGPL-2.1-only - */ - -package net.neoforged.neoforge.client.model; - -import com.google.common.collect.ImmutableList; -import com.google.gson.JsonDeserializationContext; -import com.google.gson.JsonElement; -import com.google.gson.JsonObject; -import com.google.gson.JsonParseException; -import it.unimi.dsi.fastutil.ints.Int2ObjectArrayMap; -import it.unimi.dsi.fastutil.ints.Int2ObjectMap; -import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; -import java.util.List; -import java.util.Map; -import java.util.function.Function; -import net.minecraft.client.renderer.RenderType; -import net.minecraft.client.renderer.block.model.BakedOverrides; -import net.minecraft.client.renderer.block.model.ItemModelGenerator; -import net.minecraft.client.renderer.block.model.ItemOverride; -import net.minecraft.client.renderer.texture.TextureAtlasSprite; -import net.minecraft.client.resources.model.BakedModel; -import net.minecraft.client.resources.model.ItemModel; -import net.minecraft.client.resources.model.Material; -import net.minecraft.client.resources.model.ModelBaker; -import net.minecraft.client.resources.model.ModelState; -import net.minecraft.resources.ResourceLocation; -import net.neoforged.neoforge.client.NeoForgeRenderTypes; -import net.neoforged.neoforge.client.RenderTypeGroup; -import net.neoforged.neoforge.client.model.geometry.IGeometryBakingContext; -import net.neoforged.neoforge.client.model.geometry.IGeometryLoader; -import net.neoforged.neoforge.client.model.geometry.IUnbakedGeometry; -import net.neoforged.neoforge.client.model.geometry.UnbakedGeometryHelper; -import org.jetbrains.annotations.Nullable; - -/** - * Forge reimplementation of vanilla's {@link ItemModelGenerator}, i.e. builtin/generated models with some tweaks: - * - Represented as {@link IUnbakedGeometry} so it can be baked as usual instead of being special-cased - * - Not limited to an arbitrary number of layers (5) - * - Support for per-layer render types - */ -public class ItemLayerModel implements IUnbakedGeometry { - @Nullable - private ImmutableList textures; - private final Int2ObjectMap layerData; - private final Int2ObjectMap renderTypeNames; - - private ItemLayerModel(@Nullable ImmutableList textures, Int2ObjectMap layerData, Int2ObjectMap renderTypeNames) { - this.textures = textures; - this.layerData = layerData; - this.renderTypeNames = renderTypeNames; - } - - @Override - public BakedModel bake(IGeometryBakingContext context, ModelBaker baker, Function spriteGetter, ModelState modelState, List overrides) { - if (textures == null) { - ImmutableList.Builder builder = ImmutableList.builder(); - for (int i = 0; context.hasMaterial("layer" + i); i++) { - builder.add(context.getMaterial("layer" + i)); - } - textures = builder.build(); - } - - TextureAtlasSprite particle = spriteGetter.apply( - context.hasMaterial("particle") ? context.getMaterial("particle") : textures.get(0)); - var rootTransform = context.getRootTransform(); - if (!rootTransform.isIdentity()) - modelState = UnbakedGeometryHelper.composeRootTransformIntoModelState(modelState, rootTransform); - - var normalRenderTypes = new RenderTypeGroup(RenderType.translucent(), NeoForgeRenderTypes.ITEM_UNSORTED_TRANSLUCENT.get()); - CompositeModel.Baked.Builder builder = CompositeModel.Baked.builder(context, particle, context.getTransforms()); - for (int i = 0; i < textures.size(); i++) { - TextureAtlasSprite sprite = spriteGetter.apply(textures.get(i)); - var unbaked = UnbakedGeometryHelper.createUnbakedItemElements(i, sprite, this.layerData.get(i)); - var quads = UnbakedGeometryHelper.bakeElements(unbaked, $ -> sprite, modelState); - var renderTypeName = renderTypeNames.get(i); - var renderTypes = renderTypeName != null ? context.getRenderType(renderTypeName) : null; - builder.addQuads(renderTypes != null ? renderTypes : normalRenderTypes, quads); - } - - BakedModel baked = builder.build(); - if (!overrides.isEmpty()) { - baked = new ItemModel.BakedModelWithOverrides(baked, new BakedOverrides(baker, overrides, spriteGetter)); - } - return baked; - } - - public static final class Loader implements IGeometryLoader { - public static final Loader INSTANCE = new Loader(); - - @Override - public ItemLayerModel read(JsonObject jsonObject, JsonDeserializationContext deserializationContext) { - var renderTypeNames = new Int2ObjectOpenHashMap(); - if (jsonObject.has("render_types")) { - var renderTypes = jsonObject.getAsJsonObject("render_types"); - for (Map.Entry entry : renderTypes.entrySet()) { - var renderType = ResourceLocation.parse(entry.getKey()); - for (var layer : entry.getValue().getAsJsonArray()) - if (renderTypeNames.put(layer.getAsInt(), renderType) != null) - throw new JsonParseException("Registered duplicate render type for layer " + layer); - } - } - - var emissiveLayers = new Int2ObjectArrayMap(); - if (jsonObject.has("neoforge_data")) { - JsonObject forgeData = jsonObject.get("neoforge_data").getAsJsonObject(); - readLayerData(forgeData, "layers", renderTypeNames, emissiveLayers, false); - } - return new ItemLayerModel(null, emissiveLayers, renderTypeNames); - } - - protected void readLayerData(JsonObject jsonObject, String name, Int2ObjectOpenHashMap renderTypeNames, Int2ObjectMap layerData, boolean logWarning) { - if (!jsonObject.has(name)) { - return; - } - var fullbrightLayers = jsonObject.getAsJsonObject(name); - for (var entry : fullbrightLayers.entrySet()) { - int layer = Integer.parseInt(entry.getKey()); - var data = ExtraFaceData.read(entry.getValue(), ExtraFaceData.DEFAULT); - layerData.put(layer, data); - } - } - } -} diff --git a/src/main/java/net/neoforged/neoforge/client/model/NeoForgeModelProperties.java b/src/main/java/net/neoforged/neoforge/client/model/NeoForgeModelProperties.java new file mode 100644 index 0000000000..43dd5a89c8 --- /dev/null +++ b/src/main/java/net/neoforged/neoforge/client/model/NeoForgeModelProperties.java @@ -0,0 +1,29 @@ +/* + * Copyright (c) NeoForged and contributors + * SPDX-License-Identifier: LGPL-2.1-only + */ + +package net.neoforged.neoforge.client.model; + +import com.mojang.math.Transformation; +import net.minecraft.client.renderer.block.model.BlockModel; +import net.minecraft.client.resources.model.UnbakedModel; +import net.minecraft.util.context.ContextKey; +import net.neoforged.neoforge.client.RenderTypeGroup; + +/** + * Properties that NeoForge adds for {@link BlockModel}s and {@link UnbakedModel}s. + */ +public final class NeoForgeModelProperties { + private NeoForgeModelProperties() {} + + /** + * Root transform. For block models, this can be specified under the {@code transform} JSON key. + */ + public static final ContextKey TRANSFORM = ContextKey.vanilla("transform"); + + /** + * Render type to use. For block models, this can be specified under the {@code render_type} JSON key. + */ + public static final ContextKey RENDER_TYPE = ContextKey.vanilla("render_type"); +} diff --git a/src/main/java/net/neoforged/neoforge/client/model/SeparateTransformsModel.java b/src/main/java/net/neoforged/neoforge/client/model/SeparateTransformsModel.java deleted file mode 100644 index 03e4d72b6a..0000000000 --- a/src/main/java/net/neoforged/neoforge/client/model/SeparateTransformsModel.java +++ /dev/null @@ -1,162 +0,0 @@ -/* - * Copyright (c) Forge Development LLC and contributors - * SPDX-License-Identifier: LGPL-2.1-only - */ - -package net.neoforged.neoforge.client.model; - -import com.google.common.collect.ImmutableMap; -import com.google.common.collect.Maps; -import com.google.gson.JsonDeserializationContext; -import com.google.gson.JsonObject; -import com.mojang.blaze3d.vertex.PoseStack; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.function.Function; -import net.minecraft.client.renderer.RenderType; -import net.minecraft.client.renderer.block.model.BakedOverrides; -import net.minecraft.client.renderer.block.model.BakedQuad; -import net.minecraft.client.renderer.block.model.BlockModel; -import net.minecraft.client.renderer.block.model.ItemOverride; -import net.minecraft.client.renderer.block.model.ItemTransforms; -import net.minecraft.client.renderer.texture.TextureAtlasSprite; -import net.minecraft.client.resources.model.BakedModel; -import net.minecraft.client.resources.model.ItemModel; -import net.minecraft.client.resources.model.Material; -import net.minecraft.client.resources.model.ModelBaker; -import net.minecraft.client.resources.model.ModelState; -import net.minecraft.client.resources.model.UnbakedModel; -import net.minecraft.core.Direction; -import net.minecraft.util.GsonHelper; -import net.minecraft.util.RandomSource; -import net.minecraft.world.item.ItemDisplayContext; -import net.minecraft.world.level.block.state.BlockState; -import net.neoforged.neoforge.client.ChunkRenderTypeSet; -import net.neoforged.neoforge.client.model.data.ModelData; -import net.neoforged.neoforge.client.model.geometry.IGeometryBakingContext; -import net.neoforged.neoforge.client.model.geometry.IGeometryLoader; -import net.neoforged.neoforge.client.model.geometry.IUnbakedGeometry; -import org.jetbrains.annotations.Nullable; - -/** - * A model composed of multiple sub-models which are picked based on the {@link ItemDisplayContext} being used. - */ -public class SeparateTransformsModel implements IUnbakedGeometry { - private final BlockModel baseModel; - private final ImmutableMap perspectives; - - public SeparateTransformsModel(BlockModel baseModel, ImmutableMap perspectives) { - this.baseModel = baseModel; - this.perspectives = perspectives; - } - - @Override - public BakedModel bake(IGeometryBakingContext context, ModelBaker baker, Function spriteGetter, ModelState modelState, List overrides) { - BakedModel baked = new Baked( - context.useAmbientOcclusion(), context.isGui3d(), context.useBlockLight(), - spriteGetter.apply(context.getMaterial("particle")), - baseModel.bake(baker, spriteGetter, modelState), - ImmutableMap.copyOf(Maps.transformValues(perspectives, value -> value.bake(baker, spriteGetter, modelState)))); - if (!overrides.isEmpty()) { - baked = new ItemModel.BakedModelWithOverrides(baked, new BakedOverrides(baker, overrides, spriteGetter)); - } - return baked; - } - - @Override - public void resolveDependencies(UnbakedModel.Resolver modelGetter, IGeometryBakingContext context) { - baseModel.resolveDependencies(modelGetter); - perspectives.values().forEach(model -> model.resolveDependencies(modelGetter)); - } - - public static class Baked implements IDynamicBakedModel { - private final boolean isAmbientOcclusion; - private final boolean isGui3d; - private final boolean isSideLit; - private final TextureAtlasSprite particle; - private final BakedModel baseModel; - private final ImmutableMap perspectives; - - public Baked(boolean isAmbientOcclusion, boolean isGui3d, boolean isSideLit, TextureAtlasSprite particle, BakedModel baseModel, ImmutableMap perspectives) { - this.isAmbientOcclusion = isAmbientOcclusion; - this.isGui3d = isGui3d; - this.isSideLit = isSideLit; - this.particle = particle; - this.baseModel = baseModel; - this.perspectives = perspectives; - } - - @Override - public List getQuads(@Nullable BlockState state, @Nullable Direction side, RandomSource rand, ModelData data, @Nullable RenderType renderType) { - return baseModel.getQuads(state, side, rand, data, renderType); - } - - @Override - public boolean useAmbientOcclusion() { - return isAmbientOcclusion; - } - - @Override - public boolean isGui3d() { - return isGui3d; - } - - @Override - public boolean usesBlockLight() { - return isSideLit; - } - - @Override - public boolean isCustomRenderer() { - return false; - } - - @Override - public TextureAtlasSprite getParticleIcon() { - return particle; - } - - @Override - public ItemTransforms getTransforms() { - return ItemTransforms.NO_TRANSFORMS; - } - - @Override - public BakedModel applyTransform(ItemDisplayContext cameraTransformType, PoseStack poseStack, boolean applyLeftHandTransform) { - if (perspectives.containsKey(cameraTransformType)) { - BakedModel p = perspectives.get(cameraTransformType); - return p.applyTransform(cameraTransformType, poseStack, applyLeftHandTransform); - } - return baseModel.applyTransform(cameraTransformType, poseStack, applyLeftHandTransform); - } - - @Override - public ChunkRenderTypeSet getRenderTypes(BlockState state, RandomSource rand, ModelData data) { - return baseModel.getRenderTypes(state, rand, data); - } - } - - public static final class Loader implements IGeometryLoader { - public static final Loader INSTANCE = new Loader(); - - private Loader() {} - - @Override - public SeparateTransformsModel read(JsonObject jsonObject, JsonDeserializationContext deserializationContext) { - BlockModel baseModel = deserializationContext.deserialize(GsonHelper.getAsJsonObject(jsonObject, "base"), BlockModel.class); - - JsonObject perspectiveData = GsonHelper.getAsJsonObject(jsonObject, "perspectives"); - - Map perspectives = new HashMap<>(); - for (ItemDisplayContext transform : ItemDisplayContext.values()) { - if (perspectiveData.has(transform.getSerializedName())) { - BlockModel perspectiveModel = deserializationContext.deserialize(GsonHelper.getAsJsonObject(perspectiveData, transform.getSerializedName()), BlockModel.class); - perspectives.put(transform, perspectiveModel); - } - } - - return new SeparateTransformsModel(baseModel, ImmutableMap.copyOf(perspectives)); - } - } -} diff --git a/src/main/java/net/neoforged/neoforge/client/model/CompositeModel.java b/src/main/java/net/neoforged/neoforge/client/model/UnbakedCompositeModel.java similarity index 74% rename from src/main/java/net/neoforged/neoforge/client/model/CompositeModel.java rename to src/main/java/net/neoforged/neoforge/client/model/UnbakedCompositeModel.java index 93fde9b743..b79dfa0c86 100644 --- a/src/main/java/net/neoforged/neoforge/client/model/CompositeModel.java +++ b/src/main/java/net/neoforged/neoforge/client/model/UnbakedCompositeModel.java @@ -11,30 +11,29 @@ import com.google.gson.JsonElement; import com.google.gson.JsonObject; import com.google.gson.JsonParseException; +import com.mojang.math.Transformation; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; +import java.util.HashMap; import java.util.IdentityHashMap; import java.util.List; import java.util.Map; import java.util.Objects; -import java.util.Set; -import java.util.function.Function; +import net.minecraft.client.data.models.model.TextureSlot; import net.minecraft.client.renderer.RenderType; -import net.minecraft.client.renderer.block.model.BakedOverrides; import net.minecraft.client.renderer.block.model.BakedQuad; import net.minecraft.client.renderer.block.model.BlockModel; -import net.minecraft.client.renderer.block.model.ItemOverride; import net.minecraft.client.renderer.block.model.ItemTransforms; +import net.minecraft.client.renderer.block.model.TextureSlots; import net.minecraft.client.renderer.texture.TextureAtlasSprite; import net.minecraft.client.resources.model.BakedModel; -import net.minecraft.client.resources.model.ItemModel; -import net.minecraft.client.resources.model.Material; import net.minecraft.client.resources.model.ModelBaker; import net.minecraft.client.resources.model.ModelState; import net.minecraft.client.resources.model.UnbakedModel; import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; +import net.minecraft.resources.ResourceLocation; import net.minecraft.util.RandomSource; import net.minecraft.world.item.ItemStack; import net.minecraft.world.level.BlockAndTintGetter; @@ -43,44 +42,44 @@ import net.neoforged.neoforge.client.RenderTypeGroup; import net.neoforged.neoforge.client.model.data.ModelData; import net.neoforged.neoforge.client.model.data.ModelProperty; -import net.neoforged.neoforge.client.model.geometry.IGeometryBakingContext; -import net.neoforged.neoforge.client.model.geometry.IGeometryLoader; -import net.neoforged.neoforge.client.model.geometry.IUnbakedGeometry; -import net.neoforged.neoforge.client.model.geometry.UnbakedGeometryHelper; import net.neoforged.neoforge.common.util.ConcatenatedListView; import org.jetbrains.annotations.Nullable; /** * A model composed of several named children. - *

    - * These respect component visibility as specified in {@link IGeometryBakingContext} and can additionally be provided - * with an item-specific render ordering, for multi-pass arrangements. */ -public class CompositeModel implements IUnbakedGeometry { - private final ImmutableMap children; +public class UnbakedCompositeModel implements UnbakedModel { + private final ImmutableMap children; private final ImmutableList itemPasses; + private final Transformation rootTransform; + private final Map partVisibility; - public CompositeModel(ImmutableMap children, ImmutableList itemPasses) { + public UnbakedCompositeModel(ImmutableMap children, ImmutableList itemPasses, Transformation rootTransform, Map partVisibility) { this.children = children; this.itemPasses = itemPasses; + this.rootTransform = rootTransform; + this.partVisibility = partVisibility; } @Override - public BakedModel bake(IGeometryBakingContext context, ModelBaker baker, Function spriteGetter, ModelState modelState, List overrides) { - Material particleLocation = context.getMaterial("particle"); - TextureAtlasSprite particle = spriteGetter.apply(particleLocation); + public BakedModel bake(TextureSlots slots, + ModelBaker baker, + ModelState state, + boolean useAmbientOcclusion, + boolean usesBlockLight, + ItemTransforms transforms) { + TextureAtlasSprite particle = baker.findSprite(slots, TextureSlot.PARTICLE.getId()); - var rootTransform = context.getRootTransform(); if (!rootTransform.isIdentity()) - modelState = UnbakedGeometryHelper.composeRootTransformIntoModelState(modelState, rootTransform); + state = UnbakedElementsHelper.composeRootTransformIntoModelState(state, rootTransform); var bakedPartsBuilder = ImmutableMap.builder(); for (var entry : children.entrySet()) { var name = entry.getKey(); - if (!context.isComponentVisible(name, true)) + if (!partVisibility.getOrDefault(name, true)) continue; var model = entry.getValue(); - bakedPartsBuilder.put(name, model.bake(baker, spriteGetter, modelState)); + bakedPartsBuilder.put(name, baker.bake(model, state)); } var bakedParts = bakedPartsBuilder.build(); @@ -92,36 +91,27 @@ public BakedModel bake(IGeometryBakingContext context, ModelBaker baker, Functio itemPassesBuilder.add(model); } - BakedModel baked = new Baked(context.isGui3d(), context.useBlockLight(), context.useAmbientOcclusion(), particle, context.getTransforms(), bakedParts, itemPassesBuilder.build()); - if (!overrides.isEmpty()) { - baked = new ItemModel.BakedModelWithOverrides(baked, new BakedOverrides(baker, overrides, spriteGetter)); - } - return baked; - } - - @Override - public void resolveDependencies(UnbakedModel.Resolver modelGetter, IGeometryBakingContext context) { - children.values().forEach(child -> child.resolveDependencies(modelGetter)); + return new Baked(usesBlockLight, useAmbientOcclusion, particle, transforms, bakedParts, itemPassesBuilder.build()); } @Override - public Set getConfigurableComponentNames() { - return children.keySet(); + public void resolveDependencies(Resolver resolver) { + for (ResourceLocation path : children.values()) { + resolver.resolve(path); + } } public static class Baked implements IDynamicBakedModel { private final boolean isAmbientOcclusion; - private final boolean isGui3d; private final boolean isSideLit; private final TextureAtlasSprite particle; private final ItemTransforms transforms; private final ImmutableMap children; private final ImmutableList itemPasses; - public Baked(boolean isGui3d, boolean isSideLit, boolean isAmbientOcclusion, TextureAtlasSprite particle, ItemTransforms transforms, ImmutableMap children, ImmutableList itemPasses) { + public Baked(boolean isSideLit, boolean isAmbientOcclusion, TextureAtlasSprite particle, ItemTransforms transforms, ImmutableMap children, ImmutableList itemPasses) { this.children = children; this.isAmbientOcclusion = isAmbientOcclusion; - this.isGui3d = isGui3d; this.isSideLit = isSideLit; this.particle = particle; this.transforms = transforms; @@ -133,7 +123,7 @@ public List getQuads(@Nullable BlockState state, @Nullable Direction List> quadLists = new ArrayList<>(); for (Map.Entry entry : children.entrySet()) { if (renderType == null || (state != null && entry.getValue().getRenderTypes(state, rand, data).contains(renderType))) { - quadLists.add(entry.getValue().getQuads(state, side, rand, CompositeModel.Data.resolve(data, entry.getKey()), renderType)); + quadLists.add(entry.getValue().getQuads(state, side, rand, Data.resolve(data, entry.getKey()), renderType)); } } return ConcatenatedListView.of(quadLists); @@ -154,7 +144,7 @@ public boolean useAmbientOcclusion() { @Override public boolean isGui3d() { - return isGui3d; + return true; } @Override @@ -162,11 +152,6 @@ public boolean usesBlockLight() { return isSideLit; } - @Override - public boolean isCustomRenderer() { - return false; - } - @Override public TextureAtlasSprite getParticleIcon() { return particle; @@ -181,7 +166,7 @@ public ItemTransforms getTransforms() { public ChunkRenderTypeSet getRenderTypes(BlockState state, RandomSource rand, ModelData data) { var sets = new ArrayList(); for (Map.Entry entry : children.entrySet()) - sets.add(entry.getValue().getRenderTypes(state, rand, CompositeModel.Data.resolve(data, entry.getKey()))); + sets.add(entry.getValue().getRenderTypes(state, rand, Data.resolve(data, entry.getKey()))); return ChunkRenderTypeSet.union(sets); } @@ -195,10 +180,6 @@ public BakedModel getPart(String name) { return children.get(name); } - public static Builder builder(IGeometryBakingContext owner, TextureAtlasSprite particle, ItemTransforms cameraTransforms) { - return builder(owner.useAmbientOcclusion(), owner.isGui3d(), owner.useBlockLight(), particle, cameraTransforms); - } - public static Builder builder(boolean isAmbientOcclusion, boolean isGui3d, boolean isSideLit, TextureAtlasSprite particle, ItemTransforms cameraTransforms) { return new Builder(isAmbientOcclusion, isGui3d, isSideLit, particle, cameraTransforms); } @@ -270,7 +251,7 @@ public BakedModel build() { childrenBuilder.put("model_" + (i++), model); itemPassesBuilder.add(model); } - return new Baked(isGui3d, isSideLit, isAmbientOcclusion, particle, transforms, childrenBuilder.build(), itemPassesBuilder.build()); + return new Baked(isSideLit, isAmbientOcclusion, particle, transforms, childrenBuilder.build(), itemPassesBuilder.build()); } } } @@ -325,16 +306,16 @@ public Data build() { } } - public static final class Loader implements IGeometryLoader { + public static final class Loader implements UnbakedModelLoader { public static final Loader INSTANCE = new Loader(); private Loader() {} @Override - public CompositeModel read(JsonObject jsonObject, JsonDeserializationContext deserializationContext) { + public UnbakedCompositeModel read(JsonObject jsonObject, JsonDeserializationContext jsonDeserializationContext) throws JsonParseException { List itemPasses = new ArrayList<>(); - ImmutableMap.Builder childrenBuilder = ImmutableMap.builder(); - readChildren(jsonObject, "children", deserializationContext, childrenBuilder, itemPasses); + ImmutableMap.Builder childrenBuilder = ImmutableMap.builder(); + readChildren(jsonObject, "children", childrenBuilder, itemPasses); var children = childrenBuilder.build(); if (children.isEmpty()) @@ -350,15 +331,34 @@ public CompositeModel read(JsonObject jsonObject, JsonDeserializationContext des } } - return new CompositeModel(children, ImmutableList.copyOf(itemPasses)); + final Map partVisibility; + if (jsonObject.has("visibility")) { + partVisibility = new HashMap<>(); + JsonObject visibility = jsonObject.getAsJsonObject("visibility"); + for (Map.Entry part : visibility.entrySet()) { + partVisibility.put(part.getKey(), part.getValue().getAsBoolean()); + } + } else { + partVisibility = Collections.emptyMap(); + } + + final Transformation transformation; + if (jsonObject.has("transform")) { + transformation = BlockModel.GSON.fromJson(jsonObject.get("transform"), Transformation.class); + } else { + transformation = Transformation.identity(); + } + + return new UnbakedCompositeModel(children, ImmutableList.copyOf(itemPasses), transformation, partVisibility); } - private void readChildren(JsonObject jsonObject, String name, JsonDeserializationContext deserializationContext, ImmutableMap.Builder children, List itemPasses) { + private void readChildren(JsonObject jsonObject, String name, ImmutableMap.Builder children, List itemPasses) { if (!jsonObject.has(name)) return; var childrenJsonObject = jsonObject.getAsJsonObject(name); for (Map.Entry entry : childrenJsonObject.entrySet()) { - children.put(entry.getKey(), deserializationContext.deserialize(entry.getValue(), BlockModel.class)); + ResourceLocation location = ResourceLocation.parse(entry.getValue().getAsString()); + children.put(entry.getKey(), location); itemPasses.add(entry.getKey()); // We can do this because GSON preserves ordering during deserialization } } diff --git a/src/main/java/net/neoforged/neoforge/client/model/geometry/UnbakedGeometryHelper.java b/src/main/java/net/neoforged/neoforge/client/model/UnbakedElementsHelper.java similarity index 64% rename from src/main/java/net/neoforged/neoforge/client/model/geometry/UnbakedGeometryHelper.java rename to src/main/java/net/neoforged/neoforge/client/model/UnbakedElementsHelper.java index e790cef41c..5c5e67c538 100644 --- a/src/main/java/net/neoforged/neoforge/client/model/geometry/UnbakedGeometryHelper.java +++ b/src/main/java/net/neoforged/neoforge/client/model/UnbakedElementsHelper.java @@ -1,86 +1,38 @@ /* - * Copyright (c) Forge Development LLC and contributors + * Copyright (c) NeoForged and contributors * SPDX-License-Identifier: LGPL-2.1-only */ -package net.neoforged.neoforge.client.model.geometry; +package net.neoforged.neoforge.client.model; import com.mojang.math.Transformation; import java.util.ArrayList; import java.util.BitSet; -import java.util.HashMap; import java.util.List; +import java.util.Map; import java.util.function.Function; -import java.util.regex.Matcher; -import java.util.regex.Pattern; -import net.minecraft.Util; import net.minecraft.client.renderer.block.model.BakedQuad; import net.minecraft.client.renderer.block.model.BlockElement; import net.minecraft.client.renderer.block.model.BlockElementFace; import net.minecraft.client.renderer.block.model.BlockFaceUV; -import net.minecraft.client.renderer.block.model.BlockModel; -import net.minecraft.client.renderer.block.model.FaceBakery; import net.minecraft.client.renderer.block.model.ItemModelGenerator; -import net.minecraft.client.renderer.texture.MissingTextureAtlasSprite; import net.minecraft.client.renderer.texture.SpriteContents; import net.minecraft.client.renderer.texture.TextureAtlas; import net.minecraft.client.renderer.texture.TextureAtlasSprite; import net.minecraft.client.resources.model.Material; import net.minecraft.client.resources.model.ModelState; +import net.minecraft.client.resources.model.SimpleBakedModel; import net.minecraft.core.Direction; import net.minecraft.resources.ResourceLocation; +import net.minecraft.util.Mth; import net.neoforged.neoforge.client.ClientHooks; -import net.neoforged.neoforge.client.model.ExtraFaceData; -import net.neoforged.neoforge.client.model.IModelBuilder; -import net.neoforged.neoforge.client.model.SimpleModelState; import org.jetbrains.annotations.Nullable; import org.joml.Vector3f; -/** - * Helper for dealing with unbaked models and geometries. - */ -public class UnbakedGeometryHelper { - private static final ItemModelGenerator ITEM_MODEL_GENERATOR = new ItemModelGenerator(); - private static final FaceBakery FACE_BAKERY = new FaceBakery(); - - /** - * Explanation: - * This takes anything that looks like a valid resourcepack texture location, and tries to extract a resourcelocation out of it. - * 1. it will ignore anything up to and including an /assets/ folder, - * 2. it will take the next path component as a namespace, - * 3. it will match but skip the /textures/ part of the path, - * 4. it will take the rest of the path up to but excluding the .png extension as the resource path - * It's a best-effort situation, to allow model files exported by modelling software to be used without post-processing. - * Example: - * C:\Something\Or Other\src\main\resources\assets\mymodid\textures\item\my_thing.png - * ........................................--------_______----------_____________---- - * - * Result after replacing '\' to '/': mymodid:item/my_thing - */ - private static final Pattern FILESYSTEM_PATH_TO_RESLOC = Pattern.compile("(?:.*[\\\\/]assets[\\\\/](?[a-z_-]+)[\\\\/]textures[\\\\/])?(?[a-z_\\\\/-]+)\\.png"); - - /** - * Resolves a material that may have been defined with a filesystem path instead of a proper {@link ResourceLocation}. - *

    - * The target atlas will always be {@link TextureAtlas#LOCATION_BLOCKS}. - */ - public static Material resolveDirtyMaterial(@Nullable String tex, IGeometryBakingContext owner) { - if (tex == null) - return new Material(TextureAtlas.LOCATION_BLOCKS, MissingTextureAtlasSprite.getLocation()); - if (tex.startsWith("#")) - return owner.getMaterial(tex); - - // Attempt to convert a common (windows/linux/mac) filesystem path to a ResourceLocation. - // This makes no promises, if it doesn't work, too bad, fix your mtl file. - Matcher match = FILESYSTEM_PATH_TO_RESLOC.matcher(tex); - if (match.matches()) { - String namespace = match.group("namespace"); - String path = match.group("path").replace("\\", "/"); - tex = namespace != null ? namespace + ":" + path : path; - } +public final class UnbakedElementsHelper { + private UnbakedElementsHelper() {} - return new Material(TextureAtlas.LOCATION_BLOCKS, ResourceLocation.parse(tex)); - } + private static final ItemModelGenerator ITEM_MODEL_GENERATOR = new ItemModelGenerator(); /** * @see #createUnbakedItemElements(int, TextureAtlasSprite, ExtraFaceData) @@ -118,12 +70,13 @@ public static List createUnbakedItemMaskElements(int layerIndex, T * The {@link Direction#NORTH} and {@link Direction#SOUTH} faces take up only the pixels the texture uses. */ public static List createUnbakedItemMaskElements(int layerIndex, TextureAtlasSprite sprite, @Nullable ExtraFaceData faceData) { - var elements = createUnbakedItemElements(layerIndex, sprite, faceData); - elements.remove(0); // Remove north and south faces + List elements = createUnbakedItemElements(layerIndex, sprite, faceData); + elements.removeFirst(); // Remove north and south faces + float expand = -sprite.uvShrinkRatio(); SpriteContents spriteContents = sprite.contents(); int width = spriteContents.width(), height = spriteContents.height(); - var bits = new BitSet(width * height); + BitSet bits = new BitSet(width * height); // For every frame in the texture, mark all the opaque pixels (this is what vanilla does too) spriteContents.getUniqueFrames().forEach(frame -> { @@ -137,9 +90,8 @@ public static List createUnbakedItemMaskElements(int layerIndex, T for (int y = 0; y < height; y++) { int xStart = -1; for (int x = 0; x < width; x++) { - var opaque = bits.get(x + y * width); - if (opaque == (xStart == -1)) // (opaque && -1) || (!opaque && !-1) - { + boolean opaque = bits.get(x + y * width); + if (opaque == (xStart == -1)) { // (opaque && -1) || (!opaque && !-1) if (xStart == -1) { // We have found the start of a new segment, continue xStart = x; @@ -160,16 +112,31 @@ public static List createUnbakedItemMaskElements(int layerIndex, T bits.clear(i + j * width); // Create element - elements.add(new BlockElement( + BlockElement element = new BlockElement( new Vector3f(16 * xStart / (float) width, 16 - 16 * yEnd / (float) height, 7.5F), new Vector3f(16 * x / (float) width, 16 - 16 * y / (float) height, 8.5F), - Util.make(new HashMap<>(), map -> { - for (Direction direction : Direction.values()) - map.put(direction, new BlockElementFace(null, layerIndex, "layer" + layerIndex, new BlockFaceUV(null, 0))); - }), + Map.of( + Direction.NORTH, new BlockElementFace(null, layerIndex, "layer" + layerIndex, new BlockFaceUV(null, 0)), + Direction.SOUTH, new BlockElementFace(null, layerIndex, "layer" + layerIndex, new BlockFaceUV(null, 0))), null, true, - 0)); + 0); + // Expand coordinates to match the shrunk UVs of the front/back face on a standard generated model (done after to not affect the auto-generated UVs) + element.from.x = Mth.clamp(Mth.lerp(expand, element.from.x, 8F), 0F, 16F); + element.from.y = Mth.clamp(Mth.lerp(expand, element.from.y, 8F), 0F, 16F); + element.to.x = Mth.clamp(Mth.lerp(expand, element.to.x, 8F), 0F, 16F); + element.to.y = Mth.clamp(Mth.lerp(expand, element.to.y, 8F), 0F, 16F); + // Counteract sprite expansion to ensure pixel alignment + element.faces.forEach((dir, face) -> { + float[] uv = face.uv().uvs; + float centerU = (uv[0] + uv[0] + uv[2] + uv[2]) / 4.0F; + uv[0] = Mth.clamp(Mth.lerp(expand, uv[0], centerU), 0F, 16F); + uv[2] = Mth.clamp(Mth.lerp(expand, uv[2], centerU), 0F, 16F); + float centerV = (uv[1] + uv[1] + uv[3] + uv[3]) / 4.0F; + uv[1] = Mth.clamp(Mth.lerp(expand, uv[1], centerV), 0F, 16F); + uv[3] = Mth.clamp(Mth.lerp(expand, uv[3], centerV), 0F, 16F); + }); + elements.add(element); // Reset xStart xStart = -1; @@ -186,7 +153,7 @@ public static void bakeElements(IModelBuilder builder, List ele for (BlockElement element : elements) { element.faces.forEach((side, face) -> { var sprite = spriteGetter.apply(new Material(TextureAtlas.LOCATION_BLOCKS, ResourceLocation.parse(face.texture()))); - BakedQuad quad = BlockModel.bakeFace(element, face, sprite, side, modelState); + BakedQuad quad = SimpleBakedModel.bakeFace(element, face, sprite, side, modelState); if (face.cullForDirection() == null) builder.addUnculledFace(quad); else diff --git a/src/main/java/net/neoforged/neoforge/client/model/UnbakedModelLoader.java b/src/main/java/net/neoforged/neoforge/client/model/UnbakedModelLoader.java new file mode 100644 index 0000000000..0f518f18f8 --- /dev/null +++ b/src/main/java/net/neoforged/neoforge/client/model/UnbakedModelLoader.java @@ -0,0 +1,36 @@ +/* + * Copyright (c) NeoForged and contributors + * SPDX-License-Identifier: LGPL-2.1-only + */ + +package net.neoforged.neoforge.client.model; + +import com.google.gson.JsonDeserializationContext; +import com.google.gson.JsonObject; +import com.google.gson.JsonParseException; +import net.minecraft.client.renderer.block.model.BlockModel; +import net.minecraft.client.resources.model.UnbakedModel; +import net.minecraft.server.packs.resources.ResourceManagerReloadListener; +import net.neoforged.neoforge.client.event.ModelEvent; +import net.neoforged.neoforge.client.event.RegisterClientReloadListenersEvent; + +/** + * A loader for custom {@linkplain UnbakedModel unbaked models}. + *

    + * If you do any caching, you should implement {@link ResourceManagerReloadListener} and register it with + * {@link RegisterClientReloadListenersEvent}. + * + * @see ModelEvent.RegisterLoaders + * @see RegisterClientReloadListenersEvent + */ +public interface UnbakedModelLoader { + /** + * Reads an unbaked model from the passed JSON object. + * + *

    The {@link JsonDeserializationContext} argument can be used to deserialize types that the system already understands. + * For example, {@code deserializationContext.deserialize(, Transformation.class)} to parse a transformation, + * or {@code deserializationContext.deserialize(, UnbakedModel.class)} to parse a nested model. + * The set of supported types can be found in the declaration of {@link BlockModel#GSON}. + */ + T read(JsonObject jsonObject, JsonDeserializationContext deserializationContext) throws JsonParseException; +} diff --git a/src/main/java/net/neoforged/neoforge/client/model/UnbakedModelParser.java b/src/main/java/net/neoforged/neoforge/client/model/UnbakedModelParser.java new file mode 100644 index 0000000000..37e26215c7 --- /dev/null +++ b/src/main/java/net/neoforged/neoforge/client/model/UnbakedModelParser.java @@ -0,0 +1,80 @@ +/* + * Copyright (c) NeoForged and contributors + * SPDX-License-Identifier: LGPL-2.1-only + */ + +package net.neoforged.neoforge.client.model; + +import com.google.common.collect.ImmutableMap; +import com.google.gson.JsonDeserializationContext; +import com.google.gson.JsonDeserializer; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonParseException; +import java.io.Reader; +import java.lang.reflect.Type; +import java.util.HashMap; +import java.util.stream.Collectors; +import net.minecraft.client.renderer.block.model.BlockModel; +import net.minecraft.client.resources.model.UnbakedModel; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.util.GsonHelper; +import net.neoforged.fml.ModLoader; +import net.neoforged.neoforge.client.event.ModelEvent; +import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.Nullable; + +public class UnbakedModelParser { + private static ImmutableMap> LOADERS; + + @Nullable + public static UnbakedModelLoader get(ResourceLocation name) { + return LOADERS.get(name); + } + + @ApiStatus.Internal + public static void init() { + var loaders = new HashMap>(); + ModLoader.postEventWrapContainerInModOrder(new ModelEvent.RegisterLoaders(loaders)); + LOADERS = ImmutableMap.copyOf(loaders); + } + + public static UnbakedModel parse(Reader reader) { + return GsonHelper.fromJson(BlockModel.GSON, reader, UnbakedModel.class); + } + + @ApiStatus.Internal + public static final class Deserializer implements JsonDeserializer { + @Override + public UnbakedModel deserialize(JsonElement jsonElement, Type type, JsonDeserializationContext jsonDeserializationContext) throws JsonParseException { + if (!jsonElement.isJsonObject()) { + throw new JsonParseException("Expected object, got " + jsonElement); + } else { + JsonObject jsonObject = jsonElement.getAsJsonObject(); + + if (jsonObject.has("loader")) { + final ResourceLocation loader; + final boolean optional; + if (jsonObject.get("loader").isJsonObject()) { + JsonObject loaderObject = jsonObject.getAsJsonObject("loader"); + loader = ResourceLocation.parse(GsonHelper.getAsString(loaderObject, "id")); + optional = GsonHelper.getAsBoolean(loaderObject, "optional", false); + } else { + loader = ResourceLocation.parse(GsonHelper.getAsString(jsonObject, "loader")); + optional = false; + } + + var loaderInstance = UnbakedModelParser.get(loader); + if (loaderInstance != null) { + return loaderInstance.read(jsonObject, jsonDeserializationContext); + } + if (!optional) { + throw new JsonParseException("Unknown loader: " + loader + " (did you forget to register it?) Available loaders: " + LOADERS.keySet().stream().map(ResourceLocation::toString).collect(Collectors.joining(", "))); + } + } + + return jsonDeserializationContext.deserialize(jsonObject, BlockModel.class); + } + } + } +} diff --git a/src/main/java/net/neoforged/neoforge/client/model/generators/CustomLoaderBuilder.java b/src/main/java/net/neoforged/neoforge/client/model/generators/CustomLoaderBuilder.java index a8717c551a..7e088ea4ef 100644 --- a/src/main/java/net/neoforged/neoforge/client/model/generators/CustomLoaderBuilder.java +++ b/src/main/java/net/neoforged/neoforge/client/model/generators/CustomLoaderBuilder.java @@ -10,7 +10,6 @@ import java.util.LinkedHashMap; import java.util.Map; import net.minecraft.resources.ResourceLocation; -import net.neoforged.neoforge.client.model.geometry.IGeometryLoader; import net.neoforged.neoforge.common.data.ExistingFileHelper; public abstract class CustomLoaderBuilder> { diff --git a/src/main/java/net/neoforged/neoforge/client/model/generators/ModelBuilder.java b/src/main/java/net/neoforged/neoforge/client/model/generators/ModelBuilder.java index 9afe777b8a..90d619fd9a 100644 --- a/src/main/java/net/neoforged/neoforge/client/model/generators/ModelBuilder.java +++ b/src/main/java/net/neoforged/neoforge/client/model/generators/ModelBuilder.java @@ -25,9 +25,9 @@ import net.minecraft.client.renderer.block.model.BlockElementFace; import net.minecraft.client.renderer.block.model.BlockElementRotation; import net.minecraft.client.renderer.block.model.BlockFaceUV; -import net.minecraft.client.renderer.block.model.BlockModel.GuiLight; import net.minecraft.client.renderer.block.model.ItemTransform; import net.minecraft.client.renderer.texture.MissingTextureAtlasSprite; +import net.minecraft.client.resources.model.UnbakedModel; import net.minecraft.core.Direction; import net.minecraft.resources.ResourceLocation; import net.minecraft.util.Mth; @@ -61,7 +61,8 @@ public class ModelBuilder> extends ModelFile { protected String renderType = null; protected boolean ambientOcclusion = true; - protected GuiLight guiLight = null; + + protected UnbakedModel.GuiLight guiLight = null; protected final List elements = new ArrayList<>(); @@ -185,7 +186,7 @@ public T ao(boolean ao) { return self(); } - public T guiLight(GuiLight light) { + public T guiLight(UnbakedModel.GuiLight light) { this.guiLight = light; return self(); } diff --git a/src/main/java/net/neoforged/neoforge/client/model/generators/ModelProvider.java b/src/main/java/net/neoforged/neoforge/client/model/generators/ModelProvider.java index e39d3e8e30..f52db6e3c8 100644 --- a/src/main/java/net/neoforged/neoforge/client/model/generators/ModelProvider.java +++ b/src/main/java/net/neoforged/neoforge/client/model/generators/ModelProvider.java @@ -371,6 +371,10 @@ public T leaves(String name, ResourceLocation texture) { return singleTexture(name, BLOCK_FOLDER + "/leaves", "all", texture); } + public T flowerPotCross(String name, ResourceLocation plant) { + return singleTexture(name, BLOCK_FOLDER + "/flower_pot_cross", "plant", plant); + } + /** * {@return a model builder that's not directly saved to disk. Meant for use in custom model loaders.} */ diff --git a/src/main/java/net/neoforged/neoforge/client/model/geometry/BlockGeometryBakingContext.java b/src/main/java/net/neoforged/neoforge/client/model/geometry/BlockGeometryBakingContext.java deleted file mode 100644 index 02c53783ae..0000000000 --- a/src/main/java/net/neoforged/neoforge/client/model/geometry/BlockGeometryBakingContext.java +++ /dev/null @@ -1,161 +0,0 @@ -/* - * Copyright (c) Forge Development LLC and contributors - * SPDX-License-Identifier: LGPL-2.1-only - */ - -package net.neoforged.neoforge.client.model.geometry; - -import com.mojang.math.Transformation; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.function.Function; -import net.minecraft.client.renderer.block.model.BlockModel; -import net.minecraft.client.renderer.block.model.ItemOverride; -import net.minecraft.client.renderer.block.model.ItemTransforms; -import net.minecraft.client.renderer.texture.TextureAtlasSprite; -import net.minecraft.client.resources.model.BakedModel; -import net.minecraft.client.resources.model.Material; -import net.minecraft.client.resources.model.ModelBaker; -import net.minecraft.client.resources.model.ModelState; -import net.minecraft.resources.ResourceLocation; -import org.jetbrains.annotations.ApiStatus; -import org.jetbrains.annotations.Nullable; - -/** - * A {@linkplain IGeometryBakingContext geometry baking context} that is bound to a {@link BlockModel}. - *

    - * Users should not be instantiating this themselves. - */ -public class BlockGeometryBakingContext implements IGeometryBakingContext { - public final BlockModel owner; - public final VisibilityData visibilityData = new VisibilityData(); - @Nullable - private IUnbakedGeometry customGeometry; - @Nullable - private Transformation rootTransform; - @Nullable - private ResourceLocation renderTypeHint; - private boolean gui3d = true; - - @ApiStatus.Internal - public BlockGeometryBakingContext(BlockModel owner) { - this.owner = owner; - } - - @Override - public String getModelName() { - return owner.name; - } - - public boolean hasCustomGeometry() { - return getCustomGeometry() != null; - } - - @Nullable - public IUnbakedGeometry getCustomGeometry() { - return owner.parent != null && customGeometry == null ? owner.parent.customData.getCustomGeometry() : customGeometry; - } - - public void setCustomGeometry(IUnbakedGeometry geometry) { - this.customGeometry = geometry; - } - - @Override - public boolean isComponentVisible(String part, boolean fallback) { - return owner.parent != null && !visibilityData.hasCustomVisibility(part) ? owner.parent.customData.isComponentVisible(part, fallback) : visibilityData.isVisible(part, fallback); - } - - @Override - public boolean hasMaterial(String name) { - return owner.hasTexture(name); - } - - @Override - public Material getMaterial(String name) { - return owner.getMaterial(name); - } - - @Override - public boolean isGui3d() { - return gui3d; - } - - @Override - public boolean useBlockLight() { - return owner.getGuiLight().lightLikeBlock(); - } - - @Override - public boolean useAmbientOcclusion() { - return owner.hasAmbientOcclusion(); - } - - @Override - public ItemTransforms getTransforms() { - return owner.getTransforms(); - } - - @Override - public Transformation getRootTransform() { - if (rootTransform != null) - return rootTransform; - return owner.parent != null ? owner.parent.customData.getRootTransform() : Transformation.identity(); - } - - public void setRootTransform(Transformation rootTransform) { - this.rootTransform = rootTransform; - } - - @Nullable - @Override - public ResourceLocation getRenderTypeHint() { - if (renderTypeHint != null) - return renderTypeHint; - return owner.parent != null ? owner.parent.customData.getRenderTypeHint() : null; - } - - public void setRenderTypeHint(ResourceLocation renderTypeHint) { - this.renderTypeHint = renderTypeHint; - } - - public void setGui3d(boolean gui3d) { - this.gui3d = gui3d; - } - - public void copyFrom(BlockGeometryBakingContext other) { - this.customGeometry = other.customGeometry; - this.rootTransform = other.rootTransform; - this.visibilityData.copyFrom(other.visibilityData); - this.renderTypeHint = other.renderTypeHint; - this.gui3d = other.gui3d; - } - - public BakedModel bake(ModelBaker baker, Function bakedTextureGetter, ModelState modelTransform, List overrides) { - IUnbakedGeometry geometry = getCustomGeometry(); - if (geometry == null) - throw new IllegalStateException("Can not use custom baking without custom geometry"); - return geometry.bake(this, baker, bakedTextureGetter, modelTransform, overrides); - } - - public static class VisibilityData { - private final Map data = new HashMap<>(); - - public boolean hasCustomVisibility(String part) { - return data.containsKey(part); - } - - public boolean isVisible(String part, boolean fallback) { - return data.getOrDefault(part, fallback); - } - - public void setVisibilityState(String partName, boolean type) { - data.put(partName, type); - } - - public void copyFrom(VisibilityData visibilityData) { - data.clear(); - data.putAll(visibilityData.data); - } - } -} diff --git a/src/main/java/net/neoforged/neoforge/client/model/geometry/GeometryLoaderManager.java b/src/main/java/net/neoforged/neoforge/client/model/geometry/GeometryLoaderManager.java deleted file mode 100644 index 1382c821c1..0000000000 --- a/src/main/java/net/neoforged/neoforge/client/model/geometry/GeometryLoaderManager.java +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright (c) Forge Development LLC and contributors - * SPDX-License-Identifier: LGPL-2.1-only - */ - -package net.neoforged.neoforge.client.model.geometry; - -import com.google.common.collect.ImmutableMap; -import java.util.HashMap; -import java.util.stream.Collectors; -import net.minecraft.resources.ResourceLocation; -import net.neoforged.fml.ModLoader; -import net.neoforged.neoforge.client.event.ModelEvent; -import org.jetbrains.annotations.ApiStatus; -import org.jetbrains.annotations.Nullable; - -/** - * Manager for {@linkplain IGeometryLoader geometry loaders}. - *

    - * Provides a lookup. - */ -public final class GeometryLoaderManager { - private static ImmutableMap> LOADERS; - private static String LOADER_LIST; - - /** - * Finds the {@link IGeometryLoader} for a given name, or null if not found. - */ - @Nullable - public static IGeometryLoader get(ResourceLocation name) { - return LOADERS.get(name); - } - - /** - * Retrieves a comma-separated list of all active loaders, for use in error messages. - */ - public static String getLoaderList() { - return LOADER_LIST; - } - - @ApiStatus.Internal - public static void init() { - var loaders = new HashMap>(); - var event = new ModelEvent.RegisterGeometryLoaders(loaders); - ModLoader.postEventWrapContainerInModOrder(event); - LOADERS = ImmutableMap.copyOf(loaders); - LOADER_LIST = loaders.keySet().stream().map(ResourceLocation::toString).collect(Collectors.joining(", ")); - } - - private GeometryLoaderManager() {} -} diff --git a/src/main/java/net/neoforged/neoforge/client/model/geometry/IGeometryBakingContext.java b/src/main/java/net/neoforged/neoforge/client/model/geometry/IGeometryBakingContext.java deleted file mode 100644 index 3b1950ceb1..0000000000 --- a/src/main/java/net/neoforged/neoforge/client/model/geometry/IGeometryBakingContext.java +++ /dev/null @@ -1,92 +0,0 @@ -/* - * Copyright (c) Forge Development LLC and contributors - * SPDX-License-Identifier: LGPL-2.1-only - */ - -package net.neoforged.neoforge.client.model.geometry; - -import com.mojang.math.Transformation; -import net.minecraft.client.renderer.block.model.ItemTransforms; -import net.minecraft.client.resources.model.Material; -import net.minecraft.resources.ResourceLocation; -import net.neoforged.neoforge.client.NamedRenderTypeManager; -import net.neoforged.neoforge.client.RenderTypeGroup; -import org.jetbrains.annotations.Nullable; - -/** - * The context in which a geometry is being baked, providing information such as lighting and - * {@linkplain ItemTransforms transforms}, and allowing the user to create {@linkplain Material materials} and query - * {@linkplain RenderTypeGroup render types}. - * - * @see StandaloneGeometryBakingContext - * @see BlockGeometryBakingContext - */ -public interface IGeometryBakingContext { - /** - * {@return the name of the model being baked for logging and caching purposes.} - */ - String getModelName(); - - /** - * Checks if a material is present in the model. - * - * @param name The name of the material - * @return true if the material is present, false otherwise - */ - boolean hasMaterial(String name); - - /** - * Resolves the final texture name, taking into account texture aliases and replacements. - * - * @param name The name of the material - * @return The material, or the missing texture if not found - */ - Material getMaterial(String name); - - /** - * {@return true if this model should render in 3D in a GUI, false otherwise} - */ - boolean isGui3d(); - - /** - * {@return true if block lighting should be used for this model, false otherwise} - */ - boolean useBlockLight(); - - /** - * {@return true if per-vertex ambient occlusion should be used for this model, false otherwise} - */ - boolean useAmbientOcclusion(); - - /** - * {@return the transforms for display in item form.} - */ - ItemTransforms getTransforms(); - - /** - * {@return the root transformation to be applied to all variants of this model, regardless of item transforms.} - */ - Transformation getRootTransform(); - - /** - * {@return a hint of the render type this model should use. Custom loaders may ignore this.} - */ - @Nullable - ResourceLocation getRenderTypeHint(); - - /** - * Queries the visibility of a component of this model. - * - * @param component The component for which to query visibility - * @param fallback The default visibility if an override isn't found - * @return The visibility of the component - */ - boolean isComponentVisible(String component, boolean fallback); - - /** - * {@return a {@link RenderTypeGroup} with the given name, or the empty group if not found.} - */ - default RenderTypeGroup getRenderType(ResourceLocation name) { - return NamedRenderTypeManager.get(name); - } -} diff --git a/src/main/java/net/neoforged/neoforge/client/model/geometry/IGeometryLoader.java b/src/main/java/net/neoforged/neoforge/client/model/geometry/IGeometryLoader.java deleted file mode 100644 index 67f2c7d090..0000000000 --- a/src/main/java/net/neoforged/neoforge/client/model/geometry/IGeometryLoader.java +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright (c) Forge Development LLC and contributors - * SPDX-License-Identifier: LGPL-2.1-only - */ - -package net.neoforged.neoforge.client.model.geometry; - -import com.google.gson.JsonDeserializationContext; -import com.google.gson.JsonObject; -import com.google.gson.JsonParseException; -import net.minecraft.server.packs.resources.ResourceManagerReloadListener; -import net.neoforged.neoforge.client.event.ModelEvent; -import net.neoforged.neoforge.client.event.RegisterClientReloadListenersEvent; - -/** - * A loader for custom {@linkplain IUnbakedGeometry model geometries}. - *

    - * If you do any caching, you should implement {@link ResourceManagerReloadListener} and register it with - * {@link RegisterClientReloadListenersEvent}. - * - * @see ModelEvent.RegisterGeometryLoaders - * @see RegisterClientReloadListenersEvent - */ -public interface IGeometryLoader> { - T read(JsonObject jsonObject, JsonDeserializationContext deserializationContext) throws JsonParseException; -} diff --git a/src/main/java/net/neoforged/neoforge/client/model/geometry/IUnbakedGeometry.java b/src/main/java/net/neoforged/neoforge/client/model/geometry/IUnbakedGeometry.java deleted file mode 100644 index 79be568a61..0000000000 --- a/src/main/java/net/neoforged/neoforge/client/model/geometry/IUnbakedGeometry.java +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright (c) Forge Development LLC and contributors - * SPDX-License-Identifier: LGPL-2.1-only - */ - -package net.neoforged.neoforge.client.model.geometry; - -import java.util.List; -import java.util.Set; -import java.util.function.Function; -import net.minecraft.client.renderer.block.model.BlockModel; -import net.minecraft.client.renderer.block.model.ItemOverride; -import net.minecraft.client.renderer.texture.TextureAtlasSprite; -import net.minecraft.client.resources.model.BakedModel; -import net.minecraft.client.resources.model.Material; -import net.minecraft.client.resources.model.ModelBaker; -import net.minecraft.client.resources.model.ModelState; -import net.minecraft.client.resources.model.UnbakedModel; - -/** - * General interface for any model that can be baked, superset of vanilla {@link UnbakedModel}. - *

    - * Instances of this class ar usually created via {@link IGeometryLoader}. - * - * @see IGeometryLoader - * @see IGeometryBakingContext - */ -public interface IUnbakedGeometry> { - BakedModel bake(IGeometryBakingContext context, ModelBaker baker, Function spriteGetter, ModelState modelState, List overrides); - - /** - * Resolve parents of nested {@link BlockModel}s which are later used in - * {@link IUnbakedGeometry#bake(IGeometryBakingContext, ModelBaker, Function, ModelState, List)} - * via {@link BlockModel#resolveDependencies(UnbakedModel.Resolver)} - */ - default void resolveDependencies(UnbakedModel.Resolver modelGetter, IGeometryBakingContext context) {} - - /** - * {@return a set of all the components whose visibility may be configured via {@link IGeometryBakingContext}} - */ - default Set getConfigurableComponentNames() { - return Set.of(); - } -} diff --git a/src/main/java/net/neoforged/neoforge/client/model/geometry/SimpleUnbakedGeometry.java b/src/main/java/net/neoforged/neoforge/client/model/geometry/SimpleUnbakedGeometry.java deleted file mode 100644 index 1c8ea09699..0000000000 --- a/src/main/java/net/neoforged/neoforge/client/model/geometry/SimpleUnbakedGeometry.java +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright (c) Forge Development LLC and contributors - * SPDX-License-Identifier: LGPL-2.1-only - */ - -package net.neoforged.neoforge.client.model.geometry; - -import java.util.List; -import java.util.function.Function; -import net.minecraft.client.renderer.block.model.BakedQuad; -import net.minecraft.client.renderer.block.model.ItemOverride; -import net.minecraft.client.renderer.texture.TextureAtlasSprite; -import net.minecraft.client.resources.model.BakedModel; -import net.minecraft.client.resources.model.Material; -import net.minecraft.client.resources.model.ModelBaker; -import net.minecraft.client.resources.model.ModelState; -import net.neoforged.neoforge.client.RenderTypeGroup; -import net.neoforged.neoforge.client.model.IModelBuilder; - -/** - * Base class for implementations of {@link IUnbakedGeometry} which do not wish to handle model creation themselves, - * instead supplying {@linkplain BakedQuad baked quads} through a builder. - */ -public abstract class SimpleUnbakedGeometry> implements IUnbakedGeometry { - @Override - public BakedModel bake(IGeometryBakingContext context, ModelBaker baker, Function spriteGetter, ModelState modelState, List overrides) { - TextureAtlasSprite particle = spriteGetter.apply(context.getMaterial("particle")); - - var renderTypeHint = context.getRenderTypeHint(); - var renderTypes = renderTypeHint != null ? context.getRenderType(renderTypeHint) : RenderTypeGroup.EMPTY; - IModelBuilder builder = IModelBuilder.of(context.useAmbientOcclusion(), context.useBlockLight(), context.isGui3d(), - context.getTransforms(), particle, renderTypes); - - addQuads(context, builder, baker, spriteGetter, modelState); - - return builder.build(); - } - - protected abstract void addQuads(IGeometryBakingContext owner, IModelBuilder modelBuilder, ModelBaker baker, Function spriteGetter, ModelState modelTransform); -} diff --git a/src/main/java/net/neoforged/neoforge/client/model/geometry/StandaloneGeometryBakingContext.java b/src/main/java/net/neoforged/neoforge/client/model/geometry/StandaloneGeometryBakingContext.java deleted file mode 100644 index f983b777bf..0000000000 --- a/src/main/java/net/neoforged/neoforge/client/model/geometry/StandaloneGeometryBakingContext.java +++ /dev/null @@ -1,213 +0,0 @@ -/* - * Copyright (c) Forge Development LLC and contributors - * SPDX-License-Identifier: LGPL-2.1-only - */ - -package net.neoforged.neoforge.client.model.geometry; - -import com.google.common.base.Predicates; -import com.mojang.math.Transformation; -import it.unimi.dsi.fastutil.objects.Object2BooleanMap; -import java.util.Map; -import java.util.function.BiPredicate; -import java.util.function.Function; -import java.util.function.Predicate; -import net.minecraft.client.renderer.block.model.ItemTransforms; -import net.minecraft.client.renderer.texture.MissingTextureAtlasSprite; -import net.minecraft.client.renderer.texture.TextureAtlas; -import net.minecraft.client.resources.model.Material; -import net.minecraft.resources.ResourceLocation; -import org.jetbrains.annotations.Nullable; - -/** - * A {@linkplain IGeometryBakingContext geometry baking context} that is not bound to block/item model loading. - */ -public class StandaloneGeometryBakingContext implements IGeometryBakingContext { - public static final ResourceLocation LOCATION = ResourceLocation.fromNamespaceAndPath("neoforge", "standalone"); - - public static final StandaloneGeometryBakingContext INSTANCE = create(LOCATION); - - public static StandaloneGeometryBakingContext create(ResourceLocation modelName) { - return builder().build(modelName); - } - - public static StandaloneGeometryBakingContext create(Map textures) { - return create(LOCATION, textures); - } - - public static StandaloneGeometryBakingContext create(ResourceLocation modelName, Map textures) { - return builder().withTextures(textures, MissingTextureAtlasSprite.getLocation()).build(modelName); - } - - private final ResourceLocation modelName; - private final Predicate materialCheck; - private final Function materialLookup; - private final boolean isGui3d; - private final boolean useBlockLight; - private final boolean useAmbientOcclusion; - private final ItemTransforms transforms; - private final Transformation rootTransform; - @Nullable - private final ResourceLocation renderTypeHint; - private final BiPredicate visibilityTest; - - private StandaloneGeometryBakingContext(ResourceLocation modelName, Predicate materialCheck, - Function materialLookup, boolean isGui3d, - boolean useBlockLight, boolean useAmbientOcclusion, - ItemTransforms transforms, Transformation rootTransform, - @Nullable ResourceLocation renderTypeHint, - BiPredicate visibilityTest) { - this.modelName = modelName; - this.materialCheck = materialCheck; - this.materialLookup = materialLookup; - this.isGui3d = isGui3d; - this.useBlockLight = useBlockLight; - this.useAmbientOcclusion = useAmbientOcclusion; - this.transforms = transforms; - this.rootTransform = rootTransform; - this.renderTypeHint = renderTypeHint; - this.visibilityTest = visibilityTest; - } - - @Override - public String getModelName() { - return modelName.toString(); - } - - @Override - public boolean hasMaterial(String name) { - return materialCheck.test(name); - } - - @Override - public Material getMaterial(String name) { - return materialLookup.apply(name); - } - - @Override - public boolean isGui3d() { - return isGui3d; - } - - @Override - public boolean useBlockLight() { - return useBlockLight; - } - - @Override - public boolean useAmbientOcclusion() { - return useAmbientOcclusion; - } - - @Override - public ItemTransforms getTransforms() { - return transforms; - } - - @Override - public Transformation getRootTransform() { - return rootTransform; - } - - @Nullable - @Override - public ResourceLocation getRenderTypeHint() { - return renderTypeHint; - } - - @Override - public boolean isComponentVisible(String component, boolean fallback) { - return visibilityTest.test(component, fallback); - } - - public static Builder builder() { - return new Builder(); - } - - public static Builder builder(IGeometryBakingContext parent) { - return new Builder(parent); - } - - public static final class Builder { - private static final Material NO_MATERIAL = new Material(TextureAtlas.LOCATION_BLOCKS, MissingTextureAtlasSprite.getLocation()); - private Predicate materialCheck = Predicates.alwaysFalse(); - private Function materialLookup = $ -> NO_MATERIAL; - private boolean isGui3d = true; - private boolean useBlockLight = true; - private boolean useAmbientOcclusion = true; - private ItemTransforms transforms = ItemTransforms.NO_TRANSFORMS; - private Transformation rootTransform = Transformation.identity(); - @Nullable - private ResourceLocation renderTypeHint; - private BiPredicate visibilityTest = (c, def) -> def; - - private Builder() {} - - private Builder(IGeometryBakingContext parent) { - this.materialCheck = parent::hasMaterial; - this.materialLookup = parent::getMaterial; - this.isGui3d = parent.isGui3d(); - this.useBlockLight = parent.useBlockLight(); - this.useAmbientOcclusion = parent.useAmbientOcclusion(); - this.transforms = parent.getTransforms(); - this.rootTransform = parent.getRootTransform(); - this.renderTypeHint = parent.getRenderTypeHint(); - this.visibilityTest = parent::isComponentVisible; - } - - public Builder withTextures(Map textures, ResourceLocation defaultTexture) { - return withTextures(TextureAtlas.LOCATION_BLOCKS, textures, defaultTexture); - } - - public Builder withTextures(ResourceLocation atlasLocation, Map textures, ResourceLocation defaultTexture) { - this.materialCheck = textures::containsKey; - this.materialLookup = name -> new Material(atlasLocation, textures.getOrDefault(name, defaultTexture)); - return this; - } - - public Builder withMaterials(Map materials, Material defaultMaterial) { - this.materialCheck = materials::containsKey; - this.materialLookup = name -> materials.getOrDefault(name, defaultMaterial); - return this; - } - - public Builder withGui3d(boolean isGui3d) { - this.isGui3d = isGui3d; - return this; - } - - public Builder withUseBlockLight(boolean useBlockLight) { - this.useBlockLight = useBlockLight; - return this; - } - - public Builder withUseAmbientOcclusion(boolean useAmbientOcclusion) { - this.useAmbientOcclusion = useAmbientOcclusion; - return this; - } - - public Builder withTransforms(ItemTransforms transforms) { - this.transforms = transforms; - return this; - } - - public Builder withRootTransform(Transformation rootTransform) { - this.rootTransform = rootTransform; - return this; - } - - public Builder withRenderTypeHint(ResourceLocation renderTypeHint) { - this.renderTypeHint = renderTypeHint; - return this; - } - - public Builder withVisibleComponents(Object2BooleanMap parts) { - this.visibilityTest = parts::getOrDefault; - return this; - } - - public StandaloneGeometryBakingContext build(ResourceLocation modelName) { - return new StandaloneGeometryBakingContext(modelName, materialCheck, materialLookup, isGui3d, useBlockLight, useAmbientOcclusion, transforms, rootTransform, renderTypeHint, visibilityTest); - } - } -} diff --git a/src/main/java/net/neoforged/neoforge/client/model/item/DynamicFluidContainerModel.java b/src/main/java/net/neoforged/neoforge/client/model/item/DynamicFluidContainerModel.java new file mode 100644 index 0000000000..d2f69a1d5b --- /dev/null +++ b/src/main/java/net/neoforged/neoforge/client/model/item/DynamicFluidContainerModel.java @@ -0,0 +1,206 @@ +/* + * Copyright (c) NeoForged and contributors + * SPDX-License-Identifier: LGPL-2.1-only + */ + +package net.neoforged.neoforge.client.model.item; + +import com.mojang.math.Transformation; +import com.mojang.serialization.Codec; +import com.mojang.serialization.DataResult; +import com.mojang.serialization.MapCodec; +import com.mojang.serialization.codecs.RecordCodecBuilder; +import java.util.IdentityHashMap; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import net.minecraft.client.color.item.Constant; +import net.minecraft.client.multiplayer.ClientLevel; +import net.minecraft.client.renderer.RenderType; +import net.minecraft.client.renderer.block.model.ItemTransforms; +import net.minecraft.client.renderer.item.BlockModelWrapper; +import net.minecraft.client.renderer.item.ItemModel; +import net.minecraft.client.renderer.item.ItemModelResolver; +import net.minecraft.client.renderer.item.ItemStackRenderState; +import net.minecraft.client.renderer.texture.TextureAtlasSprite; +import net.minecraft.client.resources.model.BlockModelRotation; +import net.minecraft.client.resources.model.Material; +import net.minecraft.client.resources.model.ModelState; +import net.minecraft.core.registries.BuiltInRegistries; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.entity.LivingEntity; +import net.minecraft.world.item.ItemDisplayContext; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.level.material.Fluid; +import net.minecraft.world.level.material.Fluids; +import net.neoforged.neoforge.client.ClientHooks; +import net.neoforged.neoforge.client.NeoForgeRenderTypes; +import net.neoforged.neoforge.client.RenderTypeGroup; +import net.neoforged.neoforge.client.color.item.FluidContentsTint; +import net.neoforged.neoforge.client.extensions.common.IClientFluidTypeExtensions; +import net.neoforged.neoforge.client.model.QuadTransformers; +import net.neoforged.neoforge.client.model.SimpleModelState; +import net.neoforged.neoforge.client.model.UnbakedCompositeModel; +import net.neoforged.neoforge.client.model.UnbakedElementsHelper; +import net.neoforged.neoforge.fluids.FluidStack; +import net.neoforged.neoforge.fluids.FluidUtil; +import org.jetbrains.annotations.Nullable; +import org.joml.Quaternionf; +import org.joml.Vector3f; + +/** + * A dynamic fluid container model, capable of re-texturing itself at runtime to match the contained fluid. + *

    + * Composed of a base layer, a fluid layer (applied with a mask) and a cover layer (optionally applied with a mask). + * The entire model may optionally be flipped if the fluid is gaseous, and the fluid layer may glow if light-emitting. + */ +public class DynamicFluidContainerModel implements ItemModel { + // Depth offsets to prevent Z-fighting + private static final Transformation FLUID_TRANSFORM = new Transformation(new Vector3f(), new Quaternionf(), new Vector3f(1, 1, 1.002f), new Quaternionf()); + private static final Transformation COVER_TRANSFORM = new Transformation(new Vector3f(), new Quaternionf(), new Vector3f(1, 1, 1.004f), new Quaternionf()); + + private static RenderTypeGroup getLayerRenderTypes(boolean unlit) { + return new RenderTypeGroup(RenderType.translucent(), unlit ? NeoForgeRenderTypes.ITEM_UNSORTED_UNLIT_TRANSLUCENT.get() : NeoForgeRenderTypes.ITEM_UNSORTED_TRANSLUCENT.get()); + } + + private final Unbaked unbakedModel; + private final BakingContext bakingContext; + private final ItemTransforms itemTransforms; + private final Map cache = new IdentityHashMap<>(); // contains all the baked models since they'll never change + + private DynamicFluidContainerModel(Unbaked unbakedModel, BakingContext bakingContext) { + this.unbakedModel = unbakedModel; + this.bakingContext = bakingContext; + // Source ItemTransforms from the base item model + var baseItemModel = bakingContext.blockModelBaker().getModel(ResourceLocation.withDefaultNamespace("item/generated")); + if (baseItemModel == null) { + throw new IllegalStateException("Failed to access item/generated model"); + } + this.itemTransforms = baseItemModel.getTransforms(); + } + + private ItemModel bakeModelForFluid(Fluid fluid) { + var sprites = bakingContext.blockModelBaker().sprites(); + + Material particleLocation = unbakedModel.textures.particle.map(ClientHooks::getBlockMaterial).orElse(null); + Material baseLocation = unbakedModel.textures.base.map(ClientHooks::getBlockMaterial).orElse(null); + Material fluidMaskLocation = unbakedModel.textures.fluid.map(ClientHooks::getBlockMaterial).orElse(null); + Material coverLocation = unbakedModel.textures.cover.map(ClientHooks::getBlockMaterial).orElse(null); + + TextureAtlasSprite baseSprite = baseLocation != null ? sprites.get(baseLocation) : null; + TextureAtlasSprite fluidSprite = fluid != Fluids.EMPTY ? sprites.get(ClientHooks.getBlockMaterial(IClientFluidTypeExtensions.of(fluid).getStillTexture())) : null; + TextureAtlasSprite coverSprite = (coverLocation != null && (!unbakedModel.coverIsMask || baseLocation != null)) ? sprites.get(coverLocation) : null; + + TextureAtlasSprite particleSprite = particleLocation != null ? sprites.get(particleLocation) : null; + + if (particleSprite == null) particleSprite = fluidSprite; + if (particleSprite == null) particleSprite = baseSprite; + if (particleSprite == null && !unbakedModel.coverIsMask) particleSprite = coverSprite; + + // If the fluid is lighter than air, rotate 180deg to turn it upside down + ModelState state = BlockModelRotation.X0_Y0; + if (unbakedModel.flipGas && fluid != Fluids.EMPTY && fluid.getFluidType().isLighterThanAir()) { + state = new SimpleModelState( + state.getRotation().compose( + new Transformation(null, new Quaternionf(0, 0, 1, 0), null, null))); + } + + // We need to disable GUI 3D and block lighting for this to render properly + var modelBuilder = UnbakedCompositeModel.Baked.builder(true, false, false, particleSprite, itemTransforms); + + var normalRenderTypes = getLayerRenderTypes(false); + + if (baseLocation != null) { + // Base texture + var unbaked = UnbakedElementsHelper.createUnbakedItemElements(0, baseSprite); + var quads = UnbakedElementsHelper.bakeElements(unbaked, $ -> baseSprite, state); + modelBuilder.addQuads(normalRenderTypes, quads); + } + + if (fluidMaskLocation != null && fluidSprite != null) { + TextureAtlasSprite templateSprite = sprites.get(fluidMaskLocation); + // Fluid layer + var transformedState = new SimpleModelState(state.getRotation().compose(FLUID_TRANSFORM), state.isUvLocked()); + var unbaked = UnbakedElementsHelper.createUnbakedItemMaskElements(1, templateSprite); // Use template as mask + var quads = UnbakedElementsHelper.bakeElements(unbaked, $ -> fluidSprite, transformedState); // Bake with fluid texture + + var emissive = unbakedModel.applyFluidLuminosity && fluid.getFluidType().getLightLevel() > 0; + var renderTypes = getLayerRenderTypes(emissive); + if (emissive) QuadTransformers.settingMaxEmissivity().processInPlace(quads); + + modelBuilder.addQuads(renderTypes, quads); + } + + if (coverSprite != null) { + var sprite = unbakedModel.coverIsMask ? baseSprite : coverSprite; + // Cover/overlay + var transformedState = new SimpleModelState(state.getRotation().compose(COVER_TRANSFORM), state.isUvLocked()); + var unbaked = UnbakedElementsHelper.createUnbakedItemMaskElements(2, coverSprite); // Use cover as mask + var quads = UnbakedElementsHelper.bakeElements(unbaked, $ -> sprite, transformedState); // Bake with selected texture + modelBuilder.addQuads(normalRenderTypes, quads); + } + + modelBuilder.setParticle(particleSprite); + + return new BlockModelWrapper(modelBuilder.build(), List.of(new Constant(-1), FluidContentsTint.INSTANCE)); + } + + @Override + public void update(ItemStackRenderState renderState, ItemStack stack, ItemModelResolver modelResolver, ItemDisplayContext displayContext, @Nullable ClientLevel level, @Nullable LivingEntity entity, int p_387820_) { + var fluid = FluidUtil.getFluidContained(stack) + .map(FluidStack::getFluid) + // not a fluid item apparently + .orElse(unbakedModel.fluid); + + cache.computeIfAbsent(fluid, this::bakeModelForFluid) + .update(renderState, stack, modelResolver, displayContext, level, entity, p_387820_); + } + + public record Textures( + Optional particle, + Optional base, + Optional fluid, + Optional cover) { + public static final Codec CODEC = RecordCodecBuilder.create( + instance -> instance + .group( + ResourceLocation.CODEC.optionalFieldOf("particle").forGetter(Textures::particle), + ResourceLocation.CODEC.optionalFieldOf("base").forGetter(Textures::base), + ResourceLocation.CODEC.optionalFieldOf("fluid").forGetter(Textures::fluid), + ResourceLocation.CODEC.optionalFieldOf("cover").forGetter(Textures::cover)) + .apply(instance, Textures::new)) + .validate(textures -> { + if (textures.particle.isPresent() || textures.base.isPresent() || textures.fluid.isPresent() || textures.cover.isPresent()) { + return DataResult.success(textures); + } + return DataResult.error(() -> "Dynamic fluid container model requires at least one particle, base, fluid or cover texture."); + }); + } + + public record Unbaked(Textures textures, Fluid fluid, boolean flipGas, boolean coverIsMask, boolean applyFluidLuminosity) implements ItemModel.Unbaked { + + public static final MapCodec MAP_CODEC = RecordCodecBuilder.mapCodec( + instance -> instance + .group( + Textures.CODEC.fieldOf("textures").forGetter(Unbaked::textures), + BuiltInRegistries.FLUID.byNameCodec().fieldOf("fluid").forGetter(Unbaked::fluid), + Codec.BOOL.optionalFieldOf("flip_gas", false).forGetter(Unbaked::flipGas), + Codec.BOOL.optionalFieldOf("cover_is_mask", true).forGetter(Unbaked::coverIsMask), + Codec.BOOL.optionalFieldOf("apply_fluid_luminosity", true).forGetter(Unbaked::applyFluidLuminosity)) + .apply(instance, Unbaked::new)); + @Override + public MapCodec type() { + return MAP_CODEC; + } + + @Override + public ItemModel bake(BakingContext bakingContext) { + return new DynamicFluidContainerModel(this, bakingContext); + } + + @Override + public void resolveDependencies(Resolver resolver) { + //No dependencies + } + } +} diff --git a/src/main/java/net/neoforged/neoforge/client/model/renderable/package-info.java b/src/main/java/net/neoforged/neoforge/client/model/item/package-info.java similarity index 85% rename from src/main/java/net/neoforged/neoforge/client/model/renderable/package-info.java rename to src/main/java/net/neoforged/neoforge/client/model/item/package-info.java index c5923aab3d..87c7c56dae 100644 --- a/src/main/java/net/neoforged/neoforge/client/model/renderable/package-info.java +++ b/src/main/java/net/neoforged/neoforge/client/model/item/package-info.java @@ -6,7 +6,7 @@ @FieldsAreNonnullByDefault @MethodsReturnNonnullByDefault @ParametersAreNonnullByDefault -package net.neoforged.neoforge.client.model.renderable; +package net.neoforged.neoforge.client.model.item; import javax.annotation.ParametersAreNonnullByDefault; import net.minecraft.FieldsAreNonnullByDefault; diff --git a/src/main/java/net/neoforged/neoforge/client/model/obj/ObjLoader.java b/src/main/java/net/neoforged/neoforge/client/model/obj/ObjLoader.java index 65d2b939e7..0bf9931d93 100644 --- a/src/main/java/net/neoforged/neoforge/client/model/obj/ObjLoader.java +++ b/src/main/java/net/neoforged/neoforge/client/model/obj/ObjLoader.java @@ -7,17 +7,21 @@ import com.google.common.collect.Maps; import com.google.gson.JsonDeserializationContext; +import com.google.gson.JsonElement; import com.google.gson.JsonObject; import com.google.gson.JsonParseException; +import com.mojang.math.Transformation; import java.io.FileNotFoundException; +import java.util.HashMap; import java.util.Map; import net.minecraft.client.Minecraft; +import net.minecraft.client.renderer.block.model.BlockModel; import net.minecraft.resources.ResourceLocation; import net.minecraft.server.packs.resources.Resource; import net.minecraft.server.packs.resources.ResourceManager; import net.minecraft.server.packs.resources.ResourceManagerReloadListener; import net.minecraft.util.GsonHelper; -import net.neoforged.neoforge.client.model.geometry.IGeometryLoader; +import net.neoforged.neoforge.client.model.UnbakedModelLoader; /** * A loader for {@link ObjModel OBJ models}. @@ -25,7 +29,7 @@ * Allows the user to enable automatic face culling, toggle quad shading, flip UVs, render emissively and specify a * {@link ObjMaterialLibrary material library} override. */ -public class ObjLoader implements IGeometryLoader, ResourceManagerReloadListener { +public class ObjLoader implements UnbakedModelLoader, ResourceManagerReloadListener { public static ObjLoader INSTANCE = new ObjLoader(); private final Map modelCache = Maps.newConcurrentMap(); @@ -40,7 +44,7 @@ public void onResourceManagerReload(ResourceManager resourceManager) { } @Override - public ObjModel read(JsonObject jsonObject, JsonDeserializationContext deserializationContext) { + public ObjModel read(JsonObject jsonObject, JsonDeserializationContext jsonDeserializationContext) throws JsonParseException { if (!jsonObject.has("model")) throw new JsonParseException("OBJ Loader requires a 'model' key that points to a valid .OBJ model."); @@ -52,7 +56,21 @@ public ObjModel read(JsonObject jsonObject, JsonDeserializationContext deseriali boolean emissiveAmbient = GsonHelper.getAsBoolean(jsonObject, "emissive_ambient", true); String mtlOverride = GsonHelper.getAsString(jsonObject, "mtl_override", null); - return loadModel(new ObjModel.ModelSettings(ResourceLocation.parse(modelLocation), automaticCulling, shadeQuads, flipV, emissiveAmbient, mtlOverride)); + final Map partVisibility = new HashMap<>(); + if (jsonObject.has("visibility")) { + JsonObject visibility = GsonHelper.getAsJsonObject(jsonObject, "visibility"); + for (Map.Entry part : visibility.entrySet()) { + partVisibility.put(part.getKey(), part.getValue().getAsBoolean()); + } + } + + Transformation transformation = Transformation.identity(); + if (jsonObject.has("transform")) { + JsonElement transform = jsonObject.get("transform"); + transformation = BlockModel.GSON.fromJson(transform, Transformation.class); + } + + return loadModel(new ObjModel.ModelSettings(ResourceLocation.parse(modelLocation), automaticCulling, shadeQuads, flipV, emissiveAmbient, mtlOverride, partVisibility, transformation)); } public ObjModel loadModel(ObjModel.ModelSettings settings) { diff --git a/src/main/java/net/neoforged/neoforge/client/model/obj/ObjModel.java b/src/main/java/net/neoforged/neoforge/client/model/obj/ObjModel.java index 25404e5499..98625b04e1 100644 --- a/src/main/java/net/neoforged/neoforge/client/model/obj/ObjModel.java +++ b/src/main/java/net/neoforged/neoforge/client/model/obj/ObjModel.java @@ -7,40 +7,33 @@ import com.google.common.collect.Lists; import com.google.common.collect.Maps; -import com.google.common.collect.Sets; import com.mojang.math.Transformation; import java.io.IOException; -import java.util.ArrayList; import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; -import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Set; -import java.util.function.Function; -import java.util.stream.Collectors; -import java.util.stream.Stream; import joptsimple.internal.Strings; +import net.minecraft.client.data.models.model.TextureSlot; import net.minecraft.client.renderer.LightTexture; import net.minecraft.client.renderer.block.model.BakedQuad; +import net.minecraft.client.renderer.block.model.ItemTransforms; +import net.minecraft.client.renderer.block.model.TextureSlots; import net.minecraft.client.renderer.texture.TextureAtlasSprite; -import net.minecraft.client.resources.model.Material; +import net.minecraft.client.resources.model.BakedModel; import net.minecraft.client.resources.model.ModelBaker; import net.minecraft.client.resources.model.ModelState; -import net.minecraft.client.resources.model.UnbakedModel; +import net.minecraft.client.resources.model.SimpleBakedModel; import net.minecraft.core.Direction; import net.minecraft.resources.ResourceLocation; import net.minecraft.util.Mth; +import net.minecraft.util.context.ContextMap; import net.minecraft.world.phys.Vec2; -import net.neoforged.neoforge.client.model.IModelBuilder; -import net.neoforged.neoforge.client.model.geometry.IGeometryBakingContext; -import net.neoforged.neoforge.client.model.geometry.SimpleUnbakedGeometry; -import net.neoforged.neoforge.client.model.geometry.UnbakedGeometryHelper; +import net.neoforged.neoforge.client.RenderTypeGroup; +import net.neoforged.neoforge.client.model.ExtendedUnbakedModel; +import net.neoforged.neoforge.client.model.NeoForgeModelProperties; import net.neoforged.neoforge.client.model.pipeline.QuadBakingVertexConsumer; -import net.neoforged.neoforge.client.model.renderable.CompositeRenderable; -import net.neoforged.neoforge.client.textures.UnitTextureAtlasSprite; import org.apache.commons.lang3.tuple.Pair; import org.jetbrains.annotations.Nullable; import org.joml.Vector3f; @@ -52,7 +45,7 @@ * Supports positions, texture coordinates, normals and colors. The {@link ObjMaterialLibrary material library} * has support for numerous features, including support for {@link ResourceLocation} textures (non-standard). */ -public class ObjModel extends SimpleUnbakedGeometry { +public class ObjModel implements ExtendedUnbakedModel { private static final Vector4f COLOR_WHITE = new Vector4f(1, 1, 1, 1); private static final Vec2[] DEFAULT_COORDS = { new Vec2(0, 0), @@ -62,8 +55,6 @@ public class ObjModel extends SimpleUnbakedGeometry { }; private final Map parts = Maps.newLinkedHashMap(); - private final Set rootComponentNames = Collections.unmodifiableSet(parts.keySet()); - private Set allComponentNames; private final List positions = Lists.newArrayList(); private final List texCoords = Lists.newArrayList(); @@ -78,6 +69,8 @@ public class ObjModel extends SimpleUnbakedGeometry { public final String mtlOverride; public final ResourceLocation modelLocation; + public final Map partVisibility; + public final Transformation rootTransform; private ObjModel(ModelSettings settings) { this.modelLocation = settings.modelLocation; @@ -86,6 +79,8 @@ private ObjModel(ModelSettings settings) { this.flipV = settings.flipV; this.emissiveAmbient = settings.emissiveAmbient; this.mtlOverride = settings.mtlOverride; + this.partVisibility = settings.partVisibility; + this.rootTransform = settings.rootTransform; } public static ObjModel parse(ObjTokenizer tokenizer, ModelSettings settings) throws IOException { @@ -292,26 +287,6 @@ static Vector4f parseVector4(String[] line) { }; } - @Override - protected void addQuads(IGeometryBakingContext owner, IModelBuilder modelBuilder, ModelBaker baker, Function spriteGetter, ModelState modelTransform) { - parts.values().stream().filter(part -> owner.isComponentVisible(part.name(), true)) - .forEach(part -> part.addQuads(owner, modelBuilder, baker, spriteGetter, modelTransform)); - } - - public Set getRootComponentNames() { - return rootComponentNames; - } - - @Override - public Set getConfigurableComponentNames() { - if (allComponentNames != null) - return allComponentNames; - var names = new HashSet(); - for (var group : parts.values()) - group.addNamesRecursively(names); - return allComponentNames = Collections.unmodifiableSet(names); - } - private Pair makeQuad(int[][] indices, int tintIndex, Vector4f colorTint, Vector4f ambientColor, TextureAtlasSprite texture, Transformation transform) { boolean needsNormalRecalculation = false; for (int[] ints : indices) { @@ -433,16 +408,22 @@ private Pair makeQuad(int[][] indices, int tintIndex, Vect return Pair.of(quadBaker.bakeQuad(), cull); } - public CompositeRenderable bakeRenderable(IGeometryBakingContext configuration) { - var builder = CompositeRenderable.builder(); + public boolean isComponentVisible(String part, boolean fallback) { + return partVisibility.getOrDefault(part, fallback); + } - for (var entry : parts.entrySet()) { - var name = entry.getKey(); - var part = entry.getValue(); - part.bake(builder.child(name), configuration); - } + @Override + public BakedModel bake(TextureSlots slots, ModelBaker baker, ModelState state, boolean useAmbientOcclusion, boolean usesBlockLight, ItemTransforms transforms, ContextMap additionalProperties) { + var builder = new SimpleBakedModel.Builder(useAmbientOcclusion, usesBlockLight, true, transforms); + builder.particle(baker.findSprite(slots, TextureSlot.PARTICLE.getId())); + parts.values().stream().filter(part -> isComponentVisible(part.name(), true)) + .forEach(part -> part.addQuads(builder, slots, baker, state, useAmbientOcclusion, usesBlockLight, transforms)); + return builder.build(additionalProperties.getOrDefault(NeoForgeModelProperties.RENDER_TYPE, RenderTypeGroup.EMPTY)); + } - return builder.get(); + @Override + public void resolveDependencies(Resolver resolver) { + // no dependencies } public class ModelObject { @@ -458,26 +439,12 @@ public String name() { return name; } - public void addQuads(IGeometryBakingContext owner, IModelBuilder modelBuilder, ModelBaker baker, Function spriteGetter, ModelState modelTransform) { + public void addQuads(SimpleBakedModel.Builder builder, TextureSlots slots, ModelBaker baker, ModelState state, boolean useAmbientOcclusion, boolean usesBlockLight, ItemTransforms transforms) { for (ModelMesh mesh : meshes) { - mesh.addQuads(owner, modelBuilder, spriteGetter, modelTransform); + mesh.addQuads(builder, slots, baker, state, useAmbientOcclusion, usesBlockLight, transforms); } } - public void bake(CompositeRenderable.PartBuilder builder, IGeometryBakingContext configuration) { - for (ModelMesh mesh : this.meshes) { - mesh.bake(builder, configuration); - } - } - - public Collection getTextures(IGeometryBakingContext owner, Function modelGetter, Set> missingTextureErrors) { - return meshes.stream() - .flatMap(mesh -> mesh.mat != null - ? Stream.of(UnbakedGeometryHelper.resolveDirtyMaterial(mesh.mat.diffuseColorMap, owner)) - : Stream.of()) - .collect(Collectors.toSet()); - } - protected void addNamesRecursively(Set names) { names.add(name()); } @@ -491,31 +458,11 @@ public class ModelGroup extends ModelObject { } @Override - public void addQuads(IGeometryBakingContext owner, IModelBuilder modelBuilder, ModelBaker baker, Function spriteGetter, ModelState modelTransform) { - super.addQuads(owner, modelBuilder, baker, spriteGetter, modelTransform); + public void addQuads(SimpleBakedModel.Builder builder, TextureSlots slots, ModelBaker baker, ModelState state, boolean useAmbientOcclusion, boolean usesBlockLight, ItemTransforms transforms) { + super.addQuads(builder, slots, baker, state, useAmbientOcclusion, usesBlockLight, transforms); - parts.values().stream().filter(part -> owner.isComponentVisible(part.name(), true)) - .forEach(part -> part.addQuads(owner, modelBuilder, baker, spriteGetter, modelTransform)); - } - - @Override - public void bake(CompositeRenderable.PartBuilder builder, IGeometryBakingContext configuration) { - super.bake(builder, configuration); - - for (var entry : parts.entrySet()) { - var name = entry.getKey(); - var part = entry.getValue(); - part.bake(builder.child(name), configuration); - } - } - - @Override - public Collection getTextures(IGeometryBakingContext owner, Function modelGetter, Set> missingTextureErrors) { - Set combined = Sets.newHashSet(); - combined.addAll(super.getTextures(owner, modelGetter, missingTextureErrors)); - for (ModelObject part : parts.values()) - combined.addAll(part.getTextures(owner, modelGetter, missingTextureErrors)); - return combined; + parts.values().stream().filter(part -> isComponentVisible("%s.%s".formatted(name(), part.name()), true)) + .forEach(part -> part.addQuads(builder, slots, baker, state, useAmbientOcclusion, usesBlockLight, transforms)); } @Override @@ -538,46 +485,27 @@ public ModelMesh(@Nullable ObjMaterialLibrary.Material currentMat, @Nullable Str this.smoothingGroup = currentSmoothingGroup; } - public void addQuads(IGeometryBakingContext owner, IModelBuilder modelBuilder, Function spriteGetter, ModelState modelTransform) { + public void addQuads(SimpleBakedModel.Builder builder, TextureSlots slots, ModelBaker baker, ModelState state, boolean useAmbientOcclusion, boolean usesBlockLight, ItemTransforms transforms) { if (mat == null) return; - TextureAtlasSprite texture = spriteGetter.apply(UnbakedGeometryHelper.resolveDirtyMaterial(mat.diffuseColorMap, owner)); + TextureAtlasSprite texture = baker.findSprite(slots, mat.diffuseColorMap); int tintIndex = mat.diffuseTintIndex; Vector4f colorTint = mat.diffuseColor; - var rootTransform = owner.getRootTransform(); - var transform = rootTransform.isIdentity() ? modelTransform.getRotation() : modelTransform.getRotation().compose(rootTransform); + var rootTransform = ObjModel.this.rootTransform; + var transform = rootTransform.isIdentity() ? state.getRotation() : state.getRotation().compose(rootTransform); for (int[][] face : faces) { Pair quad = makeQuad(face, tintIndex, colorTint, mat.ambientColor, texture, transform); if (quad.getRight() == null) - modelBuilder.addUnculledFace(quad.getLeft()); + builder.addUnculledFace(quad.getLeft()); else - modelBuilder.addCulledFace(quad.getRight(), quad.getLeft()); - } - } - - public void bake(CompositeRenderable.PartBuilder builder, IGeometryBakingContext configuration) { - ObjMaterialLibrary.Material mat = this.mat; - if (mat == null) - return; - int tintIndex = mat.diffuseTintIndex; - Vector4f colorTint = mat.diffuseColor; - - final List quads = new ArrayList<>(); - - for (var face : this.faces) { - var pair = makeQuad(face, tintIndex, colorTint, mat.ambientColor, UnitTextureAtlasSprite.INSTANCE, Transformation.identity()); - quads.add(pair.getLeft()); + builder.addCulledFace(quad.getRight(), quad.getLeft()); } - - ResourceLocation textureLocation = UnbakedGeometryHelper.resolveDirtyMaterial(mat.diffuseColorMap, configuration).texture(); - ResourceLocation texturePath = ResourceLocation.fromNamespaceAndPath(textureLocation.getNamespace(), "textures/" + textureLocation.getPath() + ".png"); - - builder.addMesh(texturePath, quads); } } public record ModelSettings(ResourceLocation modelLocation, boolean automaticCulling, boolean shadeQuads, boolean flipV, - boolean emissiveAmbient, @Nullable String mtlOverride) {} + boolean emissiveAmbient, @Nullable String mtlOverride, + Map partVisibility, Transformation rootTransform) {} } diff --git a/src/main/java/net/neoforged/neoforge/client/model/renderable/BakedModelRenderable.java b/src/main/java/net/neoforged/neoforge/client/model/renderable/BakedModelRenderable.java deleted file mode 100644 index ec17ba246e..0000000000 --- a/src/main/java/net/neoforged/neoforge/client/model/renderable/BakedModelRenderable.java +++ /dev/null @@ -1,86 +0,0 @@ -/* - * Copyright (c) Forge Development LLC and contributors - * SPDX-License-Identifier: LGPL-2.1-only - */ - -package net.neoforged.neoforge.client.model.renderable; - -import com.mojang.blaze3d.vertex.PoseStack; -import java.util.Arrays; -import net.minecraft.client.Minecraft; -import net.minecraft.client.renderer.MultiBufferSource; -import net.minecraft.client.renderer.block.model.BakedQuad; -import net.minecraft.client.resources.model.BakedModel; -import net.minecraft.client.resources.model.ModelResourceLocation; -import net.minecraft.core.Direction; -import net.minecraft.util.RandomSource; -import net.minecraft.util.Unit; -import net.minecraft.world.inventory.InventoryMenu; -import net.minecraft.world.level.block.state.BlockState; -import net.neoforged.neoforge.client.event.ModelEvent; -import net.neoforged.neoforge.client.model.data.ModelData; -import org.jetbrains.annotations.Nullable; -import org.joml.Vector4f; - -/** - * {@linkplain IRenderable Renderable} wrapper for {@linkplain BakedModel baked models}. - *

    - * The context can provide the {@link BlockState}, faces to be rendered, a {@link RandomSource} and seed, - * a {@link ModelData} instance, and a {@link Vector4f tint}. - * - * @see Context - */ -public class BakedModelRenderable implements IRenderable { - /** - * Constructs a {@link BakedModelRenderable} from the given model location. - * The model is expected to have been baked ahead of time. - * - * @see ModelEvent.RegisterAdditional - */ - public static BakedModelRenderable of(ModelResourceLocation model) { - return of(Minecraft.getInstance().getModelManager().getModel(model)); - } - - /** - * Constructs a {@link BakedModelRenderable} from the given baked model. - */ - public static BakedModelRenderable of(BakedModel model) { - return new BakedModelRenderable(model); - } - - private final BakedModel model; - - private BakedModelRenderable(BakedModel model) { - this.model = model; - } - - @Override - public void render(PoseStack poseStack, MultiBufferSource bufferSource, ITextureRenderTypeLookup textureRenderTypeLookup, int lightmap, int overlay, float partialTick, Context context) { - var buffer = bufferSource.getBuffer(textureRenderTypeLookup.get(InventoryMenu.BLOCK_ATLAS)); - var tint = context.tint(); - var randomSource = context.randomSource(); - for (Direction direction : context.faces()) { - randomSource.setSeed(context.seed()); - // Given the lack of context, the requested render type has to be null to ensure the model renders all of its geometry - for (BakedQuad quad : model.getQuads(context.state(), direction, randomSource, context.data(), null)) - buffer.putBulkData(poseStack.last(), quad, tint.x(), tint.y(), tint.z(), tint.w(), lightmap, overlay, true); - } - } - - public IRenderable withContext(ModelData modelData) { - return withContext(new Context(modelData)); - } - - public IRenderable withModelDataContext() { - return (poseStack, bufferSource, textureRenderTypeLookup, lightmap, overlay, partialTick, context) -> render(poseStack, bufferSource, textureRenderTypeLookup, lightmap, overlay, partialTick, new Context(context)); - } - - public record Context(@Nullable BlockState state, Direction[] faces, RandomSource randomSource, long seed, ModelData data, Vector4f tint) { - - private static final Direction[] ALL_FACES_AND_NULL = Arrays.copyOf(Direction.values(), Direction.values().length + 1); - private static final Vector4f WHITE = new Vector4f(1, 1, 1, 1); - public Context(ModelData data) { - this(null, ALL_FACES_AND_NULL, RandomSource.create(), 42, data, WHITE); - } - } -} diff --git a/src/main/java/net/neoforged/neoforge/client/model/renderable/CompositeRenderable.java b/src/main/java/net/neoforged/neoforge/client/model/renderable/CompositeRenderable.java deleted file mode 100644 index d4fec4b841..0000000000 --- a/src/main/java/net/neoforged/neoforge/client/model/renderable/CompositeRenderable.java +++ /dev/null @@ -1,153 +0,0 @@ -/* - * Copyright (c) Forge Development LLC and contributors - * SPDX-License-Identifier: LGPL-2.1-only - */ - -package net.neoforged.neoforge.client.model.renderable; - -import com.google.common.collect.ImmutableMap; -import com.mojang.blaze3d.vertex.PoseStack; -import java.util.ArrayList; -import java.util.List; -import net.minecraft.client.renderer.MultiBufferSource; -import net.minecraft.client.renderer.block.model.BakedQuad; -import net.minecraft.resources.ResourceLocation; -import org.jetbrains.annotations.Nullable; -import org.joml.Matrix4f; - -/** - * A renderable object composed of a hierarchy of parts, each made up of a number of meshes. - *

    - * Each mesh renders a set of quads using a different texture. - * - * @see Builder - */ -public class CompositeRenderable implements IRenderable { - private final List components = new ArrayList<>(); - - private CompositeRenderable() {} - - @Override - public void render(PoseStack poseStack, MultiBufferSource bufferSource, ITextureRenderTypeLookup textureRenderTypeLookup, int lightmap, int overlay, float partialTick, Transforms context) { - for (var component : components) - component.render(poseStack, bufferSource, textureRenderTypeLookup, lightmap, overlay, context); - } - - public static Builder builder() { - return new Builder(); - } - - private static class Component { - private final String name; - private final List children = new ArrayList<>(); - private final List meshes = new ArrayList<>(); - - public Component(String name) { - this.name = name; - } - - public void render(PoseStack poseStack, MultiBufferSource bufferSource, ITextureRenderTypeLookup textureRenderTypeLookup, int lightmap, int overlay, Transforms context) { - Matrix4f matrix = context.getTransform(name); - if (matrix != null) { - poseStack.pushPose(); - poseStack.mulPose(matrix); - } - - for (var part : children) - part.render(poseStack, bufferSource, textureRenderTypeLookup, lightmap, overlay, context); - - for (var mesh : meshes) - mesh.render(poseStack, bufferSource, textureRenderTypeLookup, lightmap, overlay); - - if (matrix != null) - poseStack.popPose(); - } - } - - private static class Mesh { - private final ResourceLocation texture; - private final List quads = new ArrayList<>(); - - public Mesh(ResourceLocation texture) { - this.texture = texture; - } - - public void render(PoseStack poseStack, MultiBufferSource bufferSource, ITextureRenderTypeLookup textureRenderTypeLookup, int lightmap, int overlay) { - var consumer = bufferSource.getBuffer(textureRenderTypeLookup.get(texture)); - for (var quad : quads) { - consumer.putBulkData(poseStack.last(), quad, 1, 1, 1, 1, lightmap, overlay, true); - } - } - } - - public static class Builder { - private final CompositeRenderable renderable = new CompositeRenderable(); - - private Builder() {} - - public PartBuilder child(String name) { - var child = new Component(name); - renderable.components.add(child); - return new PartBuilder<>(this, child); - } - - public CompositeRenderable get() { - return renderable; - } - } - - public static class PartBuilder { - private final T parent; - private final Component component; - - private PartBuilder(T parent, Component component) { - this.parent = parent; - this.component = component; - } - - public PartBuilder> child(String name) { - var child = new Component(component.name + "/" + name); - this.component.children.add(child); - return new PartBuilder<>(this, child); - } - - public PartBuilder addMesh(ResourceLocation texture, List quads) { - var mesh = new Mesh(texture); - mesh.quads.addAll(quads); - component.meshes.add(mesh); - return this; - } - - public T end() { - return parent; - } - } - - /** - * A context value that provides {@link Matrix4f} transforms for certain parts of the model. - */ - public static class Transforms { - /** - * A default instance that has no transforms specified. - */ - public static final Transforms EMPTY = new Transforms(ImmutableMap.of()); - - /** - * Builds a MultipartTransforms object with the given mapping. - */ - public static Transforms of(ImmutableMap parts) { - return new Transforms(parts); - } - - private final ImmutableMap parts; - - private Transforms(ImmutableMap parts) { - this.parts = parts; - } - - @Nullable - public Matrix4f getTransform(String part) { - return parts.get(part); - } - } -} diff --git a/src/main/java/net/neoforged/neoforge/client/model/renderable/IRenderable.java b/src/main/java/net/neoforged/neoforge/client/model/renderable/IRenderable.java deleted file mode 100644 index 3a55eb3e46..0000000000 --- a/src/main/java/net/neoforged/neoforge/client/model/renderable/IRenderable.java +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright (c) Forge Development LLC and contributors - * SPDX-License-Identifier: LGPL-2.1-only - */ - -package net.neoforged.neoforge.client.model.renderable; - -import com.mojang.blaze3d.vertex.PoseStack; -import net.minecraft.client.renderer.MultiBufferSource; -import net.minecraft.util.Unit; - -/** - * A standard interface for things that can be rendered to a {@link MultiBufferSource}. - * - * @param The type of context object used by the rendering logic - */ -@FunctionalInterface -public interface IRenderable { - /** - * Draws the renderable by adding the geometry to the provided {@link MultiBufferSource} - * - * @param poseStack The pose stack - * @param bufferSource The buffer source where the vertex data should be output - * @param textureRenderTypeLookup A function that provides a RenderType for the given texture - * @param lightmap The lightmap coordinates representing the current lighting conditions. See {@link net.minecraft.client.renderer.LightTexture} - * @param overlay The overlay coordinates representing the current overlay status. See {@link net.minecraft.client.renderer.texture.OverlayTexture} - * @param partialTick The current time expressed in the fraction of a tick elapsed since the last client tick - * @param context The context used for rendering - */ - void render(PoseStack poseStack, MultiBufferSource bufferSource, ITextureRenderTypeLookup textureRenderTypeLookup, int lightmap, int overlay, float partialTick, T context); - - /** - * Wraps the current renderable along with a context. - * Useful for keeping a list of various renderables paired with their contexts. - * - * @param context The context used for rendering - * @return A renderable that accepts {@link Unit#INSTANCE} as context, but uses the provided {@code context} instead - */ - default IRenderable withContext(T context) { - return (poseStack, bufferSource, textureRenderTypeLookup, lightmap, overlay, partialTick, unused) -> this.render(poseStack, bufferSource, textureRenderTypeLookup, lightmap, overlay, partialTick, context); - } -} diff --git a/src/main/java/net/neoforged/neoforge/client/model/renderable/ITextureRenderTypeLookup.java b/src/main/java/net/neoforged/neoforge/client/model/renderable/ITextureRenderTypeLookup.java deleted file mode 100644 index 8c05205fdd..0000000000 --- a/src/main/java/net/neoforged/neoforge/client/model/renderable/ITextureRenderTypeLookup.java +++ /dev/null @@ -1,17 +0,0 @@ -/* - * Copyright (c) Forge Development LLC and contributors - * SPDX-License-Identifier: LGPL-2.1-only - */ - -package net.neoforged.neoforge.client.model.renderable; - -import net.minecraft.client.renderer.RenderType; -import net.minecraft.resources.ResourceLocation; - -/** - * A generic lookup for {@link RenderType} implementations that use the specified texture. - */ -@FunctionalInterface -public interface ITextureRenderTypeLookup { - RenderType get(ResourceLocation name); -} diff --git a/src/main/java/net/neoforged/neoforge/client/renderstate/BaseRenderState.java b/src/main/java/net/neoforged/neoforge/client/renderstate/BaseRenderState.java new file mode 100644 index 0000000000..cbdd40218d --- /dev/null +++ b/src/main/java/net/neoforged/neoforge/client/renderstate/BaseRenderState.java @@ -0,0 +1,43 @@ +/* + * Copyright (c) NeoForged and contributors + * SPDX-License-Identifier: LGPL-2.1-only + */ + +package net.neoforged.neoforge.client.renderstate; + +import it.unimi.dsi.fastutil.objects.Reference2ObjectOpenHashMap; +import java.util.Map; +import net.minecraft.client.renderer.entity.state.EntityRenderState; +import net.minecraft.util.context.ContextKey; +import net.neoforged.neoforge.client.extensions.IRenderStateExtension; +import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.Nullable; + +/** + * Extension class for RenderState objects (ie {@link EntityRenderState}). + * Allows modders to add arbitrary data onto render states for use in custom rendering. + */ +public abstract class BaseRenderState implements IRenderStateExtension { + protected final Map, Object> extensions = new Reference2ObjectOpenHashMap<>(); + + @SuppressWarnings("unchecked") + @Nullable + @Override + public T getRenderData(ContextKey key) { + return (T) extensions.get(key); + } + + @Override + public void setRenderData(ContextKey key, @Nullable T data) { + if (data != null) { + extensions.put(key, data); + } else { + extensions.remove(key); + } + } + + @ApiStatus.Internal + public void resetRenderData() { + extensions.clear(); + } +} diff --git a/src/main/java/net/neoforged/neoforge/client/renderstate/MapDecorationRenderStateModifier.java b/src/main/java/net/neoforged/neoforge/client/renderstate/MapDecorationRenderStateModifier.java new file mode 100644 index 0000000000..50f8c1a1d7 --- /dev/null +++ b/src/main/java/net/neoforged/neoforge/client/renderstate/MapDecorationRenderStateModifier.java @@ -0,0 +1,27 @@ +/* + * Copyright (c) NeoForged and contributors + * SPDX-License-Identifier: LGPL-2.1-only + */ + +package net.neoforged.neoforge.client.renderstate; + +import net.minecraft.client.renderer.state.MapRenderState; +import net.minecraft.world.level.saveddata.maps.MapDecorationType; +import net.minecraft.world.level.saveddata.maps.MapItemSavedData; +import net.neoforged.neoforge.client.gui.map.IMapDecorationRenderer; + +/** + * Function interface for render state modifiers that target MapDecorations. Useful for adding custom data for rendering + * in {@link IMapDecorationRenderer}s. + */ +@FunctionalInterface +public interface MapDecorationRenderStateModifier { + /** + * Called when the registered {@link MapDecorationType} is added to a {@link MapRenderState}. + * + * @param mapItemSavedData The map SavedData. + * @param mapRenderState The render state of the map after the texture has been set and custom data is added. + * @param mapDecorationRenderState The decoration render state after vanilla has set it up. + */ + void accept(MapItemSavedData mapItemSavedData, MapRenderState mapRenderState, MapRenderState.MapDecorationRenderState mapDecorationRenderState); +} diff --git a/src/main/java/net/neoforged/neoforge/client/renderstate/RegisterRenderStateModifiersEvent.java b/src/main/java/net/neoforged/neoforge/client/renderstate/RegisterRenderStateModifiersEvent.java new file mode 100644 index 0000000000..27e30088a4 --- /dev/null +++ b/src/main/java/net/neoforged/neoforge/client/renderstate/RegisterRenderStateModifiersEvent.java @@ -0,0 +1,142 @@ +/* + * Copyright (c) NeoForged and contributors + * SPDX-License-Identifier: LGPL-2.1-only + */ + +package net.neoforged.neoforge.client.renderstate; + +import com.google.common.reflect.TypeParameter; +import com.google.common.reflect.TypeToken; +import java.lang.reflect.ParameterizedType; +import java.util.function.BiConsumer; +import net.minecraft.client.renderer.entity.EntityRenderer; +import net.minecraft.client.renderer.entity.state.EntityRenderState; +import net.minecraft.client.renderer.state.MapRenderState; +import net.minecraft.resources.ResourceKey; +import net.minecraft.util.context.ContextKey; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.level.saveddata.maps.MapDecorationType; +import net.minecraft.world.level.saveddata.maps.MapItemSavedData; +import net.neoforged.bus.api.Event; +import net.neoforged.fml.LogicalSide; +import net.neoforged.fml.event.IModBusEvent; +import net.neoforged.neoforge.client.extensions.IRenderStateExtension; +import org.jetbrains.annotations.ApiStatus; + +/** + * Fired for registering modifier functions for various render state objects. Useful for gathering context for + * custom rendering with objects that are not your own. + * + *

    This event is fired on the mod-specific event bus, only on the {@linkplain LogicalSide#CLIENT logical client}.

    + */ +public class RegisterRenderStateModifiersEvent extends Event implements IModBusEvent { + @ApiStatus.Internal + public RegisterRenderStateModifiersEvent() {} + + /** + * Registers a render state modifier for {@link EntityRenderState}s which are run after all vanilla data is + * extracted. Can add custom data to the map using {@link EntityRenderState#setRenderData(ContextKey, Object)}. + * Any subclasses of the passed renderer class will also have this modifier applied. + * + *
    +     * 
    +     *     event.registerEntityModifier(new TypeToken>() {}, (entity, renderState) -> {
    +     *         . . .
    +     *     });
    +     * 
    +     * 
    + * + * @param baseRenderer Entity renderer class. Any subclasses will also apply this modifier. + * @param modifier The function for modifying the {@link EntityRenderState} and adding custom render data. + * @param The type of the entity + * @param The specific render state type + */ + public void registerEntityModifier(TypeToken> baseRenderer, BiConsumer modifier) { + ensureParametersMatchBounds(baseRenderer); + RenderStateExtensions.registerEntity(baseRenderer.getRawType(), modifier); + } + + /** + * Convenience method for cases where generics are not present. Registers a render state modifier for + * {@link EntityRenderState}s which are run after all vanilla data is extracted. Can add custom data to the map + * using {@link EntityRenderState#setRenderData(ContextKey, Object)}. Any subclasses of the passed renderer class + * will also have this modifier applied. + * + *
    +     * 
    +     *     event.registerEntityModifier(PlayerRenderer.class, (entity, renderState) -> {
    +     *         . . .
    +     *     });
    +     * 
    +     * 
    + * + * @param baseRenderer Entity renderer class. Any subclasses will also apply this modifier. + * @param modifier The function for modifying the {@link EntityRenderState} and adding custom render data. + * @param The type of the entity + * @param The specific render state type + */ + public void registerEntityModifier(Class> baseRenderer, BiConsumer modifier) { + ensureParametersMatchBounds(TypeToken.of(baseRenderer)); + RenderStateExtensions.registerEntity(baseRenderer, modifier); + } + + /** + * Registers a render state modifier for {@link MapRenderState}s which are run after the texture has been set + * and before decorations have been added. Can add custom data to the map using + * {@link IRenderStateExtension#setRenderData(ContextKey, Object)}. + * + * @param modifier The function for modifying the {@link net.minecraft.client.renderer.state.MapRenderState} and adding custom render data. + */ + public void registerMapModifier(BiConsumer modifier) { + RenderStateExtensions.registerMap(modifier); + } + + /** + * Registers a render state modifier for {@link MapRenderState.MapDecorationRenderState}s which are run after + * vanilla map decoration data has been set. Can add custom data to the map using + * {@link IRenderStateExtension#setRenderData(ContextKey, Object)}. + * + * @param mapDecorationTypeKey Key for the registered {@link MapDecorationType} + * @param modifier The function for modifying the {@link MapRenderState.MapDecorationRenderState} and adding custom render data. + */ + public void registerMapDecorationModifier(ResourceKey mapDecorationTypeKey, MapDecorationRenderStateModifier modifier) { + RenderStateExtensions.registerMapDecoration(mapDecorationTypeKey, modifier); + } + + private static void ensureParametersMatchBounds(TypeToken> baseRenderer) { + if (baseRenderer.getType() instanceof ParameterizedType parameterizedType) { + Class bound = baseRenderer.getRawType(); + ParameterizedType parameterized = parameterizedType; + do { + var userArgs = parameterized.getActualTypeArguments(); + var typeArgs = bound.getTypeParameters(); + + for (int i = 0; i < userArgs.length; i++) { + var userArg = userArgs[i]; + var userToken = Container.of(TypeToken.of(userArg)); + var typeArg = typeArgs[i]; + for (var singleBound : typeArg.getBounds()) { + var token = Container.of(TypeToken.of(singleBound)); + if (!token.isSubtypeOf(userToken)) { + throw new IllegalArgumentException("%s does not match expected type parameter %s".formatted(userArg, singleBound)); + } + } + } + + if (!(parameterized.getOwnerType() instanceof ParameterizedType parameterizedOwner)) { + break; + } + parameterized = parameterizedOwner; + bound = bound.getEnclosingClass(); + } while (bound != null); + } + } + + @SuppressWarnings("unused") + private record Container() { + private static TypeToken> of(TypeToken parameter) { + return new TypeToken>() {} + .where(new TypeParameter<>() {}, parameter); + } + } +} diff --git a/src/main/java/net/neoforged/neoforge/client/renderstate/RenderStateExtensions.java b/src/main/java/net/neoforged/neoforge/client/renderstate/RenderStateExtensions.java new file mode 100644 index 0000000000..76c0525f13 --- /dev/null +++ b/src/main/java/net/neoforged/neoforge/client/renderstate/RenderStateExtensions.java @@ -0,0 +1,83 @@ +/* + * Copyright (c) NeoForged and contributors + * SPDX-License-Identifier: LGPL-2.1-only + */ + +package net.neoforged.neoforge.client.renderstate; + +import com.google.common.collect.ImmutableList; +import it.unimi.dsi.fastutil.objects.ObjectArrayList; +import it.unimi.dsi.fastutil.objects.Reference2ObjectArrayMap; +import it.unimi.dsi.fastutil.objects.Reference2ObjectOpenHashMap; +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.function.BiConsumer; +import net.minecraft.client.renderer.entity.EntityRenderer; +import net.minecraft.client.renderer.entity.state.EntityRenderState; +import net.minecraft.client.renderer.state.MapRenderState; +import net.minecraft.core.Holder; +import net.minecraft.resources.ResourceKey; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.level.saveddata.maps.MapDecorationType; +import net.minecraft.world.level.saveddata.maps.MapItemSavedData; +import org.jetbrains.annotations.ApiStatus; + +public final class RenderStateExtensions { + private RenderStateExtensions() {} + + private static final Map, Collection>> ENTITY = new Reference2ObjectArrayMap<>(); + private static final Map, Collection>> ENTITY_CACHE = new Reference2ObjectOpenHashMap<>(); + + private static final List> MAP = new ObjectArrayList<>(); + + private static final Map, Collection> MAP_DECORATION = new Reference2ObjectArrayMap<>(); + + @SuppressWarnings("unchecked") + @ApiStatus.Internal + public static void onUpdateEntityRenderState(EntityRenderer renderer, E entity, S renderState) { + renderState.resetRenderData(); + var modifiers = (Collection>) (Object) ENTITY_CACHE.computeIfAbsent(renderer.getClass(), aClass -> { + var builder = ImmutableList.>builder(); + for (var entry : ENTITY.entrySet()) { + if (entry.getKey().isAssignableFrom(aClass)) { + builder.addAll(entry.getValue()); + } + } + return builder.build(); + }); + for (BiConsumer modifier : modifiers) { + modifier.accept(entity, renderState); + } + } + + @ApiStatus.Internal + public static void onUpdateMapRenderState(MapItemSavedData mapItemSavedData, MapRenderState renderState) { + renderState.resetRenderData(); + for (BiConsumer modifier : MAP) { + modifier.accept(mapItemSavedData, renderState); + } + } + + @ApiStatus.Internal + public static MapRenderState.MapDecorationRenderState onUpdateMapDecorationRenderState(Holder mapDecorationTypeHolder, MapItemSavedData mapItemSavedData, MapRenderState mapRenderState, MapRenderState.MapDecorationRenderState mapDecorationRenderState) { + mapDecorationRenderState.resetRenderData(); + var modifiers = MAP_DECORATION.getOrDefault(mapDecorationTypeHolder.getKey(), List.of()); + for (var modifier : modifiers) { + modifier.accept(mapItemSavedData, mapRenderState, mapDecorationRenderState); + } + return mapDecorationRenderState; + } + + static void registerEntity(Class baseRenderer, BiConsumer modifier) { + ENTITY.computeIfAbsent(baseRenderer, aClass -> new ObjectArrayList<>()).add(modifier); + } + + static void registerMap(BiConsumer modifier) { + MAP.add(modifier); + } + + static void registerMapDecoration(ResourceKey mapDecorationTypeKey, MapDecorationRenderStateModifier modifier) { + MAP_DECORATION.computeIfAbsent(mapDecorationTypeKey, aClass -> new ObjectArrayList<>()).add(modifier); + } +} diff --git a/src/main/java/net/neoforged/neoforge/client/renderstate/package-info.java b/src/main/java/net/neoforged/neoforge/client/renderstate/package-info.java new file mode 100644 index 0000000000..c698250154 --- /dev/null +++ b/src/main/java/net/neoforged/neoforge/client/renderstate/package-info.java @@ -0,0 +1,13 @@ +/* + * Copyright (c) NeoForged and contributors + * SPDX-License-Identifier: LGPL-2.1-only + */ + +@FieldsAreNonnullByDefault +@MethodsReturnNonnullByDefault +@ParametersAreNonnullByDefault +package net.neoforged.neoforge.client.renderstate; + +import javax.annotation.ParametersAreNonnullByDefault; +import net.minecraft.FieldsAreNonnullByDefault; +import net.minecraft.MethodsReturnNonnullByDefault; diff --git a/src/main/java/net/neoforged/neoforge/common/CommonHooks.java b/src/main/java/net/neoforged/neoforge/common/CommonHooks.java index d81c78914c..f99ab9d2d1 100644 --- a/src/main/java/net/neoforged/neoforge/common/CommonHooks.java +++ b/src/main/java/net/neoforged/neoforge/common/CommonHooks.java @@ -952,7 +952,7 @@ public static ItemStack getProjectile(LivingEntity entity, ItemStack projectileW * Used as the default implementation of {@link Item#getCreatorModId}. Call that method instead. */ @Nullable - public static String getDefaultCreatorModId(ItemStack itemStack) { + public static String getDefaultCreatorModId(HolderLookup.Provider registries, ItemStack itemStack) { Item item = itemStack.getItem(); ResourceLocation registryName = BuiltInRegistries.ITEM.getKey(item); String modId = registryName == null ? null : registryName.getNamespace(); @@ -974,7 +974,7 @@ public static String getDefaultCreatorModId(ItemStack itemStack) { return key.get().location().getNamespace(); } } else if (item instanceof SpawnEggItem spawnEggItem) { - Optional>> key = BuiltInRegistries.ENTITY_TYPE.getResourceKey(spawnEggItem.getType(itemStack)); + Optional>> key = BuiltInRegistries.ENTITY_TYPE.getResourceKey(spawnEggItem.getType(registries, itemStack)); if (key.isPresent()) { return key.get().location().getNamespace(); } diff --git a/src/main/java/net/neoforged/neoforge/common/DeferredSpawnEggItem.java b/src/main/java/net/neoforged/neoforge/common/DeferredSpawnEggItem.java deleted file mode 100644 index 67c84ef09a..0000000000 --- a/src/main/java/net/neoforged/neoforge/common/DeferredSpawnEggItem.java +++ /dev/null @@ -1,100 +0,0 @@ -/* - * Copyright (c) Forge Development LLC and contributors - * SPDX-License-Identifier: LGPL-2.1-only - */ - -package net.neoforged.neoforge.common; - -import java.util.ArrayList; -import java.util.IdentityHashMap; -import java.util.List; -import java.util.Map; -import java.util.function.Supplier; -import net.minecraft.core.Direction; -import net.minecraft.core.dispenser.DispenseItemBehavior; -import net.minecraft.util.ARGB; -import net.minecraft.world.entity.EntitySpawnReason; -import net.minecraft.world.entity.EntityType; -import net.minecraft.world.entity.Mob; -import net.minecraft.world.item.ItemStack; -import net.minecraft.world.item.SpawnEggItem; -import net.minecraft.world.level.block.DispenserBlock; -import net.minecraft.world.level.gameevent.GameEvent; -import net.neoforged.api.distmarker.Dist; -import net.neoforged.bus.api.EventPriority; -import net.neoforged.bus.api.SubscribeEvent; -import net.neoforged.fml.common.EventBusSubscriber; -import net.neoforged.fml.event.lifecycle.FMLCommonSetupEvent; -import net.neoforged.neoforge.client.event.RegisterColorHandlersEvent; -import org.jetbrains.annotations.ApiStatus; -import org.jetbrains.annotations.Nullable; - -public class DeferredSpawnEggItem extends SpawnEggItem { - private static final List MOD_EGGS = new ArrayList<>(); - private static final Map, DeferredSpawnEggItem> TYPE_MAP = new IdentityHashMap<>(); - private final Supplier> typeSupplier; - - public DeferredSpawnEggItem(Supplier> type, int backgroundColor, int highlightColor, Properties props) { - super((EntityType) null, backgroundColor, highlightColor, props); - this.typeSupplier = type; - - MOD_EGGS.add(this); - } - - @Nullable - protected DispenseItemBehavior createDispenseBehavior() { - return DEFAULT_DISPENSE_BEHAVIOR; - } - - @ApiStatus.Internal - @Nullable - public static SpawnEggItem deferredOnlyById(@Nullable EntityType type) { - return TYPE_MAP.get(type); - } - - @Override - protected EntityType getDefaultType() { - return this.typeSupplier.get(); - } - - private static final DispenseItemBehavior DEFAULT_DISPENSE_BEHAVIOR = (source, stack) -> { - Direction face = source.state().getValue(DispenserBlock.FACING); - EntityType type = ((SpawnEggItem) stack.getItem()).getType(stack); - - try { - type.spawn(source.level(), stack, null, source.pos().relative(face), EntitySpawnReason.DISPENSER, face != Direction.UP, false); - } catch (Exception exception) { - DispenseItemBehavior.LOGGER.error("Error while dispensing spawn egg from dispenser at {}", source.pos(), exception); - return ItemStack.EMPTY; - } - - stack.shrink(1); - source.level().gameEvent(GameEvent.ENTITY_PLACE, source.pos(), GameEvent.Context.of(source.state())); - return stack; - }; - - @EventBusSubscriber(modid = "neoforge", bus = EventBusSubscriber.Bus.MOD) - private static class CommonHandler { - @SubscribeEvent - public static void onCommonSetup(FMLCommonSetupEvent event) { - event.enqueueWork(() -> { - MOD_EGGS.forEach(egg -> { - DispenseItemBehavior dispenseBehavior = egg.createDispenseBehavior(); - if (dispenseBehavior != null) { - DispenserBlock.registerBehavior(egg, dispenseBehavior); - } - - TYPE_MAP.put(egg.typeSupplier.get(), egg); - }); - }); - } - } - - @EventBusSubscriber(value = Dist.CLIENT, modid = "neoforge", bus = EventBusSubscriber.Bus.MOD) - private static class ColorRegisterHandler { - @SubscribeEvent(priority = EventPriority.HIGHEST) - public static void registerSpawnEggColors(RegisterColorHandlersEvent.Item event) { - MOD_EGGS.forEach(egg -> event.register((stack, layer) -> ARGB.opaque(egg.getColor(layer)), egg)); - } - } -} diff --git a/src/main/java/net/neoforged/neoforge/common/NeoForgeConfig.java b/src/main/java/net/neoforged/neoforge/common/NeoForgeConfig.java index 91a232a8ae..e7335e0b74 100644 --- a/src/main/java/net/neoforged/neoforge/common/NeoForgeConfig.java +++ b/src/main/java/net/neoforged/neoforge/common/NeoForgeConfig.java @@ -8,6 +8,7 @@ import net.neoforged.bus.api.SubscribeEvent; import net.neoforged.fml.Logging; import net.neoforged.fml.event.config.ModConfigEvent; +import net.neoforged.neoforge.client.ClientHooks; import net.neoforged.neoforge.common.ModConfigSpec.BooleanValue; import net.neoforged.neoforge.common.ModConfigSpec.ConfigValue; import org.apache.commons.lang3.tuple.Pair; @@ -64,8 +65,11 @@ public static class Server { */ public static class Common { public final ModConfigSpec.EnumValue logUntranslatedItemTagWarnings; + public final ModConfigSpec.EnumValue logLegacyTagWarnings; + public final BooleanValue attributeAdvancedTooltipDebugInfo; + Common(ModConfigSpec.Builder builder) { logUntranslatedItemTagWarnings = builder .comment("A config option mainly for developers. Logs out modded item tags that do not have translations when running on integrated server. Format desired is tag.item.. for the translation key. Defaults to SILENCED.") @@ -76,6 +80,11 @@ public static class Common { .comment("A config option mainly for developers. Logs out modded tags that are using the 'forge' namespace when running on integrated server. Defaults to DEV_SHORT.") .translation("neoforge.configgui.logLegacyTagWarnings") .defineEnum("logLegacyTagWarnings", TagConventionLogWarning.LogWarningMode.DEV_SHORT); + + attributeAdvancedTooltipDebugInfo = builder + .comment("Set this to true to enable showing debug information about attributes on an item when advanced tooltips is on.") + .translation("neoforge.configgui.attributeAdvancedTooltipDebugInfo") + .define("attributeAdvancedTooltipDebugInfo", true); } } @@ -84,6 +93,7 @@ public static class Common { */ public static class Client { public final BooleanValue experimentalForgeLightPipelineEnabled; + boolean experimentalPipelineActive; public final BooleanValue showLoadWarnings; @@ -134,11 +144,23 @@ public static class Client { @SubscribeEvent public static void onLoad(final ModConfigEvent.Loading configEvent) { LogManager.getLogger().debug(Logging.FORGEMOD, "Loaded NeoForge config file {}", configEvent.getConfig().getFileName()); + + if (configEvent.getConfig().getSpec() == clientSpec) { + CLIENT.experimentalPipelineActive = CLIENT.experimentalForgeLightPipelineEnabled.getAsBoolean(); + } } @SubscribeEvent public static void onFileChange(final ModConfigEvent.Reloading configEvent) { LogManager.getLogger().debug(Logging.FORGEMOD, "NeoForge config just got changed on the file system!"); + + if (configEvent.getConfig().getSpec() == clientSpec) { + boolean experimentalPipelineActive = CLIENT.experimentalForgeLightPipelineEnabled.getAsBoolean(); + if (experimentalPipelineActive != CLIENT.experimentalPipelineActive) { + CLIENT.experimentalPipelineActive = experimentalPipelineActive; + ClientHooks.reloadRenderer(); + } + } } //General diff --git a/src/main/java/net/neoforged/neoforge/common/NeoForgeMod.java b/src/main/java/net/neoforged/neoforge/common/NeoForgeMod.java index c19d9e788f..168192189a 100644 --- a/src/main/java/net/neoforged/neoforge/common/NeoForgeMod.java +++ b/src/main/java/net/neoforged/neoforge/common/NeoForgeMod.java @@ -11,10 +11,8 @@ import com.mojang.serialization.codecs.RecordCodecBuilder; import java.util.EnumSet; import java.util.List; -import java.util.Optional; import java.util.Set; import java.util.UUID; -import java.util.concurrent.CompletableFuture; import java.util.function.Function; import net.minecraft.DetectedVersion; import net.minecraft.advancements.critereon.EntitySubPredicate; @@ -25,22 +23,14 @@ import net.minecraft.commands.synchronization.SingletonArgumentInfo; import net.minecraft.core.BlockPos; import net.minecraft.core.Holder; -import net.minecraft.core.HolderLookup; import net.minecraft.core.RegistryCodecs; import net.minecraft.core.particles.ParticleTypes; import net.minecraft.core.registries.Registries; -import net.minecraft.data.DataGenerator; -import net.minecraft.data.PackOutput; -import net.minecraft.data.metadata.PackMetadataGenerator; -import net.minecraft.network.chat.Component; import net.minecraft.resources.ResourceKey; import net.minecraft.resources.ResourceLocation; import net.minecraft.server.level.ServerLevel; -import net.minecraft.server.packs.PackType; -import net.minecraft.server.packs.metadata.pack.PackMetadataSection; import net.minecraft.sounds.SoundEvent; import net.minecraft.sounds.SoundEvents; -import net.minecraft.util.InclusiveRange; import net.minecraft.world.damagesource.DamageSources; import net.minecraft.world.damagesource.DamageType; import net.minecraft.world.effect.MobEffects; @@ -52,6 +42,7 @@ import net.minecraft.world.entity.ai.attributes.RangedAttribute; import net.minecraft.world.entity.item.ItemEntity; import net.minecraft.world.item.Items; +import net.minecraft.world.item.SpawnEggItem; import net.minecraft.world.item.crafting.display.SlotDisplay; import net.minecraft.world.level.BlockGetter; import net.minecraft.world.level.GameRules; @@ -90,6 +81,7 @@ import net.neoforged.neoforge.common.advancements.critereon.SnowBootsEntityPredicate; import net.neoforged.neoforge.common.conditions.AndCondition; import net.neoforged.neoforge.common.conditions.FalseCondition; +import net.neoforged.neoforge.common.conditions.FeatureFlagsEnabledCondition; import net.neoforged.neoforge.common.conditions.ICondition; import net.neoforged.neoforge.common.conditions.ItemExistsCondition; import net.neoforged.neoforge.common.conditions.ModLoadedCondition; @@ -104,28 +96,12 @@ import net.neoforged.neoforge.common.crafting.DifferenceIngredient; import net.neoforged.neoforge.common.crafting.IngredientType; import net.neoforged.neoforge.common.crafting.IntersectionIngredient; -import net.neoforged.neoforge.common.data.ExistingFileHelper; -import net.neoforged.neoforge.common.data.internal.NeoForgeAdvancementProvider; -import net.neoforged.neoforge.common.data.internal.NeoForgeBiomeTagsProvider; -import net.neoforged.neoforge.common.data.internal.NeoForgeBlockTagsProvider; -import net.neoforged.neoforge.common.data.internal.NeoForgeDamageTypeTagsProvider; -import net.neoforged.neoforge.common.data.internal.NeoForgeDataMapsProvider; -import net.neoforged.neoforge.common.data.internal.NeoForgeEnchantmentTagsProvider; -import net.neoforged.neoforge.common.data.internal.NeoForgeEntityTypeTagsProvider; -import net.neoforged.neoforge.common.data.internal.NeoForgeFluidTagsProvider; -import net.neoforged.neoforge.common.data.internal.NeoForgeItemTagsProvider; -import net.neoforged.neoforge.common.data.internal.NeoForgeLanguageProvider; -import net.neoforged.neoforge.common.data.internal.NeoForgeLootTableProvider; -import net.neoforged.neoforge.common.data.internal.NeoForgeRecipeProvider; -import net.neoforged.neoforge.common.data.internal.NeoForgeRegistryOrderReportProvider; -import net.neoforged.neoforge.common.data.internal.NeoForgeSpriteSourceProvider; -import net.neoforged.neoforge.common.data.internal.NeoForgeStructureTagsProvider; -import net.neoforged.neoforge.common.data.internal.VanillaSoundDefinitionsProvider; import net.neoforged.neoforge.common.extensions.IPlayerExtension; import net.neoforged.neoforge.common.loot.AddTableLootModifier; import net.neoforged.neoforge.common.loot.CanItemPerformAbility; import net.neoforged.neoforge.common.loot.IGlobalLootModifier; import net.neoforged.neoforge.common.loot.LootTableIdCondition; +import net.neoforged.neoforge.common.util.SelfTest; import net.neoforged.neoforge.common.world.BiomeModifier; import net.neoforged.neoforge.common.world.BiomeModifiers; import net.neoforged.neoforge.common.world.BiomeModifiers.AddFeaturesBiomeModifier; @@ -136,7 +112,6 @@ import net.neoforged.neoforge.common.world.NoneStructureModifier; import net.neoforged.neoforge.common.world.StructureModifier; import net.neoforged.neoforge.common.world.StructureModifiers; -import net.neoforged.neoforge.data.event.GatherDataEvent; import net.neoforged.neoforge.event.server.ServerStoppingEvent; import net.neoforged.neoforge.fluids.BaseFlowingFluid; import net.neoforged.neoforge.fluids.CauldronFluidContent; @@ -389,6 +364,7 @@ public class NeoForgeMod { public static final DeferredHolder, MapCodec> OR_CONDITION = CONDITION_CODECS.register("or", () -> OrCondition.CODEC); public static final DeferredHolder, MapCodec> TAG_EMPTY_CONDITION = CONDITION_CODECS.register("tag_empty", () -> TagEmptyCondition.CODEC); public static final DeferredHolder, MapCodec> TRUE_CONDITION = CONDITION_CODECS.register("true", () -> TrueCondition.CODEC); + public static final DeferredHolder, MapCodec> FEATURE_FLAGS_ENABLED_CONDITION = CONDITION_CODECS.register("feature_flags_enabled", () -> FeatureFlagsEnabledCondition.CODEC); private static final DeferredRegister> ENTITY_PREDICATE_CODECS = DeferredRegister.create(Registries.ENTITY_SUB_PREDICATE_TYPE, NeoForgeVersion.MOD_ID); public static final DeferredHolder, MapCodec> PIGLIN_NEUTRAL_ARMOR_PREDICATE = ENTITY_PREDICATE_CODECS.register("piglin_neutral_armor", () -> PiglinNeutralArmorEntityPredicate.CODEC); @@ -529,6 +505,8 @@ public NeoForgeMod(IEventBus modEventBus, Dist dist, ModContainer container) { LOGGER.info(NEOFORGEMOD, "NeoForge mod loading, version {}, for MC {}", NeoForgeVersion.getVersion(), DetectedVersion.BUILT_IN.getName()); ForgeSnapshotsMod.logStartupWarning(); + SelfTest.initCommon(); + CrashReportCallables.registerCrashCallable("Crash Report UUID", () -> { final UUID uuid = UUID.randomUUID(); LOGGER.fatal("Preparing crash report with UUID {}", uuid); @@ -544,7 +522,6 @@ public NeoForgeMod(IEventBus modEventBus, Dist dist, ModContainer container) { event.dataPackRegistry(NeoForgeRegistries.Keys.STRUCTURE_MODIFIERS, StructureModifier.DIRECT_CODEC); }); modEventBus.addListener(this::preInit); - modEventBus.addListener(this::gatherData); modEventBus.addListener(this::loadComplete); modEventBus.addListener(this::registerFluids); modEventBus.addListener(this::registerLootData); @@ -588,6 +565,8 @@ public NeoForgeMod(IEventBus modEventBus, Dist dist, ModContainer container) { modEventBus.register(NeoForgeDataMaps.class); + modEventBus.register(SpawnEggItem.class); // Registers dispenser behaviour for eggs + if (isPRBuild(container.getModInfo().getVersion().toString())) { isPRBuild = true; ModLoader.addLoadingIssue(ModLoadingIssue.warning("loadwarning.neoforge.prbuild").withAffectedMod(container.getModInfo())); @@ -611,37 +590,6 @@ public void serverStopping(ServerStoppingEvent evt) { }); } - public void gatherData(GatherDataEvent event) { - DataGenerator gen = event.getGenerator(); - PackOutput packOutput = gen.getPackOutput(); - CompletableFuture lookupProvider = event.getLookupProvider(); - - ExistingFileHelper existingFileHelper = event.getExistingFileHelper(); - gen.addProvider(true, new PackMetadataGenerator(packOutput) - .add(PackMetadataSection.TYPE, new PackMetadataSection( - Component.translatable("pack.neoforge.description"), - DetectedVersion.BUILT_IN.getPackVersion(PackType.SERVER_DATA), - Optional.of(new InclusiveRange<>(0, Integer.MAX_VALUE))))); - gen.addProvider(event.includeServer(), new NeoForgeAdvancementProvider(packOutput, lookupProvider, existingFileHelper)); - NeoForgeBlockTagsProvider blockTags = new NeoForgeBlockTagsProvider(packOutput, lookupProvider, existingFileHelper); - gen.addProvider(event.includeServer(), blockTags); - gen.addProvider(event.includeServer(), new NeoForgeItemTagsProvider(packOutput, lookupProvider, blockTags.contentsGetter(), existingFileHelper)); - gen.addProvider(event.includeServer(), new NeoForgeEntityTypeTagsProvider(packOutput, lookupProvider, existingFileHelper)); - gen.addProvider(event.includeServer(), new NeoForgeFluidTagsProvider(packOutput, lookupProvider, existingFileHelper)); - gen.addProvider(event.includeServer(), new NeoForgeEnchantmentTagsProvider(packOutput, lookupProvider, existingFileHelper)); - gen.addProvider(event.includeServer(), new NeoForgeRecipeProvider.Runner(packOutput, lookupProvider)); - gen.addProvider(event.includeServer(), new NeoForgeLootTableProvider(packOutput, lookupProvider)); - gen.addProvider(event.includeServer(), new NeoForgeBiomeTagsProvider(packOutput, lookupProvider, existingFileHelper)); - gen.addProvider(event.includeServer(), new NeoForgeStructureTagsProvider(packOutput, lookupProvider, existingFileHelper)); - gen.addProvider(event.includeServer(), new NeoForgeDamageTypeTagsProvider(packOutput, lookupProvider, existingFileHelper)); - gen.addProvider(event.includeServer(), new NeoForgeRegistryOrderReportProvider(packOutput)); - gen.addProvider(event.includeServer(), new NeoForgeDataMapsProvider(packOutput, lookupProvider)); - - gen.addProvider(event.includeClient(), new NeoForgeSpriteSourceProvider(packOutput, lookupProvider, existingFileHelper)); - gen.addProvider(event.includeClient(), new VanillaSoundDefinitionsProvider(packOutput, existingFileHelper)); - gen.addProvider(event.includeClient(), new NeoForgeLanguageProvider(packOutput)); - } - // done in an event instead of deferred to only enable if a mod requests it public void registerFluids(RegisterEvent event) { if (enableMilkFluid) { diff --git a/src/main/java/net/neoforged/neoforge/common/PercentageAttribute.java b/src/main/java/net/neoforged/neoforge/common/PercentageAttribute.java index 75a845fea8..cb993e3132 100644 --- a/src/main/java/net/neoforged/neoforge/common/PercentageAttribute.java +++ b/src/main/java/net/neoforged/neoforge/common/PercentageAttribute.java @@ -10,6 +10,7 @@ import net.minecraft.world.entity.ai.attributes.AttributeModifier.Operation; import net.minecraft.world.entity.ai.attributes.RangedAttribute; import net.minecraft.world.item.TooltipFlag; +import net.neoforged.neoforge.common.extensions.IAttributeExtension; /** * A Percentage Attribute is one which always displays modifiers as percentages, including for {@link Operation#ADD_VALUE}. @@ -46,6 +47,10 @@ public PercentageAttribute(String pDescriptionId, double pDefaultValue, double p @Override public MutableComponent toValueComponent(Operation op, double value, TooltipFlag flag) { - return Component.translatable("neoforge.value.percent", FORMAT.format(value * this.scaleFactor)); + if (IAttributeExtension.isNullOrAddition(op)) { + return Component.translatable("neoforge.value.percent", FORMAT.format(value * this.scaleFactor)); + } + + return Component.translatable("neoforge.value.percent", FORMAT.format(value * 100)); } } diff --git a/src/main/java/net/neoforged/neoforge/common/Tags.java b/src/main/java/net/neoforged/neoforge/common/Tags.java index 8a57785dc8..68d31d129b 100644 --- a/src/main/java/net/neoforged/neoforge/common/Tags.java +++ b/src/main/java/net/neoforged/neoforge/common/Tags.java @@ -35,8 +35,28 @@ public static class Blocks { * This is patched into the following method: {@link net.minecraft.world.entity.monster.EnderMan.EndermanLeaveBlockGoal#canPlaceBlock(Level, BlockPos, BlockState, BlockState, BlockState, BlockPos)} */ public static final TagKey ENDERMAN_PLACE_ON_BLACKLIST = neoforgeTag("enderman_place_on_blacklist"); + + /** + * For denoting blocks that need tools that are Wood or higher to mine. + * By default, this is not added to any Minecraft tag since Wood is in the lowest "tier". + */ public static final TagKey NEEDS_WOOD_TOOL = neoforgeTag("needs_wood_tool"); + + /** + * For denoting blocks that need tools that are Gold or higher to mine. + * By default, this is not added to any Minecraft tag since Gold is in the lowest "tier". + */ public static final TagKey NEEDS_GOLD_TOOL = neoforgeTag("needs_gold_tool"); + + /** + * For denoting blocks that need tools that are Netherite or higher to mine. + * Blocks in this tag gets added to the following Minecraft tags: + * {@link BlockTags#INCORRECT_FOR_WOODEN_TOOL} + * {@link BlockTags#INCORRECT_FOR_STONE_TOOL} + * {@link BlockTags#INCORRECT_FOR_IRON_TOOL} + * {@link BlockTags#INCORRECT_FOR_GOLD_TOOL} + * {@link BlockTags#INCORRECT_FOR_DIAMOND_TOOL} + */ public static final TagKey NEEDS_NETHERITE_TOOL = neoforgeTag("needs_netherite_tool"); // `c` tags for common conventions @@ -593,13 +613,6 @@ public static class Items { */ public static final TagKey SHULKER_BOXES = tag("shulker_boxes"); public static final TagKey SLIME_BALLS = tag("slime_balls"); - /** - * Please use properly named {@link Tags.Items#SLIME_BALLS} tag and field instead - *

    - * TODO: Remove in 1.21.1 - */ - @Deprecated(since = "1.21") - public static final TagKey SLIMEBALLS = tag("slimeballs"); /** * Natural stone-like blocks that can be used as a base ingredient in recipes that takes stone. */ diff --git a/src/main/java/net/neoforged/neoforge/common/conditions/ConditionContext.java b/src/main/java/net/neoforged/neoforge/common/conditions/ConditionContext.java index de5b9b5d36..cffc6d0106 100644 --- a/src/main/java/net/neoforged/neoforge/common/conditions/ConditionContext.java +++ b/src/main/java/net/neoforged/neoforge/common/conditions/ConditionContext.java @@ -12,17 +12,30 @@ import net.minecraft.core.Registry; import net.minecraft.resources.ResourceKey; import net.minecraft.tags.TagKey; +import net.minecraft.world.flag.FeatureFlagSet; +import net.minecraft.world.flag.FeatureFlags; +import org.jetbrains.annotations.ApiStatus; public class ConditionContext implements ICondition.IContext { private final Map>, HolderLookup.RegistryLookup> pendingTags; + private final FeatureFlagSet enabledFeatures; - public ConditionContext(List> pendingTags) { + public ConditionContext(List> pendingTags, FeatureFlagSet enabledFeatures) { this.pendingTags = new IdentityHashMap<>(); + this.enabledFeatures = enabledFeatures; + for (var tags : pendingTags) { this.pendingTags.put(tags.key(), tags.lookup()); } } + // Use FeatureFlagSet sensitive constructor + @ApiStatus.ScheduledForRemoval(inVersion = "1.21.4") + @Deprecated(forRemoval = true, since = "1.21.3") + public ConditionContext(List> pendingTags) { + this(pendingTags, FeatureFlags.VANILLA_SET); + } + public void clear() { this.pendingTags.clear(); } @@ -33,4 +46,9 @@ public boolean isTagLoaded(TagKey key) { var lookup = pendingTags.get(key.registry()); return lookup != null && lookup.get((TagKey) key).isPresent(); } + + @Override + public FeatureFlagSet enabledFeatures() { + return enabledFeatures; + } } diff --git a/src/main/java/net/neoforged/neoforge/common/conditions/FeatureFlagsEnabledCondition.java b/src/main/java/net/neoforged/neoforge/common/conditions/FeatureFlagsEnabledCondition.java new file mode 100644 index 0000000000..6eb0aef8ab --- /dev/null +++ b/src/main/java/net/neoforged/neoforge/common/conditions/FeatureFlagsEnabledCondition.java @@ -0,0 +1,38 @@ +/* + * Copyright (c) NeoForged and contributors + * SPDX-License-Identifier: LGPL-2.1-only + */ + +package net.neoforged.neoforge.common.conditions; + +import com.mojang.serialization.MapCodec; +import com.mojang.serialization.codecs.RecordCodecBuilder; +import net.minecraft.world.flag.FeatureFlag; +import net.minecraft.world.flag.FeatureFlagSet; +import net.minecraft.world.flag.FeatureFlags; + +/** + * Condition checking that a set of {@link FeatureFlag feature flags} are enabled. + * + * @apiNote Mainly to be used when flagged content is not contained within the same feature pack which also enables said {@link FeatureFlag feature flags}. + */ +public record FeatureFlagsEnabledCondition(FeatureFlagSet flags) implements ICondition { + public static final MapCodec CODEC = RecordCodecBuilder.mapCodec(instance -> instance.group( + FeatureFlags.CODEC.fieldOf("flags").forGetter(condition -> condition.flags)).apply(instance, FeatureFlagsEnabledCondition::new)); + + public FeatureFlagsEnabledCondition { + if (flags.isEmpty()) { + throw new IllegalArgumentException("FeatureFlagsEnabledCondition requires a non-empty feature flag set"); + } + } + + @Override + public boolean test(IContext context) { + return flags.isSubsetOf(context.enabledFeatures()); + } + + @Override + public MapCodec codec() { + return CODEC; + } +} diff --git a/src/main/java/net/neoforged/neoforge/common/conditions/ICondition.java b/src/main/java/net/neoforged/neoforge/common/conditions/ICondition.java index 1be7e0ce94..2c8ca8583d 100644 --- a/src/main/java/net/neoforged/neoforge/common/conditions/ICondition.java +++ b/src/main/java/net/neoforged/neoforge/common/conditions/ICondition.java @@ -19,7 +19,10 @@ import net.minecraft.resources.RegistryOps; import net.minecraft.tags.TagKey; import net.minecraft.util.Unit; +import net.minecraft.world.flag.FeatureFlagSet; +import net.minecraft.world.flag.FeatureFlags; import net.neoforged.neoforge.registries.NeoForgeRegistries; +import net.neoforged.neoforge.server.ServerLifecycleHooks; public interface ICondition { Codec CODEC = NeoForgeRegistries.CONDITION_SERIALIZERS.byNameCodec() @@ -91,5 +94,15 @@ public boolean isTagLoaded(TagKey key) { * Returns {@code true} if the requested tag is available. */ boolean isTagLoaded(TagKey key); + + default FeatureFlagSet enabledFeatures() { + // returning the vanilla set causes reports false positives for flags outside of vanilla + // return FeatureFlags.VANILLA_SET; + + // lookup the active enabledFeatures from the current server + // if no server exists, delegating back to 'VANILLA_SET' should be fine (should rarely ever happen) + var server = ServerLifecycleHooks.getCurrentServer(); + return server == null ? FeatureFlags.VANILLA_SET : server.getWorldData().enabledFeatures(); + } } } diff --git a/src/main/java/net/neoforged/neoforge/common/conditions/IConditionBuilder.java b/src/main/java/net/neoforged/neoforge/common/conditions/IConditionBuilder.java index 06836647a8..ef10000e02 100644 --- a/src/main/java/net/neoforged/neoforge/common/conditions/IConditionBuilder.java +++ b/src/main/java/net/neoforged/neoforge/common/conditions/IConditionBuilder.java @@ -7,7 +7,10 @@ import java.util.List; import net.minecraft.tags.TagKey; +import net.minecraft.world.flag.FeatureFlag; +import net.minecraft.world.flag.FeatureFlagSet; import net.minecraft.world.item.Item; +import org.apache.commons.lang3.ArrayUtils; public interface IConditionBuilder { default ICondition and(ICondition... values) { @@ -41,4 +44,19 @@ default ICondition modLoaded(String modid) { default ICondition tagEmpty(TagKey tag) { return new TagEmptyCondition(tag.location()); } + + default ICondition featureFlagsEnabled(FeatureFlagSet requiredFeatures) { + return new FeatureFlagsEnabledCondition(requiredFeatures); + } + + default ICondition featureFlagsEnabled(FeatureFlag... requiredFlags) { + if (requiredFlags.length == 0) { + throw new IllegalArgumentException("FeatureFlagsEnabledCondition requires at least one feature flag."); + } + if (requiredFlags.length == 1) { + return new FeatureFlagsEnabledCondition(FeatureFlagSet.of(requiredFlags[0])); + } else { + return new FeatureFlagsEnabledCondition(FeatureFlagSet.of(requiredFlags[0], ArrayUtils.remove(requiredFlags, 0))); + } + } } diff --git a/src/main/java/net/neoforged/neoforge/common/crafting/CompoundIngredient.java b/src/main/java/net/neoforged/neoforge/common/crafting/CompoundIngredient.java index f703027003..ab4cf41c93 100644 --- a/src/main/java/net/neoforged/neoforge/common/crafting/CompoundIngredient.java +++ b/src/main/java/net/neoforged/neoforge/common/crafting/CompoundIngredient.java @@ -37,7 +37,7 @@ public static Ingredient of(Ingredient... children) { @Override public Stream> items() { - return children.stream().flatMap(child -> child.items().stream()); + return children.stream().flatMap(Ingredient::items); } @Override diff --git a/src/main/java/net/neoforged/neoforge/common/crafting/CustomDisplayIngredient.java b/src/main/java/net/neoforged/neoforge/common/crafting/CustomDisplayIngredient.java index 2c88c2fa13..12ce024b1a 100644 --- a/src/main/java/net/neoforged/neoforge/common/crafting/CustomDisplayIngredient.java +++ b/src/main/java/net/neoforged/neoforge/common/crafting/CustomDisplayIngredient.java @@ -37,7 +37,7 @@ public boolean test(ItemStack stack) { @Override public Stream> items() { - return base.items().stream(); + return base.items(); } @Override diff --git a/src/main/java/net/neoforged/neoforge/common/crafting/DifferenceIngredient.java b/src/main/java/net/neoforged/neoforge/common/crafting/DifferenceIngredient.java index 3ce8c7d3c5..8e7e3ec2ce 100644 --- a/src/main/java/net/neoforged/neoforge/common/crafting/DifferenceIngredient.java +++ b/src/main/java/net/neoforged/neoforge/common/crafting/DifferenceIngredient.java @@ -25,7 +25,7 @@ public record DifferenceIngredient(Ingredient base, Ingredient subtracted) imple @Override public Stream> items() { - return base.items().stream().filter(i -> !subtracted.test(i.value().getDefaultInstance())); + return base.items().filter(i -> !subtracted.test(i.value().getDefaultInstance())); } @Override diff --git a/src/main/java/net/neoforged/neoforge/common/crafting/IntersectionIngredient.java b/src/main/java/net/neoforged/neoforge/common/crafting/IntersectionIngredient.java index b41dc85070..d726e710c7 100644 --- a/src/main/java/net/neoforged/neoforge/common/crafting/IntersectionIngredient.java +++ b/src/main/java/net/neoforged/neoforge/common/crafting/IntersectionIngredient.java @@ -56,7 +56,7 @@ public boolean test(ItemStack stack) { @Override public Stream> items() { return children.stream() - .flatMap(child -> child.items().stream()) + .flatMap(child -> child.items()) .filter(i -> test(i.value().getDefaultInstance())); } diff --git a/src/main/java/net/neoforged/neoforge/common/data/GeneratingOverlayMetadataSection.java b/src/main/java/net/neoforged/neoforge/common/data/GeneratingOverlayMetadataSection.java index 431fdb4a1f..1b401583e0 100644 --- a/src/main/java/net/neoforged/neoforge/common/data/GeneratingOverlayMetadataSection.java +++ b/src/main/java/net/neoforged/neoforge/common/data/GeneratingOverlayMetadataSection.java @@ -22,6 +22,6 @@ public record GeneratingOverlayMetadataSection(List TYPE = MetadataSectionType.fromCodec("overlays", CODEC); - public static final MetadataSectionType NEOFORGE_TYPE = MetadataSectionType.fromCodec("neoforge:overlays", CODEC); + public static final MetadataSectionType TYPE = new MetadataSectionType<>("overlays", CODEC); + public static final MetadataSectionType NEOFORGE_TYPE = new MetadataSectionType<>("neoforge:overlays", CODEC); } diff --git a/src/main/java/net/neoforged/neoforge/common/data/internal/NeoForgeBlockTagsProvider.java b/src/main/java/net/neoforged/neoforge/common/data/internal/NeoForgeBlockTagsProvider.java index a1959d5aa8..09b564d1b0 100644 --- a/src/main/java/net/neoforged/neoforge/common/data/internal/NeoForgeBlockTagsProvider.java +++ b/src/main/java/net/neoforged/neoforge/common/data/internal/NeoForgeBlockTagsProvider.java @@ -160,6 +160,14 @@ public void addTags(HolderLookup.Provider p_256380_) { tag(Tags.Blocks.VILLAGER_FARMLANDS).add(Blocks.FARMLAND); + // Make our Needs Netherite Tool tag be functional. + tag(Tags.Blocks.NEEDS_NETHERITE_TOOL); + tag(BlockTags.INCORRECT_FOR_WOODEN_TOOL).addTag(Tags.Blocks.NEEDS_NETHERITE_TOOL); + tag(BlockTags.INCORRECT_FOR_STONE_TOOL).addTag(Tags.Blocks.NEEDS_NETHERITE_TOOL); + tag(BlockTags.INCORRECT_FOR_IRON_TOOL).addTag(Tags.Blocks.NEEDS_NETHERITE_TOOL); + tag(BlockTags.INCORRECT_FOR_GOLD_TOOL).addTag(Tags.Blocks.NEEDS_NETHERITE_TOOL); + tag(BlockTags.INCORRECT_FOR_DIAMOND_TOOL).addTag(Tags.Blocks.NEEDS_NETHERITE_TOOL); + // Backwards compat with pre-1.21 tags. Done after so optional tag is last for better readability. // TODO: Remove backwards compat tag entries in 1.22 tagWithOptionalLegacy(Tags.Blocks.BARRELS); diff --git a/src/main/java/net/neoforged/neoforge/common/data/internal/NeoForgeItemTagsProvider.java b/src/main/java/net/neoforged/neoforge/common/data/internal/NeoForgeItemTagsProvider.java index 36c67fbaba..1b0db4cfca 100644 --- a/src/main/java/net/neoforged/neoforge/common/data/internal/NeoForgeItemTagsProvider.java +++ b/src/main/java/net/neoforged/neoforge/common/data/internal/NeoForgeItemTagsProvider.java @@ -222,8 +222,7 @@ public void addTags(HolderLookup.Provider lookupProvider) { tag(Tags.Items.SEEDS_MELON).add(Items.MELON_SEEDS); tag(Tags.Items.SEEDS_PUMPKIN).add(Items.PUMPKIN_SEEDS); tag(Tags.Items.SEEDS_WHEAT).add(Items.WHEAT_SEEDS); - tag(Tags.Items.SLIMEBALLS).add(Items.SLIME_BALL); // Deprecated - tag(Tags.Items.SLIME_BALLS).add(Items.SLIME_BALL).addOptionalTag(Tags.Items.SLIMEBALLS); + tag(Tags.Items.SLIME_BALLS).add(Items.SLIME_BALL); tag(Tags.Items.SHULKER_BOXES) .add(Items.SHULKER_BOX).add(Items.WHITE_SHULKER_BOX).add(Items.ORANGE_SHULKER_BOX) .add(Items.MAGENTA_SHULKER_BOX).add(Items.LIGHT_BLUE_SHULKER_BOX).add(Items.YELLOW_SHULKER_BOX) diff --git a/src/main/java/net/neoforged/neoforge/common/data/internal/NeoForgeLanguageProvider.java b/src/main/java/net/neoforged/neoforge/common/data/internal/NeoForgeLanguageProvider.java index f764016f5c..83b4cbcc2a 100644 --- a/src/main/java/net/neoforged/neoforge/common/data/internal/NeoForgeLanguageProvider.java +++ b/src/main/java/net/neoforged/neoforge/common/data/internal/NeoForgeLanguageProvider.java @@ -304,7 +304,6 @@ protected void addTranslations() { add(Tags.Items.SEEDS_WHEAT, "Wheat Seeds"); add(Tags.Items.SHULKER_BOXES, "Shulker Boxes"); add(Tags.Items.SLIME_BALLS, "Slimeballs"); - add(Tags.Items.SLIMEBALLS, "Slimeballs"); add(Tags.Items.STONES, "Stones"); add(Tags.Items.STORAGE_BLOCKS, "Storage Blocks"); add(Tags.Items.STORAGE_BLOCKS_BONE_MEAL, "Bone Meal Storage Blocks"); diff --git a/src/main/java/net/neoforged/neoforge/common/extensions/IAttributeExtension.java b/src/main/java/net/neoforged/neoforge/common/extensions/IAttributeExtension.java index 6a5c51564d..dd7074d93c 100644 --- a/src/main/java/net/neoforged/neoforge/common/extensions/IAttributeExtension.java +++ b/src/main/java/net/neoforged/neoforge/common/extensions/IAttributeExtension.java @@ -23,6 +23,7 @@ import net.minecraft.world.entity.ai.attributes.AttributeModifier.Operation; import net.minecraft.world.entity.ai.attributes.Attributes; import net.minecraft.world.item.TooltipFlag; +import net.neoforged.neoforge.common.NeoForgeConfig; import net.neoforged.neoforge.common.NeoForgeMod; import net.neoforged.neoforge.common.util.AttributeUtil; import org.jetbrains.annotations.Nullable; @@ -83,7 +84,7 @@ default MutableComponent toComponent(AttributeModifier modif, TooltipFlag flag) default Component getDebugInfo(AttributeModifier modif, TooltipFlag flag) { Component debugInfo = CommonComponents.EMPTY; - if (flag.isAdvanced()) { + if (flag.isAdvanced() && NeoForgeConfig.COMMON.attributeAdvancedTooltipDebugInfo.get()) { // Advanced Tooltips show the underlying operation and the "true" value. We offset MULTIPLY_TOTAL by 1 due to how the operation is calculated. double advValue = (modif.operation() == Operation.ADD_MULTIPLIED_TOTAL ? 1 : 0) + modif.amount(); String valueStr = FORMAT.format(advValue); diff --git a/src/main/java/net/neoforged/neoforge/common/extensions/IBlockExtension.java b/src/main/java/net/neoforged/neoforge/common/extensions/IBlockExtension.java index 813b8a398f..6640d90439 100644 --- a/src/main/java/net/neoforged/neoforge/common/extensions/IBlockExtension.java +++ b/src/main/java/net/neoforged/neoforge/common/extensions/IBlockExtension.java @@ -73,7 +73,6 @@ import net.minecraft.world.level.material.PushReaction; import net.minecraft.world.level.pathfinder.PathType; import net.minecraft.world.level.pathfinder.WalkNodeEvaluator; -import net.minecraft.world.phys.HitResult; import net.minecraft.world.phys.Vec3; import net.neoforged.fml.loading.FMLEnvironment; import net.neoforged.neoforge.capabilities.BlockCapabilityCache; @@ -324,14 +323,12 @@ default float getExplosionResistance(BlockState state, BlockGetter level, BlockP } /** - * * Called when A user uses the creative pick block button on this block * - * @param target The full target the player is looking at * @return A ItemStack to add to the player's inventory, empty itemstack if nothing should be added. */ - default ItemStack getCloneItemStack(BlockState state, HitResult target, LevelReader level, BlockPos pos, Player player) { - return self().getCloneItemStack(level, pos, state); + default ItemStack getCloneItemStack(LevelReader level, BlockPos pos, BlockState state, boolean includeData, Player player) { + return state.getCloneItemStack(level, pos, includeData); } /** @@ -1019,4 +1016,16 @@ default BubbleColumnDirection getBubbleColumnDirection(BlockState state) { return BubbleColumnDirection.NONE; } } + + /** + * Determines if a fluid adjacent to the block on the given side should not be rendered. + * + * @param state the block state of the block + * @param selfFace the face of this block that the fluid is adjacent to + * @param adjacentFluid the fluid that is touching that face + * @return true if this block should cause the fluid's face to not render + */ + default boolean shouldHideAdjacentFluidFace(BlockState state, Direction selfFace, FluidState adjacentFluid) { + return state.getFluidState().getType().isSame(adjacentFluid.getType()); + } } diff --git a/src/main/java/net/neoforged/neoforge/common/extensions/IBlockStateExtension.java b/src/main/java/net/neoforged/neoforge/common/extensions/IBlockStateExtension.java index 3e08a8b5b0..8402500f21 100644 --- a/src/main/java/net/neoforged/neoforge/common/extensions/IBlockStateExtension.java +++ b/src/main/java/net/neoforged/neoforge/common/extensions/IBlockStateExtension.java @@ -39,7 +39,6 @@ import net.minecraft.world.level.material.FluidState; import net.minecraft.world.level.material.PushReaction; import net.minecraft.world.level.pathfinder.PathType; -import net.minecraft.world.phys.HitResult; import net.minecraft.world.phys.Vec3; import net.neoforged.neoforge.capabilities.BlockCapabilityCache; import net.neoforged.neoforge.common.ItemAbilities; @@ -223,11 +222,10 @@ default float getExplosionResistance(BlockGetter level, BlockPos pos, Explosion * * Called when A user uses the creative pick block button on this block * - * @param target The full target the player is looking at * @return A ItemStack to add to the player's inventory, empty itemstack if nothing should be added. */ - default ItemStack getCloneItemStack(HitResult target, LevelReader level, BlockPos pos, Player player) { - return self().getBlock().getCloneItemStack(self(), target, level, pos, player); + default ItemStack getCloneItemStack(BlockPos pos, LevelReader level, boolean includeData, Player player) { + return self().getBlock().getCloneItemStack(level, pos, self(), includeData, player); } /** @@ -755,4 +753,15 @@ default boolean isEmpty() { default BubbleColumnDirection getBubbleColumnDirection() { return self().getBlock().getBubbleColumnDirection(self()); } + + /** + * Determines if a fluid adjacent to the block on the given side should not be rendered. + * + * @param selfFace the face of this block that the fluid is adjacent to + * @param adjacentFluid the fluid that is touching that face + * @return true if this block should cause the fluid's face to not render + */ + default boolean shouldHideAdjacentFluidFace(Direction selfFace, FluidState adjacentFluid) { + return self().getBlock().shouldHideAdjacentFluidFace(self(), selfFace, adjacentFluid); + } } diff --git a/src/main/java/net/neoforged/neoforge/common/extensions/IEntityExtension.java b/src/main/java/net/neoforged/neoforge/common/extensions/IEntityExtension.java index 3b495ea523..ba45404353 100644 --- a/src/main/java/net/neoforged/neoforge/common/extensions/IEntityExtension.java +++ b/src/main/java/net/neoforged/neoforge/common/extensions/IEntityExtension.java @@ -20,11 +20,9 @@ import net.minecraft.world.entity.boss.enderdragon.EnderDragon; import net.minecraft.world.entity.item.ItemEntity; import net.minecraft.world.entity.player.Player; -import net.minecraft.world.item.ItemStack; import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.material.FluidState; -import net.minecraft.world.phys.HitResult; import net.neoforged.neoforge.attachment.AttachmentInternals; import net.neoforged.neoforge.attachment.AttachmentType; import net.neoforged.neoforge.common.SoundAction; @@ -89,17 +87,6 @@ default boolean shouldRiderSit() { return true; } - /** - * Called when a user uses the creative pick block button on this entity. - * - * @param target The full target the player is looking at - * @return A ItemStack to add to the player's inventory, null ItemStack if nothing should be added. - */ - @Nullable - default ItemStack getPickedResult(HitResult target) { - return self().getPickResult(); - } - /** * If a rider of this entity can interact with this entity. Should return true on the * ridden entity if so. diff --git a/src/main/java/net/neoforged/neoforge/common/extensions/IFallableExtension.java b/src/main/java/net/neoforged/neoforge/common/extensions/IFallableExtension.java new file mode 100644 index 0000000000..e4551d4661 --- /dev/null +++ b/src/main/java/net/neoforged/neoforge/common/extensions/IFallableExtension.java @@ -0,0 +1,24 @@ +/* + * Copyright (c) NeoForged and contributors + * SPDX-License-Identifier: LGPL-2.1-only + */ + +package net.neoforged.neoforge.common.extensions; + +import net.minecraft.core.BlockPos; +import net.minecraft.world.entity.item.FallingBlockEntity; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.block.Fallable; + +public interface IFallableExtension { + /** + * Called in {@link FallingBlockEntity#tick()} after vanilla processing on both server and client. + *

    + * This is not called in the tick where the entity lands, see {@link Fallable}. + * + * @param level The current level. + * @param currentPosition The current position of the entity as a {@link BlockPos}. + * @param entity The falling entity. + */ + default void fallingTick(Level level, BlockPos currentPosition, FallingBlockEntity entity) {} +} diff --git a/src/main/java/net/neoforged/neoforge/common/extensions/IItemExtension.java b/src/main/java/net/neoforged/neoforge/common/extensions/IItemExtension.java index ab5cc0f8e1..662f816944 100644 --- a/src/main/java/net/neoforged/neoforge/common/extensions/IItemExtension.java +++ b/src/main/java/net/neoforged/neoforge/common/extensions/IItemExtension.java @@ -13,13 +13,13 @@ import java.util.function.Consumer; import net.minecraft.core.BlockPos; import net.minecraft.core.Holder; +import net.minecraft.core.HolderLookup; import net.minecraft.core.HolderLookup.RegistryLookup; import net.minecraft.core.HolderSet; import net.minecraft.core.component.DataComponentMap; import net.minecraft.core.component.DataComponentType; import net.minecraft.core.component.DataComponents; import net.minecraft.network.chat.Component; -import net.minecraft.resources.ResourceLocation; import net.minecraft.server.level.ServerLevel; import net.minecraft.tags.ItemTags; import net.minecraft.util.Mth; @@ -48,7 +48,6 @@ import net.minecraft.world.item.enchantment.Enchantment.EnchantmentDefinition; import net.minecraft.world.item.enchantment.EnchantmentInstance; import net.minecraft.world.item.enchantment.ItemEnchantments; -import net.minecraft.world.item.equipment.EquipmentModel; import net.minecraft.world.level.Level; import net.minecraft.world.level.block.entity.FuelValues; import net.minecraft.world.phys.AABB; @@ -296,24 +295,6 @@ default boolean isBookEnchantable(ItemStack stack, ItemStack book) { return true; } - /** - * Called by RenderBiped and RenderPlayer to determine the armor texture that - * should be used for the currently equipped item. This will be called on - * stacks with the {@link DataComponents#EQUIPPABLE} component. - * - * Returning null from this function will use the default value. - * - * @param stack ItemStack for the equipped armor - * @param type The layer type of the armor - * @param layer The armor layer - * @param _default The default texture determined by the equipment renderer - * @return Path of texture to bind, or null to use default - */ - @Nullable - default ResourceLocation getArmorTexture(ItemStack stack, EquipmentModel.LayerType type, EquipmentModel.Layer layer, ResourceLocation _default) { - return null; - } - /** * Called when a entity tries to play the 'swing' animation. * @@ -550,8 +531,8 @@ default boolean canContinueUsing(ItemStack oldStack, ItemStack newStack) { * associated mod and {@link net.minecraft.core.Registry#getKey(Object)} would return null. */ @Nullable - default String getCreatorModId(ItemStack itemStack) { - return CommonHooks.getDefaultCreatorModId(itemStack); + default String getCreatorModId(HolderLookup.Provider registries, ItemStack itemStack) { + return CommonHooks.getDefaultCreatorModId(registries, itemStack); } /** diff --git a/src/main/java/net/neoforged/neoforge/common/extensions/ILevelExtension.java b/src/main/java/net/neoforged/neoforge/common/extensions/ILevelExtension.java index b85347746a..1b84f151de 100644 --- a/src/main/java/net/neoforged/neoforge/common/extensions/ILevelExtension.java +++ b/src/main/java/net/neoforged/neoforge/common/extensions/ILevelExtension.java @@ -5,8 +5,6 @@ package net.neoforged.neoforge.common.extensions; -import java.util.Collection; -import java.util.Collections; import net.minecraft.core.BlockPos; import net.minecraft.network.chat.Component; import net.minecraft.server.level.ServerLevel; @@ -16,7 +14,6 @@ import net.minecraft.world.level.block.state.BlockState; import net.neoforged.neoforge.capabilities.BlockCapability; import net.neoforged.neoforge.client.model.data.ModelDataManager; -import net.neoforged.neoforge.entity.PartEntity; import org.jetbrains.annotations.Nullable; public interface ILevelExtension { @@ -49,14 +46,6 @@ private Level self() { */ public double increaseMaxEntityRadius(double value); - /** - * All part entities in this world. Used when collecting entities in an AABB to fix parts being - * ignored whose parent entity is in a chunk that does not intersect with the AABB. - */ - public default Collection> getPartEntities() { - return Collections.emptyList(); - } - /** * Retrieves the model data manager for the given level. May be null on a server level. * diff --git a/src/main/java/net/neoforged/neoforge/common/loot/LootModifierManager.java b/src/main/java/net/neoforged/neoforge/common/loot/LootModifierManager.java index c7a060cf2e..f75bb6ea72 100644 --- a/src/main/java/net/neoforged/neoforge/common/loot/LootModifierManager.java +++ b/src/main/java/net/neoforged/neoforge/common/loot/LootModifierManager.java @@ -21,6 +21,7 @@ import java.util.List; import java.util.Map; import java.util.function.Function; +import net.minecraft.resources.FileToIdConverter; import net.minecraft.resources.ResourceLocation; import net.minecraft.server.packs.resources.Resource; import net.minecraft.server.packs.resources.ResourceManager; @@ -39,7 +40,7 @@ public class LootModifierManager extends SimpleJsonResourceReloadListener { + if (Minecraft.getInstance().getOverlay() instanceof LoadingOverlay) { + return; + } + if (Minecraft.getInstance().isRunning()) { + writeSelfTestReport(clientSelfTestDestination); + Minecraft.getInstance().stop(); + } + }); + } + } + + public static void initCommon() { + var serverSelfTestDestination = System.getenv("NEOFORGE_DEDICATED_SERVER_SELFTEST"); + if (serverSelfTestDestination != null) { + if (FMLLoader.getDist() != Dist.DEDICATED_SERVER) { + LOGGER.error("The server self-test ran with a dist of {} instead of dedicated server!", FMLLoader.getDist()); + System.exit(1); + } + NeoForge.EVENT_BUS.addListener((ServerTickEvent.Pre e) -> { + if (e.getServer().isRunning()) { + writeSelfTestReport(serverSelfTestDestination); + e.getServer().halt(false); + } + }); + } + } + + /** + * This is used by our GitHub Actions pipeline to run an E2E test for PRs. + * It writes a small self-test report to the file indicated by the system property and exits. + */ + private static void writeSelfTestReport(String path) { + try { + Files.createFile(Paths.get(path)); + LOGGER.info("Wrote self-test report to '{}'", path); + } catch (IOException e) { + LOGGER.error("Failed to write self-test to '{}'", path, e); + System.exit(1); + } + } +} diff --git a/src/main/java/net/neoforged/neoforge/common/util/flag/FeatureFlagLoader.java b/src/main/java/net/neoforged/neoforge/common/util/flag/FeatureFlagLoader.java new file mode 100644 index 0000000000..17bd9a5a75 --- /dev/null +++ b/src/main/java/net/neoforged/neoforge/common/util/flag/FeatureFlagLoader.java @@ -0,0 +1,71 @@ +/* + * Copyright (c) NeoForged and contributors + * SPDX-License-Identifier: LGPL-2.1-only + */ + +package net.neoforged.neoforge.common.util.flag; + +import com.google.common.base.Preconditions; +import com.google.gson.Gson; +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import java.io.BufferedReader; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.util.GsonHelper; +import net.minecraft.world.flag.FeatureFlagRegistry; +import net.neoforged.fml.ModLoader; +import net.neoforged.fml.ModLoadingIssue; +import net.neoforged.fml.loading.LoadingModList; +import net.neoforged.fml.loading.moddiscovery.ModFileInfo; +import net.neoforged.neoforgespi.language.IModInfo; +import org.jetbrains.annotations.ApiStatus; + +@ApiStatus.Internal +public final class FeatureFlagLoader { + private static final Gson GSON = new Gson(); + + private FeatureFlagLoader() {} + + public static void loadModdedFlags(FeatureFlagRegistry.Builder builder) { + Map pathPerMod = new HashMap<>(); + LoadingModList.get() + .getModFiles() + .stream() + .map(ModFileInfo::getMods) + .flatMap(List::stream) + .forEach(mod -> mod.getConfig().getConfigElement("featureFlags").ifPresent(file -> { + Path path = mod.getOwningFile().getFile().findResource(file); + if (!Files.isRegularFile(path)) { + ModLoader.addLoadingIssue(ModLoadingIssue.error("fml.modloadingissue.feature_flags.file_not_found", path).withAffectedMod(mod)); + return; + } + pathPerMod.put(mod, path); + })); + + pathPerMod.forEach((mod, path) -> { + try (BufferedReader reader = Files.newBufferedReader(path)) { + JsonObject obj = GSON.fromJson(reader, JsonObject.class); + JsonArray flagArray = GsonHelper.getAsJsonArray(obj, "flags"); + for (JsonElement elem : flagArray) { + String flagName = GsonHelper.convertToString(elem, "flag"); + ResourceLocation flagLocation = ResourceLocation.parse(flagName); + Preconditions.checkArgument( + flagLocation.getNamespace().equals(mod.getModId()), + "Cannot add new flags to foreign namespaces: %s", + flagLocation); + builder.create(flagLocation, true); + } + } catch (Throwable e) { + ModLoader.addLoadingIssue(ModLoadingIssue.error("fml.modloadingissue.feature_flags.loading_error", path) + .withAffectedMod(mod) + .withCause(e)); + } + }); + } +} diff --git a/src/main/java/net/neoforged/neoforge/common/util/flag/package-info.java b/src/main/java/net/neoforged/neoforge/common/util/flag/package-info.java new file mode 100644 index 0000000000..e2afddc95b --- /dev/null +++ b/src/main/java/net/neoforged/neoforge/common/util/flag/package-info.java @@ -0,0 +1,13 @@ +/* + * Copyright (c) NeoForged and contributors + * SPDX-License-Identifier: LGPL-2.1-only + */ + +@FieldsAreNonnullByDefault +@MethodsReturnNonnullByDefault +@ParametersAreNonnullByDefault +package net.neoforged.neoforge.common.util.flag; + +import javax.annotation.ParametersAreNonnullByDefault; +import net.minecraft.FieldsAreNonnullByDefault; +import net.minecraft.MethodsReturnNonnullByDefault; diff --git a/src/main/java/net/neoforged/neoforge/common/world/BiomeSpecialEffectsBuilder.java b/src/main/java/net/neoforged/neoforge/common/world/BiomeSpecialEffectsBuilder.java index 083d3863c5..a4e283c662 100644 --- a/src/main/java/net/neoforged/neoforge/common/world/BiomeSpecialEffectsBuilder.java +++ b/src/main/java/net/neoforged/neoforge/common/world/BiomeSpecialEffectsBuilder.java @@ -9,6 +9,7 @@ import net.minecraft.core.Holder; import net.minecraft.sounds.Music; import net.minecraft.sounds.SoundEvent; +import net.minecraft.util.random.SimpleWeightedRandomList; import net.minecraft.world.level.biome.AmbientAdditionsSettings; import net.minecraft.world.level.biome.AmbientMoodSettings; import net.minecraft.world.level.biome.AmbientParticleSettings; @@ -88,7 +89,7 @@ public Optional getAmbientAdditionsSound() { return this.ambientAdditionsSettings; } - public Optional getBackgroundMusic() { + public Optional> getBackgroundMusic() { return this.backgroundMusic; } } diff --git a/src/main/java/net/neoforged/neoforge/data/event/GatherDataEvent.java b/src/main/java/net/neoforged/neoforge/data/event/GatherDataEvent.java index 64bb7a0b75..f35bb1768a 100644 --- a/src/main/java/net/neoforged/neoforge/data/event/GatherDataEvent.java +++ b/src/main/java/net/neoforged/neoforge/data/event/GatherDataEvent.java @@ -20,12 +20,17 @@ import net.minecraft.DetectedVersion; import net.minecraft.core.HolderLookup; import net.minecraft.data.DataGenerator; +import net.minecraft.data.DataProvider; +import net.minecraft.data.PackOutput; +import net.minecraft.data.tags.TagsProvider; +import net.minecraft.world.item.Item; +import net.minecraft.world.level.block.Block; import net.neoforged.bus.api.Event; import net.neoforged.fml.ModContainer; import net.neoforged.fml.event.IModBusEvent; import net.neoforged.neoforge.common.data.ExistingFileHelper; -public class GatherDataEvent extends Event implements IModBusEvent { +public abstract class GatherDataEvent extends Event implements IModBusEvent { private final DataGenerator dataGenerator; private final DataGeneratorConfig config; private final ExistingFileHelper existingFileHelper; @@ -58,14 +63,6 @@ public CompletableFuture getLookupProvider() { return this.config.lookupProvider; } - public boolean includeServer() { - return this.config.server; - } - - public boolean includeClient() { - return this.config.client; - } - public boolean includeDev() { return this.config.dev; } @@ -78,13 +75,23 @@ public boolean validate() { return this.config.validate; } + public static class Server extends GatherDataEvent { + public Server(ModContainer mc, DataGenerator dataGenerator, DataGeneratorConfig dataGeneratorConfig, ExistingFileHelper existingFileHelper) { + super(mc, dataGenerator, dataGeneratorConfig, existingFileHelper); + } + } + + public static class Client extends GatherDataEvent { + public Client(ModContainer mc, DataGenerator dataGenerator, DataGeneratorConfig dataGeneratorConfig, ExistingFileHelper existingFileHelper) { + super(mc, dataGenerator, dataGeneratorConfig, existingFileHelper); + } + } + public static class DataGeneratorConfig { private final Set mods; private final Path path; private final Collection inputs; private final CompletableFuture lookupProvider; - private final boolean server; - private final boolean client; private final boolean dev; private final boolean reports; private final boolean validate; @@ -92,17 +99,18 @@ public static class DataGeneratorConfig { private final List generators = new ArrayList<>(); public DataGeneratorConfig(final Set mods, final Path path, final Collection inputs, final CompletableFuture lookupProvider, - final boolean server, final boolean client, final boolean dev, final boolean reports, final boolean validate, final boolean flat) { + final boolean dev, final boolean reports, final boolean validate, final boolean flat, final DataGenerator vanillaGenerator) { this.mods = mods; this.path = path; this.inputs = inputs; this.lookupProvider = lookupProvider; - this.server = server; - this.client = client; this.dev = dev; this.reports = reports; this.validate = validate; this.flat = flat; + if (mods.contains("minecraft") || mods.isEmpty()) { + this.generators.add(vanillaGenerator); + } } public Collection getInputs() { @@ -139,4 +147,59 @@ public void runAll() { }); } } + + public T addProvider(T provider) { + return dataGenerator.addProvider(true, provider); + } + + public T createProvider(DataProviderFromOutput builder) { + return addProvider(builder.create(dataGenerator.getPackOutput())); + } + + public T createProvider(DataProviderFromOutputFileHelper builder) { + return addProvider(builder.create(dataGenerator.getPackOutput(), existingFileHelper)); + } + + public T createProvider(DataProviderFromOutputLookup builder) { + return addProvider(builder.create(dataGenerator.getPackOutput(), config.lookupProvider)); + } + + public T createProvider(DataProviderFromOutputLookupFileHelper builder) { + return addProvider(builder.create(dataGenerator.getPackOutput(), config.lookupProvider, existingFileHelper)); + } + + public void createBlockAndItemTags(DataProviderFromOutputLookupFileHelper> blockTagsProvider, ItemTagsProvider itemTagsProvider) { + var blockTags = createProvider(blockTagsProvider); + addProvider(itemTagsProvider.create(this.getGenerator().getPackOutput(), this.getLookupProvider(), blockTags.contentsGetter(), this.getExistingFileHelper())); + } + + @FunctionalInterface + public interface DataProviderFromOutput { + T create(PackOutput output); + } + + @FunctionalInterface + public interface DataProviderFromOutputLookup { + T create(PackOutput output, CompletableFuture lookupProvider); + } + + @FunctionalInterface + public interface DataProviderFromOutputFileHelper { + T create(PackOutput output, ExistingFileHelper existingFileHelper); + } + + @FunctionalInterface + public interface DataProviderFromOutputLookupFileHelper { + T create(PackOutput output, CompletableFuture lookupProvider, ExistingFileHelper existingFileHelper); + } + + @FunctionalInterface + public interface GatherDataEventGenerator { + GatherDataEvent create(final ModContainer mc, final DataGenerator dataGenerator, final DataGeneratorConfig dataGeneratorConfig, ExistingFileHelper existingFileHelper); + } + + @FunctionalInterface + public interface ItemTagsProvider { + TagsProvider create(PackOutput output, CompletableFuture lookupProvider, CompletableFuture> contentsGetter, ExistingFileHelper existingFileHelper); + } } diff --git a/src/main/java/net/neoforged/neoforge/data/loading/DatagenModLoader.java b/src/main/java/net/neoforged/neoforge/data/loading/DatagenModLoader.java index 687dc8dd71..faa52ca084 100644 --- a/src/main/java/net/neoforged/neoforge/data/loading/DatagenModLoader.java +++ b/src/main/java/net/neoforged/neoforge/data/loading/DatagenModLoader.java @@ -12,11 +12,10 @@ import java.util.concurrent.CompletableFuture; import net.minecraft.Util; import net.minecraft.core.HolderLookup; +import net.minecraft.data.DataGenerator; import net.minecraft.data.registries.VanillaRegistries; import net.minecraft.server.Bootstrap; import net.neoforged.fml.ModLoader; -import net.neoforged.neoforge.client.ClientHooks; -import net.neoforged.neoforge.client.entity.animation.json.AnimationTypeManager; import net.neoforged.neoforge.common.data.ExistingFileHelper; import net.neoforged.neoforge.data.event.GatherDataEvent; import net.neoforged.neoforge.internal.CommonModLoader; @@ -37,8 +36,9 @@ public static boolean isRunningDataGen() { @ApiStatus.Internal public static void begin(final Set mods, final Path path, final Collection inputs, Collection existingPacks, - Set existingMods, final boolean serverGenerators, final boolean clientGenerators, final boolean devToolGenerators, final boolean reportsGenerator, - final boolean structureValidator, final boolean flat, final String assetIndex, final File assetsDir) { + Set existingMods, final boolean devToolGenerators, final boolean reportsGenerator, + final boolean structureValidator, final boolean flat, final String assetIndex, final File assetsDir, Runnable setup, GatherDataEvent.GatherDataEventGenerator eventGenerator, + DataGenerator vanillaGenerator) { if (mods.contains("minecraft") && mods.size() == 1) return; LOGGER.info("Initializing Data Gatherer for mods {}", mods); @@ -48,18 +48,14 @@ public static void begin(final Set mods, final Path path, final Collecti // Modify components as the (modified) defaults may be required in datagen, i.e. stack size RegistrationEvents.modifyComponents(); CompletableFuture lookupProvider = CompletableFuture.supplyAsync(VanillaRegistries::createLookup, Util.backgroundExecutor()); - dataGeneratorConfig = new GatherDataEvent.DataGeneratorConfig(mods, path, inputs, lookupProvider, serverGenerators, - clientGenerators, devToolGenerators, reportsGenerator, structureValidator, flat); + dataGeneratorConfig = new GatherDataEvent.DataGeneratorConfig(mods, path, inputs, lookupProvider, devToolGenerators, reportsGenerator, structureValidator, flat, vanillaGenerator); if (!mods.contains("neoforge")) { // If we aren't generating data for forge, automatically add forge as an existing so mods can access forge's data existingMods.add("neoforge"); } - if (clientGenerators) { - ClientHooks.registerSpriteSourceTypes(); - AnimationTypeManager.init(); - } + setup.run(); existingFileHelper = new ExistingFileHelper(existingPacks, existingMods, structureValidator, assetIndex, assetsDir); - ModLoader.runEventGenerator(mc -> new GatherDataEvent(mc, dataGeneratorConfig.makeGenerator(p -> dataGeneratorConfig.isFlat() ? p : p.resolve(mc.getModId()), + ModLoader.runEventGenerator(mc -> eventGenerator.create(mc, dataGeneratorConfig.makeGenerator(p -> dataGeneratorConfig.isFlat() ? p : p.resolve(mc.getModId()), dataGeneratorConfig.getMods().contains(mc.getModId())), dataGeneratorConfig, existingFileHelper)); dataGeneratorConfig.runAll(); } diff --git a/src/main/java/net/neoforged/neoforge/event/EventHooks.java b/src/main/java/net/neoforged/neoforge/event/EventHooks.java index c5109000f3..7529a767d1 100644 --- a/src/main/java/net/neoforged/neoforge/event/EventHooks.java +++ b/src/main/java/net/neoforged/neoforge/event/EventHooks.java @@ -908,8 +908,8 @@ public static void firePlayerCraftingEvent(Player player, ItemStack crafted, Con NeoForge.EVENT_BUS.post(new PlayerEvent.ItemCraftedEvent(player, crafted, craftMatrix)); } - public static void firePlayerSmeltedEvent(Player player, ItemStack smelted) { - NeoForge.EVENT_BUS.post(new PlayerEvent.ItemSmeltedEvent(player, smelted)); + public static void firePlayerSmeltedEvent(Player player, ItemStack smelted, int amountRemoved) { + NeoForge.EVENT_BUS.post(new PlayerEvent.ItemSmeltedEvent(player, smelted, amountRemoved)); } /** diff --git a/src/main/java/net/neoforged/neoforge/event/entity/player/PlayerEvent.java b/src/main/java/net/neoforged/neoforge/event/entity/player/PlayerEvent.java index 020c86cb0e..66c55569e4 100644 --- a/src/main/java/net/neoforged/neoforge/event/entity/player/PlayerEvent.java +++ b/src/main/java/net/neoforged/neoforge/event/entity/player/PlayerEvent.java @@ -408,15 +408,21 @@ public Container getInventory() { public static class ItemSmeltedEvent extends PlayerEvent { private final ItemStack smelting; + private final int amountRemoved; - public ItemSmeltedEvent(Player player, ItemStack crafting) { + public ItemSmeltedEvent(Player player, ItemStack crafting, int amountRemoved) { super(player); this.smelting = crafting; + this.amountRemoved = amountRemoved; } public ItemStack getSmelting() { return this.smelting; } + + public int getAmountRemoved() { + return this.amountRemoved; + } } public static class PlayerLoggedInEvent extends PlayerEvent { diff --git a/src/main/java/net/neoforged/neoforge/network/ConfigurationInitialization.java b/src/main/java/net/neoforged/neoforge/network/ConfigurationInitialization.java index 2c64664388..e74fb87a09 100644 --- a/src/main/java/net/neoforged/neoforge/network/ConfigurationInitialization.java +++ b/src/main/java/net/neoforged/neoforge/network/ConfigurationInitialization.java @@ -12,6 +12,7 @@ import net.neoforged.bus.api.SubscribeEvent; import net.neoforged.fml.common.EventBusSubscriber; import net.neoforged.neoforge.network.configuration.CheckExtensibleEnums; +import net.neoforged.neoforge.network.configuration.CheckFeatureFlags; import net.neoforged.neoforge.network.configuration.CommonRegisterTask; import net.neoforged.neoforge.network.configuration.CommonVersionTask; import net.neoforged.neoforge.network.configuration.RegistryDataMapNegotiation; @@ -53,8 +54,9 @@ private static void configureModdedClient(RegisterConfigurationTasksEvent event) event.register(new SyncConfig(listener)); } - //These two can always be registered they detect the listener connection type internally and will skip themselves. + //These can always be registered, they detect the listener connection type internally and will skip themselves. event.register(new RegistryDataMapNegotiation(listener)); event.register(new CheckExtensibleEnums(listener)); + event.register(new CheckFeatureFlags(listener)); } } diff --git a/src/main/java/net/neoforged/neoforge/network/NetworkInitialization.java b/src/main/java/net/neoforged/neoforge/network/NetworkInitialization.java index a3e9c32c35..699ece2487 100644 --- a/src/main/java/net/neoforged/neoforge/network/NetworkInitialization.java +++ b/src/main/java/net/neoforged/neoforge/network/NetworkInitialization.java @@ -9,6 +9,7 @@ import net.neoforged.fml.common.EventBusSubscriber; import net.neoforged.neoforge.internal.versions.neoforge.NeoForgeVersion; import net.neoforged.neoforge.network.configuration.CheckExtensibleEnums; +import net.neoforged.neoforge.network.configuration.CheckFeatureFlags; import net.neoforged.neoforge.network.event.RegisterPayloadHandlersEvent; import net.neoforged.neoforge.network.handlers.ClientPayloadHandler; import net.neoforged.neoforge.network.handlers.ServerPayloadHandler; @@ -21,6 +22,8 @@ import net.neoforged.neoforge.network.payload.ConfigFilePayload; import net.neoforged.neoforge.network.payload.ExtensibleEnumAcknowledgePayload; import net.neoforged.neoforge.network.payload.ExtensibleEnumDataPayload; +import net.neoforged.neoforge.network.payload.FeatureFlagAcknowledgePayload; +import net.neoforged.neoforge.network.payload.FeatureFlagDataPayload; import net.neoforged.neoforge.network.payload.FrozenRegistryPayload; import net.neoforged.neoforge.network.payload.FrozenRegistrySyncCompletedPayload; import net.neoforged.neoforge.network.payload.FrozenRegistrySyncStartPayload; @@ -64,6 +67,10 @@ private static void register(final RegisterPayloadHandlersEvent event) { ExtensibleEnumDataPayload.TYPE, ExtensibleEnumDataPayload.STREAM_CODEC, CheckExtensibleEnums::handleClientboundPayload) + .configurationToClient( + FeatureFlagDataPayload.TYPE, + FeatureFlagDataPayload.STREAM_CODEC, + CheckFeatureFlags::handleClientboundPayload) .configurationToServer( KnownRegistryDataMapsReplyPayload.TYPE, KnownRegistryDataMapsReplyPayload.STREAM_CODEC, @@ -72,6 +79,10 @@ private static void register(final RegisterPayloadHandlersEvent event) { ExtensibleEnumAcknowledgePayload.TYPE, ExtensibleEnumAcknowledgePayload.STREAM_CODEC, CheckExtensibleEnums::handleServerboundPayload) + .configurationToServer( + FeatureFlagAcknowledgePayload.TYPE, + FeatureFlagAcknowledgePayload.STREAM_CODEC, + CheckFeatureFlags::handleServerboundPayload) .playToClient( AdvancedAddEntityPayload.TYPE, AdvancedAddEntityPayload.STREAM_CODEC, diff --git a/src/main/java/net/neoforged/neoforge/network/PacketDistributor.java b/src/main/java/net/neoforged/neoforge/network/PacketDistributor.java index f09dfae0c9..ea23ac4dcc 100644 --- a/src/main/java/net/neoforged/neoforge/network/PacketDistributor.java +++ b/src/main/java/net/neoforged/neoforge/network/PacketDistributor.java @@ -87,22 +87,24 @@ public static void sendToAllPlayers(CustomPacketPayload payload, CustomPacketPay * Send the given payload(s) to all players tracking the given entity */ public static void sendToPlayersTrackingEntity(Entity entity, CustomPacketPayload payload, CustomPacketPayload... payloads) { - if (entity.level().getChunkSource() instanceof ServerChunkCache chunkCache) { - chunkCache.broadcast(entity, makeClientboundPacket(payload, payloads)); - } else { + if (entity.level().isClientSide()) { throw new IllegalStateException("Cannot send clientbound payloads on the client"); + } else if (entity.level().getChunkSource() instanceof ServerChunkCache chunkCache) { + chunkCache.broadcast(entity, makeClientboundPacket(payload, payloads)); } + // Silently ignore custom Level implementations which may not return ServerChunkCache. } /** * Send the given payload(s) to all players tracking the given entity and the entity itself if it is a player */ public static void sendToPlayersTrackingEntityAndSelf(Entity entity, CustomPacketPayload payload, CustomPacketPayload... payloads) { - if (entity.level().getChunkSource() instanceof ServerChunkCache chunkCache) { - chunkCache.broadcastAndSend(entity, makeClientboundPacket(payload, payloads)); - } else { + if (entity.level().isClientSide()) { throw new IllegalStateException("Cannot send clientbound payloads on the client"); + } else if (entity.level().getChunkSource() instanceof ServerChunkCache chunkCache) { + chunkCache.broadcastAndSend(entity, makeClientboundPacket(payload, payloads)); } + // Silently ignore custom Level implementations which may not return ServerChunkCache. } /** diff --git a/src/main/java/net/neoforged/neoforge/network/configuration/CheckExtensibleEnums.java b/src/main/java/net/neoforged/neoforge/network/configuration/CheckExtensibleEnums.java index 9d493a3ef5..a62200e369 100644 --- a/src/main/java/net/neoforged/neoforge/network/configuration/CheckExtensibleEnums.java +++ b/src/main/java/net/neoforged/neoforge/network/configuration/CheckExtensibleEnums.java @@ -75,33 +75,33 @@ public static void handleClientboundPayload(ExtensibleEnumDataPayload payload, I Map localEnumEntries = getEnumEntries(); Map remoteEnumEntries = payload.enumEntries(); - Set keyDiff = Sets.symmetricDifference(localEnumEntries.keySet(), remoteEnumEntries.keySet()); - if (!keyDiff.isEmpty()) { - context.disconnect(Component.translatable("neoforge.network.extensible_enums.enum_set_mismatch")); - return; - } - Map mismatched = new HashMap<>(); - for (EnumEntry localEntry : localEnumEntries.values()) { - EnumEntry remoteEntry = remoteEnumEntries.get(localEntry.className); - if (!localEntry.isExtended() && !remoteEntry.isExtended()) { + for (String className : Sets.union(localEnumEntries.keySet(), remoteEnumEntries.keySet())) { + EnumEntry localEntry = localEnumEntries.get(className); + EnumEntry remoteEntry = remoteEnumEntries.get(className); + if ((localEntry == null && remoteEntry.isExtended()) || (remoteEntry == null && localEntry.isExtended())) { + mismatched.put(className, Mismatch.EXTENSIBILITY); + continue; + } + + if ((localEntry == null || !localEntry.isExtended()) && (remoteEntry == null || !remoteEntry.isExtended())) { continue; } if (localEntry.networkCheck != remoteEntry.networkCheck) { - mismatched.put(localEntry.className, Mismatch.NETWORK_CHECK); + mismatched.put(className, Mismatch.NETWORK_CHECK); continue; } if (localEntry.isExtended() != remoteEntry.isExtended()) { - mismatched.put(localEntry.className, Mismatch.EXTENSION); + mismatched.put(className, Mismatch.EXTENSION); continue; } ExtensionData localData = localEntry.data.orElseThrow(); ExtensionData remoteData = remoteEntry.data.orElseThrow(); if (localData.vanillaCount != remoteData.vanillaCount || localData.totalCount != remoteData.totalCount) { - mismatched.put(localEntry.className, Mismatch.ENTRY_COUNT); + mismatched.put(className, Mismatch.ENTRY_COUNT); continue; } @@ -109,7 +109,7 @@ public static void handleClientboundPayload(ExtensibleEnumDataPayload payload, I List remoteValues = remoteData.entries; for (int i = 0; i < localData.totalCount - localData.vanillaCount; i++) { if (!localValues.get(i).equals(remoteValues.get(i))) { - mismatched.put(localEntry.className, Mismatch.ENTRY_MISMATCH); + mismatched.put(className, Mismatch.ENTRY_MISMATCH); break; } } @@ -122,6 +122,13 @@ public static void handleClientboundPayload(ExtensibleEnumDataPayload payload, I String enumClass = entry.getKey(); message.append("\n").append(enumClass).append(": "); switch (entry.getValue()) { + case EXTENSIBILITY -> { + if (remoteEnumEntries.containsKey(enumClass)) { + message.append("Enum is extensible on the server but not on the client"); + } else { + message.append("Enum is extensible on the client but not on the server"); + } + } case NETWORK_CHECK -> message.append("Mismatched NetworkCheck (server: ") .append(remoteEnumEntries.get(enumClass).networkCheck) .append(", client: ") @@ -258,6 +265,7 @@ public record ExtensionData(int vanillaCount, int totalCount, List entri } private enum Mismatch { + EXTENSIBILITY, NETWORK_CHECK, EXTENSION, ENTRY_COUNT, diff --git a/src/main/java/net/neoforged/neoforge/network/configuration/CheckFeatureFlags.java b/src/main/java/net/neoforged/neoforge/network/configuration/CheckFeatureFlags.java new file mode 100644 index 0000000000..74a1173a51 --- /dev/null +++ b/src/main/java/net/neoforged/neoforge/network/configuration/CheckFeatureFlags.java @@ -0,0 +1,108 @@ +/* + * Copyright (c) NeoForged and contributors + * SPDX-License-Identifier: LGPL-2.1-only + */ + +package net.neoforged.neoforge.network.configuration; + +import com.google.common.collect.Sets; +import com.mojang.logging.LogUtils; +import java.util.Map; +import java.util.Set; +import java.util.function.Consumer; +import java.util.stream.Collectors; +import net.minecraft.network.chat.Component; +import net.minecraft.network.protocol.Packet; +import net.minecraft.network.protocol.configuration.ClientConfigurationPacketListener; +import net.minecraft.network.protocol.configuration.ServerConfigurationPacketListener; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.server.network.ConfigurationTask; +import net.minecraft.world.flag.FeatureFlags; +import net.neoforged.neoforge.network.handling.IPayloadContext; +import net.neoforged.neoforge.network.payload.FeatureFlagAcknowledgePayload; +import net.neoforged.neoforge.network.payload.FeatureFlagDataPayload; +import org.jetbrains.annotations.ApiStatus; +import org.slf4j.Logger; + +@ApiStatus.Internal +public record CheckFeatureFlags(ServerConfigurationPacketListener listener) implements ConfigurationTask { + public static final Type TYPE = new Type(ResourceLocation.fromNamespaceAndPath("neoforge", "check_feature_flags")); + private static final Logger LOGGER = LogUtils.getLogger(); + private static Set moddedFlags = null; + + @Override + public void start(Consumer> packetSender) { + if (listener.getConnection().isMemoryConnection()) { + listener.finishCurrentTask(TYPE); + return; + } + + Set moddedFlags = getModdedFeatureFlags(); + if (listener.getConnectionType().isOther() || !listener.hasChannel(FeatureFlagDataPayload.TYPE)) { + if (!moddedFlags.isEmpty()) { + // Use plain components as vanilla connections will be missing our translation keys + listener.disconnect(Component.literal("This server does not support vanilla clients as it has custom FeatureFlags")); + } else { + listener.finishCurrentTask(TYPE); + } + return; + } + packetSender.accept(new FeatureFlagDataPayload(moddedFlags).toVanillaClientbound()); + } + + public static void handleClientboundPayload(FeatureFlagDataPayload payload, IPayloadContext context) { + Set localFlags = getModdedFeatureFlags(); + Set remoteFlags = payload.moddedFlags(); + if (localFlags.equals(remoteFlags)) { + context.reply(FeatureFlagAcknowledgePayload.INSTANCE); + } else { + context.disconnect(Component.translatable("neoforge.network.feature_flags.entry_mismatch")); + + StringBuilder message = new StringBuilder("The server and client have different sets of custom FeatureFlags"); + Set missingLocal = Sets.difference(remoteFlags, localFlags); + if (!missingLocal.isEmpty()) { + message.append("\n\tFlags missing on the client, but present on the server:"); + for (ResourceLocation flag : missingLocal) { + message.append("\n\t\t- ").append(flag); + } + } + Set missingRemote = Sets.difference(localFlags, remoteFlags); + if (!missingRemote.isEmpty()) { + message.append("\n\tFlags missing on the server, but present on the client:"); + for (ResourceLocation flag : missingRemote) { + message.append("\n\t\t- ").append(flag); + } + } + LOGGER.warn(message.toString()); + } + } + + public static void handleServerboundPayload(@SuppressWarnings("unused") FeatureFlagAcknowledgePayload payload, IPayloadContext context) { + context.finishCurrentTask(TYPE); + } + + public static boolean handleVanillaServerConnection(ClientConfigurationPacketListener listener) { + if (!getModdedFeatureFlags().isEmpty()) { + listener.disconnect(Component.translatable("neoforge.network.feature_flags.no_vanilla_server")); + return false; + } + return true; + } + + private static Set getModdedFeatureFlags() { + if (moddedFlags == null) { + moddedFlags = FeatureFlags.REGISTRY.getAllFlags() + .entrySet() + .stream() + .filter(e -> e.getValue().isModded()) + .map(Map.Entry::getKey) + .collect(Collectors.toSet()); + } + return moddedFlags; + } + + @Override + public Type type() { + return TYPE; + } +} diff --git a/src/main/java/net/neoforged/neoforge/network/payload/FeatureFlagAcknowledgePayload.java b/src/main/java/net/neoforged/neoforge/network/payload/FeatureFlagAcknowledgePayload.java new file mode 100644 index 0000000000..b8492d8343 --- /dev/null +++ b/src/main/java/net/neoforged/neoforge/network/payload/FeatureFlagAcknowledgePayload.java @@ -0,0 +1,26 @@ +/* + * Copyright (c) NeoForged and contributors + * SPDX-License-Identifier: LGPL-2.1-only + */ + +package net.neoforged.neoforge.network.payload; + +import io.netty.buffer.ByteBuf; +import net.minecraft.network.codec.StreamCodec; +import net.minecraft.network.protocol.common.custom.CustomPacketPayload; +import net.minecraft.resources.ResourceLocation; +import org.jetbrains.annotations.ApiStatus; + +@ApiStatus.Internal +public final class FeatureFlagAcknowledgePayload implements CustomPacketPayload { + public static final Type TYPE = new Type<>(ResourceLocation.fromNamespaceAndPath("neoforge", "feature_flags_ack")); + public static final FeatureFlagAcknowledgePayload INSTANCE = new FeatureFlagAcknowledgePayload(); + public static final StreamCodec STREAM_CODEC = StreamCodec.unit(INSTANCE); + + private FeatureFlagAcknowledgePayload() {} + + @Override + public Type type() { + return TYPE; + } +} diff --git a/src/main/java/net/neoforged/neoforge/network/payload/FeatureFlagDataPayload.java b/src/main/java/net/neoforged/neoforge/network/payload/FeatureFlagDataPayload.java new file mode 100644 index 0000000000..cf83d7b2b0 --- /dev/null +++ b/src/main/java/net/neoforged/neoforge/network/payload/FeatureFlagDataPayload.java @@ -0,0 +1,29 @@ +/* + * Copyright (c) NeoForged and contributors + * SPDX-License-Identifier: LGPL-2.1-only + */ + +package net.neoforged.neoforge.network.payload; + +import io.netty.buffer.ByteBuf; +import java.util.HashSet; +import java.util.Set; +import net.minecraft.network.codec.ByteBufCodecs; +import net.minecraft.network.codec.StreamCodec; +import net.minecraft.network.protocol.common.custom.CustomPacketPayload; +import net.minecraft.resources.ResourceLocation; +import net.neoforged.neoforge.internal.versions.neoforge.NeoForgeVersion; +import org.jetbrains.annotations.ApiStatus; + +@ApiStatus.Internal +public record FeatureFlagDataPayload(Set moddedFlags) implements CustomPacketPayload { + public static final Type TYPE = new Type<>(ResourceLocation.fromNamespaceAndPath(NeoForgeVersion.MOD_ID, "feature_flags")); + public static final StreamCodec STREAM_CODEC = ResourceLocation.STREAM_CODEC + .apply(ByteBufCodecs.>collection(HashSet::new)) + .map(FeatureFlagDataPayload::new, FeatureFlagDataPayload::moddedFlags); + + @Override + public Type type() { + return TYPE; + } +} diff --git a/src/main/java/net/neoforged/neoforge/network/registration/NetworkRegistry.java b/src/main/java/net/neoforged/neoforge/network/registration/NetworkRegistry.java index 899598ef6d..0d90287a93 100644 --- a/src/main/java/net/neoforged/neoforge/network/registration/NetworkRegistry.java +++ b/src/main/java/net/neoforged/neoforge/network/registration/NetworkRegistry.java @@ -48,6 +48,7 @@ import net.neoforged.neoforge.common.extensions.ICommonPacketListener; import net.neoforged.neoforge.internal.versions.neoforge.NeoForgeVersion; import net.neoforged.neoforge.network.configuration.CheckExtensibleEnums; +import net.neoforged.neoforge.network.configuration.CheckFeatureFlags; import net.neoforged.neoforge.network.configuration.CommonRegisterTask; import net.neoforged.neoforge.network.configuration.CommonVersionTask; import net.neoforged.neoforge.network.connection.ConnectionType; @@ -555,6 +556,10 @@ public static void initializeOtherConnection(ClientConfigurationPacketListener l if (!CheckExtensibleEnums.handleVanillaServerConnection(listener)) { return; } + // We are on the client, connected to a vanilla server, make sure we don't have any modded feature flags + if (!CheckFeatureFlags.handleVanillaServerConnection(listener)) { + return; + } // We are on the client, connected to a vanilla server, We have to load the default configs. ConfigTracker.INSTANCE.loadDefaultServerConfigs(); diff --git a/src/main/java/net/neoforged/neoforge/registries/DeferredRegister.java b/src/main/java/net/neoforged/neoforge/registries/DeferredRegister.java index a4b4133417..4d44f562f9 100644 --- a/src/main/java/net/neoforged/neoforge/registries/DeferredRegister.java +++ b/src/main/java/net/neoforged/neoforge/registries/DeferredRegister.java @@ -170,21 +170,6 @@ public static DataComponents createDataComponents(ResourceKey> registryKey; private final String namespace; private final Map, Supplier> entries = new LinkedHashMap<>(); @@ -424,24 +409,56 @@ public DeferredBlock register(String name, Supplier DeferredBlock registerBlock(String name, Function func, BlockBehaviour.Properties props) { return this.register(name, key -> func.apply(props.setId(ResourceKey.create(Registries.BLOCK, key)))); } /** - * Adds a new simple {@link Block} to the list of entries to be registered and returns a {@link DeferredHolder} that will be populated with the created block automatically. + * Adds a new block to the list of entries to be registered and returns a {@link DeferredHolder} that will be populated with the created block automatically. + * This method uses the default {@link BlockBehaviour.Properties}. + * + * @param name The new block's name. It will automatically have the {@linkplain #getNamespace() namespace} prefixed. + * @param func A factory for the new block. The factory should not cache the created block. + * @return A {@link DeferredHolder} that will track updates from the registry for this block. + * @see #registerBlock(String, Function, BlockBehaviour.Properties) + * @see #registerSimpleBlock(String, BlockBehaviour.Properties) + * @see #registerSimpleBlock(String) + */ + public DeferredBlock registerBlock(String name, Function func) { + return this.registerBlock(name, func, BlockBehaviour.Properties.of()); + } + + /** + * Adds a new simple {@link Block} with the given {@link BlockBehaviour.Properties properties} to the list of entries to be registered and returns a {@link DeferredHolder} that will be populated with the created block automatically. * * @param name The new block's name. It will automatically have the {@linkplain #getNamespace() namespace} prefixed. * @param props The properties for the created block. * @return A {@link DeferredHolder} that will track updates from the registry for this block. * @see #registerBlock(String, Function, BlockBehaviour.Properties) + * @see #registerBlock(String, Function) + * @see #registerSimpleBlock(String) */ public DeferredBlock registerSimpleBlock(String name, BlockBehaviour.Properties props) { return this.registerBlock(name, Block::new, props); } + /** + * Adds a new simple {@link Block} with the default {@link BlockBehaviour.Properties properties} to the list of entries to be registered and returns a {@link DeferredHolder} that will be populated with the created block automatically. + * + * @param name The new block's name. It will automatically have the {@linkplain #getNamespace() namespace} prefixed. + * @return A {@link DeferredHolder} that will track updates from the registry for this block. + * @see #registerBlock(String, Function, BlockBehaviour.Properties) + * @see #registerBlock(String, Function) + * @see #registerSimpleBlock(String, BlockBehaviour.Properties) + */ + public DeferredBlock registerSimpleBlock(String name) { + return this.registerSimpleBlock(name, BlockBehaviour.Properties.of()); + } + @Override protected DeferredBlock createHolder(ResourceKey> registryKey, ResourceLocation key) { return DeferredBlock.createBlock(ResourceKey.create(registryKey, key)); @@ -619,12 +636,6 @@ protected DataComponents(ResourceKey>> registryKey super(registryKey, namespace); } - /** @deprecated Scheduled for removal in 1.21.2; use {@link DataComponents#DataComponents(ResourceKey, String)} */ - @Deprecated(since = "1.21.1", forRemoval = true) - protected DataComponents(String namespace) { - super(Registries.DATA_COMPONENT_TYPE, namespace); - } - /** * Convenience method that constructs a builder for use in the operator. Use this to avoid inference issues. * diff --git a/src/main/java/net/neoforged/neoforge/registries/GameData.java b/src/main/java/net/neoforged/neoforge/registries/GameData.java index fdf014e85c..c5aea872dc 100644 --- a/src/main/java/net/neoforged/neoforge/registries/GameData.java +++ b/src/main/java/net/neoforged/neoforge/registries/GameData.java @@ -66,7 +66,7 @@ public static void unfreezeData() { public static void freezeData() { LOGGER.debug(REGISTRIES, "Freezing registries"); BuiltInRegistries.REGISTRY.stream().filter(r -> r instanceof MappedRegistry).forEach(r -> { - // HolderSet.Named may be used for registry objects, vanilla binds these tags so freeze won't throw for unbound tags + // HolderSet.Named may be used for registry objects, vanilla binds these tags so freeze doesn't throw for unbound tags ((MappedRegistry) r).bindAllTagsToEmpty(); ((MappedRegistry) r).freeze(); }); diff --git a/src/main/java/net/neoforged/neoforge/resource/ContextAwareReloadListener.java b/src/main/java/net/neoforged/neoforge/resource/ContextAwareReloadListener.java index 34cf074faf..077dee9671 100644 --- a/src/main/java/net/neoforged/neoforge/resource/ContextAwareReloadListener.java +++ b/src/main/java/net/neoforged/neoforge/resource/ContextAwareReloadListener.java @@ -6,9 +6,11 @@ package net.neoforged.neoforge.resource; import com.google.gson.JsonElement; +import com.mojang.serialization.DynamicOps; import com.mojang.serialization.JsonOps; import net.minecraft.core.HolderLookup; import net.minecraft.core.RegistryAccess; +import net.minecraft.resources.RegistryOps; import net.minecraft.server.packs.resources.PreparableReloadListener; import net.minecraft.server.packs.resources.SimplePreparableReloadListener; import net.neoforged.neoforge.common.conditions.ConditionalOps; @@ -54,4 +56,14 @@ protected final HolderLookup.Provider getRegistryLookup() { protected final ConditionalOps makeConditionalOps() { return new ConditionalOps<>(getRegistryLookup().createSerializationContext(JsonOps.INSTANCE), getContext()); } + + protected final ConditionalOps makeConditionalOps(DynamicOps ops) { + if (ops instanceof ConditionalOps conditionalOps) { + return conditionalOps; + } + if (ops instanceof RegistryOps registryOps) { + return new ConditionalOps<>(registryOps, getContext()); + } + return makeConditionalOps(); + } } diff --git a/src/main/java/net/neoforged/neoforge/resource/EmptyPackResources.java b/src/main/java/net/neoforged/neoforge/resource/EmptyPackResources.java index f349038cb9..3ccaf5aa35 100644 --- a/src/main/java/net/neoforged/neoforge/resource/EmptyPackResources.java +++ b/src/main/java/net/neoforged/neoforge/resource/EmptyPackResources.java @@ -13,7 +13,7 @@ import net.minecraft.server.packs.PackLocationInfo; import net.minecraft.server.packs.PackResources; import net.minecraft.server.packs.PackType; -import net.minecraft.server.packs.metadata.MetadataSectionSerializer; +import net.minecraft.server.packs.metadata.MetadataSectionType; import net.minecraft.server.packs.metadata.pack.PackMetadataSection; import net.minecraft.server.packs.repository.Pack; import net.minecraft.server.packs.resources.IoSupplier; @@ -27,11 +27,10 @@ public EmptyPackResources(PackLocationInfo packId, PackMetadataSection packMeta) this.packMeta = packMeta; } - @SuppressWarnings("unchecked") @Nullable @Override - public T getMetadataSection(MetadataSectionSerializer deserializer) { - return deserializer.getMetadataSectionName().equals("pack") ? (T) this.packMeta : null; + public T getMetadataSection(MetadataSectionType type) { + return PackMetadataSection.TYPE.equals(type) ? (T) this.packMeta : null; } @Override diff --git a/src/main/java/net/neoforged/neoforge/resource/ResourcePackLoader.java b/src/main/java/net/neoforged/neoforge/resource/ResourcePackLoader.java index 049aae13ca..3e85ad9ab4 100644 --- a/src/main/java/net/neoforged/neoforge/resource/ResourcePackLoader.java +++ b/src/main/java/net/neoforged/neoforge/resource/ResourcePackLoader.java @@ -141,7 +141,7 @@ private static void packFinder(Map modResource packAcceptor.accept(makePack(packType, hiddenPacks)); } - public static final MetadataSectionType OPTIONAL_FORMAT = MetadataSectionType.fromCodec("pack", RecordCodecBuilder.create( + public static final MetadataSectionType OPTIONAL_FORMAT = new MetadataSectionType<>("pack", RecordCodecBuilder.create( in -> in.group( ComponentSerialization.CODEC.optionalFieldOf("description", Component.empty()).forGetter(PackMetadataSection::description), Codec.INT.optionalFieldOf("pack_format", -1).forGetter(PackMetadataSection::packFormat), diff --git a/src/main/java/net/neoforged/neoforge/server/command/TagsCommand.java b/src/main/java/net/neoforged/neoforge/server/command/TagsCommand.java index 38091a87ac..8645cc8733 100644 --- a/src/main/java/net/neoforged/neoforge/server/command/TagsCommand.java +++ b/src/main/java/net/neoforged/neoforge/server/command/TagsCommand.java @@ -11,9 +11,9 @@ import com.mojang.brigadier.exceptions.CommandSyntaxException; import com.mojang.brigadier.exceptions.Dynamic2CommandExceptionType; import com.mojang.brigadier.exceptions.DynamicCommandExceptionType; +import java.util.Iterator; import java.util.Optional; import java.util.function.Supplier; -import java.util.stream.Collectors; import java.util.stream.Stream; import net.minecraft.ChatFormatting; import net.minecraft.commands.CommandSourceStack; @@ -51,6 +51,10 @@ * */ class TagsCommand { + // The limit of how long the clipboard text can be; no more elements are added to the text if they would push it over this limit + // This is roughly below 32767, the default limit for UTF-8 strings in FriendlyByteBuf. When adjusting this, make sure to leave + // ample room for the explanatory text (see #createMessage() below). + private static final long CLIPBOARD_TEXT_LIMIT = 32600; private static final long PAGE_SIZE = 8; private static final ResourceKey>> ROOT_REGISTRY_KEY = ResourceKey.createRegistryKey(ResourceLocation.withDefaultNamespace("root")); @@ -171,16 +175,45 @@ private static MutableComponent createMessage(final MutableComponent header, final long currentPage, final ChatFormatting elementColor, final Supplier> names) { - final String allElementNames = names.get().sorted().collect(Collectors.joining("\n")); final long totalPages = (count - 1) / PAGE_SIZE + 1; final long actualPage = (long) Mth.clamp(currentPage, 1, totalPages); MutableComponent containsComponent = Component.translatable(containsText, count); if (count > 0) // Highlight the count text, make it clickable, and append page counters { + final String clipboardText; + final StringBuilder clipboardTextBuilder = new StringBuilder(); + boolean reachedLimit = false; + int countedLines = 0; + + Iterator iterator = names.get().sorted().iterator(); + while (iterator.hasNext()) { + final String line = iterator.next(); + if (clipboardTextBuilder.length() + line.length() > CLIPBOARD_TEXT_LIMIT) { + // The to-be-added line puts us over the limit, so stop adding lines + reachedLimit = true; + break; + } + clipboardTextBuilder.append(line).append('\n'); + countedLines++; + } + // Remove the trailing newline if present + if (!clipboardTextBuilder.isEmpty()) { + clipboardTextBuilder.deleteCharAt(clipboardTextBuilder.length() - 1); + } + + if (reachedLimit) { + // Almost went over the limit; add additional info to clipboard text + clipboardText = "(Too many entries to fit in clipboard, showing only first " + countedLines + " entries...)" + '\n' + + clipboardTextBuilder + '\n' + + "(..." + (count - countedLines) + " more entries not shown)"; + } else { + clipboardText = clipboardTextBuilder.toString(); + } + containsComponent = ComponentUtils.wrapInSquareBrackets(containsComponent.withStyle(s -> s .withColor(ChatFormatting.GREEN) - .withClickEvent(new ClickEvent(ClickEvent.Action.COPY_TO_CLIPBOARD, allElementNames)) + .withClickEvent(new ClickEvent(ClickEvent.Action.COPY_TO_CLIPBOARD, clipboardText)) .withHoverEvent(new HoverEvent(HoverEvent.Action.SHOW_TEXT, Component.translatable(copyHoverText))))); containsComponent = Component.translatable("commands.neoforge.tags.page_info", diff --git a/src/main/resources/META-INF/accesstransformer.cfg b/src/main/resources/META-INF/accesstransformer.cfg index a9634170b7..9a143aea76 100644 --- a/src/main/resources/META-INF/accesstransformer.cfg +++ b/src/main/resources/META-INF/accesstransformer.cfg @@ -16,6 +16,13 @@ public net.minecraft.client.gui.screens.MenuScreens$ScreenConstructor public net.minecraft.client.gui.screens.Screen renderables # renderables public net.minecraft.client.gui.screens.recipebook.GhostSlots setInput(Lnet/minecraft/world/inventory/Slot;Lnet/minecraft/util/context/ContextMap;Lnet/minecraft/world/item/crafting/display/SlotDisplay;)V public net.minecraft.client.gui.screens.recipebook.GhostSlots setResult(Lnet/minecraft/world/inventory/Slot;Lnet/minecraft/util/context/ContextMap;Lnet/minecraft/world/item/crafting/display/SlotDisplay;)V +protected net.minecraft.client.gui.screens.worldselection.ExperimentsScreen getHumanReadableTitle(Lnet/minecraft/server/packs/repository/Pack;)Lnet/minecraft/network/chat/Component; +protected net.minecraft.client.gui.screens.worldselection.ExperimentsScreen INFO +protected net.minecraft.client.gui.screens.worldselection.ExperimentsScreen layout +protected net.minecraft.client.gui.screens.worldselection.ExperimentsScreen onDone()V +protected net.minecraft.client.gui.screens.worldselection.ExperimentsScreen MAIN_CONTENT_WIDTH +protected net.minecraft.client.gui.screens.worldselection.ExperimentsScreen packs +protected net.minecraft.client.gui.screens.worldselection.ExperimentsScreen TITLE public net.minecraft.client.model.geom.LayerDefinitions OUTER_ARMOR_DEFORMATION # OUTER_ARMOR_DEFORMATION public net.minecraft.client.model.geom.LayerDefinitions INNER_ARMOR_DEFORMATION # INNER_ARMOR_DEFORMATION public net.minecraft.client.multiplayer.ClientPacketListener commands # commands @@ -63,9 +70,6 @@ public net.minecraft.client.renderer.RenderStateShard$TransparencyStateShard public net.minecraft.client.renderer.RenderStateShard$WriteMaskStateShard #endgroup default net.minecraft.client.renderer.chunk.RenderChunk wrapped -protected net.minecraft.client.renderer.block.model.BakedOverrides ()V -public net.minecraft.client.renderer.block.model.BakedOverrides$BakedOverride -public net.minecraft.client.renderer.block.model.BakedOverrides$BakedOverride ([Lnet/minecraft/client/renderer/block/model/BakedOverrides$PropertyMatcher;Lnet/minecraft/client/resources/model/BakedModel;)V public net.minecraft.client.renderer.block.model.BlockElement uvsByFace(Lnet/minecraft/core/Direction;)[F # uvsByFace public net.minecraft.client.renderer.block.model.BlockElement$Deserializer public net.minecraft.client.renderer.block.model.BlockElement$Deserializer ()V # constructor @@ -73,13 +77,8 @@ public net.minecraft.client.renderer.block.model.BlockElementFace$Deserializer public net.minecraft.client.renderer.block.model.BlockElementFace$Deserializer ()V # constructor public net.minecraft.client.renderer.block.model.BlockFaceUV$Deserializer public net.minecraft.client.renderer.block.model.BlockFaceUV$Deserializer ()V # constructor -public net.minecraft.client.renderer.block.model.BlockModel textureMap # textureMap -public net.minecraft.client.renderer.block.model.BlockModel parent # parent -public net.minecraft.client.renderer.block.model.BlockModel hasAmbientOcclusion # hasAmbientOcclusion -public net.minecraft.client.renderer.block.model.BlockModel bakeFace(Lnet/minecraft/client/renderer/block/model/BlockElement;Lnet/minecraft/client/renderer/block/model/BlockElementFace;Lnet/minecraft/client/renderer/texture/TextureAtlasSprite;Lnet/minecraft/core/Direction;Lnet/minecraft/client/resources/model/ModelState;)Lnet/minecraft/client/renderer/block/model/BakedQuad; # bakeFace +public net.minecraft.client.renderer.block.model.BlockModel GSON public net.minecraft.client.renderer.block.model.ItemModelGenerator processFrames(ILjava/lang/String;Lnet/minecraft/client/renderer/texture/SpriteContents;)Ljava/util/List; # processFrames -public net.minecraft.client.renderer.block.model.ItemOverride$Deserializer -public net.minecraft.client.renderer.block.model.ItemOverride$Deserializer ()V # constructor public net.minecraft.client.renderer.block.model.ItemTransform$Deserializer public net.minecraft.client.renderer.block.model.ItemTransform$Deserializer ()V # constructor public net.minecraft.client.renderer.block.model.ItemTransform$Deserializer DEFAULT_ROTATION # DEFAULT_ROTATION @@ -90,10 +89,9 @@ public net.minecraft.client.renderer.block.model.ItemTransforms$Deserializer (Lnet/minecraft/client/resources/model/BakedModel;Ljava/util/List;)V public net.minecraft.client.renderer.texture.SpriteContents byMipLevel # byMipLevel default net.minecraft.client.renderer.texture.SpriteContents animatedTexture # animatedTexture public net.minecraft.client.renderer.texture.atlas.sources.PalettedPermutations (Ljava/util/List;Lnet/minecraft/resources/ResourceLocation;Ljava/util/Map;)V # constructor @@ -105,7 +103,8 @@ public net.minecraft.client.renderer.texture.atlas.sources.Unstitcher$RegionInst public net.minecraft.client.renderer.texture.atlas.sources.Unstitcher$RegionInstance (Lnet/minecraft/client/renderer/texture/atlas/sources/LazyLoadedImage;Lnet/minecraft/client/renderer/texture/atlas/sources/Unstitcher$Region;DD)V # constructor public net.minecraft.client.resources.ClientPackSource createVanillaPackSource(Ljava/nio/file/Path;)Lnet/minecraft/server/packs/VanillaPackResources; # createVanillaPackSource protected net.minecraft.client.resources.TextureAtlasHolder textureAtlas # textureAtlas -public net.minecraft.client.resources.model.ItemModel$BakedModelWithOverrides +public net.minecraft.client.resources.model.ModelBakery$ModelBakerImpl getModel(Lnet/minecraft/resources/ResourceLocation;)Lnet/minecraft/client/resources/model/UnbakedModel; +public net.minecraft.client.resources.model.SimpleBakedModel bakeFace(Lnet/minecraft/client/renderer/block/model/BlockElement;Lnet/minecraft/client/renderer/block/model/BlockElementFace;Lnet/minecraft/client/renderer/texture/TextureAtlasSprite;Lnet/minecraft/core/Direction;Lnet/minecraft/client/resources/model/ModelState;)Lnet/minecraft/client/renderer/block/model/BakedQuad; public net.minecraft.client.resources.model.SimpleBakedModel$Builder (ZZZLnet/minecraft/client/renderer/block/model/ItemTransforms;)V public net.minecraft.client.sounds.SoundEngine soundManager # soundManager public net.minecraft.commands.CommandSourceStack source # source @@ -116,9 +115,6 @@ protected net.minecraft.core.IdMapper nextId # nextId protected net.minecraft.core.IdMapper tToId # tToId - internal map protected net.minecraft.core.IdMapper idToT # idToT - internal index list public net.minecraft.core.particles.SimpleParticleType (Z)V # constructor -public net.minecraft.data.models.ItemModelGenerators GENERATED_TRIM_MODELS -public net.minecraft.data.models.ItemModelGenerators$TrimModelData -public net.minecraft.data.models.ItemModelGenerators$TrimModelData (Ljava/lang/String;FLjava/util/Map;)V protected net.minecraft.data.loot.BlockLootSubProvider createSilkTouchOnlyTable(Lnet/minecraft/world/level/ItemLike;)Lnet/minecraft/world/level/storage/loot/LootTable$Builder; # createSilkTouchOnlyTable protected net.minecraft.data.loot.BlockLootSubProvider createPotFlowerItemTable(Lnet/minecraft/world/level/ItemLike;)Lnet/minecraft/world/level/storage/loot/LootTable$Builder; # createPotFlowerItemTable protected net.minecraft.data.loot.BlockLootSubProvider createSelfDropDispatchTable(Lnet/minecraft/world/level/block/Block;Lnet/minecraft/world/level/storage/loot/predicates/LootItemCondition$Builder;Lnet/minecraft/world/level/storage/loot/entries/LootPoolEntryContainer$Builder;)Lnet/minecraft/world/level/storage/loot/LootTable$Builder; # createSelfDropDispatchTable @@ -188,6 +184,8 @@ public net.minecraft.world.damagesource.DamageSources source(Lnet/minecraft/reso public net.minecraft.world.damagesource.DamageSources source(Lnet/minecraft/resources/ResourceKey;Lnet/minecraft/world/entity/Entity;)Lnet/minecraft/world/damagesource/DamageSource; # source public net.minecraft.world.damagesource.DamageSources source(Lnet/minecraft/resources/ResourceKey;Lnet/minecraft/world/entity/Entity;Lnet/minecraft/world/entity/Entity;)Lnet/minecraft/world/damagesource/DamageSource; # source public net.minecraft.world.damagesource.DamageSources damageTypes # damageTypes +public net.minecraft.world.effect.MobEffect$AttributeTemplate (Lnet/minecraft/resources/ResourceLocation;DLnet/minecraft/world/entity/ai/attributes/AttributeModifier$Operation;)V # constructor +public net.minecraft.world.effect.MobEffect$AttributeTemplate # AttributeTemplate protected net.minecraft.world.entity.Entity ENTITY_COUNTER # ENTITY_COUNTER public net.minecraft.world.entity.Entity getEncodeId()Ljava/lang/String; # getEncodeId public net.minecraft.world.entity.ExperienceOrb value # value @@ -437,7 +435,7 @@ public com.mojang.blaze3d.vertex.VertexFormatElement$Usage$SetupState # Below you will find particle constructor ATs protected net.minecraft.client.particle.AttackSweepParticle (Lnet/minecraft/client/multiplayer/ClientLevel;DDDDLnet/minecraft/client/particle/SpriteSet;)V # constructor protected net.minecraft.client.particle.BlockMarker (Lnet/minecraft/client/multiplayer/ClientLevel;DDDLnet/minecraft/world/level/block/state/BlockState;)V # constructor -protected net.minecraft.client.particle.BreakingItemParticle (Lnet/minecraft/client/multiplayer/ClientLevel;DDDDDDLnet/minecraft/world/item/ItemStack;)V # constructor +protected net.minecraft.client.particle.BreakingItemParticle (Lnet/minecraft/client/multiplayer/ClientLevel;DDDDDDLnet/minecraft/client/renderer/item/ItemStackRenderState;)V # constructor protected net.minecraft.client.particle.BubbleColumnUpParticle (Lnet/minecraft/client/multiplayer/ClientLevel;DDDDDD)V # constructor protected net.minecraft.client.particle.BubbleParticle (Lnet/minecraft/client/multiplayer/ClientLevel;DDDDDD)V # constructor protected net.minecraft.client.particle.BubblePopParticle (Lnet/minecraft/client/multiplayer/ClientLevel;DDDDDDLnet/minecraft/client/particle/SpriteSet;)V # constructor @@ -494,3 +492,6 @@ public net.minecraft.world.item.enchantment.Enchantment locationContext(Lnet/min public net.minecraft.world.item.enchantment.Enchantment entityContext(Lnet/minecraft/server/level/ServerLevel;ILnet/minecraft/world/entity/Entity;Lnet/minecraft/world/phys/Vec3;)Lnet/minecraft/world/level/storage/loot/LootContext; public net.minecraft.world.item.enchantment.Enchantment blockHitContext(Lnet/minecraft/server/level/ServerLevel;ILnet/minecraft/world/entity/Entity;Lnet/minecraft/world/phys/Vec3;Lnet/minecraft/world/level/block/state/BlockState;)Lnet/minecraft/world/level/storage/loot/LootContext; public net.minecraft.world.item.enchantment.Enchantment applyEffects(Ljava/util/List;Lnet/minecraft/world/level/storage/loot/LootContext;Ljava/util/function/Consumer;)V +# Made public for mc logo render in mods list +public net.minecraft.client.gui.components.LogoRenderer LOGO_TEXTURE_WIDTH +public net.minecraft.client.gui.components.LogoRenderer LOGO_TEXTURE_HEIGHT diff --git a/src/main/resources/assets/neoforge/lang/en_us.json b/src/main/resources/assets/neoforge/lang/en_us.json index b61eef8d39..7cd26fc40b 100644 --- a/src/main/resources/assets/neoforge/lang/en_us.json +++ b/src/main/resources/assets/neoforge/lang/en_us.json @@ -64,6 +64,9 @@ "fml.resources.modresources": "Resources for %1$s mod files", "fml.resources.moddata": "Data for %1$s mod files", + "fml.modloadingissue.feature_flags.file_not_found": "FeatureFlag file {0}, provided by mod {100,modinfo,id}, does not exist", + "fml.modloadingissue.feature_flags.loading_error": "Failed to load FeatureFlag data at {0} in mod {100,modinfo,id}: {102,exc,msg}", + "loadwarning.neoforge.prbuild": "This build of NeoForge was created by a community member and is thus §c§lUNSUPPORTED§r", "commands.neoforge.arguments.enum.invalid": "Enum constant must be one of %1$s, found %2$s", @@ -189,6 +192,8 @@ "neoforge.configuration.section.neoforge.server.toml.title": "Server settings", "neoforge.configgui.advertiseDedicatedServerToLan": "Advertise Dedicated Server To LAN", "neoforge.configgui.advertiseDedicatedServerToLan.tooltip": "Set this to true to enable advertising the dedicated server to local LAN clients so that it shows up in the Multiplayer screen automatically.", + "neoforge.configgui.attributeAdvancedTooltipDebugInfo": "Additional Attribute Advanced Tooltips", + "neoforge.configgui.attributeAdvancedTooltipDebugInfo.tooltip": "Set this to true to enable additional information about attributes on an item when advanced tooltips is on.", "neoforge.configgui.forgeLightPipelineEnabled": "NeoForge Light Pipeline", "neoforge.configgui.forgeLightPipelineEnabled.tooltip": "Enable the NeoForge block rendering pipeline - fixes the lighting of custom models.", "neoforge.configgui.fullBoundingBoxLadders": "Full Bounding Box Ladders", @@ -264,9 +269,11 @@ "neoforge.network.data_maps.missing_their": "Cannot connect to server as it has mandatory registry data maps not present on the client: %s", "neoforge.network.extensible_enums.no_vanilla_server": "This client does not support vanilla servers as it has extended enums used in serverbound networking", - "neoforge.network.extensible_enums.enum_set_mismatch": "The set of extensible enums on the client and server do not match. Make sure you are using the same NeoForge version as the server", "neoforge.network.extensible_enums.enum_entry_mismatch": "The set of values added to extensible enums on the client and server do not match. Make sure you are using the same mod and NeoForge versions as the server. See the log for more details", + "neoforge.network.feature_flags.entry_mismatch": "The server and client have different sets of custom FeatureFlags. Make sure you are using the same mod and NeoForge versions as the server. See the log for more details", + "neoforge.network.feature_flags.no_vanilla_server": "This client does not support vanilla servers as it has custom FeatureFlags", + "neoforge.attribute.debug.base": "[Entity: %s | Item: %s]", "neoforge.value.flat": "%s", diff --git a/testframework/build.gradle b/testframework/build.gradle index 91a01c1c76..d5d98b4059 100644 --- a/testframework/build.gradle +++ b/testframework/build.gradle @@ -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 diff --git a/testframework/src/main/java/net/neoforged/testframework/client/AbstractTestScreen.java b/testframework/src/main/java/net/neoforged/testframework/client/AbstractTestScreen.java index d2fc91efe3..1dbbc80b6c 100644 --- a/testframework/src/main/java/net/neoforged/testframework/client/AbstractTestScreen.java +++ b/testframework/src/main/java/net/neoforged/testframework/client/AbstractTestScreen.java @@ -73,12 +73,12 @@ public void resetRows(String search) { } @Override - protected boolean isValidMouseClick(int button) { + protected boolean isValidClickButton(int button) { return button == GLFW.GLFW_MOUSE_BUTTON_LEFT || button == GLFW.GLFW_MOUSE_BUTTON_RIGHT || button == GLFW.GLFW_MOUSE_BUTTON_MIDDLE; } @Override - protected int getScrollbarPosition() { + protected int scrollBarX() { return this.width / 2 + 144; } diff --git a/testframework/src/main/java/net/neoforged/testframework/condition/TestEnabledIngredient.java b/testframework/src/main/java/net/neoforged/testframework/condition/TestEnabledIngredient.java index 7cd6675258..ce331c6ce6 100644 --- a/testframework/src/main/java/net/neoforged/testframework/condition/TestEnabledIngredient.java +++ b/testframework/src/main/java/net/neoforged/testframework/condition/TestEnabledIngredient.java @@ -36,7 +36,7 @@ public boolean test(ItemStack stack) { @Override public Stream> items() { - return base.items().stream(); + return base.items(); } @Override diff --git a/testframework/src/main/java/net/neoforged/testframework/gametest/ExtendedGameTestHelper.java b/testframework/src/main/java/net/neoforged/testframework/gametest/ExtendedGameTestHelper.java index 3dce9e062c..7d0a259726 100644 --- a/testframework/src/main/java/net/neoforged/testframework/gametest/ExtendedGameTestHelper.java +++ b/testframework/src/main/java/net/neoforged/testframework/gametest/ExtendedGameTestHelper.java @@ -142,6 +142,7 @@ protected void keepConnectionAlive() {} serverplayer.setYRot(180); serverplayer.connection.chunkSender.sendNextChunks(serverplayer); serverplayer.connection.chunkSender.onChunkBatchReceivedByClient(64f); + serverplayer.setClientLoaded(true); return serverplayer; } @@ -163,7 +164,7 @@ public boolean isLocalPlayer() { } @Override - protected int getPermissionLevel() { + public int getPermissionLevel() { return commandLevel; } }; diff --git a/testframework/src/main/java/net/neoforged/testframework/impl/reg/RegistrationHelperImpl.java b/testframework/src/main/java/net/neoforged/testframework/impl/reg/RegistrationHelperImpl.java index bf9743250c..d357f6dcd6 100644 --- a/testframework/src/main/java/net/neoforged/testframework/impl/reg/RegistrationHelperImpl.java +++ b/testframework/src/main/java/net/neoforged/testframework/impl/reg/RegistrationHelperImpl.java @@ -108,8 +108,10 @@ protected void start() { } private final String modId; - private final ListMultimap, Consumer> providers = Multimaps.newListMultimap(new IdentityHashMap<>(), ArrayList::new); - private final List> directProviders = new ArrayList<>(); + private final ListMultimap, Consumer> clientProviders = Multimaps.newListMultimap(new IdentityHashMap<>(), ArrayList::new); + private final ListMultimap, Consumer> serverProviders = Multimaps.newListMultimap(new IdentityHashMap<>(), ArrayList::new); + private final List> directClientProviders = new ArrayList<>(); + private final List> directServerProviders = new ArrayList<>(); private final Map>, DeferredRegister> registrars = new ConcurrentHashMap<>(); @Override @@ -199,13 +201,23 @@ public String registerSubpack(String name) { } @Override - public void provider(Class type, Consumer consumer) { - providers.put(type, consumer); + public void serverProvider(Class type, Consumer consumer) { + serverProviders.put(type, consumer); } @Override - public void addProvider(Function provider) { - directProviders.add(provider); + public void clientProvider(Class type, Consumer consumer) { + clientProviders.put(type, consumer); + } + + @Override + public void addClientProvider(Function provider) { + directClientProviders.add(provider); + } + + @Override + public void addServerProvider(Function provider) { + directServerProviders.add(provider); } private IEventBus bus; @@ -214,7 +226,8 @@ public void addProvider(Function provider) { public void register(IEventBus bus, ModContainer container) { this.bus = bus; this.owner = container; - bus.addListener(this::gather); + bus.addListener(this::gatherServer); + bus.addListener(this::gatherClient); listeners.forEach(bus::addListener); registrars.values().forEach(r -> r.register(bus)); } @@ -226,7 +239,15 @@ public Consumer> eventListeners() { return bus == null ? listeners::add : bus::addListener; } - private void gather(final GatherDataEvent event) { + private void gatherServer(final GatherDataEvent.Server event) { + gather(event, serverProviders, directServerProviders); + } + + private void gatherClient(final GatherDataEvent.Client event) { + gather(event, clientProviders, directClientProviders); + } + + private void gather(final T event, ListMultimap, Consumer> providers, List> directProviders) { providers.asMap().forEach((cls, cons) -> event.getGenerator().addProvider(true, PROVIDERS.get(cls).create( event.getGenerator().getPackOutput(), event.getLookupProvider(), event.getGenerator(), event.getExistingFileHelper(), modId, (List) cons))); diff --git a/testframework/src/main/java/net/neoforged/testframework/registration/DeferredBlockBuilder.java b/testframework/src/main/java/net/neoforged/testframework/registration/DeferredBlockBuilder.java index 1de427adae..9595db892b 100644 --- a/testframework/src/main/java/net/neoforged/testframework/registration/DeferredBlockBuilder.java +++ b/testframework/src/main/java/net/neoforged/testframework/registration/DeferredBlockBuilder.java @@ -5,11 +5,17 @@ package net.neoforged.testframework.registration; +import com.mojang.serialization.MapCodec; +import java.util.Objects; import java.util.function.Consumer; +import net.minecraft.client.color.item.ItemTintSource; +import net.minecraft.client.multiplayer.ClientLevel; import net.minecraft.resources.ResourceKey; import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.entity.LivingEntity; import net.minecraft.world.item.BlockItem; import net.minecraft.world.item.Item; +import net.minecraft.world.item.ItemStack; import net.minecraft.world.level.block.Block; import net.neoforged.fml.loading.FMLLoader; import net.neoforged.neoforge.client.event.RegisterColorHandlersEvent; @@ -17,6 +23,7 @@ import net.neoforged.neoforge.client.model.generators.BlockStateProvider; import net.neoforged.neoforge.common.data.LanguageProvider; import net.neoforged.neoforge.registries.DeferredBlock; +import org.jetbrains.annotations.Nullable; public class DeferredBlockBuilder extends DeferredBlock { private final RegistrationHelper helper; @@ -41,7 +48,7 @@ public DeferredBlockBuilder withBlockItem(Item.Properties properties, Consume } public DeferredBlockBuilder withLang(String name) { - helper.provider(LanguageProvider.class, prov -> prov.add(value(), name)); + helper.clientProvider(LanguageProvider.class, prov -> prov.add(value(), name)); return this; } @@ -49,7 +56,7 @@ public DeferredBlockBuilder withLang(String name) { private boolean hasColor = false; public DeferredBlockBuilder withDefaultWhiteModel() { - helper.provider(BlockStateProvider.class, prov -> { + helper.clientProvider(BlockStateProvider.class, prov -> { final BlockModelBuilder model; if (hasColor) { model = prov.models().getBuilder(key.location().getPath()) @@ -81,11 +88,57 @@ public DeferredBlockBuilder withColor(int color) { } private void colorInternal(int color) { + //Capture the color into a local tint source, which has a unit mapcodec for serialization + final ConstantItemTintSourceBuilder source = new ConstantItemTintSourceBuilder(color); + helper.eventListeners().accept((final RegisterColorHandlersEvent.Block event) -> event.register((p_92567_, p_92568_, p_92569_, p_92570_) -> color, value())); - helper.eventListeners().accept((final RegisterColorHandlersEvent.Item event) -> { + helper.eventListeners().accept((final RegisterColorHandlersEvent.ItemTintSources event) -> { if (hasItem) { - event.register((stack, index) -> color, value()); + event.register(key.location(), source.type()); } }); } + + private static final class ConstantItemTintSourceBuilder implements ItemTintSource { + public final MapCodec codec = MapCodec.unit(this); + + private final int color; + + private ConstantItemTintSourceBuilder(int color) { + this.color = color; + } + + @Override + public int calculate(ItemStack p_388652_, @Nullable ClientLevel p_390356_, @Nullable LivingEntity p_390510_) { + return color; + } + + @Override + public MapCodec type() { + return codec; + } + + public int color() { + return color; + } + + @Override + public boolean equals(Object obj) { + if (obj == this) return true; + if (obj == null || obj.getClass() != this.getClass()) return false; + var that = (ConstantItemTintSourceBuilder) obj; + return this.color == that.color; + } + + @Override + public int hashCode() { + return Objects.hash(color); + } + + @Override + public String toString() { + return "ConstantItemTintSourceBuilder[" + + "color=" + color + ']'; + } + } } diff --git a/testframework/src/main/java/net/neoforged/testframework/registration/DeferredEntityTypeBuilder.java b/testframework/src/main/java/net/neoforged/testframework/registration/DeferredEntityTypeBuilder.java index 8434dd0a65..cd1b4569df 100644 --- a/testframework/src/main/java/net/neoforged/testframework/registration/DeferredEntityTypeBuilder.java +++ b/testframework/src/main/java/net/neoforged/testframework/registration/DeferredEntityTypeBuilder.java @@ -40,7 +40,7 @@ public DeferredEntityTypeBuilder withAttributes(Supplier withLang(String name) { - helper.provider(LanguageProvider.class, prov -> prov.add(value(), name)); + helper.clientProvider(LanguageProvider.class, prov -> prov.add(value(), name)); return this; } } diff --git a/testframework/src/main/java/net/neoforged/testframework/registration/DeferredItemBuilder.java b/testframework/src/main/java/net/neoforged/testframework/registration/DeferredItemBuilder.java index 34f267a4d1..4065c74bb7 100644 --- a/testframework/src/main/java/net/neoforged/testframework/registration/DeferredItemBuilder.java +++ b/testframework/src/main/java/net/neoforged/testframework/registration/DeferredItemBuilder.java @@ -24,7 +24,7 @@ protected DeferredItemBuilder(ResourceKey key, RegistrationHelper registra } public DeferredItemBuilder withLang(String name) { - registrationHelper.provider(LanguageProvider.class, prov -> prov.add(value(), name)); + registrationHelper.clientProvider(LanguageProvider.class, prov -> prov.add(value(), name)); return this; } @@ -38,7 +38,7 @@ public DeferredItemBuilder tab(ResourceKey tab) { } public DeferredItemBuilder withModel(Consumer modelConsumer) { - registrationHelper.provider(ItemModelProvider.class, prov -> modelConsumer.accept(prov.getBuilder(key.location().toString()))); + registrationHelper.clientProvider(ItemModelProvider.class, prov -> modelConsumer.accept(prov.getBuilder(key.location().toString()))); return this; } } diff --git a/testframework/src/main/java/net/neoforged/testframework/registration/RegistrationHelper.java b/testframework/src/main/java/net/neoforged/testframework/registration/RegistrationHelper.java index 7a35f06dc2..77c23b8d95 100644 --- a/testframework/src/main/java/net/neoforged/testframework/registration/RegistrationHelper.java +++ b/testframework/src/main/java/net/neoforged/testframework/registration/RegistrationHelper.java @@ -59,9 +59,13 @@ public interface RegistrationHelper { String registerSubpack(String name); - void addProvider(Function provider); + void addClientProvider(Function provider); - void provider(Class type, Consumer consumer); + void addServerProvider(Function provider); + + void serverProvider(Class type, Consumer consumer); + + void clientProvider(Class type, Consumer consumer); Consumer> eventListeners(); diff --git a/tests/build.gradle b/tests/build.gradle index 2da20a4e40..6243371418 100644 --- a/tests/build.gradle +++ b/tests/build.gradle @@ -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,82 @@ 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 +junitTest { + useJUnitPlatform() + classpath = sourceSets.junit.output + sourceSets.junit.runtimeClasspath + testClassesDirs = sourceSets.junit.output.classesDirs + outputs.upToDateWhen { false } +} + +neoDev { + mods { + neotests { + sourceSet sourceSets.main + } + testframework { + sourceSet project(":testframework").sourceSets.main + } + junit { + sourceSet sourceSets.junit + } + coremods { + sourceSet coremodsProject.sourceSets.main + } } - data { - configure neoforgeProject.runTypes.data - - 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 - - final File gameDir = project.file("runs/${name}") as File - gameDir.mkdirs(); - - workingDirectory.set gameDir - arguments.addAll '--gameDir', gameDir.absolutePath + + runs { + configureEach { + gameDirectory = layout.projectDir.dir("run/$name") + } + client { + client() + } + server { + server() + } + gameTestServer { + type = "gameTestServer" + } + data { + clientData() + + 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 + } } -} -//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 -} + 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] -afterEvaluate { - runs.data { - // Override --output that forge already has - def args = new ArrayList(arguments.get()); - def outputIndex = args.indexOf('--output'); - args.set(outputIndex+1, file('src/generated/resources/').absolutePath); - arguments.set(args); + gameDirectory.set project.file("runs/${it.name}") as File } - 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 { diff --git a/tests/src/generated/resources/assets/neotests_test_water_glass_face_removal/blockstates/water_glass.json b/tests/src/generated/resources/assets/neotests_test_water_glass_face_removal/blockstates/water_glass.json new file mode 100644 index 0000000000..ed35a1c6c0 --- /dev/null +++ b/tests/src/generated/resources/assets/neotests_test_water_glass_face_removal/blockstates/water_glass.json @@ -0,0 +1,7 @@ +{ + "variants": { + "": { + "model": "neotests_test_water_glass_face_removal:block/water_glass" + } + } +} \ No newline at end of file diff --git a/tests/src/generated/resources/assets/neotests_test_water_glass_face_removal/lang/en_us.json b/tests/src/generated/resources/assets/neotests_test_water_glass_face_removal/lang/en_us.json new file mode 100644 index 0000000000..e016ca7125 --- /dev/null +++ b/tests/src/generated/resources/assets/neotests_test_water_glass_face_removal/lang/en_us.json @@ -0,0 +1,3 @@ +{ + "block.neotests_test_water_glass_face_removal.water_glass": "Water Glass" +} \ No newline at end of file diff --git a/tests/src/generated/resources/assets/neotests_test_water_glass_face_removal/models/block/water_glass.json b/tests/src/generated/resources/assets/neotests_test_water_glass_face_removal/models/block/water_glass.json new file mode 100644 index 0000000000..10740130a9 --- /dev/null +++ b/tests/src/generated/resources/assets/neotests_test_water_glass_face_removal/models/block/water_glass.json @@ -0,0 +1,7 @@ +{ + "parent": "minecraft:block/cube_all", + "render_type": "minecraft:cutout", + "textures": { + "all": "minecraft:block/glass" + } +} \ No newline at end of file diff --git a/tests/src/generated/resources/data/neotests_reloadable_reg_data_maps/data_maps/loot_table/effect_grant.json b/tests/src/generated/resources/data/neotests_reloadable_reg_data_maps/data_maps/loot_table/effect_grant.json new file mode 100644 index 0000000000..ff7693fcb2 --- /dev/null +++ b/tests/src/generated/resources/data/neotests_reloadable_reg_data_maps/data_maps/loot_table/effect_grant.json @@ -0,0 +1,9 @@ +{ + "values": { + "minecraft:blocks/copper_block": { + "duration": 100, + "id": "minecraft:nausea", + "show_icon": true + } + } +} \ No newline at end of file diff --git a/tests/src/generated/resources/data/neotests_test_conditional_recipe/advancement/recipes/misc/always_disabled_recipe.json b/tests/src/generated/resources/data/neotests_test_conditional_recipe/advancement/recipes/misc/always_disabled_recipe.json new file mode 100644 index 0000000000..e13d7504ae --- /dev/null +++ b/tests/src/generated/resources/data/neotests_test_conditional_recipe/advancement/recipes/misc/always_disabled_recipe.json @@ -0,0 +1,37 @@ +{ + "neoforge:conditions": [ + { + "type": "neoforge:false" + } + ], + "parent": "minecraft:recipes/root", + "criteria": { + "has_stone": { + "conditions": { + "items": [ + { + "items": "minecraft:stone" + } + ] + }, + "trigger": "minecraft:inventory_changed" + }, + "has_the_recipe": { + "conditions": { + "recipe": "neotests_test_conditional_recipe:always_disabled_recipe" + }, + "trigger": "minecraft:recipe_unlocked" + } + }, + "requirements": [ + [ + "has_the_recipe", + "has_stone" + ] + ], + "rewards": { + "recipes": [ + "neotests_test_conditional_recipe:always_disabled_recipe" + ] + } +} \ No newline at end of file diff --git a/tests/src/generated/resources/data/neotests_test_conditional_recipe/recipe/always_disabled_recipe.json b/tests/src/generated/resources/data/neotests_test_conditional_recipe/recipe/always_disabled_recipe.json new file mode 100644 index 0000000000..404842c813 --- /dev/null +++ b/tests/src/generated/resources/data/neotests_test_conditional_recipe/recipe/always_disabled_recipe.json @@ -0,0 +1,16 @@ +{ + "neoforge:conditions": [ + { + "type": "neoforge:false" + } + ], + "type": "minecraft:crafting_shapeless", + "category": "misc", + "ingredients": [ + "minecraft:stone" + ], + "result": { + "count": 1, + "id": "minecraft:bedrock" + } +} \ No newline at end of file diff --git a/tests/src/generated/resources/data/neotests_test_flag_condition/advancement/recipes/misc/diamonds_from_dirt.json b/tests/src/generated/resources/data/neotests_test_flag_condition/advancement/recipes/misc/diamonds_from_dirt.json new file mode 100644 index 0000000000..e0d40e6e34 --- /dev/null +++ b/tests/src/generated/resources/data/neotests_test_flag_condition/advancement/recipes/misc/diamonds_from_dirt.json @@ -0,0 +1,40 @@ +{ + "neoforge:conditions": [ + { + "type": "neoforge:feature_flags_enabled", + "flags": [ + "custom_feature_flags_pack_test:test_flag" + ] + } + ], + "parent": "minecraft:recipes/root", + "criteria": { + "has_dirt": { + "conditions": { + "items": [ + { + "items": "#minecraft:dirt" + } + ] + }, + "trigger": "minecraft:inventory_changed" + }, + "has_the_recipe": { + "conditions": { + "recipe": "neotests_test_flag_condition:diamonds_from_dirt" + }, + "trigger": "minecraft:recipe_unlocked" + } + }, + "requirements": [ + [ + "has_the_recipe", + "has_dirt" + ] + ], + "rewards": { + "recipes": [ + "neotests_test_flag_condition:diamonds_from_dirt" + ] + } +} \ No newline at end of file diff --git a/tests/src/generated/resources/data/neotests_test_flag_condition/recipe/diamonds_from_dirt.json b/tests/src/generated/resources/data/neotests_test_flag_condition/recipe/diamonds_from_dirt.json new file mode 100644 index 0000000000..5accc57d95 --- /dev/null +++ b/tests/src/generated/resources/data/neotests_test_flag_condition/recipe/diamonds_from_dirt.json @@ -0,0 +1,19 @@ +{ + "neoforge:conditions": [ + { + "type": "neoforge:feature_flags_enabled", + "flags": [ + "custom_feature_flags_pack_test:test_flag" + ] + } + ], + "type": "minecraft:crafting_shapeless", + "category": "misc", + "ingredients": [ + "#minecraft:dirt" + ], + "result": { + "count": 1, + "id": "minecraft:diamond" + } +} \ No newline at end of file diff --git a/tests/src/generated/resources/pack.mcmeta b/tests/src/generated/resources/pack.mcmeta index 7454aa99dd..9620710f1d 100644 --- a/tests/src/generated/resources/pack.mcmeta +++ b/tests/src/generated/resources/pack.mcmeta @@ -49,7 +49,7 @@ }, "pack": { "description": "NeoForge tests resource pack", - "pack_format": 42, + "pack_format": 46, "supported_formats": [ 0, 2147483647 diff --git a/tests/src/junit/java/net/neoforged/neoforge/unittest/CustomFeatureFlagsTest.java b/tests/src/junit/java/net/neoforged/neoforge/unittest/CustomFeatureFlagsTest.java new file mode 100644 index 0000000000..a89e9b44bd --- /dev/null +++ b/tests/src/junit/java/net/neoforged/neoforge/unittest/CustomFeatureFlagsTest.java @@ -0,0 +1,207 @@ +/* + * Copyright (c) NeoForged and contributors + * SPDX-License-Identifier: LGPL-2.1-only + */ + +package net.neoforged.neoforge.unittest; + +import static org.assertj.core.api.Assertions.assertThat; + +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.flag.FeatureFlag; +import net.minecraft.world.flag.FeatureFlagSet; +import net.minecraft.world.flag.FeatureFlags; +import net.neoforged.testframework.junit.EphemeralTestServerProvider; +import org.junit.jupiter.api.MethodOrderer; +import org.junit.jupiter.api.Order; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestMethodOrder; +import org.junit.jupiter.api.extension.ExtendWith; + +@ExtendWith(EphemeralTestServerProvider.class) +@TestMethodOrder(MethodOrderer.OrderAnnotation.class) +public class CustomFeatureFlagsTest { + @Test + @Order(1) + void testFlagLoaded() { + ResourceLocation name = ResourceLocation.fromNamespaceAndPath("custom_feature_flags_test", "test_flag"); + FeatureFlag flag = FeatureFlags.REGISTRY.getFlag(name); + assertThat(flag).isNotNull(); + } + + @Test + @Order(2) + void testFlagSetContains() { + ResourceLocation name = ResourceLocation.fromNamespaceAndPath("custom_feature_flags_test", "many_flags_96"); + + FeatureFlag flag = FeatureFlags.REGISTRY.getFlag(name); + assertThat(flag).isNotNull(); + + FeatureFlagSet flagSet = FeatureFlagSet.of(flag); + assertThat(flagSet.contains(flag)).isTrue(); + } + + @Test + @Order(3) + void testFlagSetContainsVanilla() { + FeatureFlagSet flagSet = FeatureFlagSet.of(FeatureFlags.VANILLA); + assertThat(flagSet.contains(FeatureFlags.VANILLA)).isTrue(); + } + + @Test + @Order(4) + void testFlagSetEquals() { + ResourceLocation nameOne = ResourceLocation.fromNamespaceAndPath("custom_feature_flags_test", "many_flags_96"); + ResourceLocation nameTwo = ResourceLocation.fromNamespaceAndPath("custom_feature_flags_test", "many_flags_97"); + // Same mask as nameOne, but at a different offset + ResourceLocation nameThree = ResourceLocation.fromNamespaceAndPath("custom_feature_flags_test", "many_flags_32"); + + FeatureFlag flagOne = FeatureFlags.REGISTRY.getFlag(nameOne); + FeatureFlag flagTwo = FeatureFlags.REGISTRY.getFlag(nameTwo); + FeatureFlag flagThree = FeatureFlags.REGISTRY.getFlag(nameThree); + assertThat(flagOne).isNotNull(); + assertThat(flagTwo).isNotNull(); + assertThat(flagThree).isNotNull(); + + FeatureFlagSet flagSetOne = FeatureFlagSet.of(flagOne); + FeatureFlagSet flagSetTwo = FeatureFlagSet.of(flagTwo); + FeatureFlagSet flagSetThree = FeatureFlagSet.of(flagOne, flagTwo); + FeatureFlagSet flagSetFour = FeatureFlagSet.of(flagOne); + FeatureFlagSet flagSetFive = FeatureFlagSet.of(flagThree); + + assertThat(flagSetOne).isNotEqualTo(flagSetTwo); + assertThat(flagSetOne).isNotEqualTo(flagSetThree); + assertThat(flagSetTwo).isNotEqualTo(flagSetThree); + assertThat(flagSetOne).isEqualTo(flagSetFour); + assertThat(flagSetOne).isNotEqualTo(flagSetFive); + } + + @Test + @Order(5) + void testFlagSetEqualsVanilla() { + FeatureFlagSet flagSetOne = FeatureFlagSet.of(FeatureFlags.VANILLA); + FeatureFlagSet flagSetTwo = FeatureFlagSet.of(FeatureFlags.MINECART_IMPROVEMENTS); + FeatureFlagSet flagSetThree = FeatureFlagSet.of(FeatureFlags.VANILLA, FeatureFlags.MINECART_IMPROVEMENTS); + FeatureFlagSet flagSetFour = FeatureFlagSet.of(FeatureFlags.VANILLA); + + assertThat(flagSetOne).isNotEqualTo(flagSetTwo); + assertThat(flagSetOne).isNotEqualTo(flagSetThree); + assertThat(flagSetTwo).isNotEqualTo(flagSetThree); + assertThat(flagSetOne).isEqualTo(flagSetFour); + } + + @Test + @Order(6) + void testFlagSetIsSubsetOf() { + ResourceLocation nameOne = ResourceLocation.fromNamespaceAndPath("custom_feature_flags_test", "many_flags_96"); + ResourceLocation nameTwo = ResourceLocation.fromNamespaceAndPath("custom_feature_flags_test", "many_flags_97"); + ResourceLocation nameThree = ResourceLocation.fromNamespaceAndPath("custom_feature_flags_test", "many_flags_98"); + + FeatureFlag flagOne = FeatureFlags.REGISTRY.getFlag(nameOne); + FeatureFlag flagTwo = FeatureFlags.REGISTRY.getFlag(nameTwo); + FeatureFlag flagThree = FeatureFlags.REGISTRY.getFlag(nameThree); + assertThat(flagOne).isNotNull(); + assertThat(flagTwo).isNotNull(); + assertThat(flagThree).isNotNull(); + + FeatureFlagSet flagSetOne = FeatureFlagSet.of(flagOne, flagTwo, flagThree); + FeatureFlagSet flagSetTwo = FeatureFlagSet.of(flagOne, flagTwo); + assertThat(flagSetTwo.isSubsetOf(flagSetOne)).isTrue(); + assertThat(flagSetOne.isSubsetOf(flagSetTwo)).isFalse(); + } + + @Test + @Order(7) + void testFlagSetIsSubsetOfVanilla() { + FeatureFlagSet flagSetOne = FeatureFlagSet.of(FeatureFlags.VANILLA, FeatureFlags.MINECART_IMPROVEMENTS, FeatureFlags.TRADE_REBALANCE); + FeatureFlagSet flagSetTwo = FeatureFlagSet.of(FeatureFlags.VANILLA, FeatureFlags.MINECART_IMPROVEMENTS); + assertThat(flagSetTwo.isSubsetOf(flagSetOne)).isTrue(); + assertThat(flagSetOne.isSubsetOf(flagSetTwo)).isFalse(); + } + + @Test + @Order(8) + void testFlagSetIntersects() { + ResourceLocation nameOne = ResourceLocation.fromNamespaceAndPath("custom_feature_flags_test", "many_flags_96"); + ResourceLocation nameTwo = ResourceLocation.fromNamespaceAndPath("custom_feature_flags_test", "many_flags_97"); + + FeatureFlag flagOne = FeatureFlags.REGISTRY.getFlag(nameOne); + FeatureFlag flagTwo = FeatureFlags.REGISTRY.getFlag(nameTwo); + assertThat(flagOne).isNotNull(); + assertThat(flagTwo).isNotNull(); + + FeatureFlagSet flagSetOne = FeatureFlagSet.of(flagOne, flagTwo); + FeatureFlagSet flagSetTwo = FeatureFlagSet.of(flagOne); + FeatureFlagSet flagSetThree = FeatureFlagSet.of(flagTwo); + assertThat(flagSetOne.intersects(flagSetTwo)).isTrue(); + assertThat(flagSetOne.intersects(flagSetThree)).isTrue(); + assertThat(flagSetTwo.intersects(flagSetThree)).isFalse(); + } + + @Test + @Order(9) + void testFlagSetIntersectsVanilla() { + FeatureFlagSet flagSetOne = FeatureFlagSet.of(FeatureFlags.VANILLA, FeatureFlags.MINECART_IMPROVEMENTS); + FeatureFlagSet flagSetTwo = FeatureFlagSet.of(FeatureFlags.VANILLA); + FeatureFlagSet flagSetThree = FeatureFlagSet.of(FeatureFlags.MINECART_IMPROVEMENTS); + assertThat(flagSetOne.intersects(flagSetTwo)).isTrue(); + assertThat(flagSetOne.intersects(flagSetThree)).isTrue(); + assertThat(flagSetTwo.intersects(flagSetThree)).isFalse(); + } + + @Test + @Order(10) + void testFlagSetJoin() { + ResourceLocation nameOne = ResourceLocation.fromNamespaceAndPath("custom_feature_flags_test", "many_flags_96"); + ResourceLocation nameTwo = ResourceLocation.fromNamespaceAndPath("custom_feature_flags_test", "many_flags_97"); + + FeatureFlag flagOne = FeatureFlags.REGISTRY.getFlag(nameOne); + FeatureFlag flagTwo = FeatureFlags.REGISTRY.getFlag(nameTwo); + assertThat(flagOne).isNotNull(); + assertThat(flagTwo).isNotNull(); + + FeatureFlagSet flagSetOne = FeatureFlagSet.of(flagOne); + FeatureFlagSet flagSetTwo = FeatureFlagSet.of(flagTwo); + FeatureFlagSet flagSetThree = FeatureFlagSet.of(flagOne, flagTwo); + + assertThat(flagSetOne.join(flagSetTwo)).isEqualTo(flagSetThree); + } + + @Test + @Order(11) + void testFlagSetJoinVanilla() { + FeatureFlagSet flagSetOne = FeatureFlagSet.of(FeatureFlags.VANILLA); + FeatureFlagSet flagSetTwo = FeatureFlagSet.of(FeatureFlags.MINECART_IMPROVEMENTS); + FeatureFlagSet flagSetThree = FeatureFlagSet.of(FeatureFlags.VANILLA, FeatureFlags.MINECART_IMPROVEMENTS); + + assertThat(flagSetOne.join(flagSetTwo)).isEqualTo(flagSetThree); + } + + @Test + @Order(12) + void testFlagSetSubtract() { + ResourceLocation nameOne = ResourceLocation.fromNamespaceAndPath("custom_feature_flags_test", "many_flags_96"); + ResourceLocation nameTwo = ResourceLocation.fromNamespaceAndPath("custom_feature_flags_test", "many_flags_97"); + + FeatureFlag flagOne = FeatureFlags.REGISTRY.getFlag(nameOne); + FeatureFlag flagTwo = FeatureFlags.REGISTRY.getFlag(nameTwo); + assertThat(flagOne).isNotNull(); + assertThat(flagTwo).isNotNull(); + + FeatureFlagSet flagSetOne = FeatureFlagSet.of(flagOne, flagTwo); + FeatureFlagSet flagSetTwo = FeatureFlagSet.of(flagOne); + FeatureFlagSet flagSetThree = FeatureFlagSet.of(flagTwo); + + assertThat(flagSetOne.subtract(flagSetTwo)).isEqualTo(flagSetThree); + } + + @Test + @Order(13) + void testFlagSetSubtractVanilla() { + FeatureFlagSet flagSetOne = FeatureFlagSet.of(FeatureFlags.VANILLA, FeatureFlags.MINECART_IMPROVEMENTS); + FeatureFlagSet flagSetTwo = FeatureFlagSet.of(FeatureFlags.VANILLA); + FeatureFlagSet flagSetThree = FeatureFlagSet.of(FeatureFlags.MINECART_IMPROVEMENTS); + + assertThat(flagSetOne.subtract(flagSetTwo)).isEqualTo(flagSetThree); + } +} diff --git a/tests/src/junit/java/net/neoforged/neoforge/unittest/IngredientTests.java b/tests/src/junit/java/net/neoforged/neoforge/unittest/IngredientTests.java index 164ab1a686..b2424d96bc 100644 --- a/tests/src/junit/java/net/neoforged/neoforge/unittest/IngredientTests.java +++ b/tests/src/junit/java/net/neoforged/neoforge/unittest/IngredientTests.java @@ -32,7 +32,7 @@ @ExtendWith(EphemeralTestServerProvider.class) public class IngredientTests { private static List ingredientItemsAsStacks(Ingredient ingredient) { - return ingredient.items().stream().map(i -> i.value().getDefaultInstance()).toList(); + return ingredient.items().map(i -> i.value().getDefaultInstance()).toList(); } @ParameterizedTest @@ -59,7 +59,7 @@ void testIntersectionIngredient(MinecraftServer server) { final var second = Ingredient.of(Items.BIRCH_LOG, Items.SPRUCE_LOG, Items.DISPENSER); final var ingredient = IntersectionIngredient.of(Ingredient.of(server.registryAccess().lookupOrThrow(Registries.ITEM).getOrThrow(ItemTags.LOGS)), second); - Assertions.assertThat(ingredient.items().stream().map(Holder::value).distinct()) + Assertions.assertThat(ingredient.items().map(Holder::value).distinct()) .containsExactlyInAnyOrder(Items.BIRCH_LOG, Items.SPRUCE_LOG); } diff --git a/tests/src/junit/resources/META-INF/neoforge.mods.toml b/tests/src/junit/resources/META-INF/neoforge.mods.toml index b0ae03f583..a5b8004dbe 100644 --- a/tests/src/junit/resources/META-INF/neoforge.mods.toml +++ b/tests/src/junit/resources/META-INF/neoforge.mods.toml @@ -8,3 +8,6 @@ modId="block_entity_type_valid_blocks_event_test" modId="creative_tab_order_test" [[mods]] modId="startup_config_test" +[[mods]] +modId="custom_feature_flags_test" +featureFlags="feature_flags.json" diff --git a/tests/src/junit/resources/feature_flags.json b/tests/src/junit/resources/feature_flags.json new file mode 100644 index 0000000000..e8b6c25dcd --- /dev/null +++ b/tests/src/junit/resources/feature_flags.json @@ -0,0 +1,133 @@ +{ + "flags": [ + "custom_feature_flags_test:test_flag", + "custom_feature_flags_test:many_flags_0", + "custom_feature_flags_test:many_flags_1", + "custom_feature_flags_test:many_flags_2", + "custom_feature_flags_test:many_flags_3", + "custom_feature_flags_test:many_flags_4", + "custom_feature_flags_test:many_flags_5", + "custom_feature_flags_test:many_flags_6", + "custom_feature_flags_test:many_flags_7", + "custom_feature_flags_test:many_flags_8", + "custom_feature_flags_test:many_flags_9", + "custom_feature_flags_test:many_flags_10", + "custom_feature_flags_test:many_flags_11", + "custom_feature_flags_test:many_flags_12", + "custom_feature_flags_test:many_flags_13", + "custom_feature_flags_test:many_flags_14", + "custom_feature_flags_test:many_flags_15", + "custom_feature_flags_test:many_flags_16", + "custom_feature_flags_test:many_flags_17", + "custom_feature_flags_test:many_flags_18", + "custom_feature_flags_test:many_flags_19", + "custom_feature_flags_test:many_flags_20", + "custom_feature_flags_test:many_flags_21", + "custom_feature_flags_test:many_flags_22", + "custom_feature_flags_test:many_flags_23", + "custom_feature_flags_test:many_flags_24", + "custom_feature_flags_test:many_flags_25", + "custom_feature_flags_test:many_flags_26", + "custom_feature_flags_test:many_flags_27", + "custom_feature_flags_test:many_flags_28", + "custom_feature_flags_test:many_flags_29", + "custom_feature_flags_test:many_flags_30", + "custom_feature_flags_test:many_flags_31", + "custom_feature_flags_test:many_flags_32", + "custom_feature_flags_test:many_flags_33", + "custom_feature_flags_test:many_flags_34", + "custom_feature_flags_test:many_flags_35", + "custom_feature_flags_test:many_flags_36", + "custom_feature_flags_test:many_flags_37", + "custom_feature_flags_test:many_flags_38", + "custom_feature_flags_test:many_flags_39", + "custom_feature_flags_test:many_flags_40", + "custom_feature_flags_test:many_flags_41", + "custom_feature_flags_test:many_flags_42", + "custom_feature_flags_test:many_flags_43", + "custom_feature_flags_test:many_flags_44", + "custom_feature_flags_test:many_flags_45", + "custom_feature_flags_test:many_flags_46", + "custom_feature_flags_test:many_flags_47", + "custom_feature_flags_test:many_flags_48", + "custom_feature_flags_test:many_flags_49", + "custom_feature_flags_test:many_flags_50", + "custom_feature_flags_test:many_flags_51", + "custom_feature_flags_test:many_flags_52", + "custom_feature_flags_test:many_flags_53", + "custom_feature_flags_test:many_flags_54", + "custom_feature_flags_test:many_flags_55", + "custom_feature_flags_test:many_flags_56", + "custom_feature_flags_test:many_flags_57", + "custom_feature_flags_test:many_flags_58", + "custom_feature_flags_test:many_flags_59", + "custom_feature_flags_test:many_flags_60", + "custom_feature_flags_test:many_flags_61", + "custom_feature_flags_test:many_flags_62", + "custom_feature_flags_test:many_flags_63", + "custom_feature_flags_test:many_flags_64", + "custom_feature_flags_test:many_flags_65", + "custom_feature_flags_test:many_flags_66", + "custom_feature_flags_test:many_flags_67", + "custom_feature_flags_test:many_flags_68", + "custom_feature_flags_test:many_flags_69", + "custom_feature_flags_test:many_flags_70", + "custom_feature_flags_test:many_flags_71", + "custom_feature_flags_test:many_flags_72", + "custom_feature_flags_test:many_flags_73", + "custom_feature_flags_test:many_flags_74", + "custom_feature_flags_test:many_flags_75", + "custom_feature_flags_test:many_flags_76", + "custom_feature_flags_test:many_flags_77", + "custom_feature_flags_test:many_flags_78", + "custom_feature_flags_test:many_flags_79", + "custom_feature_flags_test:many_flags_80", + "custom_feature_flags_test:many_flags_81", + "custom_feature_flags_test:many_flags_82", + "custom_feature_flags_test:many_flags_83", + "custom_feature_flags_test:many_flags_84", + "custom_feature_flags_test:many_flags_85", + "custom_feature_flags_test:many_flags_86", + "custom_feature_flags_test:many_flags_87", + "custom_feature_flags_test:many_flags_88", + "custom_feature_flags_test:many_flags_89", + "custom_feature_flags_test:many_flags_90", + "custom_feature_flags_test:many_flags_91", + "custom_feature_flags_test:many_flags_92", + "custom_feature_flags_test:many_flags_93", + "custom_feature_flags_test:many_flags_94", + "custom_feature_flags_test:many_flags_95", + "custom_feature_flags_test:many_flags_96", + "custom_feature_flags_test:many_flags_97", + "custom_feature_flags_test:many_flags_98", + "custom_feature_flags_test:many_flags_99", + "custom_feature_flags_test:many_flags_100", + "custom_feature_flags_test:many_flags_101", + "custom_feature_flags_test:many_flags_102", + "custom_feature_flags_test:many_flags_103", + "custom_feature_flags_test:many_flags_104", + "custom_feature_flags_test:many_flags_105", + "custom_feature_flags_test:many_flags_106", + "custom_feature_flags_test:many_flags_107", + "custom_feature_flags_test:many_flags_108", + "custom_feature_flags_test:many_flags_109", + "custom_feature_flags_test:many_flags_110", + "custom_feature_flags_test:many_flags_111", + "custom_feature_flags_test:many_flags_112", + "custom_feature_flags_test:many_flags_113", + "custom_feature_flags_test:many_flags_114", + "custom_feature_flags_test:many_flags_115", + "custom_feature_flags_test:many_flags_116", + "custom_feature_flags_test:many_flags_117", + "custom_feature_flags_test:many_flags_118", + "custom_feature_flags_test:many_flags_119", + "custom_feature_flags_test:many_flags_120", + "custom_feature_flags_test:many_flags_121", + "custom_feature_flags_test:many_flags_122", + "custom_feature_flags_test:many_flags_123", + "custom_feature_flags_test:many_flags_124", + "custom_feature_flags_test:many_flags_125", + "custom_feature_flags_test:many_flags_126", + "custom_feature_flags_test:many_flags_127" + ] +} diff --git a/tests/src/main/java/net/neoforged/neoforge/debug/ConditionalRecipeTest.java b/tests/src/main/java/net/neoforged/neoforge/debug/ConditionalRecipeTest.java new file mode 100644 index 0000000000..fde67adaf8 --- /dev/null +++ b/tests/src/main/java/net/neoforged/neoforge/debug/ConditionalRecipeTest.java @@ -0,0 +1,61 @@ +/* + * Copyright (c) NeoForged and contributors + * SPDX-License-Identifier: LGPL-2.1-only + */ + +package net.neoforged.neoforge.debug; + +import net.minecraft.core.HolderLookup; +import net.minecraft.core.registries.Registries; +import net.minecraft.data.recipes.RecipeCategory; +import net.minecraft.data.recipes.RecipeOutput; +import net.minecraft.data.recipes.RecipeProvider; +import net.minecraft.resources.ResourceKey; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.item.Items; +import net.neoforged.neoforge.common.conditions.FalseCondition; +import net.neoforged.neoforge.event.server.ServerStartedEvent; +import net.neoforged.testframework.DynamicTest; +import net.neoforged.testframework.annotation.ForEachTest; +import net.neoforged.testframework.annotation.TestHolder; +import net.neoforged.testframework.registration.RegistrationHelper; + +@ForEachTest(groups = "conditional_recipes") +public interface ConditionalRecipeTest { + @TestHolder(description = "Validates that recipes support conditionals by generating a new recipe disabled by the FALSE condition", enabledByDefault = true) + static void testConditionalRecipe(DynamicTest test, RegistrationHelper reg) { + // name pointing to recipe which should never be enabled + var recipeName = ResourceKey.create(Registries.RECIPE, ResourceLocation.fromNamespaceAndPath(reg.modId(), "always_disabled_recipe")); + + reg.addClientProvider(event -> new RecipeProvider.Runner(event.getGenerator().getPackOutput(), event.getLookupProvider()) { + @Override + protected RecipeProvider createRecipeProvider(HolderLookup.Provider registries, RecipeOutput output) { + return new RecipeProvider(registries, output) { + @Override + protected void buildRecipes() { + // generic stone -> bedrock recipe + shapeless(RecipeCategory.MISC, Items.BEDROCK) + .requires(Items.STONE) + .unlockedBy("has_stone", has(Items.STONE)) + // false condition to have this recipe always disabled + .save(output.withConditions(FalseCondition.INSTANCE), recipeName); + } + }; + } + + @Override + public String getName() { + return "always_disabled_recipe_provider"; + } + }); + + test.eventListeners().forge().addListener((ServerStartedEvent event) -> { + var recipe = event.getServer().getRecipeManager().recipeMap().byKey(recipeName); + + if (recipe == null) + test.pass(); + else + test.fail("Found recipe: '" + recipeName.location() + "', This should always be disabled due to 'FALSE' condition!"); + }); + } +} diff --git a/tests/src/main/java/net/neoforged/neoforge/debug/block/BlockTests.java b/tests/src/main/java/net/neoforged/neoforge/debug/block/BlockTests.java index 9050b2126b..12fa397f0e 100644 --- a/tests/src/main/java/net/neoforged/neoforge/debug/block/BlockTests.java +++ b/tests/src/main/java/net/neoforged/neoforge/debug/block/BlockTests.java @@ -81,7 +81,7 @@ static void decoratedPotBreaking(final DynamicTest test) { static void woodlessFenceGate(final DynamicTest test, final RegistrationHelper reg) { final var gate = reg.blocks().registerBlock("gate", props -> new FenceGateBlock(props, SoundEvents.BARREL_OPEN, SoundEvents.CHEST_CLOSE), BlockBehaviour.Properties.ofFullCopy(Blocks.ACACIA_FENCE_GATE)) .withLang("Woodless Fence Gate").withBlockItem(); - reg.provider(BlockStateProvider.class, prov -> prov.fenceGateBlock(gate.get(), ResourceLocation.withDefaultNamespace("block/iron_block"))); + reg.clientProvider(BlockStateProvider.class, prov -> prov.fenceGateBlock(gate.get(), ResourceLocation.withDefaultNamespace("block/iron_block"))); test.onGameTest(helper -> helper.startSequence(() -> helper.makeTickingMockServerPlayerInCorner(GameType.SURVIVAL)) .thenExecute(() -> helper.setBlock(1, 1, 1, gate.get().defaultBlockState().setValue(FenceGateBlock.OPEN, true))) diff --git a/tests/src/main/java/net/neoforged/neoforge/debug/client/ClientEventTests.java b/tests/src/main/java/net/neoforged/neoforge/debug/client/ClientEventTests.java index 6b724e66b9..3eed63a3e8 100644 --- a/tests/src/main/java/net/neoforged/neoforge/debug/client/ClientEventTests.java +++ b/tests/src/main/java/net/neoforged/neoforge/debug/client/ClientEventTests.java @@ -5,12 +5,27 @@ package net.neoforged.neoforge.debug.client; +import com.google.common.reflect.TypeToken; import com.mojang.blaze3d.vertex.BufferBuilder; +import com.mojang.math.Axis; import java.util.Map; import net.minecraft.client.Minecraft; import net.minecraft.client.renderer.RenderType; +import net.minecraft.client.renderer.entity.AbstractHoglinRenderer; +import net.minecraft.client.renderer.entity.LivingEntityRenderer; +import net.minecraft.client.renderer.entity.MobRenderer; +import net.minecraft.client.renderer.entity.player.PlayerRenderer; +import net.minecraft.client.renderer.entity.state.LivingEntityRenderState; +import net.minecraft.client.renderer.texture.OverlayTexture; import net.minecraft.core.SectionPos; import net.minecraft.network.chat.Component; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.util.context.ContextKey; +import net.minecraft.world.entity.LivingEntity; +import net.minecraft.world.entity.Mob; +import net.minecraft.world.entity.monster.hoglin.HoglinBase; +import net.minecraft.world.item.ItemDisplayContext; +import net.minecraft.world.item.Items; import net.minecraft.world.level.block.Blocks; import net.minecraft.world.level.levelgen.SingleThreadedRandomSource; import net.neoforged.api.distmarker.Dist; @@ -19,7 +34,10 @@ import net.neoforged.neoforge.client.event.ClientPlayerChangeGameTypeEvent; import net.neoforged.neoforge.client.event.RegisterRenderBuffersEvent; import net.neoforged.neoforge.client.event.RenderLevelStageEvent; +import net.neoforged.neoforge.client.event.RenderLivingEvent; +import net.neoforged.neoforge.client.event.RenderPlayerEvent; import net.neoforged.neoforge.client.model.data.ModelData; +import net.neoforged.neoforge.client.renderstate.RegisterRenderStateModifiersEvent; import net.neoforged.neoforge.common.NeoForge; import net.neoforged.testframework.DynamicTest; import net.neoforged.testframework.annotation.ForEachTest; @@ -103,4 +121,68 @@ static void addSectionGeometryTest(final ClientChatEvent chatEvent, final Dynami test.requestConfirmation(player, Component.literal("Is a diamond block rendered above you?")); } } + + @TestHolder(description = { "Tests that RenderPlayerEvent is fired correctly and functions as expected" }) + static void renderPlayerEvent(final DynamicTest test) { + test.whenEnabled(listeners -> { + var item = Items.IRON_BLOCK; + var itemStack = item.getDefaultInstance(); + listeners.forge().addListener((final RenderPlayerEvent.Post event) -> { + event.getPoseStack().pushPose(); + event.getPoseStack().translate(0, 2, 0); + Minecraft.getInstance().getItemRenderer().renderStatic(itemStack, ItemDisplayContext.GROUND, event.getPackedLight(), OverlayTexture.NO_OVERLAY, event.getPoseStack(), event.getMultiBufferSource(), Minecraft.getInstance().level, 0); + event.getPoseStack().popPose(); + }); + test.requestConfirmation(Minecraft.getInstance().player, Component.literal("Is an iron block rendered above you in third-person?")); + }); + } + + @TestHolder(description = { "Test render state modifier system and registration event" }) + static void updateRenderState(final DynamicTest test) { + var rotationKey = new ContextKey(ResourceLocation.fromNamespaceAndPath(test.createModId(), "rotation")); + var numRenderAttachmentKey = new ContextKey(ResourceLocation.fromNamespaceAndPath(test.createModId(), "times_to_render")); + var testAttachment = test.registrationHelper().attachments().registerSimpleAttachment("test", () -> 3); + test.framework().modEventBus().addListener((RegisterRenderStateModifiersEvent event) -> { + event.registerEntityModifier(PlayerRenderer.class, (entity, renderState) -> { + renderState.setRenderData(rotationKey, 45f); + }); + event.registerEntityModifier(new TypeToken>() {}, (entity, renderState) -> { + renderState.setRenderData(numRenderAttachmentKey, entity.getData(testAttachment)); + }); + // Test other type parameters for safety + event.registerEntityModifier(new TypeToken>() {}, (entity, renderState) -> {}); + event.registerEntityModifier(new TypeToken>() {}, (entity, renderState) -> {}); + try { + class TestBrokenHoglinRendererTypeToken extends TypeToken> {} + event.registerEntityModifier(new TestBrokenHoglinRendererTypeToken<>(), (entity, renderState) -> {}); + test.fail("Unsafe type parameter succeeded. Cannot assume T can be ?."); + } catch (IllegalArgumentException ignored) {} + }); + test.whenEnabled(listeners -> { + listeners.forge().addListener((RenderLivingEvent.Post event) -> { + int numRender = event.getRenderState().getRenderDataOrDefault(numRenderAttachmentKey, -1); + if (numRender == -1) { + test.fail("Attachment render data not set"); + return; + } + float xRotation = event.getRenderState().getRenderDataOrDefault(rotationKey, 0f); + if (event.getRenderer() instanceof PlayerRenderer && numRender == 0) { + test.fail("Custom render data not set for player"); + return; + } + var poseStack = event.getPoseStack(); + poseStack.pushPose(); + poseStack.scale(0.3f, 0.3f, 0.3f); + for (int i = 0; i < numRender; i++) { + poseStack.translate(0, 1, 0); + poseStack.pushPose(); + poseStack.mulPose(Axis.XP.rotation(xRotation)); + Minecraft.getInstance().getBlockRenderer().renderSingleBlock(Blocks.CALCITE.defaultBlockState(), poseStack, event.getMultiBufferSource(), event.getPackedLight(), OverlayTexture.NO_OVERLAY, ModelData.EMPTY, RenderType.solid()); + poseStack.popPose(); + } + poseStack.popPose(); + test.pass(); + }); + }); + } } diff --git a/tests/src/main/java/net/neoforged/neoforge/debug/client/ClientTests.java b/tests/src/main/java/net/neoforged/neoforge/debug/client/ClientTests.java index bf1bfcb49c..b89c9e5394 100644 --- a/tests/src/main/java/net/neoforged/neoforge/debug/client/ClientTests.java +++ b/tests/src/main/java/net/neoforged/neoforge/debug/client/ClientTests.java @@ -56,7 +56,7 @@ static void keyMappingTest(final DynamicTest test) { final KeyMapping stickKey = new KeyMapping("stick_key", InputConstants.KEY_BACKSLASH, KeyMapping.CATEGORY_MISC); final KeyMapping rockKey = new KeyMapping("rock_key", InputConstants.KEY_BACKSLASH, KeyMapping.CATEGORY_MISC); - test.registrationHelper().provider(LanguageProvider.class, lang -> { + test.registrationHelper().clientProvider(LanguageProvider.class, lang -> { lang.add(stickKey.getName(), "Stick key"); lang.add(rockKey.getName(), "Rock key"); }); diff --git a/tests/src/main/java/net/neoforged/neoforge/debug/client/MapDecorationRenderTests.java b/tests/src/main/java/net/neoforged/neoforge/debug/client/MapDecorationRenderTests.java index ad8ddb7aba..8f1ad0ab8e 100644 --- a/tests/src/main/java/net/neoforged/neoforge/debug/client/MapDecorationRenderTests.java +++ b/tests/src/main/java/net/neoforged/neoforge/debug/client/MapDecorationRenderTests.java @@ -17,6 +17,7 @@ import net.minecraft.network.chat.Component; import net.minecraft.resources.ResourceLocation; import net.minecraft.util.Mth; +import net.minecraft.util.context.ContextKey; import net.minecraft.world.entity.player.Player; import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.MapItem; @@ -25,6 +26,7 @@ import net.neoforged.api.distmarker.Dist; import net.neoforged.neoforge.client.gui.map.IMapDecorationRenderer; import net.neoforged.neoforge.client.gui.map.RegisterMapDecorationRenderersEvent; +import net.neoforged.neoforge.client.renderstate.RegisterRenderStateModifiersEvent; import net.neoforged.neoforge.event.entity.player.PlayerEvent; import net.neoforged.testframework.DynamicTest; import net.neoforged.testframework.annotation.ForEachTest; @@ -49,7 +51,7 @@ static void customRenderer(DynamicTest test) { false)); test.framework().modEventBus().addListener((RegisterMapDecorationRenderersEvent event) -> { - event.register(decorationType.value(), new TestDecorationRenderer()); + event.register(decorationType.value(), new TestDecorationRenderer(null)); }); test.eventListeners().forge().addListener((PlayerEvent.PlayerLoggedInEvent event) -> { @@ -67,7 +69,50 @@ static void customRenderer(DynamicTest test) { }); } + @EmptyTemplate + @TestHolder(description = "Tests if custom map decoration render state data works") + static void customRenderData(DynamicTest test) { + var key = new ContextKey(ResourceLocation.fromNamespaceAndPath(test.createModId(), "custom_color")); + var decorationType = test.registrationHelper().registrar(Registries.MAP_DECORATION_TYPE).register( + "test", + () -> new MapDecorationType( + ResourceLocation.withDefaultNamespace("target_x"), + false, + -1, + false, + false)); + + test.framework().modEventBus().addListener((RegisterMapDecorationRenderersEvent event) -> { + event.register(decorationType.value(), new TestDecorationRenderer(key)); + }); + test.framework().modEventBus().addListener((RegisterRenderStateModifiersEvent event) -> { + event.registerMapDecorationModifier(decorationType.getKey(), (mapItemSavedData, mapRenderState, mapDecorationRenderState) -> { + mapDecorationRenderState.setRenderData(key, 0xFFFFAABB); + }); + }); + + test.eventListeners().forge().addListener((PlayerEvent.PlayerLoggedInEvent event) -> { + Player player = event.getEntity(); + ItemStack mapItem = MapItem.create(player.level(), player.getBlockX(), player.getBlockZ(), (byte) 0, true, false); + MapItemSavedData data = MapItem.getSavedData(mapItem, player.level()); + if (data == null) { + test.fail("Map data missing for new map"); + return; + } + String markerName = player.getName().getString() + "_test_marker"; + data.addDecoration(decorationType, player.level(), markerName, data.centerX, data.centerZ, 0, null); + player.getInventory().add(mapItem); + test.requestConfirmation(player, Component.literal("Does the map show a pink cross?")); + }); + } + private static final class TestDecorationRenderer implements IMapDecorationRenderer { + private final ContextKey customColorKey; + + TestDecorationRenderer(ContextKey customColorKey) { + this.customColorKey = customColorKey; + } + @Override public boolean render( MapRenderState.MapDecorationRenderState decoration, @@ -93,6 +138,9 @@ public boolean render( float hue = (System.currentTimeMillis() % 3000L) / 3000F; int color = Mth.hsvToArgb(hue, 1F, 1F, 0xFF); + if (decoration.getRenderData(customColorKey) != null) { + color = decoration.getRenderDataOrThrow(customColorKey); + } VertexConsumer buffer = bufferSource.getBuffer(RenderType.text(sprite.atlasLocation())); buffer.addVertex(pose, -1.0F, 1.0F, index * -0.001F).setColor(color).setUv(u0, v0).setLight(packedLight); diff --git a/tests/src/main/java/net/neoforged/neoforge/debug/client/TextureAtlasTests.java b/tests/src/main/java/net/neoforged/neoforge/debug/client/TextureAtlasTests.java new file mode 100644 index 0000000000..11caca9757 --- /dev/null +++ b/tests/src/main/java/net/neoforged/neoforge/debug/client/TextureAtlasTests.java @@ -0,0 +1,60 @@ +/* + * Copyright (c) NeoForged and contributors + * SPDX-License-Identifier: LGPL-2.1-only + */ + +package net.neoforged.neoforge.debug.client; + +import net.minecraft.client.Minecraft; +import net.minecraft.client.renderer.texture.MissingTextureAtlasSprite; +import net.minecraft.client.renderer.texture.TextureAtlasSprite; +import net.minecraft.client.resources.model.Material; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.server.packs.resources.ResourceManagerReloadListener; +import net.neoforged.api.distmarker.Dist; +import net.neoforged.neoforge.client.event.RegisterClientReloadListenersEvent; +import net.neoforged.neoforge.client.event.RegisterMaterialAtlasesEvent; +import net.neoforged.testframework.DynamicTest; +import net.neoforged.testframework.annotation.ForEachTest; +import net.neoforged.testframework.annotation.TestHolder; + +@ForEachTest(side = Dist.CLIENT, groups = { "client.texture_atlas", "texture_atlas" }) +public class TextureAtlasTests { + @TestHolder(description = { "Tests that texture atlases intended for use with Material are correctly registered and loaded" }, enabledByDefault = true) + static void testMaterialAtlas(final DynamicTest test) { + String modId = test.createModId(); + ResourceLocation atlasLoc = ResourceLocation.fromNamespaceAndPath(modId, "textures/atlas/material_test.png"); + + test.framework().modEventBus().addListener(RegisterMaterialAtlasesEvent.class, event -> { + ResourceLocation infoLoc = ResourceLocation.fromNamespaceAndPath(modId, "material_test"); + event.register(atlasLoc, infoLoc); + }); + + test.framework().modEventBus().addListener(RegisterClientReloadListenersEvent.class, event -> { + event.registerReloadListener((ResourceManagerReloadListener) manager -> { + try { + Minecraft.getInstance().getModelManager().getAtlas(atlasLoc); + } catch (NullPointerException npe) { + test.fail("Atlas was not registered"); + return; + } catch (Throwable t) { + test.fail("Atlas lookup failed: " + t.getMessage()); + return; + } + + try { + Material material = new Material(atlasLoc, ResourceLocation.withDefaultNamespace("block/stone")); + TextureAtlasSprite sprite = material.sprite(); + if (sprite.contents().name().equals(MissingTextureAtlasSprite.getLocation())) { + test.fail("Expected sprite was not stitched"); + return; + } + } catch (Throwable t) { + test.fail("Sprite lookup via material failed: " + t.getMessage()); + } + + test.pass(); + }); + }); + } +} diff --git a/tests/src/main/java/net/neoforged/neoforge/debug/crafting/CraftingEventTests.java b/tests/src/main/java/net/neoforged/neoforge/debug/crafting/CraftingEventTests.java new file mode 100644 index 0000000000..d33eb4eb72 --- /dev/null +++ b/tests/src/main/java/net/neoforged/neoforge/debug/crafting/CraftingEventTests.java @@ -0,0 +1,61 @@ +/* + * Copyright (c) NeoForged and contributors + * SPDX-License-Identifier: LGPL-2.1-only + */ + +package net.neoforged.neoforge.debug.crafting; + +import com.mojang.blaze3d.platform.InputConstants; +import java.util.concurrent.atomic.AtomicInteger; +import net.minecraft.core.BlockPos; +import net.minecraft.gametest.framework.GameTest; +import net.minecraft.world.inventory.ClickType; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.Items; +import net.minecraft.world.level.GameType; +import net.minecraft.world.level.block.Blocks; +import net.minecraft.world.level.block.entity.FurnaceBlockEntity; +import net.neoforged.neoforge.event.entity.player.PlayerEvent; +import net.neoforged.testframework.DynamicTest; +import net.neoforged.testframework.annotation.ForEachTest; +import net.neoforged.testframework.annotation.TestHolder; +import net.neoforged.testframework.gametest.EmptyTemplate; + +@ForEachTest(groups = "crafting.event") +public class CraftingEventTests { + @GameTest + @EmptyTemplate + @TestHolder(description = "Tests that ItemSmeltedEvent is fired correctly") + static void itemSmeltedEventTest(final DynamicTest test) { + AtomicInteger timesFired = new AtomicInteger(0); + test.whenEnabled(listeners -> { + listeners.forge().addListener((final PlayerEvent.ItemSmeltedEvent event) -> { + timesFired.incrementAndGet(); + var removed = event.getAmountRemoved(); + if (removed != 32) { + test.fail("Test should be removing half of a stack, yet extracted a different amount"); + } + }); + }); + test.onGameTest(helper -> { + helper.setBlock(BlockPos.ZERO, Blocks.FURNACE); + var be = helper.getBlockEntity(BlockPos.ZERO, FurnaceBlockEntity.class); + helper.assertFalse(be == null, "FurnaceBlockEntity was not found for furnace position"); + // Slot 2 is the result slot + be.setItem(2, new ItemStack(Items.IRON_INGOT, 64)); + var player = helper.makeTickingMockServerPlayerInLevel(GameType.CREATIVE); + player.openMenu(be); + // Test that right-clicking half of the stack out of the FurnaceResultSlot functions as expected + player.containerMenu.clicked(2, InputConstants.MOUSE_BUTTON_RIGHT, ClickType.PICKUP, player); + helper.assertTrue(timesFired.getPlain() == 1, "Event was not fired the expected number of times for right-click pickup. Fired: " + timesFired.getPlain()); + player.containerMenu.setCarried(ItemStack.EMPTY); + // Test that shift-left-clicking the rest of the stack out works (should only fire once, not twice) + player.containerMenu.clicked(2, InputConstants.MOUSE_BUTTON_LEFT, ClickType.QUICK_MOVE, player); + helper.assertTrue(timesFired.getPlain() == 2, "Event was not fired the expected number of times for shift-left-click quick-move. Fired: " + timesFired.getPlain()); + // The slot is now empty, this should not fire the event + player.containerMenu.clicked(2, InputConstants.MOUSE_BUTTON_LEFT, ClickType.QUICK_MOVE, player); + helper.assertTrue(timesFired.getPlain() == 2, "Event fired for an empty slot, which should not happen."); + helper.succeed(); + }); + } +} diff --git a/tests/src/main/java/net/neoforged/neoforge/debug/crafting/IngredientTests.java b/tests/src/main/java/net/neoforged/neoforge/debug/crafting/IngredientTests.java index c64cfde25a..216d5de6c6 100644 --- a/tests/src/main/java/net/neoforged/neoforge/debug/crafting/IngredientTests.java +++ b/tests/src/main/java/net/neoforged/neoforge/debug/crafting/IngredientTests.java @@ -70,7 +70,7 @@ public class IngredientTests { @EmptyTemplate @TestHolder(description = "Tests if BlockTagIngredient works") static void blockTagIngredient(final DynamicTest test, final RegistrationHelper reg) { - reg.addProvider(event -> new RecipeProvider.Runner(event.getGenerator().getPackOutput(), event.getLookupProvider()) { + reg.addClientProvider(event -> new RecipeProvider.Runner(event.getGenerator().getPackOutput(), event.getLookupProvider()) { @Override protected RecipeProvider createRecipeProvider(HolderLookup.Provider registries, RecipeOutput output) { return new RecipeProvider(registries, output) { @@ -111,7 +111,7 @@ public String getName() { @EmptyTemplate @TestHolder(description = "Tests if partial NBT ingredients match the correct stacks") static void partialNBTIngredient(final DynamicTest test, final RegistrationHelper reg) { - reg.addProvider(event -> new RecipeProvider.Runner(event.getGenerator().getPackOutput(), event.getLookupProvider()) { + reg.addClientProvider(event -> new RecipeProvider.Runner(event.getGenerator().getPackOutput(), event.getLookupProvider()) { @Override protected RecipeProvider createRecipeProvider(HolderLookup.Provider registries, RecipeOutput output) { return new RecipeProvider(registries, output) { @@ -167,7 +167,7 @@ public String getName() { @EmptyTemplate @TestHolder(description = "Tests if strict NBT ingredients match the correct stacks") static void strictNBTIngredient(final DynamicTest test, final RegistrationHelper reg) { - reg.addProvider(event -> new RecipeProvider.Runner(event.getGenerator().getPackOutput(), event.getLookupProvider()) { + reg.addClientProvider(event -> new RecipeProvider.Runner(event.getGenerator().getPackOutput(), event.getLookupProvider()) { @Override protected RecipeProvider createRecipeProvider(HolderLookup.Provider registries, RecipeOutput output) { return new RecipeProvider(registries, output) { @@ -377,7 +377,7 @@ private void ensureValid(ResourceKey> p_379745_) { @EmptyTemplate @TestHolder(description = "Tests if sized ingredients serialize and deserialize correctly") static void testSizedIngredient(final DynamicTest test, final RegistrationHelper reg) { - reg.addProvider(event -> new RecipeProvider.Runner(event.getGenerator().getPackOutput(), event.getLookupProvider()) { + reg.addClientProvider(event -> new RecipeProvider.Runner(event.getGenerator().getPackOutput(), event.getLookupProvider()) { @Override protected RecipeProvider createRecipeProvider(HolderLookup.Provider registries, RecipeOutput output) { return new RecipeProvider(registries, output) { diff --git a/tests/src/main/java/net/neoforged/neoforge/debug/damagesource/DamageTypeTests.java b/tests/src/main/java/net/neoforged/neoforge/debug/damagesource/DamageTypeTests.java index 6800316600..c8ac07a90d 100644 --- a/tests/src/main/java/net/neoforged/neoforge/debug/damagesource/DamageTypeTests.java +++ b/tests/src/main/java/net/neoforged/neoforge/debug/damagesource/DamageTypeTests.java @@ -96,7 +96,7 @@ public boolean onLeftClickEntity(ItemStack stack, Player player, Entity entity) bootstrap.register(TEST_DMG_TYPE, new DamageType("test_mod", scaling, 0.0f, effects, msgType)); }); - reg.addProvider(event -> new DatapackBuiltinEntriesProvider( + reg.addClientProvider(event -> new DatapackBuiltinEntriesProvider( event.getGenerator().getPackOutput(), event.getLookupProvider(), registrySetBuilder, diff --git a/tests/src/main/java/net/neoforged/neoforge/debug/data/CustomFeatureFlagsTests.java b/tests/src/main/java/net/neoforged/neoforge/debug/data/CustomFeatureFlagsTests.java new file mode 100644 index 0000000000..090309ffca --- /dev/null +++ b/tests/src/main/java/net/neoforged/neoforge/debug/data/CustomFeatureFlagsTests.java @@ -0,0 +1,158 @@ +/* + * Copyright (c) NeoForged and contributors + * SPDX-License-Identifier: LGPL-2.1-only + */ + +package net.neoforged.neoforge.debug.data; + +import net.minecraft.core.HolderLookup; +import net.minecraft.core.registries.Registries; +import net.minecraft.data.recipes.RecipeCategory; +import net.minecraft.data.recipes.RecipeOutput; +import net.minecraft.data.recipes.RecipeProvider; +import net.minecraft.network.chat.Component; +import net.minecraft.resources.ResourceKey; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.server.packs.PackType; +import net.minecraft.server.packs.repository.Pack; +import net.minecraft.server.packs.repository.PackSource; +import net.minecraft.tags.ItemTags; +import net.minecraft.world.flag.FeatureFlag; +import net.minecraft.world.flag.FeatureFlagSet; +import net.minecraft.world.flag.FeatureFlags; +import net.minecraft.world.item.Item; +import net.minecraft.world.item.Items; +import net.minecraft.world.level.Level; +import net.neoforged.neoforge.common.conditions.IConditionBuilder; +import net.neoforged.neoforge.event.AddPackFindersEvent; +import net.neoforged.neoforge.event.server.ServerStartedEvent; +import net.neoforged.neoforge.registries.DeferredItem; +import net.neoforged.testframework.DynamicTest; +import net.neoforged.testframework.annotation.ForEachTest; +import net.neoforged.testframework.annotation.TestHolder; +import net.neoforged.testframework.registration.RegistrationHelper; + +@ForEachTest(groups = "data.feature_flags") +public class CustomFeatureFlagsTests { + @TestHolder(description = "Tests that feature flag packs get shown in the experiments screen", enabledByDefault = true) + static void testFeatureFlagPacks(final DynamicTest test) { + test.framework().modEventBus().addListener((AddPackFindersEvent event) -> { + event.addPackFinders( + ResourceLocation.fromNamespaceAndPath("neotests", "feature_flag_test_packs/flag_test_pack"), + PackType.SERVER_DATA, + Component.literal("Custom FeatureFlag test pack"), + PackSource.FEATURE, + false, + Pack.Position.TOP); + + // Add 6 additional packs to visually overflow the vanilla experiments screen + for (int i = 0; i < 6; i++) { + event.addPackFinders( + ResourceLocation.fromNamespaceAndPath("neotests", "feature_flag_test_packs/flag_test_pack_" + i), + PackType.SERVER_DATA, + Component.literal("Custom FeatureFlag test pack " + i), + PackSource.FEATURE, + false, + Pack.Position.TOP); + } + + test.pass(); + }); + } + + @TestHolder(description = "Verifies that registered objects using a custom feature flag are not accessible without the feature flag being enabled", enabledByDefault = true) + static void testFeatureGating(final DynamicTest test) { + test.framework().modEventBus().addListener((AddPackFindersEvent event) -> event.addPackFinders( + ResourceLocation.fromNamespaceAndPath("neotests", "feature_flag_test_packs/gating_test_pack"), + PackType.SERVER_DATA, + Component.literal("Custom FeatureFlag gating test pack"), + PackSource.FEATURE, + true, + Pack.Position.TOP)); + + FeatureFlag baseRangeEnabledTestFlag = FeatureFlags.REGISTRY.getFlag(ResourceLocation.fromNamespaceAndPath("custom_feature_flags_pack_test", "many_flags_9")); + FeatureFlag baseRangeDisabledTestFlag = FeatureFlags.REGISTRY.getFlag(ResourceLocation.fromNamespaceAndPath("custom_feature_flags_pack_test", "many_flags_10")); + FeatureFlag extRangeEnabledTestFlag = FeatureFlags.REGISTRY.getFlag(ResourceLocation.fromNamespaceAndPath("custom_feature_flags_pack_test", "many_flags_99")); + FeatureFlag extRangeDisabledTestFlag = FeatureFlags.REGISTRY.getFlag(ResourceLocation.fromNamespaceAndPath("custom_feature_flags_pack_test", "many_flags_100")); + + DeferredItem baseRangeEnabledTestItem = test.registrationHelper().items() + .registerSimpleItem("base_range_enabled_test", new Item.Properties().requiredFeatures(baseRangeEnabledTestFlag)); + DeferredItem baseRangeDisabledTestItem = test.registrationHelper().items() + .registerSimpleItem("base_range_disabled_test", new Item.Properties().requiredFeatures(baseRangeDisabledTestFlag)); + DeferredItem extRangeEnabledTestItem = test.registrationHelper().items() + .registerSimpleItem("ext_range_enabled_test", new Item.Properties().requiredFeatures(extRangeEnabledTestFlag)); + DeferredItem extRangeDisabledTestItem = test.registrationHelper().items() + .registerSimpleItem("ext_range_disabled_test", new Item.Properties().requiredFeatures(extRangeDisabledTestFlag)); + + test.eventListeners().forge().addListener((ServerStartedEvent event) -> { + FeatureFlagSet flagSet = event.getServer().getLevel(Level.OVERWORLD).enabledFeatures(); + if (!baseRangeEnabledTestItem.get().isEnabled(flagSet)) { + test.fail("Item with enabled custom flag in base mask range was unexpectedly disabled"); + } else if (baseRangeDisabledTestItem.get().isEnabled(flagSet)) { + test.fail("Item with disabled custom flag in base mask range was unexpectedly enabled"); + } else if (!extRangeEnabledTestItem.get().isEnabled(flagSet)) { + test.fail("Item with enabled custom flag in extended mask range was unexpectedly disabled"); + } else if (extRangeDisabledTestItem.get().isEnabled(flagSet)) { + test.fail("Item with disabled custom flag in extended mask range was unexpectedly enabled"); + } else { + test.pass(); + } + }); + } + + @TestHolder(description = "Tests that elements can be toggled via conditions using the flag condition", enabledByDefault = true) + static void testFlagCondition(DynamicTest test, RegistrationHelper reg) { + // custom flag are provided by our other flag tests + // and enabled via our `custom featureflag test pack` + var flagName = ResourceLocation.fromNamespaceAndPath("custom_feature_flags_pack_test", "test_flag"); + var flag = FeatureFlags.REGISTRY.getFlag(flagName); + + var modId = reg.modId(); + var enabledRecipeName = ResourceKey.create(Registries.RECIPE, ResourceLocation.fromNamespaceAndPath(modId, "diamonds_from_dirt")); + + reg.addClientProvider(event -> new RecipeProvider.Runner(event.getGenerator().getPackOutput(), event.getLookupProvider()) { + @Override + protected RecipeProvider createRecipeProvider(HolderLookup.Provider registries, RecipeOutput output) { + class Provider extends RecipeProvider implements IConditionBuilder { + protected Provider(HolderLookup.Provider p_360573_, RecipeOutput p_360872_) { + super(p_360573_, p_360872_); + } + + @Override + protected void buildRecipes() { + // recipe available when above flag is enabled + shapeless(RecipeCategory.MISC, Items.DIAMOND) + .requires(ItemTags.DIRT) + .unlockedBy("has_dirt", has(ItemTags.DIRT)) + .save(output.withConditions(featureFlagsEnabled(flag)), enabledRecipeName); + } + } + return new Provider(registries, output); + } + + @Override + public String getName() { + return "conditional_flag_recipes"; + } + }); + + test.eventListeners().forge().addListener((ServerStartedEvent event) -> { + var server = event.getServer(); + var isFlagEnabled = server.getWorldData().enabledFeatures().contains(flag); + var recipeMap = server.getRecipeManager().recipeMap(); + var hasEnabledRecipe = recipeMap.byKey(enabledRecipeName) != null; + + if (isFlagEnabled) { + if (!hasEnabledRecipe) { + test.fail("Missing recipe '" + enabledRecipeName.location() + "', This should be enabled due to our flag '" + flagName + "' being enabled"); + } + } else { + if (hasEnabledRecipe) { + test.fail("Found recipe '" + enabledRecipeName.location() + "', This should be disabled due to our flag '" + flagName + "' being enabled"); + } + } + + test.pass(); + }); + } +} diff --git a/tests/src/main/java/net/neoforged/neoforge/debug/data/DataMapTests.java b/tests/src/main/java/net/neoforged/neoforge/debug/data/DataMapTests.java index 2e4bf42bd9..08b3960e77 100644 --- a/tests/src/main/java/net/neoforged/neoforge/debug/data/DataMapTests.java +++ b/tests/src/main/java/net/neoforged/neoforge/debug/data/DataMapTests.java @@ -33,6 +33,8 @@ import net.minecraft.world.InteractionHand; import net.minecraft.world.damagesource.DamageType; import net.minecraft.world.damagesource.DamageTypes; +import net.minecraft.world.effect.MobEffectInstance; +import net.minecraft.world.effect.MobEffects; import net.minecraft.world.entity.player.Player; import net.minecraft.world.item.HoneycombItem; import net.minecraft.world.item.Item; @@ -43,12 +45,14 @@ import net.minecraft.world.level.block.WeatheringCopper; import net.minecraft.world.level.block.WeatheringCopperFullBlock; import net.minecraft.world.level.block.state.BlockBehaviour; +import net.minecraft.world.level.storage.loot.LootTable; import net.neoforged.neoforge.common.DataMapHooks; import net.neoforged.neoforge.common.NeoForge; import net.neoforged.neoforge.common.data.DataMapProvider; import net.neoforged.neoforge.debug.EventTests; import net.neoforged.neoforge.event.entity.living.LivingDamageEvent; import net.neoforged.neoforge.event.entity.player.UseItemOnBlockEvent; +import net.neoforged.neoforge.event.level.BlockEvent; import net.neoforged.neoforge.registries.datamaps.AdvancedDataMapType; import net.neoforged.neoforge.registries.datamaps.DataMapType; import net.neoforged.neoforge.registries.datamaps.DataMapValueMerger; @@ -82,7 +86,7 @@ static void dataMapMerger(final DynamicTest test, final RegistrationHelper reg) final String subpackName = reg.registerSubpack("second_layer"); - reg.addProvider(event -> new DataMapProvider(event.getGenerator().getPackOutput(), event.getLookupProvider()) { + reg.addClientProvider(event -> new DataMapProvider(event.getGenerator().getPackOutput(), event.getLookupProvider()) { @Override protected void gather(HolderLookup.Provider provider) { builder(someData) @@ -100,7 +104,7 @@ public String getName() { } }); - reg.addProvider(event -> new DataMapProvider(event.getGenerator().getPackOutput(subpackName), event.getLookupProvider()) { + reg.addClientProvider(event -> new DataMapProvider(event.getGenerator().getPackOutput(subpackName), event.getLookupProvider()) { @Override protected void gather(HolderLookup.Provider provider) { builder(someData) @@ -162,7 +166,7 @@ public Optional> remove(Map value, R final String subpackName = reg.registerSubpack("second_layer"); - reg.addProvider(event -> new DataMapProvider(event.getGenerator().getPackOutput(), event.getLookupProvider()) { + reg.addClientProvider(event -> new DataMapProvider(event.getGenerator().getPackOutput(), event.getLookupProvider()) { @Override protected void gather(HolderLookup.Provider provider) { builder(someData) @@ -182,7 +186,7 @@ public String getName() { } }); - reg.addProvider(event -> new DataMapProvider(event.getGenerator().getPackOutput(subpackName), event.getLookupProvider()) { + reg.addClientProvider(event -> new DataMapProvider(event.getGenerator().getPackOutput(subpackName), event.getLookupProvider()) { @Override protected void gather(HolderLookup.Provider provider) { builder(someData) @@ -226,7 +230,7 @@ static void dataMapTest(final DynamicTest test, final RegistrationHelper reg) { test.framework().modEventBus().addListener((final RegisterDataMapTypesEvent event) -> event.register(someData)); - reg.addProvider(event -> new DataMapProvider(event.getGenerator().getPackOutput(), event.getLookupProvider()) { + reg.addClientProvider(event -> new DataMapProvider(event.getGenerator().getPackOutput(), event.getLookupProvider()) { @Override protected void gather(HolderLookup.Provider provider) { builder(someData) @@ -279,7 +283,7 @@ record ExperienceGrant(int amount) { Registries.DAMAGE_TYPE, ExperienceGrant.CODEC) .build()); - reg.addProvider(event -> new DataMapProvider(event.getGenerator().getPackOutput(), event.getLookupProvider()) { + reg.addClientProvider(event -> new DataMapProvider(event.getGenerator().getPackOutput(), event.getLookupProvider()) { @Override protected void gather(HolderLookup.Provider provider) { builder(xpGrant) @@ -302,11 +306,45 @@ protected void gather(HolderLookup.Provider provider) { }); } + @GameTest + @EmptyTemplate + @TestHolder(description = "Tests if data maps can be successfully attached to reloadable registries") + static void reloadableRegDataMaps(final DynamicTest test, final RegistrationHelper reg) { + final DataMapType effectGrant = reg.registerDataMap(DataMapType.builder( + ResourceLocation.fromNamespaceAndPath(reg.modId(), "effect_grant"), + Registries.LOOT_TABLE, MobEffectInstance.CODEC) + .build()); + + reg.addClientProvider(event -> new DataMapProvider(event.getGenerator().getPackOutput(), event.getLookupProvider()) { + @Override + protected void gather(HolderLookup.Provider provider) { + builder(effectGrant) + .add(Blocks.COPPER_BLOCK.getLootTable().orElseThrow(), new MobEffectInstance(MobEffects.CONFUSION, 100), false); + } + }); + + test.eventListeners().forge().addListener((final BlockEvent.EntityPlaceEvent event) -> { + var table = event.getPlacedBlock().getBlock().getLootTable(); + if (table.isEmpty()) return; + final var grant = event.getLevel().getServer().reloadableRegistries().lookup().lookupOrThrow(Registries.LOOT_TABLE).getOrThrow(table.get()).getData(effectGrant); + if (grant != null && event.getEntity() instanceof Player player) { + player.addEffect(grant); + } + }); + + test.onGameTest(helper -> { + final Player player = helper.makeMockPlayer(); + helper.useBlock(new BlockPos(0, 1, 0), player, Blocks.COPPER_BLOCK.asItem().getDefaultInstance()); + helper.assertMobEffectPresent(player, MobEffects.CONFUSION, "has confusion"); + helper.succeed(); + }); + } + @GameTest @EmptyTemplate @TestHolder(description = "Tests if custom compostables work") static void compostablesMapTest(final DynamicTest test, final RegistrationHelper reg) { - reg.addProvider(event -> new DataMapProvider(event.getGenerator().getPackOutput(), event.getLookupProvider()) { + reg.addClientProvider(event -> new DataMapProvider(event.getGenerator().getPackOutput(), event.getLookupProvider()) { @Override protected void gather(HolderLookup.Provider provider) { builder(NeoForgeDataMaps.COMPOSTABLES) @@ -329,7 +367,7 @@ static void dataMapUpdateEventTest(final DynamicTest test, final RegistrationHel ResourceLocation.fromNamespaceAndPath(reg.modId(), "weight"), Registries.ITEM, Codec.INT) .build()); - reg.addProvider(event -> new DataMapProvider(event.getGenerator().getPackOutput(), event.getLookupProvider()) { + reg.addClientProvider(event -> new DataMapProvider(event.getGenerator().getPackOutput(), event.getLookupProvider()) { @Override protected void gather(HolderLookup.Provider provider) { builder(dataMap) @@ -375,7 +413,7 @@ static void oxidizablesAndWaxablesMapTest(final DynamicTest test, final Registra Holder lightlyOxidizedWaxedIron = reg.blocks().registerBlock("lightly_oxidized_waxed_iron", Block::new, BlockBehaviour.Properties.of()); - reg.addProvider(event -> new DataMapProvider(event.getGenerator().getPackOutput(), event.getLookupProvider()) { + reg.addClientProvider(event -> new DataMapProvider(event.getGenerator().getPackOutput(), event.getLookupProvider()) { @Override protected void gather(HolderLookup.Provider provider) { builder(NeoForgeDataMaps.OXIDIZABLES) diff --git a/tests/src/main/java/net/neoforged/neoforge/debug/data/registries/DatapackEntryTests.java b/tests/src/main/java/net/neoforged/neoforge/debug/data/registries/DatapackEntryTests.java index 9824ffda4b..3d11736872 100644 --- a/tests/src/main/java/net/neoforged/neoforge/debug/data/registries/DatapackEntryTests.java +++ b/tests/src/main/java/net/neoforged/neoforge/debug/data/registries/DatapackEntryTests.java @@ -41,7 +41,7 @@ static void conditionalDatapackEntries(final DynamicTest test, final Registratio bootstrap.register(REGULAR_DAMAGE_TYPE, new DamageType("inFire", 0.1f, DamageEffects.BURNING)); }); - reg.addProvider(event -> new DatapackBuiltinEntriesProvider( + reg.addClientProvider(event -> new DatapackBuiltinEntriesProvider( event.getGenerator().getPackOutput(), event.getLookupProvider(), builder, diff --git a/tests/src/main/java/net/neoforged/neoforge/debug/entity/player/AdvancementTests.java b/tests/src/main/java/net/neoforged/neoforge/debug/entity/player/AdvancementTests.java index 56bf999fdc..b17af6164c 100644 --- a/tests/src/main/java/net/neoforged/neoforge/debug/entity/player/AdvancementTests.java +++ b/tests/src/main/java/net/neoforged/neoforge/debug/entity/player/AdvancementTests.java @@ -92,7 +92,7 @@ static void customPredicateTest(final DynamicTest test, final RegistrationHelper reg.registrar(Registries.ITEM_SUB_PREDICATE_TYPE) .register("custom_name", () -> type); - reg.addProvider(event -> new AdvancementProvider( + reg.addClientProvider(event -> new AdvancementProvider( event.getGenerator().getPackOutput(), event.getLookupProvider(), event.getExistingFileHelper(), diff --git a/tests/src/main/java/net/neoforged/neoforge/debug/fluid/ClientFluidTests.java b/tests/src/main/java/net/neoforged/neoforge/debug/fluid/ClientFluidTests.java new file mode 100644 index 0000000000..d20ad28557 --- /dev/null +++ b/tests/src/main/java/net/neoforged/neoforge/debug/fluid/ClientFluidTests.java @@ -0,0 +1,72 @@ +/* + * Copyright (c) NeoForged and contributors + * SPDX-License-Identifier: LGPL-2.1-only + */ + +package net.neoforged.neoforge.debug.fluid; + +import net.minecraft.client.renderer.block.LiquidBlockRenderer; +import net.minecraft.core.BlockPos; +import net.minecraft.core.Direction; +import net.minecraft.gametest.framework.GameTest; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.level.block.Blocks; +import net.minecraft.world.level.block.TransparentBlock; +import net.minecraft.world.level.block.state.BlockBehaviour; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.material.FluidState; +import net.minecraft.world.level.material.Fluids; +import net.neoforged.api.distmarker.Dist; +import net.neoforged.neoforge.client.model.generators.BlockStateProvider; +import net.neoforged.testframework.DynamicTest; +import net.neoforged.testframework.annotation.ForEachTest; +import net.neoforged.testframework.annotation.TestHolder; +import net.neoforged.testframework.gametest.EmptyTemplate; +import net.neoforged.testframework.registration.RegistrationHelper; + +@ForEachTest(groups = ClientFluidTests.GROUP, side = Dist.CLIENT) +public class ClientFluidTests { + public static final String GROUP = "level.fluid.client"; + + static class WaterGlassBlock extends TransparentBlock { + private static final Direction HIDE_DIRECTION = Direction.NORTH; + + public WaterGlassBlock(Properties p_309186_) { + super(p_309186_); + } + + @Override + public boolean shouldHideAdjacentFluidFace(BlockState state, Direction selfFace, FluidState adjacentFluid) { + if (selfFace == HIDE_DIRECTION) { + return adjacentFluid.getFluidType() == Fluids.WATER.getFluidType(); + } else { + return super.shouldHideAdjacentFluidFace(state, selfFace, adjacentFluid); + } + } + } + + @GameTest + @EmptyTemplate + @TestHolder(description = "Tests if blocks can prevent neighboring fluids from rendering against them") + static void testWaterGlassFaceRemoval(final DynamicTest test, final RegistrationHelper reg) { + final var glass = reg.blocks().registerBlock("water_glass", WaterGlassBlock::new, BlockBehaviour.Properties.ofFullCopy(Blocks.GLASS)).withLang("Water Glass").withBlockItem(); + reg.clientProvider(BlockStateProvider.class, prov -> prov.simpleBlock(glass.get(), prov.models() + .cubeAll("water_glass", ResourceLocation.withDefaultNamespace("block/glass")) + .renderType("cutout"))); + final var waterPosition = new BlockPos(1, 1, 2); + final var glassDirection = WaterGlassBlock.HIDE_DIRECTION.getOpposite(); + final var glassPosition = waterPosition.relative(glassDirection); + test.onGameTest(helper -> helper.startSequence() + .thenExecute(() -> helper.setBlock(glassPosition, glass.get().defaultBlockState())) + .thenExecute(() -> helper.setBlock(waterPosition, Blocks.WATER.defaultBlockState())) + // Check that the north side of the water is not rendered + .thenExecute(() -> helper.assertFalse( + LiquidBlockRenderer.shouldRenderFace( + helper.getBlockState(waterPosition).getFluidState(), + helper.getBlockState(waterPosition), + glassDirection, + helper.getBlockState(glassPosition)), + "Fluid face rendering is not skipped")) + .thenSucceed()); + } +} diff --git a/tests/src/main/java/net/neoforged/neoforge/debug/item/ItemTests.java b/tests/src/main/java/net/neoforged/neoforge/debug/item/ItemTests.java index 25954a3f0d..08d076e486 100644 --- a/tests/src/main/java/net/neoforged/neoforge/debug/item/ItemTests.java +++ b/tests/src/main/java/net/neoforged/neoforge/debug/item/ItemTests.java @@ -5,9 +5,11 @@ package net.neoforged.neoforge.debug.item; +import java.util.EnumMap; import java.util.function.Supplier; import java.util.function.UnaryOperator; import net.minecraft.ChatFormatting; +import net.minecraft.Util; import net.minecraft.client.renderer.entity.PigRenderer; import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; @@ -37,10 +39,13 @@ import net.minecraft.world.item.MobBucketItem; import net.minecraft.world.item.PickaxeItem; import net.minecraft.world.item.Rarity; +import net.minecraft.world.item.SpawnEggItem; import net.minecraft.world.item.ToolMaterial; import net.minecraft.world.item.context.UseOnContext; +import net.minecraft.world.item.equipment.ArmorMaterial; import net.minecraft.world.item.equipment.ArmorMaterials; import net.minecraft.world.item.equipment.ArmorType; +import net.minecraft.world.item.equipment.EquipmentAssets; import net.minecraft.world.level.Level; import net.minecraft.world.level.block.Blocks; import net.minecraft.world.level.block.DispenserBlock; @@ -48,7 +53,7 @@ import net.minecraft.world.level.material.Fluids; import net.neoforged.fml.event.lifecycle.FMLCommonSetupEvent; import net.neoforged.neoforge.client.model.generators.ModelFile; -import net.neoforged.neoforge.common.DeferredSpawnEggItem; +import net.neoforged.neoforge.common.Tags; import net.neoforged.testframework.DynamicTest; import net.neoforged.testframework.annotation.ForEachTest; import net.neoforged.testframework.annotation.TestHolder; @@ -117,7 +122,7 @@ static void forgeSpawnEggTest(final DynamicTest test, final RegistrationHelper r .withRenderer(() -> PigRenderer::new) .withLang("Test Pig spawn egg"); - final var egg = reg.items().registerItem("test_spawn_egg", props -> new DeferredSpawnEggItem(testEntity, 0x0000FF, 0xFF0000, props) { + final var egg = reg.items().registerItem("test_spawn_egg", props -> new SpawnEggItem(testEntity.get(), props) { @Override public InteractionResult useOn(UseOnContext ctx) { final var result = super.useOn(ctx); @@ -199,9 +204,19 @@ public boolean canWalkOnPowderedSnow(ItemStack stack, LivingEntity wearer) { .thenSucceed()); } - @TestHolder(description = "Adds a stone-pickaxe-like item that cannot mine bamboo blocks and is repaired with beds. Tests that registries can correctly handle named holder set references.") + private static final ArmorMaterial NEO_MATERIAL = new ArmorMaterial(5, Util.make(new EnumMap<>(ArmorType.class), map -> { + map.put(ArmorType.BOOTS, 1); + map.put(ArmorType.LEGGINGS, 2); + map.put(ArmorType.CHESTPLATE, 3); + map.put(ArmorType.HELMET, 1); + map.put(ArmorType.BODY, 3); + }), 15, SoundEvents.AMBIENT_CAVE, 0.0F, 0.0F, Tags.Items.BARRELS, EquipmentAssets.LEATHER); + + @TestHolder(description = "Tests that registries can correctly handle named holder set references.") static void toolItem(final DynamicTest test, final RegistrationHelper reg) { var material = new ToolMaterial(BlockTags.BAMBOO_BLOCKS, 160, 5.0F, 0.5F, 10, ItemTags.BEDS); reg.items().registerItem("neo_pickaxe", properties -> new PickaxeItem(material, 1.0F, -2.8F, properties)); + // This is invalid registration, but replicates an error suppression issue found in RegistryManager#applySnapshot +// reg.items().register("neo_helmet", () -> new ArmorItem(NEO_MATERIAL, ArmorType.HELMET, new Item.Properties())); } } diff --git a/tests/src/main/java/net/neoforged/neoforge/debug/loot/GlobalLootModifiersTest.java b/tests/src/main/java/net/neoforged/neoforge/debug/loot/GlobalLootModifiersTest.java index 23eb25dd51..c24349eeb3 100644 --- a/tests/src/main/java/net/neoforged/neoforge/debug/loot/GlobalLootModifiersTest.java +++ b/tests/src/main/java/net/neoforged/neoforge/debug/loot/GlobalLootModifiersTest.java @@ -247,7 +247,7 @@ static void smeltingModifierTest(final DynamicTest test) { .build(SMELT.location()))); var subpack = HELPER.registerSubpack("smelt_glms"); - HELPER.addProvider(event -> new GlobalLootModifierProvider(event.getGenerator().getPackOutput(subpack), CompletableFuture.supplyAsync(() -> registrySetBuilder.build(RegistryAccess.fromRegistryOfRegistries(BuiltInRegistries.REGISTRY)), Util.backgroundExecutor()), HELPER.modId()) { + HELPER.addClientProvider(event -> new GlobalLootModifierProvider(event.getGenerator().getPackOutput(subpack), CompletableFuture.supplyAsync(() -> registrySetBuilder.build(RegistryAccess.fromRegistryOfRegistries(BuiltInRegistries.REGISTRY)), Util.backgroundExecutor()), HELPER.modId()) { @Override protected void start() { add("smelting", new SmeltingEnchantmentModifier( @@ -266,7 +266,7 @@ public String getName() { return super.getName() + " - smelting modifier"; } }); - HELPER.addProvider(event -> new DatapackBuiltinEntriesProvider(event.getGenerator().getPackOutput(), event.getLookupProvider(), + HELPER.addClientProvider(event -> new DatapackBuiltinEntriesProvider(event.getGenerator().getPackOutput(), event.getLookupProvider(), registrySetBuilder, Set.of(HELPER.modId()))); test.onGameTest(helper -> helper.startSequence(() -> helper.makeTickingMockServerPlayerInCorner(GameType.SURVIVAL).preventItemPickup()) @@ -297,7 +297,7 @@ public String getName() { @TestHolder(description = "Tests if a GLM replacing loot table values works, by replacing seeds with wheat when harvesting wheat") static void wheatSeedReplacerTest(final DynamicTest test) { var subpack = HELPER.registerSubpack("wheat_seed_glms"); - HELPER.addProvider(event -> new GlobalLootModifierProvider(event.getGenerator().getPackOutput(subpack), event.getLookupProvider(), HELPER.modId()) { + HELPER.addClientProvider(event -> new GlobalLootModifierProvider(event.getGenerator().getPackOutput(subpack), event.getLookupProvider(), HELPER.modId()) { @Override protected void start() { this.add("wheat_harvest", new WheatSeedsConverterModifier( @@ -335,7 +335,7 @@ public String getName() { @EmptyTemplate @TestHolder(description = "Tests if dungeon loot modifiers work, by rolling the simple_dungeon loot table") static void dungeonLootTest(final DynamicTest test) { - HELPER.provider(GlobalLootModifierProvider.class, prov -> prov.add("dungeon_loot", new DungeonLootEnhancerModifier( + HELPER.clientProvider(GlobalLootModifierProvider.class, prov -> prov.add("dungeon_loot", new DungeonLootEnhancerModifier( new LootItemCondition[] { LootTableIdCondition.builder(ResourceLocation.withDefaultNamespace("chests/simple_dungeon")).build(), new TestEnabledLootCondition(test) diff --git a/tests/src/main/java/net/neoforged/neoforge/debug/loot/LootPoolTest.java b/tests/src/main/java/net/neoforged/neoforge/debug/loot/LootPoolTest.java index fce7b70d4a..a9fddd9cd3 100644 --- a/tests/src/main/java/net/neoforged/neoforge/debug/loot/LootPoolTest.java +++ b/tests/src/main/java/net/neoforged/neoforge/debug/loot/LootPoolTest.java @@ -46,7 +46,7 @@ public class LootPoolTest { @EmptyTemplate @TestHolder(description = "Tests if loading loot pools with custom names works") public static void testPoolLoading(DynamicTest test, RegistrationHelper reg) { - reg.addProvider(event -> new LootTableProvider( + reg.addClientProvider(event -> new LootTableProvider( event.getGenerator().getPackOutput(), Set.of(), List.of( @@ -78,7 +78,7 @@ public static void testPoolLoading(DynamicTest test, RegistrationHelper reg) { static void pinkConcreteLootTableCanceled(final DynamicTest test, final RegistrationHelper reg) { ResourceKey lootTableToUse = TEST_LOOT_TABLE_2; - reg.addProvider(event -> new LootTableProvider( + reg.addClientProvider(event -> new LootTableProvider( event.getGenerator().getPackOutput(), Set.of(), List.of( @@ -117,7 +117,7 @@ static void pinkConcreteLootTableCanceled(final DynamicTest test, final Registra static void orangeConcreteLootTableReplaced(final DynamicTest test, final RegistrationHelper reg) { ResourceKey lootTableToUse = TEST_LOOT_TABLE_3; - reg.addProvider(event -> new LootTableProvider( + reg.addClientProvider(event -> new LootTableProvider( event.getGenerator().getPackOutput(), Set.of(), List.of( @@ -158,7 +158,7 @@ static void orangeConcreteLootTableReplaced(final DynamicTest test, final Regist static void yellowConcreteLootTableAppended(final DynamicTest test, final RegistrationHelper reg) { ResourceKey lootTableToUse = TEST_LOOT_TABLE_4; - reg.addProvider(event -> new LootTableProvider( + reg.addClientProvider(event -> new LootTableProvider( event.getGenerator().getPackOutput(), Set.of(), List.of( diff --git a/tests/src/main/java/net/neoforged/neoforge/debug/registry/BiomeModifierSyncTest.java b/tests/src/main/java/net/neoforged/neoforge/debug/registry/BiomeModifierSyncTest.java index 02068930b5..985468cb0c 100644 --- a/tests/src/main/java/net/neoforged/neoforge/debug/registry/BiomeModifierSyncTest.java +++ b/tests/src/main/java/net/neoforged/neoforge/debug/registry/BiomeModifierSyncTest.java @@ -73,7 +73,7 @@ static void init(final TestFramework framework) { @WithListener(Listener.class) static void biomeModifierSync(final DynamicTest test) { ResourceKey modifyTaigaModifier = ResourceKey.create(NeoForgeRegistries.Keys.BIOME_MODIFIERS, ResourceLocation.fromNamespaceAndPath(HELPER.modId(), "modify_taiga")); - HELPER.addProvider(event -> new DatapackBuiltinEntriesProvider( + HELPER.addClientProvider(event -> new DatapackBuiltinEntriesProvider( event.getGenerator().getPackOutput(), event.getLookupProvider(), new RegistrySetBuilder().add(NeoForgeRegistries.Keys.BIOME_MODIFIERS, context -> { diff --git a/tests/src/main/java/net/neoforged/neoforge/debug/resources/ModDatapackTest.java b/tests/src/main/java/net/neoforged/neoforge/debug/resources/ModDatapackTest.java index deb783a8d7..ccd65e4e6a 100644 --- a/tests/src/main/java/net/neoforged/neoforge/debug/resources/ModDatapackTest.java +++ b/tests/src/main/java/net/neoforged/neoforge/debug/resources/ModDatapackTest.java @@ -31,7 +31,7 @@ public class ModDatapackTest { static void modDatapack(final DynamicTest test) { final ResourceLocation testAdvancement = ResourceLocation.fromNamespaceAndPath(test.createModId(), "recipes/misc/test_advancement"); - test.registrationHelper().addProvider(event -> { + test.registrationHelper().addClientProvider(event -> { List generators = List.of((registries, saver, existingFileHelper) -> Advancement.Builder.recipeAdvancement() .parent(RecipeBuilder.ROOT_RECIPE_ADVANCEMENT) .addCriterion("has_scute", CriteriaTriggers.INVENTORY_CHANGED.createCriterion( diff --git a/tests/src/main/java/net/neoforged/neoforge/oldtest/DataGeneratorTest.java b/tests/src/main/java/net/neoforged/neoforge/oldtest/DataGeneratorTest.java index a5d5f1041f..43723c7ca5 100644 --- a/tests/src/main/java/net/neoforged/neoforge/oldtest/DataGeneratorTest.java +++ b/tests/src/main/java/net/neoforged/neoforge/oldtest/DataGeneratorTest.java @@ -42,10 +42,10 @@ import net.minecraft.advancements.AdvancementHolder; import net.minecraft.advancements.AdvancementType; import net.minecraft.advancements.critereon.InventoryChangeTrigger; -import net.minecraft.client.renderer.block.model.BlockModel.GuiLight; import net.minecraft.client.renderer.block.model.ItemTransform; import net.minecraft.client.renderer.block.model.ItemTransforms; import net.minecraft.client.renderer.block.model.Variant; +import net.minecraft.client.resources.model.UnbakedModel.GuiLight; import net.minecraft.core.Direction; import net.minecraft.core.Holder; import net.minecraft.core.HolderGetter; @@ -152,7 +152,7 @@ public class DataGeneratorTest { .add(Registries.LEVEL_STEM, DataGeneratorTest::levelStem); @SubscribeEvent - public static void gatherData(GatherDataEvent event) { + public static void gatherData(GatherDataEvent.Client event) { GSON = new GsonBuilder() .registerTypeAdapter(Variant.class, new Variant.Deserializer()) .registerTypeAdapter(ItemTransforms.class, new ItemTransforms.Deserializer()) @@ -174,18 +174,18 @@ public static void gatherData(GatherDataEvent event) { Component.literal("NeoForge tests resource pack"), DetectedVersion.BUILT_IN.getPackVersion(PackType.CLIENT_RESOURCES), Optional.of(new InclusiveRange<>(0, Integer.MAX_VALUE))))); - gen.addProvider(event.includeClient(), new Lang(packOutput)); + gen.addProvider(true, new Lang(packOutput)); // Let blockstate provider see generated item models by passing its existing file helper ItemModelProvider itemModels = new ItemModels(packOutput, event.getExistingFileHelper()); - gen.addProvider(event.includeClient(), itemModels); - gen.addProvider(event.includeClient(), new BlockStates(packOutput, itemModels.existingFileHelper)); - gen.addProvider(event.includeClient(), new SoundDefinitions(packOutput, event.getExistingFileHelper())); - gen.addProvider(event.includeClient(), new ParticleDescriptions(packOutput, event.getExistingFileHelper())); - - gen.addProvider(event.includeServer(), new Recipes.Runner(packOutput, lookupProvider)); - gen.addProvider(event.includeServer(), new Tags(packOutput, lookupProvider, event.getExistingFileHelper())); - gen.addProvider(event.includeServer(), new AdvancementProvider(packOutput, lookupProvider, event.getExistingFileHelper(), List.of(new Advancements()))); - gen.addProvider(event.includeServer(), new DatapackBuiltinEntriesProvider(packOutput, lookupProvider, BUILDER, Set.of(MODID))); + gen.addProvider(true, itemModels); + gen.addProvider(true, new BlockStates(packOutput, itemModels.existingFileHelper)); + gen.addProvider(true, new SoundDefinitions(packOutput, event.getExistingFileHelper())); + gen.addProvider(true, new ParticleDescriptions(packOutput, event.getExistingFileHelper())); + + gen.addProvider(true, new Recipes.Runner(packOutput, lookupProvider)); + gen.addProvider(true, new Tags(packOutput, lookupProvider, event.getExistingFileHelper())); + gen.addProvider(true, new AdvancementProvider(packOutput, lookupProvider, event.getExistingFileHelper(), List.of(new Advancements()))); + gen.addProvider(true, new DatapackBuiltinEntriesProvider(packOutput, lookupProvider, BUILDER, Set.of(MODID))); } public static void levelStem(BootstrapContext context) { diff --git a/tests/src/main/java/net/neoforged/neoforge/oldtest/DeferredRegistryTest.java b/tests/src/main/java/net/neoforged/neoforge/oldtest/DeferredRegistryTest.java index 1976621c13..a81a3650af 100644 --- a/tests/src/main/java/net/neoforged/neoforge/oldtest/DeferredRegistryTest.java +++ b/tests/src/main/java/net/neoforged/neoforge/oldtest/DeferredRegistryTest.java @@ -94,10 +94,10 @@ public void serverStarted(ServerStartedEvent event) { //PLACED_FEATURE.get(); } - public void gatherData(GatherDataEvent event) { + public void gatherData(GatherDataEvent.Client event) { DataGenerator gen = event.getGenerator(); - gen.addProvider(event.includeClient(), new BlockStateProvider(gen.getPackOutput(), MODID, event.getExistingFileHelper()) { + gen.addProvider(true, new BlockStateProvider(gen.getPackOutput(), MODID, event.getExistingFileHelper()) { @Override protected void registerStatesAndModels() { simpleBlockWithItem(BLOCK.get(), models().cubeAll(BLOCK.getId().getPath(), mcLoc("block/furnace_top"))); diff --git a/tests/src/main/java/net/neoforged/neoforge/oldtest/RemoveTagDatagenTest.java b/tests/src/main/java/net/neoforged/neoforge/oldtest/RemoveTagDatagenTest.java index 7ac89d1be1..9ef0b8f795 100644 --- a/tests/src/main/java/net/neoforged/neoforge/oldtest/RemoveTagDatagenTest.java +++ b/tests/src/main/java/net/neoforged/neoforge/oldtest/RemoveTagDatagenTest.java @@ -32,7 +32,7 @@ public RemoveTagDatagenTest(IEventBus modBus) { modBus.addListener(this::onGatherData); } - private void onGatherData(GatherDataEvent event) { + private void onGatherData(GatherDataEvent.Client event) { DataGenerator generator = event.getGenerator(); ExistingFileHelper helper = event.getExistingFileHelper(); @@ -50,9 +50,9 @@ protected void addTags(HolderLookup.Provider provider) { } }; - generator.addProvider(event.includeServer(), blocks); + generator.addProvider(true, blocks); - generator.addProvider(event.includeServer(), new ItemTagsProvider(generator.getPackOutput(), event.getLookupProvider(), blocks.contentsGetter(), MODID, helper) { + generator.addProvider(true, new ItemTagsProvider(generator.getPackOutput(), event.getLookupProvider(), blocks.contentsGetter(), MODID, helper) { @Override protected void addTags(HolderLookup.Provider provider) { // This is for testing if it is functional, remove spruce_planks from planks, which makes us unable to craft beds with them. diff --git a/tests/src/main/java/net/neoforged/neoforge/oldtest/block/FullPotsAccessorDemo.java b/tests/src/main/java/net/neoforged/neoforge/oldtest/block/FullPotsAccessorDemo.java index 4014e2b0b7..9ea4f3778d 100644 --- a/tests/src/main/java/net/neoforged/neoforge/oldtest/block/FullPotsAccessorDemo.java +++ b/tests/src/main/java/net/neoforged/neoforge/oldtest/block/FullPotsAccessorDemo.java @@ -7,18 +7,17 @@ import com.google.gson.JsonDeserializationContext; import com.google.gson.JsonObject; +import com.google.gson.JsonParseException; import java.util.ArrayList; import java.util.List; -import java.util.function.Function; import java.util.stream.Collectors; import net.minecraft.client.Minecraft; import net.minecraft.client.renderer.RenderType; import net.minecraft.client.renderer.block.model.BakedQuad; -import net.minecraft.client.renderer.block.model.BlockModel; -import net.minecraft.client.renderer.block.model.ItemOverride; -import net.minecraft.client.renderer.texture.TextureAtlasSprite; +import net.minecraft.client.renderer.block.model.ItemTransforms; +import net.minecraft.client.renderer.block.model.TextureSlots; import net.minecraft.client.resources.model.BakedModel; -import net.minecraft.client.resources.model.Material; +import net.minecraft.client.resources.model.DelegateBakedModel; import net.minecraft.client.resources.model.ModelBaker; import net.minecraft.client.resources.model.ModelState; import net.minecraft.client.resources.model.UnbakedModel; @@ -32,6 +31,7 @@ import net.minecraft.resources.ResourceLocation; import net.minecraft.stats.Stats; import net.minecraft.util.RandomSource; +import net.minecraft.util.context.ContextMap; import net.minecraft.world.InteractionHand; import net.minecraft.world.InteractionResult; import net.minecraft.world.entity.player.Player; @@ -60,12 +60,10 @@ import net.neoforged.fml.common.Mod; import net.neoforged.neoforge.client.ChunkRenderTypeSet; import net.neoforged.neoforge.client.event.ModelEvent; -import net.neoforged.neoforge.client.model.BakedModelWrapper; +import net.neoforged.neoforge.client.model.DelegateUnbakedModel; +import net.neoforged.neoforge.client.model.UnbakedModelLoader; import net.neoforged.neoforge.client.model.data.ModelData; import net.neoforged.neoforge.client.model.data.ModelProperty; -import net.neoforged.neoforge.client.model.geometry.IGeometryBakingContext; -import net.neoforged.neoforge.client.model.geometry.IGeometryLoader; -import net.neoforged.neoforge.client.model.geometry.IUnbakedGeometry; import net.neoforged.neoforge.common.util.ConcatenatedListView; import net.neoforged.neoforge.event.BuildCreativeModeTabContentsEvent; import net.neoforged.neoforge.registries.DeferredBlock; @@ -226,31 +224,30 @@ protected void saveAdditional(CompoundTag tag, HolderLookup.Provider holderLooku @EventBusSubscriber(modid = MOD_ID, value = Dist.CLIENT, bus = EventBusSubscriber.Bus.MOD) private static class ClientHandler { @SubscribeEvent - public static void registerLoader(final ModelEvent.RegisterGeometryLoaders event) { + public static void registerLoader(final ModelEvent.RegisterLoaders event) { event.register(ResourceLocation.fromNamespaceAndPath(MOD_ID, "diorite_pot"), new DioritePotGeometryLoader()); } - private static class DioritePotGeometryLoader implements IGeometryLoader { + private static class DioritePotGeometryLoader implements UnbakedModelLoader { @Override - public DioritePotModelGeometry read(JsonObject jsonObject, JsonDeserializationContext deserializationContext) { + public DioritePotModelGeometry read(JsonObject jsonObject, JsonDeserializationContext jsonDeserializationContext) throws JsonParseException { JsonObject wrappedModel = jsonObject.getAsJsonObject("model"); - return new DioritePotModelGeometry(deserializationContext.deserialize(wrappedModel, BlockModel.class)); + return new DioritePotModelGeometry(jsonDeserializationContext.deserialize(wrappedModel, UnbakedModel.class)); } } - private record DioritePotModelGeometry(UnbakedModel wrappedModel) implements IUnbakedGeometry { - @Override - public BakedModel bake(IGeometryBakingContext context, ModelBaker baker, Function spriteGetter, ModelState modelState, List overrides) { - return new DioritePotModel(wrappedModel.bake(baker, spriteGetter, modelState)); + private static final class DioritePotModelGeometry extends DelegateUnbakedModel { + private DioritePotModelGeometry(UnbakedModel wrappedModel) { + super(wrappedModel); } @Override - public void resolveDependencies(UnbakedModel.Resolver modelGetter, IGeometryBakingContext context) { - wrappedModel.resolveDependencies(modelGetter); + public BakedModel bake(TextureSlots p_386641_, ModelBaker p_250133_, ModelState p_119536_, boolean p_387129_, boolean p_388638_, ItemTransforms p_386911_, ContextMap additionalProperties) { + return new DioritePotModel(wrapped.bake(p_386641_, p_250133_, p_119536_, p_387129_, p_388638_, p_386911_, additionalProperties)); } } - private static class DioritePotModel extends BakedModelWrapper { + private static class DioritePotModel extends DelegateBakedModel { private static final ChunkRenderTypeSet CUTOUT = ChunkRenderTypeSet.of(RenderType.cutout()); private static final ResourceLocation POT_TEXTURE = ResourceLocation.fromNamespaceAndPath("minecraft", "block/flower_pot"); private static final ResourceLocation DIRT_TEXTURE = ResourceLocation.fromNamespaceAndPath("minecraft", "block/dirt"); @@ -262,7 +259,7 @@ public DioritePotModel(BakedModel wrappedModel) { @Override public List getQuads(@Nullable BlockState state, @Nullable Direction side, RandomSource rand, ModelData extraData, @Nullable RenderType renderType) { List> quads = new ArrayList<>(); - quads.add(originalModel.getQuads(state, side, rand, extraData, renderType)); + quads.add(parent.getQuads(state, side, rand, extraData, renderType)); Block plant = extraData.get(DioriteFlowerPotBlockEntity.PLANT_PROPERTY); if (plant != null && plant != Blocks.AIR) { diff --git a/tests/src/main/java/net/neoforged/neoforge/oldtest/block/ScaffoldingTest.java b/tests/src/main/java/net/neoforged/neoforge/oldtest/block/ScaffoldingTest.java index bc95052150..a5fe858ab8 100644 --- a/tests/src/main/java/net/neoforged/neoforge/oldtest/block/ScaffoldingTest.java +++ b/tests/src/main/java/net/neoforged/neoforge/oldtest/block/ScaffoldingTest.java @@ -46,9 +46,9 @@ public ScaffoldingTest(IEventBus modBus) { modBus.addListener(this::gatherData); } - private void gatherData(final GatherDataEvent event) { + private void gatherData(final GatherDataEvent.Client event) { DataGenerator gen = event.getGenerator(); - gen.addProvider(event.includeClient(), new ScaffoldingBlockState(gen.getPackOutput(), MODID, event.getExistingFileHelper())); + event.addProvider(new ScaffoldingBlockState(gen.getPackOutput(), MODID, event.getExistingFileHelper())); } static class ScaffoldingBlockState extends BlockStateProvider { diff --git a/tests/src/main/java/net/neoforged/neoforge/oldtest/client/CustomArmorModelTest.java b/tests/src/main/java/net/neoforged/neoforge/oldtest/client/CustomArmorModelTest.java index a5cac68259..cc3a1486d2 100644 --- a/tests/src/main/java/net/neoforged/neoforge/oldtest/client/CustomArmorModelTest.java +++ b/tests/src/main/java/net/neoforged/neoforge/oldtest/client/CustomArmorModelTest.java @@ -10,6 +10,7 @@ import net.minecraft.client.model.Model; import net.minecraft.client.model.geom.ModelLayers; import net.minecraft.client.renderer.entity.state.HumanoidRenderState; +import net.minecraft.client.resources.model.EquipmentClientInfo; import net.minecraft.core.component.DataComponents; import net.minecraft.resources.ResourceLocation; import net.minecraft.world.item.ArmorItem; @@ -17,10 +18,8 @@ import net.minecraft.world.item.Item; import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.component.DyedItemColor; -import net.minecraft.world.item.equipment.ArmorMaterial; import net.minecraft.world.item.equipment.ArmorMaterials; import net.minecraft.world.item.equipment.ArmorType; -import net.minecraft.world.item.equipment.EquipmentModel; import net.neoforged.bus.api.IEventBus; import net.neoforged.fml.common.Mod; import net.neoforged.fml.loading.FMLEnvironment; @@ -44,8 +43,8 @@ public class CustomArmorModelTest { ArmorType.LEGGINGS, props.stacksTo(1).component(DataComponents.DYED_COLOR, new DyedItemColor(0xFF0000, false)))); // demonstrates the properties are copied from the vanilla model - private static final DeferredItem ENDERMAN_CHESTPLATE = ITEMS.registerItem("enderman_chestplate", props -> new EndermanArmorItem(ArmorMaterials.GOLD, ArmorType.CHESTPLATE, props.stacksTo(1))); - private static final DeferredItem ENDERMAN_BOOTS = ITEMS.registerItem("enderman_boots", props -> new EndermanArmorItem(ArmorMaterials.GOLD, ArmorType.BOOTS, props.stacksTo(1))); + private static final DeferredItem ENDERMAN_CHESTPLATE = ITEMS.registerItem("enderman_chestplate", props -> new ArmorItem(ArmorMaterials.GOLD, ArmorType.CHESTPLATE, props.stacksTo(1))); + private static final DeferredItem ENDERMAN_BOOTS = ITEMS.registerItem("enderman_boots", props -> new ArmorItem(ArmorMaterials.GOLD, ArmorType.BOOTS, props.stacksTo(1))); public CustomArmorModelTest(IEventBus modBus) { ITEMS.register(modBus); @@ -64,29 +63,22 @@ private void addCreative(BuildCreativeModeTabContentsEvent event) { } } - private static class EndermanArmorItem extends ArmorItem { - private static final ResourceLocation ARMOR_TEXTURE = ResourceLocation.withDefaultNamespace("textures/entity/enderman/enderman.png"); - - public EndermanArmorItem(ArmorMaterial material, ArmorType slot, Properties props) { - super(material, slot, props); - } - - @Nullable - @Override - public ResourceLocation getArmorTexture(ItemStack stack, EquipmentModel.LayerType type, EquipmentModel.Layer layer, ResourceLocation _default) { - return ARMOR_TEXTURE; - } - } - private static final class ClientEvents { + private static final ResourceLocation ARMOR_TEXTURE = ResourceLocation.withDefaultNamespace("textures/entity/enderman/enderman.png"); private static final Lazy> ENDERMAN = Lazy.of(() -> new HumanoidModel<>(Minecraft.getInstance().getEntityModels().bakeLayer(ModelLayers.ENDERMAN))); private static void onRegisterClientExtensions(RegisterClientExtensionsEvent event) { event.registerItem(new IClientItemExtensions() { @Override - public HumanoidModel getHumanoidArmorModel(ItemStack itemStack, EquipmentModel.LayerType armorSlot, Model _default) { + public HumanoidModel getHumanoidArmorModel(ItemStack itemStack, EquipmentClientInfo.LayerType armorSlot, Model _default) { return ENDERMAN.get(); } + + @Nullable + @Override + public ResourceLocation getArmorTexture(ItemStack stack, EquipmentClientInfo.LayerType type, EquipmentClientInfo.Layer layer, ResourceLocation _default) { + return ARMOR_TEXTURE; + } }, ENDERMAN_BOOTS.get(), ENDERMAN_CHESTPLATE.get()); } } diff --git a/tests/src/main/java/net/neoforged/neoforge/oldtest/client/CustomPresetEditorTest.java b/tests/src/main/java/net/neoforged/neoforge/oldtest/client/CustomPresetEditorTest.java index b5701b006a..10edb82823 100644 --- a/tests/src/main/java/net/neoforged/neoforge/oldtest/client/CustomPresetEditorTest.java +++ b/tests/src/main/java/net/neoforged/neoforge/oldtest/client/CustomPresetEditorTest.java @@ -51,7 +51,7 @@ public class CustomPresetEditorTest { @EventBusSubscriber(modid = MODID, bus = EventBusSubscriber.Bus.MOD) public static class CommonModEvents { @SubscribeEvent - public static void onGatherData(GatherDataEvent event) { + public static void onGatherData(GatherDataEvent.Client event) { DataGenerator gen = event.getGenerator(); PackOutput packOutput = gen.getPackOutput(); CompletableFuture lookupProvider = event.getLookupProvider(); @@ -59,7 +59,7 @@ public static void onGatherData(GatherDataEvent event) { RegistrySetBuilder registrySetBuilder = new RegistrySetBuilder() .add(Registries.WORLD_PRESET, context -> context.register(WORLD_PRESET_KEY, makeWorldPreset(context))); - gen.addProvider(event.includeServer(), new DatapackBuiltinEntriesProvider(packOutput, lookupProvider, registrySetBuilder, Set.of(MODID)) { + gen.addProvider(true, new DatapackBuiltinEntriesProvider(packOutput, lookupProvider, registrySetBuilder, Set.of(MODID)) { @Override public String getName() { return MODID + ":" + super.getName(); // dataproviders must have unique names diff --git a/tests/src/main/java/net/neoforged/neoforge/oldtest/client/model/CustomItemDisplayContextTest.java b/tests/src/main/java/net/neoforged/neoforge/oldtest/client/model/CustomItemDisplayContextTest.java index c281cc6ee4..2a2e2e98cd 100644 --- a/tests/src/main/java/net/neoforged/neoforge/oldtest/client/model/CustomItemDisplayContextTest.java +++ b/tests/src/main/java/net/neoforged/neoforge/oldtest/client/model/CustomItemDisplayContextTest.java @@ -5,6 +5,8 @@ package net.neoforged.neoforge.oldtest.client.model; +import static net.minecraft.client.data.models.model.ModelLocationUtils.getModelLocation; + import com.mojang.blaze3d.vertex.PoseStack; import com.mojang.serialization.MapCodec; import net.minecraft.client.Minecraft; @@ -18,7 +20,6 @@ import net.minecraft.core.registries.BuiltInRegistries; import net.minecraft.data.DataGenerator; import net.minecraft.data.PackOutput; -import net.minecraft.data.models.model.ModelLocationUtils; import net.minecraft.nbt.CompoundTag; import net.minecraft.network.Connection; import net.minecraft.network.protocol.Packet; @@ -100,9 +101,7 @@ public void render(ItemHangerBlockEntity blocken, float partialTicks, PoseStack ItemRenderer itemRenderer = Minecraft.getInstance().getItemRenderer(); - var model = itemRenderer.getModel(blocken.heldItem, blocken.getLevel(), null, 0); - - itemRenderer.render(blocken.heldItem, HANGING, false, poseStack, bufferSource, packedLight, overlayCoord, model); + itemRenderer.renderStatic(blocken.heldItem, HANGING, packedLight, overlayCoord, poseStack, bufferSource, blocken.getLevel(), 0); poseStack.popPose(); } @@ -130,12 +129,12 @@ private void addCreative(BuildCreativeModeTabContentsEvent event) { event.accept(ITEM_HANGER_ITEM); } - public void gatherData(GatherDataEvent event) { + public void gatherData(GatherDataEvent.Client event) { DataGenerator gen = event.getGenerator(); final PackOutput output = gen.getPackOutput(); - gen.addProvider(event.includeClient(), new ItemModels(output, event.getExistingFileHelper())); - gen.addProvider(event.includeClient(), new BlockStateModels(output, event.getExistingFileHelper())); + gen.addProvider(true, new ItemModels(output, event.getExistingFileHelper())); + gen.addProvider(true, new BlockStateModels(output, event.getExistingFileHelper())); } public static class BlockStateModels extends BlockStateProvider { @@ -147,7 +146,7 @@ public BlockStateModels(PackOutput output, ExistingFileHelper exFileHelper) { protected void registerStatesAndModels() { { Block block = ITEM_HANGER_BLOCK.get(); - horizontalBlock(block, models().getExistingFile(ModelLocationUtils.getModelLocation(block))); + horizontalBlock(block, models().getExistingFile(getModelLocation(block))); } } } @@ -210,7 +209,7 @@ public BlockEntity newBlockEntity(BlockPos pos, BlockState state) { @Deprecated @Override public RenderShape getRenderShape(BlockState state) { - return RenderShape.ENTITYBLOCK_ANIMATED; + return RenderShape.MODEL; } } diff --git a/tests/src/main/java/net/neoforged/neoforge/oldtest/client/model/MegaModelTest.java b/tests/src/main/java/net/neoforged/neoforge/oldtest/client/model/MegaModelTest.java index e864f03a17..54fc9d6a20 100644 --- a/tests/src/main/java/net/neoforged/neoforge/oldtest/client/model/MegaModelTest.java +++ b/tests/src/main/java/net/neoforged/neoforge/oldtest/client/model/MegaModelTest.java @@ -11,6 +11,7 @@ import net.minecraft.client.renderer.RenderType; import net.minecraft.client.renderer.block.model.BakedQuad; import net.minecraft.client.resources.model.BakedModel; +import net.minecraft.client.resources.model.DelegateBakedModel; import net.minecraft.client.resources.model.ModelResourceLocation; import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; @@ -39,7 +40,6 @@ import net.neoforged.fml.common.EventBusSubscriber; import net.neoforged.fml.common.Mod; import net.neoforged.neoforge.client.event.ModelEvent; -import net.neoforged.neoforge.client.model.BakedModelWrapper; import net.neoforged.neoforge.client.model.QuadTransformers; import net.neoforged.neoforge.client.model.data.ModelData; import net.neoforged.neoforge.client.model.data.ModelProperty; @@ -98,7 +98,7 @@ public static class ClientEvents { @SubscribeEvent public static void onModelBakingCompleted(ModelEvent.ModifyBakingResult event) { var name = new ModelResourceLocation(ResourceLocation.fromNamespaceAndPath(MOD_ID, REG_NAME), ""); - event.getModels().computeIfPresent(name, (n, m) -> new TransformingModelWrapper(m)); + event.getBakingResult().blockStateModels().computeIfPresent(name, (n, m) -> new TransformingModelWrapper(m)); } } @@ -147,7 +147,7 @@ private record TestData(Transformation transform) { public static final ModelProperty PROPERTY = new ModelProperty<>(); } - private static class TransformingModelWrapper extends BakedModelWrapper { + private static class TransformingModelWrapper extends DelegateBakedModel { public TransformingModelWrapper(BakedModel originalModel) { super(originalModel); } diff --git a/tests/src/main/java/net/neoforged/neoforge/oldtest/client/model/NewModelLoaderTest.java b/tests/src/main/java/net/neoforged/neoforge/oldtest/client/model/NewModelLoaderTest.java index db7b863456..199266704f 100644 --- a/tests/src/main/java/net/neoforged/neoforge/oldtest/client/model/NewModelLoaderTest.java +++ b/tests/src/main/java/net/neoforged/neoforge/oldtest/client/model/NewModelLoaderTest.java @@ -7,16 +7,22 @@ import com.google.gson.JsonDeserializationContext; import com.google.gson.JsonObject; +import com.google.gson.JsonParseException; import java.util.Arrays; -import java.util.function.Function; +import net.minecraft.client.data.models.model.TextureSlot; +import net.minecraft.client.renderer.block.model.ItemTransforms; +import net.minecraft.client.renderer.block.model.TextureSlots; import net.minecraft.client.renderer.texture.TextureAtlasSprite; +import net.minecraft.client.resources.model.BakedModel; import net.minecraft.client.resources.model.ModelBaker; import net.minecraft.client.resources.model.ModelState; +import net.minecraft.client.resources.model.SimpleBakedModel; import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; import net.minecraft.data.DataGenerator; import net.minecraft.data.PackOutput; import net.minecraft.resources.ResourceLocation; +import net.minecraft.util.context.ContextMap; import net.minecraft.world.entity.EquipmentSlot; import net.minecraft.world.entity.LivingEntity; import net.minecraft.world.item.BlockItem; @@ -35,8 +41,11 @@ import net.minecraft.world.phys.shapes.VoxelShape; import net.neoforged.bus.api.IEventBus; import net.neoforged.fml.common.Mod; +import net.neoforged.neoforge.client.RenderTypeGroup; import net.neoforged.neoforge.client.event.ModelEvent; -import net.neoforged.neoforge.client.model.IModelBuilder; +import net.neoforged.neoforge.client.model.ExtendedUnbakedModel; +import net.neoforged.neoforge.client.model.NeoForgeModelProperties; +import net.neoforged.neoforge.client.model.UnbakedModelLoader; import net.neoforged.neoforge.client.model.generators.BlockModelBuilder; import net.neoforged.neoforge.client.model.generators.BlockStateProvider; import net.neoforged.neoforge.client.model.generators.ConfiguredModel; @@ -44,9 +53,6 @@ import net.neoforged.neoforge.client.model.generators.loaders.ItemLayerModelBuilder; import net.neoforged.neoforge.client.model.generators.loaders.ObjModelBuilder; import net.neoforged.neoforge.client.model.generators.loaders.SeparateTransformsModelBuilder; -import net.neoforged.neoforge.client.model.geometry.IGeometryBakingContext; -import net.neoforged.neoforge.client.model.geometry.IGeometryLoader; -import net.neoforged.neoforge.client.model.geometry.SimpleUnbakedGeometry; import net.neoforged.neoforge.client.model.pipeline.QuadBakingVertexConsumer; import net.neoforged.neoforge.common.data.ExistingFileHelper; import net.neoforged.neoforge.data.event.GatherDataEvent; @@ -119,21 +125,21 @@ private void addCreative(BuildCreativeModeTabContentsEvent event) { } } - public void modelRegistry(ModelEvent.RegisterGeometryLoaders event) { + public void modelRegistry(ModelEvent.RegisterLoaders event) { event.register(ResourceLocation.fromNamespaceAndPath(MODID, "custom_loader"), new TestLoader()); } - static class TestLoader implements IGeometryLoader { + static class TestLoader implements UnbakedModelLoader { @Override - public TestModel read(JsonObject jsonObject, JsonDeserializationContext deserializationContext) { + public TestModel read(JsonObject jsonObject, JsonDeserializationContext jsonDeserializationContext) throws JsonParseException { return new TestModel(); } } - static class TestModel extends SimpleUnbakedGeometry { + static class TestModel implements ExtendedUnbakedModel { @Override - protected void addQuads(IGeometryBakingContext owner, IModelBuilder modelBuilder, ModelBaker baker, Function spriteGetter, ModelState modelTransform) { - TextureAtlasSprite texture = spriteGetter.apply(owner.getMaterial("particle")); + public BakedModel bake(TextureSlots textures, ModelBaker baker, ModelState modelState, boolean useAmbientOcclusion, boolean usesBlockLight, ItemTransforms itemTransforms, ContextMap additionalProperties) { + TextureAtlasSprite texture = baker.findSprite(textures, TextureSlot.PARTICLE.getId()); var quadBaker = new QuadBakingVertexConsumer(); @@ -145,18 +151,26 @@ protected void addQuads(IGeometryBakingContext owner, IModelBuilder modelBuil quadBaker.addVertex(1, 0, 0.5f).setColor(255, 255, 255, 255).setUv(texture.getU(16), texture.getV(16)).setOverlay(0).setNormal(0, 0, 0); quadBaker.addVertex(1, 1, 0.5f).setColor(255, 255, 255, 255).setUv(texture.getU(16), texture.getV(0)).setOverlay(0).setNormal(0, 0, 0); - modelBuilder.addUnculledFace(quadBaker.bakeQuad()); + return new SimpleBakedModel.Builder(useAmbientOcclusion, usesBlockLight, true, itemTransforms) + .particle(texture) + .addUnculledFace(quadBaker.bakeQuad()) + .build(additionalProperties.getOrDefault(NeoForgeModelProperties.RENDER_TYPE, RenderTypeGroup.EMPTY)); + } + + @Override + public void resolveDependencies(Resolver p_387087_) { + // No dependencies } } - private void datagen(GatherDataEvent event) { + private void datagen(GatherDataEvent.Client event) { DataGenerator gen = event.getGenerator(); final PackOutput output = gen.getPackOutput(); // Let blockstate provider see generated item models by passing its existing file helper ItemModelProvider itemModels = new ItemModels(output, event.getExistingFileHelper()); - gen.addProvider(event.includeClient(), itemModels); - gen.addProvider(event.includeClient(), new BlockStates(output, itemModels.existingFileHelper)); + gen.addProvider(true, itemModels); + gen.addProvider(true, new BlockStates(output, itemModels.existingFileHelper)); } public static class ItemModels extends ItemModelProvider { diff --git a/tests/src/main/java/net/neoforged/neoforge/oldtest/client/model/TRSRTransformerTest.java b/tests/src/main/java/net/neoforged/neoforge/oldtest/client/model/TRSRTransformerTest.java index f30a5f7cc6..aed6f66b18 100644 --- a/tests/src/main/java/net/neoforged/neoforge/oldtest/client/model/TRSRTransformerTest.java +++ b/tests/src/main/java/net/neoforged/neoforge/oldtest/client/model/TRSRTransformerTest.java @@ -7,9 +7,10 @@ import com.mojang.math.Transformation; import java.util.List; +import java.util.Map; import net.minecraft.client.renderer.RenderType; -import net.minecraft.client.renderer.block.model.BakedOverrides; import net.minecraft.client.renderer.block.model.BakedQuad; +import net.minecraft.client.renderer.block.model.ItemTransforms; import net.minecraft.client.renderer.texture.TextureAtlasSprite; import net.minecraft.client.resources.model.BakedModel; import net.minecraft.client.resources.model.ModelResourceLocation; @@ -61,9 +62,10 @@ private void addCreative(BuildCreativeModeTabContentsEvent event) { } public void onModelBake(ModelEvent.ModifyBakingResult e) { - for (ModelResourceLocation id : e.getModels().keySet()) { + Map models = e.getBakingResult().blockStateModels(); + for (ModelResourceLocation id : models.keySet()) { if (MODID.equals(id.id().getNamespace()) && "test".equals(id.id().getPath())) { - e.getModels().put(id, new MyBakedModel(e.getModels().get(id))); + models.put(id, new MyBakedModel(models.get(id))); } } } @@ -101,19 +103,14 @@ public boolean usesBlockLight() { return base.usesBlockLight(); } - @Override - public boolean isCustomRenderer() { - return base.isCustomRenderer(); - } - @Override public TextureAtlasSprite getParticleIcon() { return base.getParticleIcon(); } @Override - public BakedOverrides overrides() { - return base.overrides(); + public ItemTransforms getTransforms() { + return base.getTransforms(); } } } diff --git a/tests/src/main/java/net/neoforged/neoforge/oldtest/client/rendering/CustomParticleTypeTest.java b/tests/src/main/java/net/neoforged/neoforge/oldtest/client/rendering/CustomParticleTypeTest.java index 6e6a8e1183..7a1de85be4 100644 --- a/tests/src/main/java/net/neoforged/neoforge/oldtest/client/rendering/CustomParticleTypeTest.java +++ b/tests/src/main/java/net/neoforged/neoforge/oldtest/client/rendering/CustomParticleTypeTest.java @@ -5,13 +5,12 @@ package net.neoforged.neoforge.oldtest.client.rendering; -import com.mojang.blaze3d.vertex.BufferBuilder; -import com.mojang.blaze3d.vertex.Tesselator; import net.minecraft.client.Minecraft; import net.minecraft.client.multiplayer.ClientLevel; import net.minecraft.client.particle.ParticleRenderType; import net.minecraft.client.particle.TerrainParticle; -import net.minecraft.client.renderer.texture.TextureManager; +import net.minecraft.client.renderer.RenderType; +import net.minecraft.client.renderer.texture.TextureAtlas; import net.minecraft.world.entity.player.Player; import net.minecraft.world.level.block.Blocks; import net.neoforged.api.distmarker.Dist; @@ -29,30 +28,8 @@ public CustomParticleTypeTest() {} @EventBusSubscriber(modid = CustomParticleTypeTest.MOD_ID, bus = EventBusSubscriber.Bus.GAME, value = Dist.CLIENT) public static class ClientEvents { - private static final ParticleRenderType CUSTOM_TYPE = new ParticleRenderType() { - @Override - public BufferBuilder begin(Tesselator tesselator, TextureManager texMgr) { - Minecraft.getInstance().gameRenderer.lightTexture().turnOnLightLayer(); - return ParticleRenderType.TERRAIN_SHEET.begin(tesselator, texMgr); - } - - @Override - public String toString() { - return "CUSTOM_TYPE"; - } - }; - private static final ParticleRenderType CUSTOM_TYPE_TWO = new ParticleRenderType() { - @Override - public BufferBuilder begin(Tesselator tesselator, TextureManager texMgr) { - Minecraft.getInstance().gameRenderer.lightTexture().turnOnLightLayer(); - return ParticleRenderType.TERRAIN_SHEET.begin(tesselator, texMgr); - } - - @Override - public String toString() { - return "CUSTOM_TYPE_TWO"; - } - }; + private static final ParticleRenderType CUSTOM_TYPE = new ParticleRenderType("CUSTOM_TYPE", RenderType.translucentParticle(TextureAtlas.LOCATION_BLOCKS)); + private static final ParticleRenderType CUSTOM_TYPE_TWO = new ParticleRenderType("CUSTOM_TYPE_TWO", RenderType.translucentParticle(TextureAtlas.LOCATION_BLOCKS)); private static class CustomParticle extends TerrainParticle { public CustomParticle(ClientLevel level, double x, double y, double z) { diff --git a/tests/src/main/java/net/neoforged/neoforge/oldtest/client/rendering/LinearTextTextureFilteringTest.java b/tests/src/main/java/net/neoforged/neoforge/oldtest/client/rendering/LinearTextTextureFilteringTest.java index 5cf62659e2..e8da3992f0 100644 --- a/tests/src/main/java/net/neoforged/neoforge/oldtest/client/rendering/LinearTextTextureFilteringTest.java +++ b/tests/src/main/java/net/neoforged/neoforge/oldtest/client/rendering/LinearTextTextureFilteringTest.java @@ -5,12 +5,12 @@ package net.neoforged.neoforge.oldtest.client.rendering; +import net.minecraft.client.Minecraft; import net.minecraft.client.gui.screens.TitleScreen; import net.neoforged.api.distmarker.Dist; import net.neoforged.bus.api.SubscribeEvent; import net.neoforged.fml.common.EventBusSubscriber; import net.neoforged.fml.common.Mod; -import net.neoforged.neoforge.client.NeoForgeRenderTypes; import net.neoforged.neoforge.client.event.ScreenEvent.Render; @Mod(LinearTextTextureFilteringTest.MODID) @@ -22,14 +22,14 @@ public class LinearTextTextureFilteringTest { @SubscribeEvent public static void onGuiRenderPre(Render.Pre event) { if (ENABLED && event.getScreen() instanceof TitleScreen) { - NeoForgeRenderTypes.enableTextTextureLinearFiltering = true; + Minecraft.getInstance().font.enableTextTextureLinearFiltering = true; } } @SubscribeEvent public static void onGuiRenderPost(Render.Post event) { if (ENABLED && event.getScreen() instanceof TitleScreen) { - NeoForgeRenderTypes.enableTextTextureLinearFiltering = false; + Minecraft.getInstance().font.enableTextTextureLinearFiltering = false; } } } diff --git a/tests/src/main/java/net/neoforged/neoforge/oldtest/client/rendering/RenderableTest.java b/tests/src/main/java/net/neoforged/neoforge/oldtest/client/rendering/RenderableTest.java deleted file mode 100644 index 3e5e7f34a9..0000000000 --- a/tests/src/main/java/net/neoforged/neoforge/oldtest/client/rendering/RenderableTest.java +++ /dev/null @@ -1,186 +0,0 @@ -/* - * Copyright (c) Forge Development LLC and contributors - * SPDX-License-Identifier: LGPL-2.1-only - */ - -package net.neoforged.neoforge.oldtest.client.rendering; - -import com.google.common.collect.ImmutableMap; -import com.mojang.blaze3d.vertex.PoseStack; -import java.util.Map; -import net.minecraft.client.DeltaTracker; -import net.minecraft.client.Minecraft; -import net.minecraft.client.renderer.LightTexture; -import net.minecraft.client.renderer.RenderType; -import net.minecraft.client.renderer.texture.OverlayTexture; -import net.minecraft.client.resources.model.ModelResourceLocation; -import net.minecraft.core.BlockPos; -import net.minecraft.resources.ResourceLocation; -import net.minecraft.server.packs.resources.ResourceManager; -import net.minecraft.server.packs.resources.SimplePreparableReloadListener; -import net.minecraft.util.profiling.Profiler; -import net.minecraft.util.profiling.ProfilerFiller; -import net.minecraft.world.phys.Vec3; -import net.neoforged.bus.api.IEventBus; -import net.neoforged.fml.common.Mod; -import net.neoforged.fml.loading.FMLEnvironment; -import net.neoforged.neoforge.client.NeoForgeRenderTypes; -import net.neoforged.neoforge.client.event.ModelEvent; -import net.neoforged.neoforge.client.event.RegisterClientReloadListenersEvent; -import net.neoforged.neoforge.client.event.RenderLevelStageEvent; -import net.neoforged.neoforge.client.event.RenderLevelStageEvent.Stage; -import net.neoforged.neoforge.client.model.data.ModelData; -import net.neoforged.neoforge.client.model.geometry.StandaloneGeometryBakingContext; -import net.neoforged.neoforge.client.model.obj.ObjLoader; -import net.neoforged.neoforge.client.model.obj.ObjModel; -import net.neoforged.neoforge.client.model.renderable.BakedModelRenderable; -import net.neoforged.neoforge.client.model.renderable.CompositeRenderable; -import net.neoforged.neoforge.client.model.renderable.IRenderable; -import net.neoforged.neoforge.common.NeoForge; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; -import org.joml.Matrix4f; - -/** - * This mod is testing the use of {@link RenderLevelStageEvent} and is a modifaction of a pre-existing test mod that used the old - * RenderLevelLastEvent. To restore the old behavior, set {@link #USE_LEVEL_RENDERER_STAGE} to false. - * - * When you enter a world, there should be 6 sugar gliders rendering at (0, 120, 0) that test the various stages in {@link RenderLevelStageEvent}. - * From left to right (with the sugar gliders facing you) they represent {@link Stage#AFTER_SKY}, {@link Stage#AFTER_SOLID_BLOCKS}, - * {@link Stage#AFTER_TRANSLUCENT_BLOCKS}, {@link Stage#AFTER_TRIPWIRE_BLOCKS}, {@link Stage#AFTER_PARTICLES}, and {@link Stage#AFTER_WEATHER}. - * Due to how weather modifies the projection matrix, it's sugar glider will be positioned weirdly. Below each sugar gliders is a render of - * blue stained glass to test translucency with fabulous graphics. {@link Stage#AFTER_PARTICLES} will render the stained glass using - * {@link NeoForgeRenderTypes#TRANSLUCENT_ON_PARTICLES_TARGET}. - */ -@Mod(RenderableTest.MODID) -public class RenderableTest { - public static final String MODID = "renderable_test"; - private static final Logger LOGGER = LogManager.getLogger(); - - public static final boolean ENABLED = false; // Renders at (0, 120, 0) - - public RenderableTest(IEventBus modEventBus) { - if (ENABLED) { - if (FMLEnvironment.dist.isClient()) { - Client.init(modEventBus); - } - } - } - - private static class Client { - private static ModelResourceLocation MODEL_LOC = ModelResourceLocation.standalone( - ResourceLocation.fromNamespaceAndPath("minecraft", "block/blue_stained_glass")); - - private static IRenderable renderable; - private static IRenderable bakedRenderable; - - public static void init(IEventBus modBus) { - var forgeBus = NeoForge.EVENT_BUS; - modBus.addListener(Client::registerModels); - modBus.addListener(Client::registerReloadListeners); - modBus.addListener(Client::registerStage); - forgeBus.addListener(Client::renderStage); - } - - private static void registerModels(ModelEvent.RegisterAdditional event) { - event.register(MODEL_LOC); - } - - public static void registerReloadListeners(RegisterClientReloadListenersEvent event) { - event.registerReloadListener(new SimplePreparableReloadListener() { - @Override - protected ObjModel prepare(ResourceManager resourceManager, ProfilerFiller profilerFiller) { - var settings = new ObjModel.ModelSettings( - ResourceLocation.fromNamespaceAndPath("new_model_loader_test", "models/item/sugar_glider.obj"), - false, - true, - true, - false, - null); - return ObjLoader.INSTANCE.loadModel(settings); - } - - @Override - protected void apply(ObjModel model, ResourceManager resourceManager, ProfilerFiller profilerFiller) { - var config = StandaloneGeometryBakingContext.create(Map.of( - "#qr", ResourceLocation.fromNamespaceAndPath("minecraft", "block/quartz_block_top"))); - renderable = model.bakeRenderable(config); - } - }); - } - - public static void registerStage(RenderLevelStageEvent.RegisterStageEvent event) { - var stage = event.register(ResourceLocation.fromNamespaceAndPath(MODID, "test_stage"), null); - LOGGER.info("Registered RenderLevelStageEvent.Stage: {}", stage); - } - - private static void renderStage(RenderLevelStageEvent event) { - int xOffset = -1; - var stage = event.getStage(); - if (stage == Stage.AFTER_SKY) - xOffset = 0; - else if (stage == Stage.AFTER_SOLID_BLOCKS) - xOffset = 1; - else if (stage == Stage.AFTER_TRANSLUCENT_BLOCKS) - xOffset = 2; - else if (stage == Stage.AFTER_TRIPWIRE_BLOCKS) - xOffset = 3; - else if (stage == Stage.AFTER_PARTICLES) - xOffset = 4; - else if (stage == Stage.AFTER_WEATHER) - xOffset = 5; - - Vec3 cam = event.getCamera().getPosition(); - if (xOffset > -1) { - PoseStack poseStack = event.getPoseStack(); - render(stage, poseStack, event.getRenderTick(), event.getPartialTick(), cam.x, cam.y, cam.z, xOffset); - } - } - - private static void render(Stage stage, PoseStack poseStack, int renderTick, DeltaTracker deltaTracker, double camX, double camY, double camZ, int xOffset) { - double x = camX, y = camY, z = camZ; - if (!BlockPos.containing(0, y, 0).closerThan(BlockPos.containing(x, y, z), 100)) - return; - - var profiler = Profiler.get(); - profiler.push("renderable_test"); - if (bakedRenderable == null) { - bakedRenderable = BakedModelRenderable.of(MODEL_LOC).withModelDataContext(); - } - - var bufferSource = Minecraft.getInstance().renderBuffers().bufferSource(); - - float partialTick = deltaTracker.getGameTimeDeltaPartialTick(false); - double time = renderTick + partialTick; - - var map = ImmutableMap.builder(); - - var left = new Matrix4f(); - left.rotation((float) Math.sin(time * 0.4) * 0.1f, 0, 0, 1); - map.put("object_1", left); - - var right = new Matrix4f(); - right.rotation(-(float) Math.sin(time * 0.4) * 0.1f, 0, 0, 1); - map.put("object_9", right); - - var transforms = CompositeRenderable.Transforms.of(map.build()); - - poseStack.pushPose(); - poseStack.translate(0 - x + xOffset, 120 - y, 0 - z); - renderable.render(poseStack, bufferSource, RenderType::entitySolid, LightTexture.FULL_BRIGHT, OverlayTexture.NO_OVERLAY, partialTick, transforms); - - poseStack.translate(0, -1, 0); - bakedRenderable.render(poseStack, bufferSource, texture -> getRenderType(stage, texture), LightTexture.FULL_BRIGHT, OverlayTexture.NO_OVERLAY, partialTick, ModelData.EMPTY); - poseStack.popPose(); - - bufferSource.endBatch(); - profiler.pop(); - } - - private static RenderType getRenderType(Stage stage, ResourceLocation texture) { - if (stage == Stage.AFTER_PARTICLES) - return NeoForgeRenderTypes.TRANSLUCENT_ON_PARTICLES_TARGET.get(); - return RenderType.entityTranslucent(texture); - } - } -} diff --git a/tests/src/main/java/net/neoforged/neoforge/oldtest/item/CustomElytraTest.java b/tests/src/main/java/net/neoforged/neoforge/oldtest/item/CustomElytraTest.java index 1b487ba11d..db79755c77 100644 --- a/tests/src/main/java/net/neoforged/neoforge/oldtest/item/CustomElytraTest.java +++ b/tests/src/main/java/net/neoforged/neoforge/oldtest/item/CustomElytraTest.java @@ -6,11 +6,14 @@ package net.neoforged.neoforge.oldtest.item; import net.minecraft.core.component.DataComponents; +import net.minecraft.resources.ResourceKey; import net.minecraft.resources.ResourceLocation; import net.minecraft.sounds.SoundEvents; import net.minecraft.world.entity.EquipmentSlot; import net.minecraft.world.item.CreativeModeTabs; import net.minecraft.world.item.Item; +import net.minecraft.world.item.equipment.EquipmentAsset; +import net.minecraft.world.item.equipment.EquipmentAssets; import net.minecraft.world.item.equipment.Equippable; import net.neoforged.bus.api.IEventBus; import net.neoforged.fml.common.Mod; @@ -21,11 +24,12 @@ @Mod(CustomElytraTest.MOD_ID) public class CustomElytraTest { public static final String MOD_ID = "custom_elytra_test"; + private static final ResourceKey TEST_ELYTRA_ASSET = ResourceKey.create(EquipmentAssets.ROOT_ID, ResourceLocation.fromNamespaceAndPath(MOD_ID, "test_elytra")); private static final DeferredRegister.Items ITEMS = DeferredRegister.createItems(MOD_ID); private static final DeferredItem TEST_ELYTRA = ITEMS.registerItem( "test_elytra", props -> new Item(props.durability(100) .component(DataComponents.EQUIPPABLE, Equippable.builder(EquipmentSlot.CHEST) - .setModel(ResourceLocation.fromNamespaceAndPath(MOD_ID, "test_elytra")) + .setAsset(TEST_ELYTRA_ASSET) .setEquipSound(SoundEvents.ARMOR_EQUIP_ELYTRA) .setDamageOnHurt(false) .build()))); diff --git a/tests/src/main/java/net/neoforged/neoforge/oldtest/item/RangedMobsUseModdedWeaponsTest.java b/tests/src/main/java/net/neoforged/neoforge/oldtest/item/RangedMobsUseModdedWeaponsTest.java index a20a22f2ed..aeafcaaa62 100644 --- a/tests/src/main/java/net/neoforged/neoforge/oldtest/item/RangedMobsUseModdedWeaponsTest.java +++ b/tests/src/main/java/net/neoforged/neoforge/oldtest/item/RangedMobsUseModdedWeaponsTest.java @@ -5,18 +5,12 @@ package net.neoforged.neoforge.oldtest.item; -import net.minecraft.client.renderer.item.ItemProperties; -import net.minecraft.core.component.DataComponents; -import net.minecraft.resources.ResourceLocation; import net.minecraft.world.item.BowItem; import net.minecraft.world.item.CreativeModeTabs; import net.minecraft.world.item.CrossbowItem; import net.minecraft.world.item.Item; -import net.minecraft.world.item.Items; -import net.minecraft.world.item.component.ChargedProjectiles; import net.neoforged.bus.api.IEventBus; import net.neoforged.fml.common.Mod; -import net.neoforged.fml.event.lifecycle.FMLClientSetupEvent; import net.neoforged.neoforge.event.BuildCreativeModeTabContentsEvent; import net.neoforged.neoforge.registries.DeferredItem; import net.neoforged.neoforge.registries.DeferredRegister; @@ -39,7 +33,6 @@ public class RangedMobsUseModdedWeaponsTest { public RangedMobsUseModdedWeaponsTest(IEventBus modEventBus) { if (ENABLE) { ITEMS.register(modEventBus); - modEventBus.addListener(this::onClientSetup); modEventBus.addListener(this::addCreative); } } @@ -50,38 +43,4 @@ private void addCreative(BuildCreativeModeTabContentsEvent event) { event.accept(MODDED_CROSSBOW); } } - - private void onClientSetup(FMLClientSetupEvent event) { - RangedWeaponModeLProperties.initBowModelProperties(); - RangedWeaponModeLProperties.initCrossbowModelProperties(); - } - - private static class RangedWeaponModeLProperties { - static void initBowModelProperties() { - ItemProperties.register(MODDED_BOW.get(), ResourceLocation.withDefaultNamespace("pull"), (itemStack, clientWorld, livingEntity, seed) -> { - if (livingEntity == null) { - return 0.0F; - } else { - return livingEntity.getUseItem() != itemStack ? 0.0F : (float) (itemStack.getUseDuration(livingEntity) - livingEntity.getUseItemRemainingTicks()) / 20.0F; - } - }); - ItemProperties.register(MODDED_BOW.get(), ResourceLocation.withDefaultNamespace("pulling"), (itemStack, clientWorld, livingEntity, seed) -> livingEntity != null && livingEntity.isUsingItem() && livingEntity.getUseItem() == itemStack ? 1.0F : 0.0F); - } - - static void initCrossbowModelProperties() { - ItemProperties.register(MODDED_CROSSBOW.get(), ResourceLocation.withDefaultNamespace("pull"), (itemStack, clientWorld, livingEntity, seed) -> { - if (livingEntity == null) { - return 0.0F; - } else { - return CrossbowItem.isCharged(itemStack) ? 0.0F : (float) (itemStack.getUseDuration(livingEntity) - livingEntity.getUseItemRemainingTicks()) / (float) CrossbowItem.getChargeDuration(itemStack, livingEntity); - } - }); - ItemProperties.register(MODDED_CROSSBOW.get(), ResourceLocation.withDefaultNamespace("pulling"), (itemStack, clientWorld, livingEntity, seed) -> livingEntity != null && livingEntity.isUsingItem() && livingEntity.getUseItem() == itemStack && !CrossbowItem.isCharged(itemStack) ? 1.0F : 0.0F); - ItemProperties.register(MODDED_CROSSBOW.get(), ResourceLocation.withDefaultNamespace("charged"), (itemStack, clientWorld, livingEntity, seed) -> livingEntity != null && CrossbowItem.isCharged(itemStack) ? 1.0F : 0.0F); - ItemProperties.register(MODDED_CROSSBOW.get(), ResourceLocation.withDefaultNamespace("firework"), (itemStack, clientWorld, livingEntity, seed) -> { - ChargedProjectiles chargedprojectiles = itemStack.get(DataComponents.CHARGED_PROJECTILES); - return chargedprojectiles != null && chargedprojectiles.contains(Items.FIREWORK_ROCKET) ? 1.0F : 0.0F; - }); - } - } } diff --git a/tests/src/main/java/net/neoforged/neoforge/oldtest/misc/DataPackRegistriesTest.java b/tests/src/main/java/net/neoforged/neoforge/oldtest/misc/DataPackRegistriesTest.java index 6e27bf2a21..f120f78030 100644 --- a/tests/src/main/java/net/neoforged/neoforge/oldtest/misc/DataPackRegistriesTest.java +++ b/tests/src/main/java/net/neoforged/neoforge/oldtest/misc/DataPackRegistriesTest.java @@ -81,7 +81,7 @@ public DataPackRegistriesTest(IEventBus modBus) { } } - private void onGatherData(final GatherDataEvent event) { + private void onGatherData(final GatherDataEvent.Client event) { // Example of how to datagen datapack registry objects. // Objects to be datagenerated must be registered (e.g. via DeferredRegister above). // This outputs to data/data_pack_registries_test/data_pack_registries_test/unsyncable/datagen_test.json @@ -95,7 +95,7 @@ private void onGatherData(final GatherDataEvent event) { final String pathString = String.join("/", PackType.SERVER_DATA.getDirectory(), id.getNamespace(), registryId.getNamespace(), registryId.getPath(), id.getPath() + ".json"); final Path path = outputFolder.resolve(pathString); - generator.addProvider(event.includeServer(), new DataProvider() { + generator.addProvider(true, new DataProvider() { @Override public CompletableFuture run(final CachedOutput cache) { return providerCompletableFuture.thenCompose(provider -> { diff --git a/tests/src/main/java/net/neoforged/neoforge/oldtest/world/BiomeModifierTest.java b/tests/src/main/java/net/neoforged/neoforge/oldtest/world/BiomeModifierTest.java index a6a6b19699..1b5aeb7395 100644 --- a/tests/src/main/java/net/neoforged/neoforge/oldtest/world/BiomeModifierTest.java +++ b/tests/src/main/java/net/neoforged/neoforge/oldtest/world/BiomeModifierTest.java @@ -125,8 +125,8 @@ public BiomeModifierTest(IEventBus modBus) { modBus.addListener(this::onGatherData); } - private void onGatherData(GatherDataEvent event) { - event.getGenerator().addProvider(event.includeServer(), (DataProvider.Factory) output -> new BiomeModifiers(output, event.getLookupProvider())); + private void onGatherData(GatherDataEvent.Client event) { + event.getGenerator().addProvider(true, (DataProvider.Factory) output -> new BiomeModifiers(output, event.getLookupProvider())); } private static class BiomeModifiers extends DatapackBuiltinEntriesProvider { diff --git a/tests/src/main/java/net/neoforged/neoforge/oldtest/world/LoginPacketSplitTest.java b/tests/src/main/java/net/neoforged/neoforge/oldtest/world/LoginPacketSplitTest.java index 45235579bd..e4d88fbc1b 100644 --- a/tests/src/main/java/net/neoforged/neoforge/oldtest/world/LoginPacketSplitTest.java +++ b/tests/src/main/java/net/neoforged/neoforge/oldtest/world/LoginPacketSplitTest.java @@ -43,7 +43,7 @@ import net.minecraft.server.packs.PackResources; import net.minecraft.server.packs.PackSelectionConfig; import net.minecraft.server.packs.PackType; -import net.minecraft.server.packs.metadata.MetadataSectionSerializer; +import net.minecraft.server.packs.metadata.MetadataSectionType; import net.minecraft.server.packs.repository.BuiltInPackSource; import net.minecraft.server.packs.repository.Pack; import net.minecraft.server.packs.repository.PackSource; @@ -210,12 +210,14 @@ public Set getNamespaces(PackType type) { @Nullable @Override - public T getMetadataSection(MetadataSectionSerializer section) throws IOException { + public T getMetadataSection(MetadataSectionType section) throws IOException { final JsonObject json = GsonHelper.parse(new String(root.get("pack.mcmeta").get())); - if (!json.has(section.getMetadataSectionName())) { + if (!json.has(section.name())) { return null; } else { - return section.fromJson(GsonHelper.getAsJsonObject(json, section.getMetadataSectionName())); + return section.codec().parse(JsonOps.INSTANCE, json.get(section.name())) + .result() + .orElse(null); } } diff --git a/tests/src/main/java/net/neoforged/neoforge/oldtest/world/StructureModifierTest.java b/tests/src/main/java/net/neoforged/neoforge/oldtest/world/StructureModifierTest.java index 36f3494f6e..4608334011 100644 --- a/tests/src/main/java/net/neoforged/neoforge/oldtest/world/StructureModifierTest.java +++ b/tests/src/main/java/net/neoforged/neoforge/oldtest/world/StructureModifierTest.java @@ -61,7 +61,7 @@ public StructureModifierTest(IEventBus modBus) { modBus.addListener(this::onGatherData); } - private void onGatherData(GatherDataEvent event) { + private void onGatherData(GatherDataEvent.Client event) { /* TODO: During the update to 1.19.3 data providers got partially turned into async executions. Creating a registry ops requires this. // Example of how to datagen datapack registry objects. diff --git a/tests/src/main/resources/META-INF/neoforge.mods.toml b/tests/src/main/resources/META-INF/neoforge.mods.toml index 5de9001098..54ccb00e57 100644 --- a/tests/src/main/resources/META-INF/neoforge.mods.toml +++ b/tests/src/main/resources/META-INF/neoforge.mods.toml @@ -248,5 +248,8 @@ modId="conditional_codec_test" modId="may_fly_attribute_item" [[mods]] modId="client_information_updated_test" +[[mods]] +modId="custom_feature_flags_pack_test" +featureFlags="game_test_feature_flags.json" # ADD ABOVE THIS LINE diff --git a/tests/src/main/resources/assets/custom_fluid_container_test/items/custom_fluid_container.json b/tests/src/main/resources/assets/custom_fluid_container_test/items/custom_fluid_container.json new file mode 100644 index 0000000000..1e8e2a0398 --- /dev/null +++ b/tests/src/main/resources/assets/custom_fluid_container_test/items/custom_fluid_container.json @@ -0,0 +1,10 @@ +{ + "model": { + "type": "neoforge:fluid_container", + "fluid": "minecraft:empty", + "textures": { + "base": "item/bucket", + "fluid": "neoforge:item/mask/bucket_fluid_drip" + } + } +} diff --git a/tests/src/main/resources/assets/custom_fluid_container_test/models/item/custom_fluid_container.json b/tests/src/main/resources/assets/custom_fluid_container_test/models/item/custom_fluid_container.json deleted file mode 100644 index 0376391827..0000000000 --- a/tests/src/main/resources/assets/custom_fluid_container_test/models/item/custom_fluid_container.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "parent": "neoforge:item/default", - "loader": "neoforge:fluid_container", - "fluid": "minecraft:empty", - "cover_is_mask": true, - "textures": { - "base": "item/bucket", - "fluid": "neoforge:item/mask/bucket_fluid_drip", - "cover": "neoforge:item/mask/bucket_fluid_cover_drip" - } -} diff --git a/tests/src/main/resources/assets/neotests_test_material_atlas/atlases/material_test.json b/tests/src/main/resources/assets/neotests_test_material_atlas/atlases/material_test.json new file mode 100644 index 0000000000..fcad3a1669 --- /dev/null +++ b/tests/src/main/resources/assets/neotests_test_material_atlas/atlases/material_test.json @@ -0,0 +1,8 @@ +{ + "sources": [ + { + "type": "minecraft:single", + "resource": "minecraft:block/stone" + } + ] +} diff --git a/tests/src/main/resources/feature_flag_test_packs/flag_test_pack/pack.mcmeta b/tests/src/main/resources/feature_flag_test_packs/flag_test_pack/pack.mcmeta new file mode 100644 index 0000000000..152d1458c8 --- /dev/null +++ b/tests/src/main/resources/feature_flag_test_packs/flag_test_pack/pack.mcmeta @@ -0,0 +1,11 @@ +{ + "features": { + "enabled": [ + "custom_feature_flags_pack_test:test_flag" + ] + }, + "pack": { + "description": "Custom FeatureFlag test pack description", + "pack_format": 57 + } +} diff --git a/tests/src/main/resources/feature_flag_test_packs/flag_test_pack_0/pack.mcmeta b/tests/src/main/resources/feature_flag_test_packs/flag_test_pack_0/pack.mcmeta new file mode 100644 index 0000000000..8dc5f07953 --- /dev/null +++ b/tests/src/main/resources/feature_flag_test_packs/flag_test_pack_0/pack.mcmeta @@ -0,0 +1,11 @@ +{ + "features": { + "enabled": [ + "custom_feature_flags_pack_test:many_flags_0" + ] + }, + "pack": { + "description": "Custom FeatureFlag test pack description", + "pack_format": 57 + } +} diff --git a/tests/src/main/resources/feature_flag_test_packs/flag_test_pack_1/pack.mcmeta b/tests/src/main/resources/feature_flag_test_packs/flag_test_pack_1/pack.mcmeta new file mode 100644 index 0000000000..7f21767982 --- /dev/null +++ b/tests/src/main/resources/feature_flag_test_packs/flag_test_pack_1/pack.mcmeta @@ -0,0 +1,11 @@ +{ + "features": { + "enabled": [ + "custom_feature_flags_pack_test:many_flags_1" + ] + }, + "pack": { + "description": "Custom FeatureFlag test pack description", + "pack_format": 57 + } +} diff --git a/tests/src/main/resources/feature_flag_test_packs/flag_test_pack_2/pack.mcmeta b/tests/src/main/resources/feature_flag_test_packs/flag_test_pack_2/pack.mcmeta new file mode 100644 index 0000000000..dedc001dd8 --- /dev/null +++ b/tests/src/main/resources/feature_flag_test_packs/flag_test_pack_2/pack.mcmeta @@ -0,0 +1,11 @@ +{ + "features": { + "enabled": [ + "custom_feature_flags_pack_test:many_flags_2" + ] + }, + "pack": { + "description": "Custom FeatureFlag test pack description", + "pack_format": 57 + } +} diff --git a/tests/src/main/resources/feature_flag_test_packs/flag_test_pack_3/pack.mcmeta b/tests/src/main/resources/feature_flag_test_packs/flag_test_pack_3/pack.mcmeta new file mode 100644 index 0000000000..2ef12186b9 --- /dev/null +++ b/tests/src/main/resources/feature_flag_test_packs/flag_test_pack_3/pack.mcmeta @@ -0,0 +1,11 @@ +{ + "features": { + "enabled": [ + "custom_feature_flags_pack_test:many_flags_3" + ] + }, + "pack": { + "description": "Custom FeatureFlag test pack description", + "pack_format": 57 + } +} diff --git a/tests/src/main/resources/feature_flag_test_packs/flag_test_pack_4/pack.mcmeta b/tests/src/main/resources/feature_flag_test_packs/flag_test_pack_4/pack.mcmeta new file mode 100644 index 0000000000..310bd97e34 --- /dev/null +++ b/tests/src/main/resources/feature_flag_test_packs/flag_test_pack_4/pack.mcmeta @@ -0,0 +1,11 @@ +{ + "features": { + "enabled": [ + "custom_feature_flags_pack_test:many_flags_4" + ] + }, + "pack": { + "description": "Custom FeatureFlag test pack description", + "pack_format": 57 + } +} diff --git a/tests/src/main/resources/feature_flag_test_packs/flag_test_pack_5/pack.mcmeta b/tests/src/main/resources/feature_flag_test_packs/flag_test_pack_5/pack.mcmeta new file mode 100644 index 0000000000..4672e09934 --- /dev/null +++ b/tests/src/main/resources/feature_flag_test_packs/flag_test_pack_5/pack.mcmeta @@ -0,0 +1,11 @@ +{ + "features": { + "enabled": [ + "custom_feature_flags_pack_test:many_flags_5" + ] + }, + "pack": { + "description": "Custom FeatureFlag test pack description", + "pack_format": 57 + } +} diff --git a/tests/src/main/resources/feature_flag_test_packs/gating_test_pack/pack.mcmeta b/tests/src/main/resources/feature_flag_test_packs/gating_test_pack/pack.mcmeta new file mode 100644 index 0000000000..bc0a6d11ae --- /dev/null +++ b/tests/src/main/resources/feature_flag_test_packs/gating_test_pack/pack.mcmeta @@ -0,0 +1,12 @@ +{ + "features": { + "enabled": [ + "custom_feature_flags_pack_test:many_flags_9", + "custom_feature_flags_pack_test:many_flags_99" + ] + }, + "pack": { + "description": "Custom FeatureFlag gating test pack description", + "pack_format": 57 + } +} diff --git a/tests/src/main/resources/game_test_feature_flags.json b/tests/src/main/resources/game_test_feature_flags.json new file mode 100644 index 0000000000..39a5c9c0ff --- /dev/null +++ b/tests/src/main/resources/game_test_feature_flags.json @@ -0,0 +1,106 @@ +{ + "flags": [ + "custom_feature_flags_pack_test:test_flag", + "custom_feature_flags_pack_test:many_flags_0", + "custom_feature_flags_pack_test:many_flags_1", + "custom_feature_flags_pack_test:many_flags_2", + "custom_feature_flags_pack_test:many_flags_3", + "custom_feature_flags_pack_test:many_flags_4", + "custom_feature_flags_pack_test:many_flags_5", + "custom_feature_flags_pack_test:many_flags_6", + "custom_feature_flags_pack_test:many_flags_7", + "custom_feature_flags_pack_test:many_flags_8", + "custom_feature_flags_pack_test:many_flags_9", + "custom_feature_flags_pack_test:many_flags_10", + "custom_feature_flags_pack_test:many_flags_11", + "custom_feature_flags_pack_test:many_flags_12", + "custom_feature_flags_pack_test:many_flags_13", + "custom_feature_flags_pack_test:many_flags_14", + "custom_feature_flags_pack_test:many_flags_15", + "custom_feature_flags_pack_test:many_flags_16", + "custom_feature_flags_pack_test:many_flags_17", + "custom_feature_flags_pack_test:many_flags_18", + "custom_feature_flags_pack_test:many_flags_19", + "custom_feature_flags_pack_test:many_flags_20", + "custom_feature_flags_pack_test:many_flags_21", + "custom_feature_flags_pack_test:many_flags_22", + "custom_feature_flags_pack_test:many_flags_23", + "custom_feature_flags_pack_test:many_flags_24", + "custom_feature_flags_pack_test:many_flags_25", + "custom_feature_flags_pack_test:many_flags_26", + "custom_feature_flags_pack_test:many_flags_27", + "custom_feature_flags_pack_test:many_flags_28", + "custom_feature_flags_pack_test:many_flags_29", + "custom_feature_flags_pack_test:many_flags_30", + "custom_feature_flags_pack_test:many_flags_31", + "custom_feature_flags_pack_test:many_flags_32", + "custom_feature_flags_pack_test:many_flags_33", + "custom_feature_flags_pack_test:many_flags_34", + "custom_feature_flags_pack_test:many_flags_35", + "custom_feature_flags_pack_test:many_flags_36", + "custom_feature_flags_pack_test:many_flags_37", + "custom_feature_flags_pack_test:many_flags_38", + "custom_feature_flags_pack_test:many_flags_39", + "custom_feature_flags_pack_test:many_flags_40", + "custom_feature_flags_pack_test:many_flags_41", + "custom_feature_flags_pack_test:many_flags_42", + "custom_feature_flags_pack_test:many_flags_43", + "custom_feature_flags_pack_test:many_flags_44", + "custom_feature_flags_pack_test:many_flags_45", + "custom_feature_flags_pack_test:many_flags_46", + "custom_feature_flags_pack_test:many_flags_47", + "custom_feature_flags_pack_test:many_flags_48", + "custom_feature_flags_pack_test:many_flags_49", + "custom_feature_flags_pack_test:many_flags_50", + "custom_feature_flags_pack_test:many_flags_51", + "custom_feature_flags_pack_test:many_flags_52", + "custom_feature_flags_pack_test:many_flags_53", + "custom_feature_flags_pack_test:many_flags_54", + "custom_feature_flags_pack_test:many_flags_55", + "custom_feature_flags_pack_test:many_flags_56", + "custom_feature_flags_pack_test:many_flags_57", + "custom_feature_flags_pack_test:many_flags_58", + "custom_feature_flags_pack_test:many_flags_59", + "custom_feature_flags_pack_test:many_flags_60", + "custom_feature_flags_pack_test:many_flags_61", + "custom_feature_flags_pack_test:many_flags_62", + "custom_feature_flags_pack_test:many_flags_63", + "custom_feature_flags_pack_test:many_flags_64", + "custom_feature_flags_pack_test:many_flags_65", + "custom_feature_flags_pack_test:many_flags_66", + "custom_feature_flags_pack_test:many_flags_67", + "custom_feature_flags_pack_test:many_flags_68", + "custom_feature_flags_pack_test:many_flags_69", + "custom_feature_flags_pack_test:many_flags_70", + "custom_feature_flags_pack_test:many_flags_71", + "custom_feature_flags_pack_test:many_flags_72", + "custom_feature_flags_pack_test:many_flags_73", + "custom_feature_flags_pack_test:many_flags_74", + "custom_feature_flags_pack_test:many_flags_75", + "custom_feature_flags_pack_test:many_flags_76", + "custom_feature_flags_pack_test:many_flags_77", + "custom_feature_flags_pack_test:many_flags_78", + "custom_feature_flags_pack_test:many_flags_79", + "custom_feature_flags_pack_test:many_flags_80", + "custom_feature_flags_pack_test:many_flags_81", + "custom_feature_flags_pack_test:many_flags_82", + "custom_feature_flags_pack_test:many_flags_83", + "custom_feature_flags_pack_test:many_flags_84", + "custom_feature_flags_pack_test:many_flags_85", + "custom_feature_flags_pack_test:many_flags_86", + "custom_feature_flags_pack_test:many_flags_87", + "custom_feature_flags_pack_test:many_flags_88", + "custom_feature_flags_pack_test:many_flags_89", + "custom_feature_flags_pack_test:many_flags_90", + "custom_feature_flags_pack_test:many_flags_91", + "custom_feature_flags_pack_test:many_flags_92", + "custom_feature_flags_pack_test:many_flags_93", + "custom_feature_flags_pack_test:many_flags_94", + "custom_feature_flags_pack_test:many_flags_95", + "custom_feature_flags_pack_test:many_flags_96", + "custom_feature_flags_pack_test:many_flags_97", + "custom_feature_flags_pack_test:many_flags_98", + "custom_feature_flags_pack_test:many_flags_99", + "custom_feature_flags_pack_test:many_flags_100" + ] +}